Browse Source

Now highlighting the full identifier for diag

Instead of just underlining the first char of an identifier, we now underline.
the full identifier.
Strahinja Val Markovic 11 years ago
parent
commit
05552efd19

+ 43 - 3
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() ) );

+ 2 - 1
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
 

+ 2 - 0
cpp/ycm/ClangCompleter/Diagnostic.h

@@ -37,6 +37,8 @@ struct Diagnostic {
 
   Location location_;
 
+  Range location_extent_;
+
   std::vector< Range > ranges_;
 
   // Vim's error "kind"

+ 1 - 0
cpp/ycm/ClangCompleter/Range.h

@@ -23,6 +23,7 @@
 
 namespace YouCompleteMe {
 
+// Half-open, [start, end>
 struct Range {
   Range() {}
 

+ 3 - 2
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 );

+ 1 - 0
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_ );

+ 7 - 3
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,

+ 1 - 0
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_
   }

+ 44 - 1
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 )

+ 3 - 3
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 )