Browse Source

Properly handle diag text property length in multiline diags

Previously GetTextPropertyForDiag() assumed it was dealing with a
single-line diagnostic.

For multiline diagnostics we have three additional cases:

- From start of the diagnostic to the end of the line.
  - Happens on the first line of diagnostic.
- From start of the line to the end of the diagnostic.
  - Happens on the last line of diagnostic.
- Whole line
  - Happens between first and last line of diagnostic.
Boris Staletic 1 year ago
parent
commit
a5595e7f53
2 changed files with 96 additions and 1 deletions
  1. 16 1
      python/ycm/vimsupport.py
  2. 80 0
      test/diagnostics.test.vim

+ 16 - 1
python/ycm/vimsupport.py

@@ -284,7 +284,22 @@ def GetTextPropertyForDiag( buffer_number, line_number, diag ):
   range = diag[ 'location_extent' ]
   start = range[ 'start' ]
   end = range[ 'end' ]
-  length = end[ 'column_num' ] - start[ 'column_num' ]
+  start_line = start[ 'line_num' ]
+  end_line = end[ 'line_num' ]
+  if start_line == end_line:
+    length = end[ 'column_num' ] - start[ 'column_num' ]
+  elif start_line == line_number:
+    # -1 switches to 0-based indexing.
+    current_line_len = len( vim.buffers[ buffer_number ][ line_number - 1 ] )
+    # +2 includes the start columnand accounts for properties at the end of line
+    # covering \n as well.
+    length = current_line_len - start[ 'column_num' ] + 2
+  elif end_line == line_number:
+    length = end[ 'column_num' ] - 1
+  else:
+    # -1 switches to 0-based indexing.
+    # +1 accounts for properties at the end of line covering \n as well.
+    length = len( vim.buffers[ buffer_number ][ line_number - 1 ] ) + 1
   if diag[ 'kind' ] == 'ERROR':
     property_name = 'YcmErrorProperty'
   else:

+ 80 - 0
test/diagnostics.test.vim

@@ -347,3 +347,83 @@ function! Test_ShowDetailedDiagnostic_Popup_WithCharacters()
 
   %bwipe!
 endfunction
+
+function! Test_ShowDetailedDiagnostic_Popup_MultilineDiag()
+  let f = tempname() . '.cc'
+  execut 'edit' f
+  call setline( 1, [
+        \   'int main () {',
+        \   'const int &&',
+        \   '        /* */',
+        \   '    rd = 1;',
+        \   'rd = 4;',
+        \   '}',
+        \ ] )
+  call youcompleteme#test#setup#WaitForInitialParse( {} )
+
+  call WaitForAssert( {->
+    \ assert_true(
+      \ py3eval(
+         \ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )'
+    \ ) ) } )
+
+  " Start of multiline diagnostic.
+  call cursor( [ 2, 1 ] )
+  YcmShowDetailedDiagnostic popup
+
+  let popup_location = screenpos( bufwinid( '%' ), 3, 13 )
+  let id = popup_locate( popup_location[ 'row' ], popup_location[ 'col' ] )
+  call assert_notequal( 0, id, "Couldn't find popup!" )
+
+  call youcompleteme#test#popup#CheckPopupPosition( id, {
+        \ 'visible': 1,
+        \ 'col': 13,
+        \ 'line': 3,
+        \ } )
+  call assert_match(
+        \ "^Variable 'rd' declared const here.*",
+        \ getbufline( winbufnr(id), 1, '$' )[ 0 ] )
+
+  " Middle of multiline diagnostic.
+  call cursor( [ 3, 9 ] )
+  YcmShowDetailedDiagnostic popup
+
+  let popup_location = screenpos( bufwinid( '%' ), 3, 13 )
+  let id = popup_locate( popup_location[ 'row' ], popup_location[ 'col' ] )
+  call assert_notequal( 0, id, "Couldn't find popup!" )
+
+  " End of multiline diagnostic.
+  call youcompleteme#test#popup#CheckPopupPosition( id, {
+        \ 'visible': 1,
+        \ 'col': 13,
+        \ 'line': 3,
+        \ } )
+  call assert_match(
+        \ "^Variable 'rd' declared const here.*",
+        \ getbufline( winbufnr(id), 1, '$' )[ 0 ] )
+
+  call cursor( [ 4, 5 ] )
+  YcmShowDetailedDiagnostic popup
+
+  let popup_location = screenpos( bufwinid( '%' ), 3, 13 )
+  let id = popup_locate( popup_location[ 'row' ], popup_location[ 'col' ] )
+  call assert_notequal( 0, id, "Couldn't find popup!" )
+
+  call youcompleteme#test#popup#CheckPopupPosition( id, {
+        \ 'visible': 1,
+        \ 'col': 13,
+        \ 'line': 3,
+        \ } )
+  call assert_match(
+        \ "^Variable 'rd' declared const here.*",
+        \ getbufline( winbufnr(id), 1, '$' )[ 0 ] )
+
+  " From vim's test_popupwin.vim
+  " trigger the check for last_cursormoved by going into insert mode
+  call test_override( 'char_avail', 1 )
+  call feedkeys( "ji\<Esc>", 'xt' )
+  call assert_equal( {}, popup_getpos( id ) )
+  call test_override( 'ALL', 0 )
+
+  %bwipe!
+endfunction