Support for showing extra info for diagstics

This was intended to show the full clang output for a given diagnostic,
including notes. But it appears that libclang does not provide this
functionality...
This commit is contained in:
Strahinja Val Markovic 2012-08-15 19:39:03 -07:00
parent a4d344aa36
commit daef17feb4
8 changed files with 105 additions and 8 deletions

View File

@ -74,6 +74,9 @@ function! youcompleteme#Enable()
inoremap <unique> <C-Space> <C-X><C-O><C-P>
endif
" TODO: make this a nicer, customizable map
nnoremap <unique> <leader>d :call <sid>ShowDetailedDiagnostic()<cr>
" Calling this once solves the problem of BufRead/BufEnter not triggering for
" the first loaded file. This should be the last command executed in this
" function!
@ -350,6 +353,11 @@ function! youcompleteme#OmniComplete( findstart, base )
endfunction
function! s:ShowDetailedDiagnostic()
py ycm_state.ShowDetailedDiagnostic()
endfunction
" This is what Syntastic calls indirectly when it decides an auto-check is
" required (currently that's on buffer save) OR when the SyntasticCheck command
" is invoked
@ -357,6 +365,7 @@ function! youcompleteme#CurrentFileDiagnostics()
return pyeval( 'ycm_state.GetDiagnosticsForCurrentFile()' )
endfunction
" This is basic vim plugin boilerplate
let &cpo = s:save_cpo
unlet s:save_cpo

View File

@ -123,6 +123,38 @@ std::vector< CompletionData > ToCompletionDataVector(
}
// NOTE: The passed in pointer should never be NULL!
// TODO: move all functions that are not external into an unnamed namespace
std::string FullDiagnosticText( CXDiagnostic cxdiagnostic )
{
std::string full_text = CXStringToString( clang_formatDiagnostic(
cxdiagnostic,
clang_defaultDiagnosticDisplayOptions() ) );
// Note: clang docs say that a CXDiagnosticSet retrieved with
// clang_getChildDiagnostics do NOT need to be released with
// clang_diposeDiagnosticSet
CXDiagnosticSet diag_set = clang_getChildDiagnostics( cxdiagnostic );
if ( !diag_set )
return full_text;
uint num_child_diagnostics = clang_getNumDiagnosticsInSet( diag_set );
if ( !num_child_diagnostics )
return full_text;
for ( uint i = 0; i < num_child_diagnostics; ++i )
{
CXDiagnostic diagnostic = clang_getDiagnosticInSet( diag_set, i );
if ( !diagnostic )
continue;
full_text.append( FullDiagnosticText( diagnostic ) );
}
return full_text;
}
Diagnostic CXDiagnosticToDiagnostic( CXDiagnostic cxdiagnostic )
{
Diagnostic diagnostic;
@ -145,9 +177,11 @@ Diagnostic CXDiagnosticToDiagnostic( CXDiagnostic cxdiagnostic )
&diagnostic.line_number_,
&diagnostic.column_number_,
&unused_offset );
diagnostic.filename_ = CXStringToString( clang_getFileName( file ) );
diagnostic.text_ = CXStringToString(
clang_getDiagnosticSpelling( cxdiagnostic ) );
diagnostic.long_formatted_text_ = FullDiagnosticText( cxdiagnostic );
clang_disposeDiagnostic( cxdiagnostic );
return diagnostic;

View File

@ -47,6 +47,8 @@ struct Diagnostic
std::string filename_;
std::string text_;
std::string long_formatted_text_;
};
} // namespace YouCompleteMe

View File

@ -54,7 +54,8 @@ BOOST_PYTHON_MODULE(ycm_core)
.def_readonly( "column_number_", &Diagnostic::column_number_ )
.def_readonly( "kind_", &Diagnostic::kind_ )
.def_readonly( "filename_", &Diagnostic::filename_ )
.def_readonly( "text_", &Diagnostic::text_ );
.def_readonly( "text_", &Diagnostic::text_ )
.def_readonly( "long_formatted_text_", &Diagnostic::long_formatted_text_ );
class_< std::vector< Diagnostic > >( "DiagnosticVec" )
.def( vector_indexing_suite< std::vector< Diagnostic > >() );

