Skip to content

Commit

Permalink
Take care of start column in multiline diags for detailed diag popups
Browse files Browse the repository at this point in the history
Previously, we have always used the diagnostics start column of the
*starting* line of a multiline diagnostic. This is wrong, as the
starting column for a line in the middle of a multiline diagnostic is
always 1.

Previous patch just happens to work if the start column of the first
line of the diagnostic is also 1.
  • Loading branch information
bstaletic committed Jan 31, 2024
1 parent 2b33bf3 commit 06aa107
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 54 deletions.
6 changes: 5 additions & 1 deletion python/ycm/vimsupport.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,18 +288,22 @@ def GetTextPropertyForDiag( buffer_number, line_number, diag ):
end_line = end[ 'line_num' ]
if start_line == end_line:
length = end[ 'column_num' ] - start[ 'column_num' ]
column = start[ 'column_num' ]

Check warning on line 291 in python/ycm/vimsupport.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/vimsupport.py#L291

Added line #L291 was not covered by tests
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
column = start[ 'column_num' ]

Check warning on line 298 in python/ycm/vimsupport.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/vimsupport.py#L298

Added line #L298 was not covered by tests
elif end_line == line_number:
length = end[ 'column_num' ] - 1
column = 1

Check warning on line 301 in python/ycm/vimsupport.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/vimsupport.py#L301

Added line #L301 was not covered by tests
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
column = 1

Check warning on line 306 in python/ycm/vimsupport.py

View check run for this annotation

Codecov / codecov/patch

python/ycm/vimsupport.py#L306

Added line #L306 was not covered by tests
if diag[ 'kind' ] == 'ERROR':
property_name = 'YcmErrorProperty'
else:
Expand All @@ -309,7 +313,7 @@ def GetTextPropertyForDiag( buffer_number, line_number, diag ):
f'{{ "bufnr": { buffer_number }, '
f'"types": [ "{ property_name }" ] }} )' )
return next( filter(
lambda p: start[ 'column_num' ] == int( p[ 'col' ] ) and
lambda p: column == int( p[ 'col' ] ) and
length == int( p[ 'length' ] ),
vim_props ) )
else:
Expand Down
125 changes: 72 additions & 53 deletions test/diagnostics.test.vim
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ function! SetUp()
let g:ycm_log_level = 'DEBUG'
let g:ycm_always_populate_location_list = 1
let g:ycm_enable_semantic_highlighting = 1
let g:ycm_auto_hover = ''

" diagnostics take ages
let g:ycm_test_min_delay = 7
Expand Down Expand Up @@ -348,15 +349,15 @@ function! Test_ShowDetailedDiagnostic_Popup_WithCharacters()
%bwipe!
endfunction

function! Test_ShowDetailedDiagnostic_Popup_MultilineDiag()
function! Test_ShowDetailedDiagnostic_Popup_MultilineDiagNotFromStartOfLine()
let f = tempname() . '.cc'
execut 'edit' f
call setline( 1, [
\ 'int main () {',
\ 'const int &&',
\ ' /* */',
\ ' rd = 1;',
\ 'rd = 4;',
\ ' int a \',
\ '=\',
\ '=',
\ '3;',
\ '}',
\ ] )
call youcompleteme#test#setup#WaitForInitialParse( {} )
Expand All @@ -367,62 +368,80 @@ function! Test_ShowDetailedDiagnostic_Popup_MultilineDiag()
\ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )'
\ ) ) } )

" Start of multiline diagnostic.
call cursor( [ 2, 1 ] )
YcmShowDetailedDiagnostic popup
call test_override( 'char_avail', 1 )

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!" )
for cursor_pos in [ [ 2, 9 ], [ 3, 1], [ 4, 1 ] ]
call cursor( cursor_pos )
YcmShowDetailedDiagnostic popup

call assert_equal( len( popup_list() ), 1 )
let id = popup_list()[ 0 ]
call assert_notequal( 0, id, "Couldn't find popup!" )
call assert_equal( [ 3, 10 ], win_screenpos( id ) )

call youcompleteme#test#popup#CheckPopupPosition( id, {
\ 'visible': 1,
\ 'col': 10,
\ 'line': 3,
\ } )
call assert_match(
\ "^Invalid '==' at end of declaration; did you mean '='?.*",
\ getbufline( winbufnr(id), 1, '$' )[ 0 ] )
" From vim's test_popupwin.vim
" trigger the check for last_cursormoved by going into insert mode
call feedkeys( "ji\<Esc>", 'xt' )
call assert_equal( {}, popup_getpos( id ) )
endfor

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
call test_override( 'ALL', 0 )

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!" )
%bwipe!
endfunction

" 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 ] )
function! Test_ShowDetailedDiagnostic_Popup_MultilineDiagFromStartOfLine()
let f = tempname() . '.cc'
execut 'edit' f
call setline( 1, [
\ 'int main () {',
\ 'const int &&',
\ ' /* */',
\ ' rd = 1;',
\ 'rd = 4;',
\ '}',
\ ] )
call youcompleteme#test#setup#WaitForInitialParse( {} )

call cursor( [ 4, 5 ] )
YcmShowDetailedDiagnostic popup
call WaitForAssert( {->
\ assert_true(
\ py3eval(
\ 'len( ycm_state.CurrentBuffer()._diag_interface._diagnostics )'
\ ) ) } )

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 test_override( 'char_avail', 1 )

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 ] )
for cursor_pos in [ [ 2, 1 ], [ 3, 9 ], [ 4, 5 ] ]
call cursor( cursor_pos )
YcmShowDetailedDiagnostic popup

call assert_equal( 1, len( popup_list() ) )
let id = popup_list()[ 0 ]
call assert_notequal( 0, id, "Couldn't find popup!" )
call assert_equal( [ 3, 13 ], win_screenpos( id ) )

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 feedkeys( "ji\<Esc>ki\<Esc>", 'xt' )
call assert_equal( {}, popup_getpos( id ) )
endfor

" 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!
Expand Down

0 comments on commit 06aa107

Please sign in to comment.