Browse Source

Use virtual text for printing current diagnostic

TODO:
 - neovim?
 - feature/patch guard
 - tests
 - option
Ben Jackson 2 years ago
parent
commit
002283606d
2 changed files with 58 additions and 30 deletions
  1. 30 13
      python/ycm/diagnostic_interface.py
  2. 28 17
      python/ycm/vimsupport.py

+ 30 - 13
python/ycm/diagnostic_interface.py

@@ -31,7 +31,7 @@ class DiagnosticInterface:
     # Line and column numbers are 1-based
     self._line_to_diags = defaultdict( list )
     self._previous_diag_line_number = -1
-    self._diag_message_needs_clearing = False
+    self._diag_message_needs_clearing = None
 
 
   def OnCursorMoved( self ):
@@ -97,14 +97,17 @@ class DiagnosticInterface:
 
 
   def _EchoDiagnosticForLine( self, line_num ):
+    global YCM_VIM_PROPERTY_ID
+
+    if self._diag_message_needs_clearing is not None:
+      # Clear any previous diag echo
+      vimsupport.RemoveTextProperty( **self._diag_message_needs_clearing )
+      self._diag_message_needs_clearing = None
+
     self._previous_diag_line_number = line_num
 
     diags = self._line_to_diags[ line_num ]
     if not diags:
-      if self._diag_message_needs_clearing:
-        # Clear any previous diag echo
-        vimsupport.PostVimMessage( '', warning = False )
-        self._diag_message_needs_clearing = False
       return
 
     first_diag = diags[ 0 ]
@@ -112,8 +115,21 @@ class DiagnosticInterface:
     if first_diag.get( 'fixit_available', False ):
       text += ' (FixIt)'
 
-    vimsupport.PostVimMessage( text, warning = False, truncate = True )
-    self._diag_message_needs_clearing = True
+    self._diag_message_needs_clearing = {
+      'buffer_number': self._bufnr,
+      'prop_id': vimsupport.AddTextProperty(
+        self._bufnr,
+        line_num,
+        0,
+        'YcmErrorProperty',
+        {
+          'text': '    ' + ' '.join( text.splitlines() ),
+          'text_align': 'right',
+          'text_wrap': 'wrap'
+        } ),
+      'line_num': line_num,
+      'prop_type': 'YcmErrorProperty'
+    }
 
 
   def _DiagnosticsCount( self, predicate ):
@@ -143,9 +159,8 @@ class DiagnosticInterface:
             diag ):
           global YCM_VIM_PROPERTY_ID
 
-          # FIXME: This remove() gambit probably never works because the IDs are
-          # almost certain to not match
-          # Perhaps we should have AddTextProperty return the ID?
+          # Note the following .remove() works because the __eq__ on
+          # DiagnosticProperty does not actually check the IDs match...
           diag_prop = vimsupport.DiagnosticProperty(
               YCM_VIM_PROPERTY_ID,
               name,
@@ -155,15 +170,17 @@ class DiagnosticInterface:
           try:
             props_to_remove.remove( diag_prop )
           except ValueError:
+            extras.update( {
+              'id': YCM_VIM_PROPERTY_ID
+            } )
             vimsupport.AddTextProperty( self._bufnr,
                                         line,
                                         column,
                                         name,
-                                        extras,
-                                        YCM_VIM_PROPERTY_ID )
+                                        extras )
           YCM_VIM_PROPERTY_ID += 1
     for prop in props_to_remove:
-      vimsupport.RemoveTextProperty( self._bufnr, prop )
+      vimsupport.RemoveDiagnosticProperty( self._bufnr, prop )
 
 
   def _UpdateSigns( self ):

+ 28 - 17
python/ycm/vimsupport.py

@@ -341,14 +341,15 @@ def AddTextProperty( buffer_number,
                      line,
                      column,
                      prop_type,
-                     extra_args,
-                     prop_id ):
+                     extra_args ):
   if not VimIsNeovim():
     extra_args.update( {
       'type': prop_type,
-      'bufnr': buffer_number,
-      'id': prop_id } )
-    vim.eval( f'prop_add( { line }, { column }, { extra_args } )' )
+      'bufnr': buffer_number
+    } )
+    return GetIntValue(
+      vim.eval( f'prop_add( { line }, { column }, { extra_args } )' )
+    )
   else:
     extra_args[ 'hl_group' ] = prop_type
     # Neovim uses 0-based offsets
@@ -358,25 +359,35 @@ def AddTextProperty( buffer_number,
       extra_args[ 'end_col' ] = extra_args.pop( 'end_col' ) - 1
     line -= 1
     column -= 1
-    vim.eval( f'nvim_buf_set_extmark( { buffer_number }, '
-                                    f'{ YCM_NEOVIM_NS_ID }, '
-                                    f'{ line }, '
-                                    f'{ column }, '
-                                    f'{ extra_args } )' )
+    return GetIntValue( 
+      vim.eval( f'nvim_buf_set_extmark( { buffer_number }, '
+                                         f'{ YCM_NEOVIM_NS_ID }, '
+                                         f'{ line }, '
+                                         f'{ column }, '
+                                         f'{ extra_args } )' ) )
+
+
+def RemoveDiagnosticProperty( buffer_number: int, prop: DiagnosticProperty ):
+  RemoveTextProperty( buffer_number,
+                      prop.line,
+                      prop.id,
+                      prop.type )
 
 
-def RemoveTextProperty( buffer_number: int, prop: DiagnosticProperty ):
+def RemoveTextProperty( buffer_number, line_num, prop_id, prop_type ):
   if not VimIsNeovim():
     p = {
-        'bufnr': buffer_number,
-        'id': prop.id,
-        'type': prop.type,
-        'both': 1 }
-    vim.eval( f'prop_remove( { p } )' )
+      'bufnr': buffer_number,
+      'id': prop_id,
+      'type': prop_type,
+      'both': 1,
+      'all': 1
+    }
+    vim.eval( f'prop_remove( { p }, { line_num } )' )
   else:
     vim.eval( f'nvim_buf_del_extmark( { buffer_number }, '
                                     f'{ YCM_NEOVIM_NS_ID }, '
-                                    f'{ prop.id } )' )
+                                    f'{ prop_id } )' )
 
 
 # Clamps the line and column numbers so that they are not past the contents of