Parcourir la 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 il y a 11 ans
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 )