Browse Source

Inlay hints: don't always request on scroll, only when we hit the bounds of our requested window. this reduces stutter when scrolling

Ben Jackson 2 years ago
parent
commit
5495d8fafb
3 changed files with 43 additions and 13 deletions
  1. 10 7
      autoload/youcompleteme.vim
  2. 20 6
      python/ycm/inlay_hints.py
  3. 13 0
      python/ycm/vimsupport.py

+ 10 - 7
autoload/youcompleteme.vim

@@ -788,7 +788,7 @@ function! s:OnFileReadyToParse( ... )
           \ function( 's:PollFileParseResponse' ) )
 
     call s:UpdateSemanticHighlighting( bufnr() )
-    call s:UpdateInlayHints( bufnr() )
+    call s:UpdateInlayHints( bufnr(), 1 )
 
   endif
 endfunction
@@ -809,16 +809,19 @@ function! s:UpdateSemanticHighlighting( bufnr ) abort
 endfunction
 
 
-function! s:UpdateInlayHints( bufnr )
+function! s:UpdateInlayHints( bufnr, force )
   call s:StopPoller( s:pollers.inlay_hints )
   if s:enable_inlay_hints &&
         \ get( b:, 'ycm_enable_inlay_hints',
         \   get( g:, 'ycm_enable_inlay_hints', 0 ) )
 
-    py3 ycm_state.Buffer( int( vim.eval( 'a:bufnr' ) ) ).inlay_hints.Request()
-    let s:pollers.inlay_hints.id = timer_start(
-          \ s:pollers.inlay_hints.wait_milliseconds,
-          \ function( 's:PollInlayHints', [ a:bufnr ] ) )
+    if py3eval(
+        \ 'ycm_state.Buffer( int( vim.eval( "a:bufnr" ) ) ).'
+        \ . 'inlay_hints.Request( force=int( vim.eval( "a:force" ) ) )' )
+      let s:pollers.inlay_hints.id = timer_start(
+            \ s:pollers.inlay_hints.wait_milliseconds,
+            \ function( 's:PollInlayHints', [ a:bufnr ] ) )
+    endif
 
   endif
 endfunction
@@ -930,7 +933,7 @@ function! s:OnWinScrolled()
   endif
   let bufnr = winbufnr( expand( '<afile>' ) )
   call s:UpdateSemanticHighlighting( bufnr )
-  call s:UpdateInlayHints( bufnr )
+  call s:UpdateInlayHints( bufnr, 0 )
 endfunction
 
 

+ 20 - 6
python/ycm/inlay_hints.py

@@ -64,17 +64,31 @@ class InlayHints:
   def __init__( self, bufnr, user_options ):
     self._request = None
     self._bufnr = bufnr
-    self._prop_ids = set()
     self.tick = -1
     self._latest_inlay_hints = []
+    self._last_requested_range = None
 
 
-  def Request( self ):
+  def Request( self, force=False ):
     if self._request and not self.Ready():
-      return
+      return True
+
+    # Check to see if the buffer ranges would actually change anything visible.
+    # This avoids a round-trip for every single line scroll event
+    if ( not force and
+         self.tick == vimsupport.GetBufferChangedTick( self._bufnr ) and
+         vimsupport.VisibleRangeOfBufferOverlaps(
+           self._bufnr,
+           self._last_requested_range ) ):
+      return False # don't poll
 
     # We're requesting changes, so the existing results are now invalid
     self._latest_inlay_hints = []
+    # FIXME: This call is duplicated in the call to VisibleRangeOfBufferOverlaps
+    #  - remove the expansion param
+    #  - look up the actual visible range, then call this function
+    #  - if not overlapping, do the factor expansion and request
+    self._last_requested_range = vimsupport.RangeVisibleInBuffer( self._bufnr )
     self.tick = vimsupport.GetBufferChangedTick( self._bufnr )
 
     # TODO: How to determine the range to display ? Should we do the range
@@ -85,10 +99,11 @@ class InlayHints:
     # Perhaps the maximal range of visible windows or something.
     request_data = BuildRequestData( self._bufnr )
     request_data.update( {
-      'range': vimsupport.RangeVisibleInBuffer( self._bufnr )
+      'range': self._last_requested_range
     } )
     self._request = InlayHintsRequest( request_data )
     self._request.Start()
+    return True
 
 
   def Ready( self ):
@@ -116,13 +131,12 @@ class InlayHints:
 
     # We're ready to use this response. Clear it (to avoid repeatedly
     # re-polling).
-    self._latest_inlay_hints = [] # in case there was an error in request
     self._latest_inlay_hints = self._request.Response()
     self._request = None
 
     if self.tick != vimsupport.GetBufferChangedTick( self._bufnr ):
       # Buffer has changed, we should ignore the data and retry
-      self.Request()
+      self.Request( force=True )
       return False # poll again
 
     self._Draw()

+ 13 - 0
python/ycm/vimsupport.py

@@ -237,6 +237,19 @@ def RangeVisibleInBuffer( bufnr, grow_factor=0.5 ):
   }
 
 
+def VisibleRangeOfBufferOverlaps( bufnr, expanded_range ):
+  visible_range = RangeVisibleInBuffer( bufnr, 0 )
+  # As above, we ignore horizontal scroll and only check lines
+  return (
+    expanded_range is not None and
+    visible_range is not None and
+    visible_range[ 'start' ][ 'line_num' ]
+      >= expanded_range[ 'start' ][ 'line_num' ] and
+    visible_range[ 'end' ][ 'line_num' ]
+      <= expanded_range[ 'end' ][ 'line_num' ]
+  )
+
+
 def CaptureVimCommand( command ):
   vim.command( 'redir => b:ycm_command' )
   vim.command( f'silent! { command }' )