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> inoremap <unique> <C-Space> <C-X><C-O><C-P>
endif 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 " 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 " the first loaded file. This should be the last command executed in this
" function! " function!
@ -350,6 +353,11 @@ function! youcompleteme#OmniComplete( findstart, base )
endfunction endfunction
function! s:ShowDetailedDiagnostic()
py ycm_state.ShowDetailedDiagnostic()
endfunction
" This is what Syntastic calls indirectly when it decides an auto-check is " 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 " required (currently that's on buffer save) OR when the SyntasticCheck command
" is invoked " is invoked
@ -357,6 +365,7 @@ function! youcompleteme#CurrentFileDiagnostics()
return pyeval( 'ycm_state.GetDiagnosticsForCurrentFile()' ) return pyeval( 'ycm_state.GetDiagnosticsForCurrentFile()' )
endfunction endfunction
" This is basic vim plugin boilerplate " This is basic vim plugin boilerplate
let &cpo = s:save_cpo let &cpo = s:save_cpo
unlet 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 CXDiagnosticToDiagnostic( CXDiagnostic cxdiagnostic )
{ {
Diagnostic diagnostic; Diagnostic diagnostic;
@ -145,9 +177,11 @@ Diagnostic CXDiagnosticToDiagnostic( CXDiagnostic cxdiagnostic )
&diagnostic.line_number_, &diagnostic.line_number_,
&diagnostic.column_number_, &diagnostic.column_number_,
&unused_offset ); &unused_offset );
diagnostic.filename_ = CXStringToString( clang_getFileName( file ) ); diagnostic.filename_ = CXStringToString( clang_getFileName( file ) );
diagnostic.text_ = CXStringToString( diagnostic.text_ = CXStringToString(
clang_getDiagnosticSpelling( cxdiagnostic ) ); clang_getDiagnosticSpelling( cxdiagnostic ) );
diagnostic.long_formatted_text_ = FullDiagnosticText( cxdiagnostic );
clang_disposeDiagnostic( cxdiagnostic ); clang_disposeDiagnostic( cxdiagnostic );
return diagnostic; return diagnostic;

View File

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

View File

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

View File

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

View File

@ -18,6 +18,7 @@
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>. # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from completers.completer import Completer from completers.completer import Completer
from collections import defaultdict
import vim import vim
import vimsupport import vimsupport
import ycm_core import ycm_core
@ -32,7 +33,7 @@ class ClangCompleter( Completer ):
self.completer.EnableThreading() self.completer.EnableThreading()
self.contents_holder = [] self.contents_holder = []
self.filename_holder = [] self.filename_holder = []
self.last_diagnostics = [] self.last_prepared_diagnostics = []
self.parse_future = None self.parse_future = None
self.flags = Flags() self.flags = Flags()
@ -127,11 +128,38 @@ class ClangCompleter( Completer ):
def GetDiagnosticsForCurrentFile( self ): def GetDiagnosticsForCurrentFile( self ):
if self.DiagnosticsForCurrentFileReady(): if self.DiagnosticsForCurrentFileReady():
self.last_diagnostics = [ DiagnosticToDict( x ) for x in diagnostics = self.completer.DiagnosticsForFile( vim.current.buffer.name )
self.completer.DiagnosticsForFile( self.diagnostic_store = DiagnosticsToDiagStructure( diagnostics )
vim.current.buffer.name ) ] self.last_prepared_diagnostics = [ DiagnosticToDict( x ) for x in
diagnostics ]
self.parse_future = None 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 ): 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 ): def ClangAvailableForBuffer( buffer_object ):
filetype = vim.eval( 'getbufvar({0}, "&ft")'.format( buffer_object.number ) ) filetype = vim.eval( 'getbufvar({0}, "&ft")'.format( buffer_object.number ) )
return filetype in CLANG_FILETYPES return filetype in CLANG_FILETYPES

View File

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

View File

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