浏览代码

Support sending a range for inlay_hints and semantic tokens

We use the full range of all visible buffers, which is kind of weak but
we model this all at buffer level not window level.

Note that clangd doesn't support range tokens request so this might not
work well yet.
Ben Jackson 2 年之前
父节点
当前提交
eb6ac04071
共有 5 个文件被更改,包括 117 次插入40 次删除
  1. 55 28
      autoload/youcompleteme.vim
  2. 1 10
      python/ycm/inlay_hints.py
  3. 5 1
      python/ycm/semantic_highlighting.py
  4. 51 0
      python/ycm/vimsupport.py
  5. 5 1
      python/ycm/youcompleteme.py

+ 55 - 28
autoload/youcompleteme.vim

@@ -236,6 +236,7 @@ function! youcompleteme#EnableCursorMovedAutocommands()
     autocmd TextChangedI * call s:OnTextChangedInsertMode( v:false )
     autocmd TextChangedI * call s:OnTextChangedInsertMode( v:false )
     autocmd TextChangedP * call s:OnTextChangedInsertMode( v:true )
     autocmd TextChangedP * call s:OnTextChangedInsertMode( v:true )
     autocmd InsertCharPre * call s:OnInsertChar()
     autocmd InsertCharPre * call s:OnInsertChar()
+    autocmd WinScrolled * call s:OnWinScrolled()
   augroup END
   augroup END
 endfunction
 endfunction
 
 
@@ -768,29 +769,39 @@ function! s:OnFileReadyToParse( ... )
           \ s:pollers.file_parse_response.wait_milliseconds,
           \ s:pollers.file_parse_response.wait_milliseconds,
           \ function( 's:PollFileParseResponse' ) )
           \ function( 's:PollFileParseResponse' ) )
 
 
-    call s:StopPoller( s:pollers.semantic_highlighting )
-    if !s:is_neovim &&
-          \ get( b:, 'ycm_enable_semantic_highlighting',
-          \   get( g:, 'ycm_enable_semantic_highlighting', 0 ) )
+    call s:UpdateSemanticHighlighting( bufnr() )
+    call s:UpdateInlayHints( bufnr() )
 
 
-      py3 ycm_state.CurrentBuffer().SendSemanticTokensRequest()
-      let s:pollers.semantic_highlighting.id = timer_start(
-            \ s:pollers.semantic_highlighting.wait_milliseconds,
-            \ function( 's:PollSemanticHighlighting' ) )
+  endif
+endfunction
 
 
-    endif
+function! s:UpdateSemanticHighlighting( bufnr ) abort
+  call s:StopPoller( s:pollers.semantic_highlighting )
+  if !s:is_neovim &&
+        \ get( b:, 'ycm_enable_semantic_highlighting',
+        \   get( g:, 'ycm_enable_semantic_highlighting', 0 ) )
+
+    py3 ycm_state.Buffer(
+          \ int( vim.eval( "a:bufnr" ) ) ).SendSemanticTokensRequest()
+    let s:pollers.semantic_highlighting.id = timer_start(
+          \ s:pollers.semantic_highlighting.wait_milliseconds,
+          \ function( 's:PollSemanticHighlighting', [ a:bufnr ] ) )
 
 
-    call s:StopPoller( s:pollers.inlay_hints )
-    if !s:is_neovim &&
-          \ get( b:, 'ycm_enable_inlay_hints',
-          \   get( g:, 'ycm_enable_inlay_hints', 0 ) )
+  endif
+endfunction
 
 
-      py3 ycm_state.CurrentBuffer().SendInlayHintsRequest()
-      let s:pollers.inlay_hints.id = timer_start(
-            \ s:pollers.inlay_hints.wait_milliseconds,
-            \ function( 's:PollInlayHints' ) )
 
 
-    endif
+function! s:UpdateInlayHints( bufnr )
+  call s:StopPoller( s:pollers.inlay_hints )
+  if !s:is_neovim &&
+        \ get( b:, 'ycm_enable_inlay_hints',
+        \   get( g:, 'ycm_enable_inlay_hints', 0 ) )
+
+    py3 ycm_state.Buffer( int( vim.eval( 'a:bufnr' ) ) ).SendInlayHintsRequest()
+    let s:pollers.inlay_hints.id = timer_start(
+          \ s:pollers.inlay_hints.wait_milliseconds,
+          \ function( 's:PollInlayHints', [ a:bufnr ] ) )
+
   endif
   endif
 endfunction
 endfunction
 
 
