diff --git a/python/ycm/tests/vimsupport_test.py b/python/ycm/tests/vimsupport_test.py index bcc39709..19dae398 100644 --- a/python/ycm/tests/vimsupport_test.py +++ b/python/ycm/tests/vimsupport_test.py @@ -681,7 +681,7 @@ def _BuildLocations( start_line, start_column, end_line, end_column ): def ReplaceChunksInBuffer_SortedChunks_test(): chunks = [ - _BuildChunk( 1, 4, 1, 4, '('), + _BuildChunk( 1, 4, 1, 4, '(' ), _BuildChunk( 1, 11, 1, 11, ')' ) ] @@ -694,7 +694,7 @@ def ReplaceChunksInBuffer_SortedChunks_test(): def ReplaceChunksInBuffer_UnsortedChunks_test(): chunks = [ - _BuildChunk( 1, 11, 1, 11, ')'), + _BuildChunk( 1, 11, 1, 11, ')' ), _BuildChunk( 1, 4, 1, 4, '(' ) ] @@ -1220,6 +1220,32 @@ def _BuildChunk( start_line, } +@patch( 'vim.eval', new_callable = ExtendedMock ) +def AddDiagnosticSyntaxMatch_ErrorInMiddleOfLine_test( vim_eval ): + current_buffer = MockBuffer( [ + 'Highlight this error please' + ], 'some_file', 1 ) + + with patch( 'vim.current.buffer', current_buffer ): + vimsupport.AddDiagnosticSyntaxMatch( 1, 16, 1, 21 ) + + vim_eval.assert_called_once_with( + r"matchadd('YcmErrorSection', '\%1l\%16c\_.\{-}\%1l\%21c')" ) + + +@patch( 'vim.eval', new_callable = ExtendedMock ) +def AddDiagnosticSyntaxMatch_WarningAtEndOfLine_test( vim_eval ): + current_buffer = MockBuffer( [ + 'Highlight this warning' + ], 'some_file', 1 ) + + with patch( 'vim.current.buffer', current_buffer ): + vimsupport.AddDiagnosticSyntaxMatch( 1, 16, 1, 23, is_error = False ) + + vim_eval.assert_called_once_with( + r"matchadd('YcmWarningSection', '\%1l\%16c\_.\{-}\%1l\%23c')" ) + + @patch( 'vim.command', new_callable=ExtendedMock ) @patch( 'vim.current', new_callable=ExtendedMock) def WriteToPreviewWindow_test( vim_current, vim_command ): diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py index 17f32e68..e1d750d7 100644 --- a/python/ycm/vimsupport.py +++ b/python/ycm/vimsupport.py @@ -208,29 +208,32 @@ def ClearYcmSyntaxMatches(): vim.eval( 'matchdelete({0})'.format( match[ 'id' ] ) ) -# Returns the ID of the newly added match -# Both line and column numbers are 1-based def AddDiagnosticSyntaxMatch( line_num, column_num, line_end_num = None, column_end_num = None, is_error = True ): + """Highlight a range in the current window starting from + (|line_num|, |column_num|) included to (|line_end_num|, |column_end_num|) + excluded. If |line_end_num| or |column_end_num| are not given, highlight the + character at (|line_num|, |column_num|). Both line and column numbers are + 1-based. Return the ID of the newly added match.""" group = 'YcmErrorSection' if is_error else 'YcmWarningSection' - if not line_end_num: - line_end_num = line_num - line_num, column_num = LineAndColumnNumbersClamped( line_num, column_num ) - line_end_num, column_end_num = LineAndColumnNumbersClamped( line_end_num, - column_end_num ) - if not column_end_num: + if not line_end_num or not column_end_num: return GetIntValue( "matchadd('{0}', '\%{1}l\%{2}c')".format( group, line_num, column_num ) ) - else: - return GetIntValue( - "matchadd('{0}', '\%{1}l\%{2}c\_.\\{{-}}\%{3}l\%{4}c')".format( - group, line_num, column_num, line_end_num, column_end_num ) ) + + # -1 and then +1 to account for column end not included in the range. + line_end_num, column_end_num = LineAndColumnNumbersClamped( + line_end_num, column_end_num - 1 ) + column_end_num += 1 + + return GetIntValue( + "matchadd('{0}', '\%{1}l\%{2}c\_.\\{{-}}\%{3}l\%{4}c')".format( + group, line_num, column_num, line_end_num, column_end_num ) ) # Clamps the line and column numbers so that they are not past the contents of