From 05552efd19fa9d2b323cd777d9543ae9ed094e7d Mon Sep 17 00:00:00 2001 From: Strahinja Val Markovic Date: Sun, 5 Jan 2014 16:09:40 -0800 Subject: [PATCH] Now highlighting the full identifier for diag Instead of just underlining the first char of an identifier, we now underline. the full identifier. --- cpp/ycm/ClangCompleter/ClangHelpers.cpp | 46 +++++++++++++++++++-- cpp/ycm/ClangCompleter/ClangHelpers.h | 3 +- cpp/ycm/ClangCompleter/Diagnostic.h | 2 + cpp/ycm/ClangCompleter/Range.h | 1 + cpp/ycm/ClangCompleter/TranslationUnit.cpp | 5 ++- cpp/ycm/ycm_core.cpp | 1 + python/ycm/diagnostic_interface.py | 10 +++-- python/ycm/server/responses.py | 1 + python/ycm/server/tests/diagnostics_test.py | 45 +++++++++++++++++++- python/ycm/vimsupport.py | 6 +-- 10 files changed, 107 insertions(+), 13 deletions(-) diff --git a/cpp/ycm/ClangCompleter/ClangHelpers.cpp b/cpp/ycm/ClangCompleter/ClangHelpers.cpp index abfaeb4f..5f4ad07e 100644 --- a/cpp/ycm/ClangCompleter/ClangHelpers.cpp +++ b/cpp/ycm/ClangCompleter/ClangHelpers.cpp @@ -107,6 +107,42 @@ std::vector< Range > GetRanges( const DiagnosticWrap &diagnostic_wrap ) { return ranges; } + +Range GetLocationExtent( CXSourceLocation source_location, + CXTranslationUnit translation_unit ) { + // If you think the below code is an idiotic way of getting the source range + // for an identifier at a specific source location, you are not the only one. + // I cannot believe that this is the only way to achieve this with the + // libclang API in a robust way. + // I've tried many simpler ways of doing this and they all fail in various + // situations. + + CXSourceRange range = clang_getCursorExtent( + clang_getCursor( translation_unit, source_location ) ); + CXToken *tokens; + uint num_tokens; + clang_tokenize( translation_unit, range, &tokens, &num_tokens ); + + Location location( source_location ); + Range final_range; + for ( uint i = 0; i < num_tokens; ++i ) { + Location token_location( clang_getTokenLocation( translation_unit, + tokens[ i ] ) ); + if ( token_location == location ) { + std::string name = CXStringToString( + clang_getTokenSpelling( translation_unit, tokens[ i ] ) ); + Location end_location = location; + end_location.column_number_ += name.length(); + final_range = Range( location, end_location ); + break; + } + } + + clang_disposeTokens( translation_unit, tokens, num_tokens ); + return final_range; +} + + } // unnamed namespace std::vector< CXUnsavedFile > ToCXUnsavedFiles( @@ -164,7 +200,8 @@ std::vector< CompletionData > ToCompletionDataVector( } -Diagnostic DiagnosticWrapToDiagnostic( DiagnosticWrap diagnostic_wrap ) { +Diagnostic BuildDiagnostic( DiagnosticWrap diagnostic_wrap, + CXTranslationUnit translation_unit ) { Diagnostic diagnostic; if ( !diagnostic_wrap ) @@ -178,8 +215,11 @@ Diagnostic DiagnosticWrapToDiagnostic( DiagnosticWrap diagnostic_wrap ) { if ( diagnostic.kind_ == 'I' ) return diagnostic; - diagnostic.location_ = Location( - clang_getDiagnosticLocation( diagnostic_wrap.get() ) ); + CXSourceLocation source_location = + clang_getDiagnosticLocation( diagnostic_wrap.get() ); + diagnostic.location_ = Location( source_location ); + diagnostic.location_extent_ = GetLocationExtent( source_location, + translation_unit ); diagnostic.ranges_ = GetRanges( diagnostic_wrap ); diagnostic.text_ = CXStringToString( clang_getDiagnosticSpelling( diagnostic_wrap.get() ) ); diff --git a/cpp/ycm/ClangCompleter/ClangHelpers.h b/cpp/ycm/ClangCompleter/ClangHelpers.h index da2009ca..6ddb385a 100644 --- a/cpp/ycm/ClangCompleter/ClangHelpers.h +++ b/cpp/ycm/ClangCompleter/ClangHelpers.h @@ -40,7 +40,8 @@ std::vector< CompletionData > ToCompletionDataVector( std::vector< CXUnsavedFile > ToCXUnsavedFiles( const std::vector< UnsavedFile > &unsaved_files ); -Diagnostic DiagnosticWrapToDiagnostic( DiagnosticWrap diagnostic_wrap ); +Diagnostic BuildDiagnostic( DiagnosticWrap diagnostic_wrap, + CXTranslationUnit translation_unit ); } // namespace YouCompleteMe diff --git a/cpp/ycm/ClangCompleter/Diagnostic.h b/cpp/ycm/ClangCompleter/Diagnostic.h index 9a1ad7e9..d84b07c9 100644 --- a/cpp/ycm/ClangCompleter/Diagnostic.h +++ b/cpp/ycm/ClangCompleter/Diagnostic.h @@ -37,6 +37,8 @@ struct Diagnostic { Location location_; + Range location_extent_; + std::vector< Range > ranges_; // Vim's error "kind" diff --git a/cpp/ycm/ClangCompleter/Range.h b/cpp/ycm/ClangCompleter/Range.h index d6d63a72..e4830fa6 100644 --- a/cpp/ycm/ClangCompleter/Range.h +++ b/cpp/ycm/ClangCompleter/Range.h @@ -23,6 +23,7 @@ namespace YouCompleteMe { +// Half-open, [start, end> struct Range { Range() {} diff --git a/cpp/ycm/ClangCompleter/TranslationUnit.cpp b/cpp/ycm/ClangCompleter/TranslationUnit.cpp index 1c48db54..3c2e8974 100644 --- a/cpp/ycm/ClangCompleter/TranslationUnit.cpp +++ b/cpp/ycm/ClangCompleter/TranslationUnit.cpp @@ -270,9 +270,10 @@ void TranslationUnit::UpdateLatestDiagnostics() { for ( uint i = 0; i < num_diagnostics; ++i ) { Diagnostic diagnostic = - DiagnosticWrapToDiagnostic( + BuildDiagnostic( DiagnosticWrap( clang_getDiagnostic( clang_translation_unit_, i ), - clang_disposeDiagnostic ) ); + clang_disposeDiagnostic ), + clang_translation_unit_ ); if ( diagnostic.kind_ != 'I' ) latest_diagnostics_.push_back( diagnostic ); diff --git a/cpp/ycm/ycm_core.cpp b/cpp/ycm/ycm_core.cpp index 0d7c6ac7..a5d55dc1 100644 --- a/cpp/ycm/ycm_core.cpp +++ b/cpp/ycm/ycm_core.cpp @@ -128,6 +128,7 @@ BOOST_PYTHON_MODULE(ycm_core) class_< Diagnostic >( "Diagnostic" ) .def_readonly( "ranges_", &Diagnostic::ranges_ ) .def_readonly( "location_", &Diagnostic::location_ ) + .def_readonly( "location_extent_", &Diagnostic::location_extent_ ) .def_readonly( "kind_", &Diagnostic::kind_ ) .def_readonly( "text_", &Diagnostic::text_ ) .def_readonly( "long_formatted_text_", &Diagnostic::long_formatted_text_ ); diff --git a/python/ycm/diagnostic_interface.py b/python/ycm/diagnostic_interface.py index e77f7146..c44ee0f2 100644 --- a/python/ycm/diagnostic_interface.py +++ b/python/ycm/diagnostic_interface.py @@ -62,14 +62,18 @@ def _UpdateSquiggles( buffer_number_to_line_to_diags ): for diags in line_to_diags.itervalues(): for diag in diags: - location = diag[ 'location' ] + location_extent = diag[ 'location_extent' ] is_error = _DiagnosticIsError( diag ) vimsupport.AddDiagnosticSyntaxMatch( - location[ 'line_num' ] + 1, - location[ 'column_num' ] + 1, + location_extent[ 'start' ][ 'line_num' ] + 1, + location_extent[ 'start' ][ 'column_num' ] + 1, + location_extent[ 'end' ][ 'line_num' ] + 1, + location_extent[ 'end' ][ 'column_num' ] + 1, is_error = is_error ) + vimsupport.EchoText( diag ) + for diag_range in diag[ 'ranges' ]: vimsupport.AddDiagnosticSyntaxMatch( diag_range[ 'start' ][ 'line_num' ] + 1, diff --git a/python/ycm/server/responses.py b/python/ycm/server/responses.py index 88b97ffd..ae86d935 100644 --- a/python/ycm/server/responses.py +++ b/python/ycm/server/responses.py @@ -118,6 +118,7 @@ def BuildDiagnosticData( diagnostic ): return { 'ranges': [ BuildRangeData( x ) for x in diagnostic.ranges_ ], 'location': BuildLocationData( diagnostic.location_ ), + 'location_extent': BuildRangeData( diagnostic.location_extent_ ), 'text': diagnostic.text_, 'kind': diagnostic.kind_ } diff --git a/python/ycm/server/tests/diagnostics_test.py b/python/ycm/server/tests/diagnostics_test.py index f426df51..90c00eae 100644 --- a/python/ycm/server/tests/diagnostics_test.py +++ b/python/ycm/server/tests/diagnostics_test.py @@ -48,7 +48,6 @@ void foo() { filetype = 'cpp' ) results = app.post_json( '/event_notification', event_data ).json - print results assert_that( results, contains( has_entries( { @@ -66,9 +65,53 @@ void foo() { 'location': has_entries( { 'line_num': 2, 'column_num': 9 + } ), + 'location_extent': has_entries( { + 'start': has_entries( { + 'line_num': 2, + 'column_num': 9, + } ), + 'end': has_entries( { + 'line_num': 2, + 'column_num': 12, + } ), } ) } ) ) ) + +@with_setup( Setup ) +def Diagnostics_ClangCompleter_SimpleLocationExtent_test(): + app = TestApp( handlers.app ) + contents = """ +void foo() { + baz = 5; +} +// Padding to 5 lines +// Padding to 5 lines +""" + + event_data = BuildRequest( compilation_flags = ['-x', 'c++'], + event_name = 'FileReadyToParse', + contents = contents, + filetype = 'cpp' ) + + results = app.post_json( '/event_notification', event_data ).json + assert_that( results, + contains( + has_entries( { + 'location_extent': has_entries( { + 'start': has_entries( { + 'line_num': 2, + 'column_num': 2, + } ), + 'end': has_entries( { + 'line_num': 2, + 'column_num': 5, + } ), + } ) + } ) ) ) + + @with_setup( Setup ) def Diagnostics_ClangCompleter_PragmaOnceWarningIgnored_test(): app = TestApp( handlers.app ) diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py index f269dd53..ff57840b 100644 --- a/python/ycm/vimsupport.py +++ b/python/ycm/vimsupport.py @@ -143,7 +143,7 @@ def AddDiagnosticSyntaxMatch( line_num, "matchadd('{0}', '\%{1}l\%{2}c')".format( group, line_num, column_num ) ) else: return GetIntValue( - "matchadd('{0}', '\%{1}l\%{2}c.*\%{3}l\%{4}c')".format( + "matchadd('{0}', '\%{1}l\%{2}c\_.*\%{3}l\%{4}c')".format( group, line_num, column_num, line_end_num, column_end_num ) ) @@ -253,14 +253,14 @@ def EchoText( text, log_as_message = True ): command = 'echom' if log_as_message else 'echo' vim.command( "{0} '{1}'".format( command, EscapeForVim( text ) ) ) - for line in text.split( '\n' ): + for line in str( text ).split( '\n' ): EchoLine( line ) # Echos text but truncates the text so that it all fits on one line def EchoTextVimWidth( text ): vim_width = GetIntValue( '&columns' ) - truncated_text = text[ : int( vim_width * 0.9 ) ] + truncated_text = str( text )[ : int( vim_width * 0.9 ) ] truncated_text.replace( '\n', ' ' ) EchoText( truncated_text, False )