@@ -810,28 +821,34 @@ function! s:PollFileParseResponse( ... )
 endfunction
 endfunction
 
 
 
 
-function! s:PollSemanticHighlighting( ... )
-  if !py3eval( 'ycm_state.CurrentBuffer().SemanticTokensRequestReady()' )
+function! s:PollSemanticHighlighting( bufnr, ... )
+  if !py3eval(
+      \ 'ycm_state.Buffer( int( vim.eval( "a:bufnr" ) ) )'
+      \ . '.SemanticTokensRequestReady()' )
     let s:pollers.semantic_highlighting.id = timer_start(
     let s:pollers.semantic_highlighting.id = timer_start(
           \ s:pollers.semantic_highlighting.wait_milliseconds,
           \ s:pollers.semantic_highlighting.wait_milliseconds,
-          \ function( 's:PollSemanticHighlighting' ) )
-  elseif ! py3eval( 'ycm_state.CurrentBuffer().UpdateSemanticTokens()' )
+          \ function( 's:PollSemanticHighlighting', [ a:bufnr ] ) )
+  elseif !py3eval(
+      \ 'ycm_state.Buffer( int( vim.eval( "a:bufnr" ) ) )'
+      \ . '.UpdateSemanticTokens()' )
     let s:pollers.semantic_highlighting.id = timer_start(
     let s:pollers.semantic_highlighting.id = timer_start(
           \ s:pollers.semantic_highlighting.wait_milliseconds,
           \ s:pollers.semantic_highlighting.wait_milliseconds,
-          \ function( 's:PollSemanticHighlighting' ) )
+          \ function( 's:PollSemanticHighlighting', [ a:bufnr ] ) )
   endif
   endif
 endfunction
 endfunction
 
 
 
 
-function! s:PollInlayHints( ... )
-  if !py3eval( 'ycm_state.CurrentBuffer().InlayHintsReady()' )
+function! s:PollInlayHints( bufnr, ... )
+  if !py3eval(
+      \ 'ycm_state.Buffer( int( vim.eval( "a:bufnr" ) ) ).InlayHintsReady()' )
     let s:pollers.inlay_hints.id = timer_start(
     let s:pollers.inlay_hints.id = timer_start(
           \ s:pollers.inlay_hints.wait_milliseconds,
           \ s:pollers.inlay_hints.wait_milliseconds,
-          \ function( 's:PollInlayHints' ) )
-  elseif ! py3eval( 'ycm_state.CurrentBuffer().UpdateInlayHints()' )
+          \ function( 's:PollInlayHints', [ a:bufnr ] ) )
+  elseif ! py3eval(
+      \ 'ycm_state.Buffer( int( vim.eval( "a:bufnr" ) ) ).UpdateInlayHints()' )
     let s:pollers.inlay_hints.id = timer_start(
     let s:pollers.inlay_hints.id = timer_start(
           \ s:pollers.inlay_hints.wait_milliseconds,
           \ s:pollers.inlay_hints.wait_milliseconds,
-          \ function( 's:PollInlayHints' ) )
+          \ function( 's:PollInlayHints', [ a:bufnr ] ) )
   endif
   endif
 endfunction
 endfunction
 
 
@@ -887,6 +904,16 @@ function! s:OnCursorMovedNormalMode()
 endfunction
 endfunction
 
 
 
 
+function! s:OnWinScrolled()
+  if !s:AllowedToCompleteInCurrentBuffer()
+    return
+  endif
+  let bufnr = winbufnr( expand( '<afile>' ) )
+  call s:UpdateSemanticHighlighting( bufnr )
+  call s:UpdateInlayHints( bufnr )
+endfunction
+
+
 function! s:OnTextChangedNormalMode()
 function! s:OnTextChangedNormalMode()
   if !s:AllowedToCompleteInCurrentBuffer()
   if !s:AllowedToCompleteInCurrentBuffer()
     return
     return

+ 1 - 10
python/ycm/inlay_hints.py