View File

@ -78,6 +78,10 @@ class Completer( object ):
return []
def ShowDetailedDiagnostic( self ):
pass
@abc.abstractmethod
def SupportedFiletypes( self ):
pass

View File

@ -18,6 +18,7 @@
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from completers.completer import Completer
from collections import defaultdict
import vim
import vimsupport
import ycm_core
@ -32,7 +33,7 @@ class ClangCompleter( Completer ):
self.completer.EnableThreading()
self.contents_holder = []
self.filename_holder = []
self.last_diagnostics = []
self.last_prepared_diagnostics = []
self.parse_future = None
self.flags = Flags()
@ -127,11 +128,38 @@ class ClangCompleter( Completer ):
def GetDiagnosticsForCurrentFile( self ):
if self.DiagnosticsForCurrentFileReady():
self.last_diagnostics = [ DiagnosticToDict( x ) for x in
self.completer.DiagnosticsForFile(
vim.current.buffer.name ) ]
diagnostics = self.completer.DiagnosticsForFile( vim.current.buffer.name )
self.diagnostic_store = DiagnosticsToDiagStructure( diagnostics )
self.last_prepared_diagnostics = [ DiagnosticToDict( x ) for x in
diagnostics ]
self.parse_future = None
return self.last_diagnostics
return self.last_prepared_diagnostics
def ShowDetailedDiagnostic( self ):
current_line, current_column = vimsupport.CurrentLineAndColumn()
# CurrentLineAndColumn() numbers are 0-based, clang numbers are 1-based
current_line += 1
current_column += 1
current_file = vim.current.buffer.name
diagnostics = self.diagnostic_store[ current_file ][ current_line ]
if not diagnostics:
vimsupport.PostVimMessage( "No diagnostic for current line!" )
return
closest_diagnostic = None
distance_to_closest_diagnostic = 999
for diagnostic in diagnostics:
distance = abs( current_column - diagnostic.column_number_ )
if distance < distance_to_closest_diagnostic:
distance_to_closest_diagnostic = distance
closest_diagnostic = diagnostic
vimsupport.EchoText( closest_diagnostic.long_formatted_text_ )
def ShouldUseNow( self, start_column ):
@ -165,6 +193,14 @@ def DiagnosticToDict( diagnostic ):
}
def DiagnosticsToDiagStructure( diagnostics ):
structure = defaultdict(lambda : defaultdict(list))
for diagnostic in diagnostics:
structure[ diagnostic.filename_ ][ diagnostic.line_number_ ].append(
diagnostic )
return structure
def ClangAvailableForBuffer( buffer_object ):
filetype = vim.eval( 'getbufvar({0}, "&ft")'.format( buffer_object.number ) )
return filetype in CLANG_FILETYPES

View File

@ -20,6 +20,7 @@
import vim
def CurrentLineAndColumn():
"""Returns the 0-based current line."""
# See the comment in CurrentColumn about the calculation for the line and
# column number
line, column = vim.current.window.cursor
@ -28,8 +29,9 @@ def CurrentLineAndColumn():
def CurrentColumn():
"""Do NOT access the CurrentColumn in vim.current.line. It doesn't exist yet.
Only the chars before the current column exist in vim.current.line."""
"""Returns the 0-based current column. Do NOT access the CurrentColumn in
vim.current.line. It doesn't exist yet. Only the chars before the current
column exist in vim.current.line."""
# vim's columns are 1-based while vim.current.line columns are 0-based
# ... but vim.current.window.cursor (which returns a (line, column) tuple)
@ -58,6 +60,10 @@ def PostVimMessage( message ):
.format( message ) )
def EchoText( text ):
vim.command( "echom '{0}'".format( text.replace( "'", r"''") ) )
def EscapeForVim( text ):
return text.replace( "'", "''" )

View File

@ -106,6 +106,11 @@ class YouCompleteMe( object ):
return []
def ShowDetailedDiagnostic( self ):
if self.FiletypeCompletionEnabledForCurrentFile():
return self.GetFiletypeCompleterForCurrentFile().ShowDetailedDiagnostic()
def OnCurrentIdentifierFinished( self ):
self.identcomp.OnCurrentIdentifierFinished()