@@ -72,16 +72,7 @@ class InlayHints:
     # Perhaps the maximal range of visible windows or something.
     # Perhaps the maximal range of visible windows or something.
     request_data = BuildRequestData( self._bufnr )
     request_data = BuildRequestData( self._bufnr )
     request_data.update( {
     request_data.update( {
-      'range': {
-        'start': {
-          'line_num': 1,
-          'column_num': 1
-        },
-        'end': {
-          'line_num': max( len( vim.buffers[ self._bufnr ] ), 1 ),
-          'column_num': len( ToBytes( vim.buffers[ self._bufnr ][ -1 ] ) ) + 1
-        }
-      }
+      'range': vimsupport.RangeVisibleInBuffer( self._bufnr )
     } )
     } )
     self._request = InlayHintsRequest( request_data )
     self._request = InlayHintsRequest( request_data )
     self._request.Start()
     self._request.Start()

+ 5 - 1
python/ycm/semantic_highlighting.py

@@ -96,7 +96,11 @@ class SemanticHighlighting:
 
 
     self.tick = vimsupport.GetBufferChangedTick( self._bufnr )
     self.tick = vimsupport.GetBufferChangedTick( self._bufnr )
 
 
-    self._request = SemanticTokensRequest( BuildRequestData( self._bufnr ) )
+    request: dict = BuildRequestData( self._bufnr )
+    request.update( {
+      'range': vimsupport.RangeVisibleInBuffer( self._bufnr )
+    } )
+    self._request = SemanticTokensRequest( request )
     self._request.Start()
     self._request.Start()
 
 
   def IsResponseReady( self ):
   def IsResponseReady( self ):

+ 51 - 0
python/ycm/vimsupport.py

@@ -181,6 +181,57 @@ def GetBufferChangedTick( bufnr ):
   return GetIntValue( f'getbufvar({ bufnr }, "changedtick")' )
   return GetIntValue( f'getbufvar({ bufnr }, "changedtick")' )
 
 
 
 
+# Returns a range covering the earliest and latest lines visible in the current
+# tab page for the supplied buffer number. By default this range is then
+# extended by half of the resulting range size
+def RangeVisibleInBuffer( bufnr, factor=0.5 ):
+  windows = [ w for w in vim.eval( f'win_findbuf( { bufnr } )' )
+              if GetIntValue( vim.eval( f'win_id2tabwin( { w } )[ 0 ]' ) ) ==
+                vim.current.tabpage.number ]
+
+  class Location:
+    line: int = None
+    col: int = None
+
+  class Range:
+    start: Location = Location()
+    end: Location = Location()
+
+  buffer = vim.buffers[ bufnr ]
+
+  if not windows:
+    return None
+
+  r = Range()
+  for winid in windows:
+    win_info = vim.eval( f'getwininfo( { winid } )[ 0 ]' )
+    if r.start.line is None or r.start.line > int( win_info[ 'topline' ] ):
+      r.start.line = int( win_info[ 'topline' ] )
+    if r.end.line is None or r.end.line < int( win_info[ 'botline' ] ):
+      r.end.line = int( win_info[ 'botline' ] )
+
+  # Extend the range by 1 factor, and calculate the columns
+  num_lines = r.end.line - r.start.line + 1
+  r.start.line = max( r.start.line - int( num_lines * factor ), 1 )
+  r.start.col = 1
+  r.end.line = min( r.end.line + int( num_lines * factor ), len( buffer ) )
+  r.end.col = len( buffer[ r.end.line - 1 ] )
+
+  filepath = GetBufferFilepath( buffer )
+  return {
+    'start': {
+      'line_num': r.start.line,
+      'column_num': r.start.col,
+      'filepath': filepath,
+    },
+    'end': {
+      'line_num': r.end.line,
+      'column_num': r.end.col,
+      'filepath': filepath,
+    }
+  }
+
+
 def CaptureVimCommand( command ):
 def CaptureVimCommand( command ):
   vim.command( 'redir => b:ycm_command' )
   vim.command( 'redir => b:ycm_command' )
   vim.command( f'silent! { command }' )
   vim.command( f'silent! { command }' )

+ 5 - 1
python/ycm/youcompleteme.py

@@ -586,7 +586,11 @@ class YouCompleteMe:
 
 
 
 
   def CurrentBuffer( self ):
   def CurrentBuffer( self ):
-    return self._buffers[ vimsupport.GetCurrentBufferNumber() ]
+    return self.Buffer( vimsupport.GetCurrentBufferNumber() )
+
+
+  def Buffer( self, bufnr ):
+    return self._buffers[ bufnr ]
 
 
 
 
   def OnInsertLeave( self ):
   def OnInsertLeave( self ):