From 87702559f32365d56572b3d736c52b68c6375d0e Mon Sep 17 00:00:00 2001 From: micbou Date: Tue, 24 Jul 2018 17:12:50 +0200 Subject: [PATCH] Support modifiers for GoTo commands --- README.md | 27 +++- autoload/youcompleteme.vim | 6 +- doc/youcompleteme.txt | 24 +++- python/ycm/client/command_request.py | 13 +- .../ycm/tests/client/command_request_test.py | 19 +-- python/ycm/tests/command_test.py | 12 +- python/ycm/tests/vimsupport_test.py | 128 ++++++++++++++++-- python/ycm/vimsupport.py | 84 ++++++++---- python/ycm/youcompleteme.py | 3 +- 9 files changed, 242 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 0d267d89..28e21ea0 100644 --- a/README.md +++ b/README.md @@ -2868,13 +2868,30 @@ let g:ycm_use_ultisnips_completer = 1 ### The `g:ycm_goto_buffer_command` option -Defines where `GoTo*` commands result should be opened. -Can take one of the following values: -`[ 'same-buffer', 'horizontal-split', 'vertical-split', 'new-tab', - 'new-or-existing-tab' ]` +Defines where `GoTo*` commands result should be opened. Can take one of the +following values: +`[ 'same-buffer', 'split', 'split-or-existing-window' ]` If this option is set to the `'same-buffer'` but current buffer can not be switched (when buffer is modified and `nohidden` option is set), -then result will be opened in horizontal split. +then result will be opened in a split. When the option is set to +`'split-or-existing-window'`, if the result is already open in a window of the +current tab page (or any tab pages with the `:tab` modifier; see below), it +will jump to that window. Otherwise, the result will be opened in a split as if +the option was set to `'split'`. + +To customize the way a new window is split, prefix the `GoTo*` command with one +of the following modifiers: `:aboveleft`, `:belowright`, `:botright`, +`:leftabove`, `:rightbelow`, `:topleft`, and `:vertical`. For instance, to +split vertically to the right of the current window, run the command: +```viml +:rightbelow vertical YcmCompleter GoTo +``` + +To open in a new tab page, use the `:tab` modifier with the `'split'` or +`'split-or-existing-window'` options e.g.: +```viml +:tab YcmCompleter GoTo +``` Default: `'same-buffer'` diff --git a/autoload/youcompleteme.vim b/autoload/youcompleteme.vim index 597eb020..f618db16 100644 --- a/autoload/youcompleteme.vim +++ b/autoload/youcompleteme.vim @@ -845,7 +845,8 @@ function! s:SetUpCommands() command! -nargs=* -complete=custom,youcompleteme#LogsComplete \ YcmToggleLogs call s:ToggleLogs() command! -nargs=* -complete=custom,youcompleteme#SubCommandsComplete -range - \ YcmCompleter call s:CompleterCommand(, + \ YcmCompleter call s:CompleterCommand(, + \ , \ , \ , \ ) @@ -889,7 +890,7 @@ function! youcompleteme#LogsComplete( arglead, cmdline, cursorpos ) endfunction -function! s:CompleterCommand( count, line1, line2, ... ) +function! s:CompleterCommand( mods, count, line1, line2, ... ) " CompleterCommand will call the OnUserCommand function of a completer. If " the first arguments is of the form "ft=..." it can be used to specify the " completer to use (for example "ft=cpp"). Else the native filetype completer @@ -910,6 +911,7 @@ function! s:CompleterCommand( count, line1, line2, ... ) exec s:python_command "ycm_state.SendCommandRequest(" . \ "vim.eval( 'l:arguments' )," . \ "vim.eval( 'l:completer' )," . + \ "vim.eval( 'a:mods' )," . \ "vimsupport.GetBoolValue( 'a:count != -1' )," . \ "vimsupport.GetIntValue( 'a:line1' )," . \ "vimsupport.GetIntValue( 'a:line2' ) )" diff --git a/doc/youcompleteme.txt b/doc/youcompleteme.txt index 4de8fbe5..5b399e24 100644 --- a/doc/youcompleteme.txt +++ b/doc/youcompleteme.txt @@ -3107,11 +3107,27 @@ Default: '1' The *g:ycm_goto_buffer_command* option Defines where 'GoTo*' commands result should be opened. Can take one of the -following values: "[ 'same-buffer', 'horizontal-split', 'vertical-split', 'new- -tab', 'new-or-existing-tab' ]" If this option is set to the "'same-buffer'" but -current buffer can not be switched (when buffer is modified and 'nohidden' -option is set), then result will be opened in horizontal split. +following values: "[ 'same-buffer', 'split', 'split-or-existing-window' ]" If +this option is set to the "'same-buffer'" but current buffer can not be +switched (when buffer is modified and 'nohidden' option is set), then result +will be opened in a split. When the option is set to "'split-or-existing- +window'", if the result is already open in a window of the current tab page (or +any tab pages with the ':tab' modifier; see below), it will jump to that +window. Otherwise, the result will be opened in a split as if the option was +set to "'split'". +To customize the way a new window is split, prefix the 'GoTo*' command with one +of the following modifiers: ':aboveleft', ':belowright', ':botright', +':leftabove', ':rightbelow', ':topleft', and ':vertical'. For instance, to +split vertically to the right of the current window, run the command: +> + :rightbelow vertical YcmCompleter GoTo +< +To open in a new tab page, use the ':tab' modifier with the "'split'" or +"'split-or-existing-window'" options e.g.: +> + :tab YcmCompleter GoTo +< Default: "'same-buffer'" > let g:ycm_goto_buffer_command = 'same-buffer' diff --git a/python/ycm/client/command_request.py b/python/ycm/client/command_request.py index 4a5e16d1..d9a5e502 100644 --- a/python/ycm/client/command_request.py +++ b/python/ycm/client/command_request.py @@ -60,7 +60,7 @@ class CommandRequest( BaseRequest ): return self._response - def RunPostCommandActionsIfNeeded( self ): + def RunPostCommandActionsIfNeeded( self, modifiers ): if not self.Done() or self._response is None: return @@ -82,10 +82,10 @@ class CommandRequest( BaseRequest ): # The only other type of response we understand is GoTo, and that is the # only one that we can't detect just by inspecting the response (it should # either be a single location or a list) - return self._HandleGotoResponse() + return self._HandleGotoResponse( modifiers ) - def _HandleGotoResponse( self ): + def _HandleGotoResponse( self, modifiers ): if isinstance( self._response, list ): vimsupport.SetQuickFixList( [ _BuildQfListItem( x ) for x in self._response ] ) @@ -93,7 +93,8 @@ class CommandRequest( BaseRequest ): else: vimsupport.JumpToLocation( self._response[ 'filepath' ], self._response[ 'line_num' ], - self._response[ 'column_num' ] ) + self._response[ 'column_num' ], + modifiers ) def _HandleFixitResponse( self ): @@ -131,11 +132,11 @@ class CommandRequest( BaseRequest ): vimsupport.WriteToPreviewWindow( self._response[ 'detailed_info' ] ) -def SendCommandRequest( arguments, completer, extra_data = None ): +def SendCommandRequest( arguments, completer, modifiers, extra_data = None ): request = CommandRequest( arguments, completer, extra_data ) # This is a blocking call. request.Start() - request.RunPostCommandActionsIfNeeded() + request.RunPostCommandActionsIfNeeded( modifiers ) return request.Response() diff --git a/python/ycm/tests/client/command_request_test.py b/python/ycm/tests/client/command_request_test.py index 46f66723..45502216 100644 --- a/python/ycm/tests/client/command_request_test.py +++ b/python/ycm/tests/client/command_request_test.py @@ -100,7 +100,7 @@ class GoToResponse_QuickFix_test( object ): variable_exists ): self._request._response = completer_response - self._request.RunPostCommandActionsIfNeeded() + self._request.RunPostCommandActionsIfNeeded( 'aboveleft' ) vim_eval.assert_has_exact_calls( [ call( 'setqflist( {0} )'.format( json.dumps( expected_qf_list ) ) ) @@ -120,7 +120,7 @@ class Response_Detection_test( object ): with patch( 'vim.command' ) as vim_command: request = CommandRequest( [ command ] ) request._response = response - request.RunPostCommandActionsIfNeeded() + request.RunPostCommandActionsIfNeeded( 'belowright' ) vim_command.assert_called_with( "echo '{0}'".format( response ) ) tests = [ @@ -144,7 +144,7 @@ class Response_Detection_test( object ): request._response = { 'fixits': [] } - request.RunPostCommandActionsIfNeeded() + request.RunPostCommandActionsIfNeeded( 'botright' ) post_vim_message.assert_called_with( 'No fixits found for current line', warning = False ) @@ -163,7 +163,7 @@ class Response_Detection_test( object ): return_value = selection ): request = CommandRequest( [ command ] ) request._response = response - request.RunPostCommandActionsIfNeeded() + request.RunPostCommandActionsIfNeeded( 'leftabove' ) replace_chunks.assert_called_with( chunks, silent = silent ) post_vim_message.assert_not_called() @@ -222,7 +222,7 @@ class Response_Detection_test( object ): with patch( 'ycm.vimsupport.PostVimMessage' ) as post_vim_message: request = CommandRequest( [ command ] ) request._response = { 'message': message } - request.RunPostCommandActionsIfNeeded() + request.RunPostCommandActionsIfNeeded( 'rightbelow' ) post_vim_message.assert_called_with( message, warning = False ) tests = [ @@ -243,7 +243,7 @@ class Response_Detection_test( object ): with patch( 'ycm.vimsupport.WriteToPreviewWindow' ) as write_to_preview: request = CommandRequest( [ command ] ) request._response = { 'detailed_info': info } - request.RunPostCommandActionsIfNeeded() + request.RunPostCommandActionsIfNeeded( 'topleft' ) write_to_preview.assert_called_with( info ) tests = [ @@ -263,11 +263,12 @@ class Response_Detection_test( object ): with patch( 'ycm.vimsupport.JumpToLocation' ) as jump_to_location: request = CommandRequest( [ command ] ) request._response = response - request.RunPostCommandActionsIfNeeded() + request.RunPostCommandActionsIfNeeded( 'rightbelow' ) jump_to_location.assert_called_with( response[ 'filepath' ], response[ 'line_num' ], - response[ 'column_num' ] ) + response[ 'column_num' ], + 'rightbelow' ) def GoToListTest( command, response ): # Note: the detail of these called are tested by @@ -277,7 +278,7 @@ class Response_Detection_test( object ): with patch( 'ycm.vimsupport.OpenQuickFixList' ) as open_qf_list: request = CommandRequest( [ command ] ) request._response = response - request.RunPostCommandActionsIfNeeded() + request.RunPostCommandActionsIfNeeded( 'tab' ) ok_( set_qf_list.called ) ok_( open_qf_list.called ) diff --git a/python/ycm/tests/command_test.py b/python/ycm/tests/command_test.py index bbe2a9ba..490f615d 100644 --- a/python/ycm/tests/command_test.py +++ b/python/ycm/tests/command_test.py @@ -36,13 +36,14 @@ def SendCommandRequest_ExtraConfVimData_Works_test( ycm ): current_buffer = VimBuffer( 'buffer' ) with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: - ycm.SendCommandRequest( [ 'GoTo' ], 'python', False, 1, 1 ) + ycm.SendCommandRequest( [ 'GoTo' ], 'python', 'aboveleft', False, 1, 1 ) assert_that( # Positional arguments passed to SendCommandRequest. send_request.call_args[ 0 ], contains( contains( 'GoTo' ), 'python', + 'aboveleft', has_entries( { 'options': has_entries( { 'tab_size': 2, @@ -61,13 +62,14 @@ def SendCommandRequest_ExtraConfData_UndefinedValue_test( ycm ): current_buffer = VimBuffer( 'buffer' ) with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: - ycm.SendCommandRequest( [ 'GoTo' ], 'python', False, 1, 1 ) + ycm.SendCommandRequest( [ 'GoTo' ], 'python', 'belowright', False, 1, 1 ) assert_that( # Positional arguments passed to SendCommandRequest. send_request.call_args[ 0 ], contains( contains( 'GoTo' ), 'python', + 'belowright', has_entries( { 'options': has_entries( { 'tab_size': 2, @@ -84,10 +86,11 @@ def SendCommandRequest_BuildRange_NoVisualMarks_test( ycm, *args ): 'second line' ] ) with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: - ycm.SendCommandRequest( [ 'GoTo' ], 'python', True, 1, 2 ) + ycm.SendCommandRequest( [ 'GoTo' ], 'python', '', True, 1, 2 ) send_request.assert_called_once_with( [ 'GoTo' ], 'python', + '', { 'options': { 'tab_size': 2, @@ -116,10 +119,11 @@ def SendCommandRequest_BuildRange_VisualMarks_test( ycm, *args ): visual_end = [ 2, 8 ] ) with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: - ycm.SendCommandRequest( [ 'GoTo' ], 'python', True, 1, 2 ) + ycm.SendCommandRequest( [ 'GoTo' ], 'python', 'tab', True, 1, 2 ) send_request.assert_called_once_with( [ 'GoTo' ], 'python', + 'tab', { 'options': { 'tab_size': 2, diff --git a/python/ycm/tests/vimsupport_test.py b/python/ycm/tests/vimsupport_test.py index 82a3c4f1..e5bff11a 100644 --- a/python/ycm/tests/vimsupport_test.py +++ b/python/ycm/tests/vimsupport_test.py @@ -1619,7 +1619,10 @@ def JumpToLocation_SameFile_SameBuffer_NoSwapFile_test( vim_command ): # bytes on Python 2 but unicode on Python 3. current_buffer = VimBuffer( 'uni¢𐍈d€' ) with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: - vimsupport.JumpToLocation( os.path.realpath( u'uni¢𐍈d€' ), 2, 5 ) + vimsupport.JumpToLocation( os.path.realpath( u'uni¢𐍈d€' ), + 2, + 5, + 'aboveleft' ) assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) vim_command.assert_has_exact_calls( [ @@ -1636,12 +1639,12 @@ def JumpToLocation_DifferentFile_SameBuffer_Unmodified_test( vim_command ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: target_name = os.path.realpath( u'different_uni¢𐍈d€' ) - vimsupport.JumpToLocation( target_name, 2, 5 ) + vimsupport.JumpToLocation( target_name, 2, 5, 'belowright' ) assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) vim_command.assert_has_exact_calls( [ call( 'normal! m\'' ), - call( u'keepjumps edit {0}'.format( target_name ) ), + call( u'keepjumps belowright edit {0}'.format( target_name ) ), call( 'normal! zz' ) ] ) @@ -1656,12 +1659,12 @@ def JumpToLocation_DifferentFile_SameBuffer_Modified_CannotHide_test( with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: target_name = os.path.realpath( u'different_uni¢𐍈d€' ) - vimsupport.JumpToLocation( target_name, 2, 5 ) + vimsupport.JumpToLocation( target_name, 2, 5, 'botright' ) assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) vim_command.assert_has_exact_calls( [ call( 'normal! m\'' ), - call( u'keepjumps split {0}'.format( target_name ) ), + call( u'keepjumps botright split {0}'.format( target_name ) ), call( 'normal! zz' ) ] ) @@ -1676,12 +1679,12 @@ def JumpToLocation_DifferentFile_SameBuffer_Modified_CanHide_test( with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: target_name = os.path.realpath( u'different_uni¢𐍈d€' ) - vimsupport.JumpToLocation( target_name, 2, 5 ) + vimsupport.JumpToLocation( target_name, 2, 5, 'leftabove' ) assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) vim_command.assert_has_exact_calls( [ call( 'normal! m\'' ), - call( u'keepjumps edit {0}'.format( target_name ) ), + call( u'keepjumps leftabove edit {0}'.format( target_name ) ), call( 'normal! zz' ) ] ) @@ -1697,7 +1700,7 @@ def JumpToLocation_DifferentFile_SameBuffer_SwapFile_Unexpected_test( with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): assert_that( calling( vimsupport.JumpToLocation ).with_args( - os.path.realpath( u'different_uni¢𐍈d€' ), 2, 5 ), + os.path.realpath( u'different_uni¢𐍈d€' ), 2, 5, 'rightbelow' ), raises( VimError, 'Unknown code' ) ) @@ -1712,11 +1715,11 @@ def JumpToLocation_DifferentFile_SameBuffer_SwapFile_Quit_test( vim_command ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): target_name = os.path.realpath( u'different_uni¢𐍈d€' ) - vimsupport.JumpToLocation( target_name, 2, 5 ) + vimsupport.JumpToLocation( target_name, 2, 5, 'topleft' ) vim_command.assert_has_exact_calls( [ call( 'normal! m\'' ), - call( u'keepjumps edit {0}'.format( target_name ) ) + call( u'keepjumps topleft edit {0}'.format( target_name ) ) ] ) @@ -1730,14 +1733,109 @@ def JumpToLocation_DifferentFile_SameBuffer_SwapFile_Abort_test( vim_command ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): target_name = os.path.realpath( u'different_uni¢𐍈d€' ) - vimsupport.JumpToLocation( target_name, 2, 5 ) + vimsupport.JumpToLocation( target_name, 2, 5, 'vertical' ) vim_command.assert_has_exact_calls( [ call( 'normal! m\'' ), - call( u'keepjumps edit {0}'.format( target_name ) ) + call( u'keepjumps vertical edit {0}'.format( target_name ) ) ] ) +@patch( 'ycmd.user_options_store._USER_OPTIONS', + { 'goto_buffer_command': 'split-or-existing-window' } ) +@patch( 'vim.command', new_callable = ExtendedMock ) +def JumpToLocation_DifferentFile_Split_CurrentTab_NotAlreadyOpened_test( + vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + current_window = MagicMock( buffer = current_buffer ) + current_tab = MagicMock( windows = [ current_window ] ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: + vim.current.tabpage = current_tab + + target_name = os.path.realpath( u'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, 2, 5, 'aboveleft' ) + + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( u'keepjumps aboveleft split {0}'.format( target_name ) ), + call( 'normal! zz' ) + ] ) + + +@patch( 'ycmd.user_options_store._USER_OPTIONS', + { 'goto_buffer_command': 'split-or-existing-window' } ) +@patch( 'vim.command', new_callable = ExtendedMock ) +def JumpToLocation_DifferentFile_Split_CurrentTab_AlreadyOpened_test( + vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + different_buffer = VimBuffer( 'different_uni¢𐍈d€' ) + current_window = MagicMock( buffer = current_buffer ) + different_window = MagicMock( buffer = different_buffer ) + current_tab = MagicMock( windows = [ current_window, different_window ] ) + with MockVimBuffers( [ current_buffer, different_buffer ], + [ current_buffer ] ) as vim: + vim.current.tabpage = current_tab + + vimsupport.JumpToLocation( os.path.realpath( u'different_uni¢𐍈d€' ), + 2, 5, 'belowright' ) + + assert_that( vim.current.tabpage, equal_to( current_tab ) ) + assert_that( vim.current.window, equal_to( different_window ) ) + assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( 'normal! zz' ) + ] ) + + +@patch( 'ycmd.user_options_store._USER_OPTIONS', + { 'goto_buffer_command': 'split-or-existing-window' } ) +@patch( 'vim.command', new_callable = ExtendedMock ) +def JumpToLocation_DifferentFile_Split_AllTabs_NotAlreadyOpened_test( + vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): + target_name = os.path.realpath( u'different_uni¢𐍈d€' ) + + vimsupport.JumpToLocation( target_name, 2, 5, 'tab' ) + + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( u'keepjumps tab split {0}'.format( target_name ) ), + call( 'normal! zz' ) + ] ) + + +@patch( 'ycmd.user_options_store._USER_OPTIONS', + { 'goto_buffer_command': 'split-or-existing-window' } ) +@patch( 'vim.command', new_callable = ExtendedMock ) +def JumpToLocation_DifferentFile_Split_AllTabs_AlreadyOpened_test( + vim_command ): + + current_buffer = VimBuffer( 'uni¢𐍈d€' ) + different_buffer = VimBuffer( 'different_uni¢𐍈d€' ) + current_window = MagicMock( buffer = current_buffer ) + different_window = MagicMock( buffer = different_buffer ) + current_tab = MagicMock( windows = [ current_window, different_window ] ) + with patch( 'vim.tabpages', [ current_tab ] ): + with MockVimBuffers( [ current_buffer, different_buffer ], + [ current_buffer ] ) as vim: + vimsupport.JumpToLocation( os.path.realpath( u'different_uni¢𐍈d€' ), + 2, 5, 'tab' ) + + assert_that( vim.current.tabpage, equal_to( current_tab ) ) + assert_that( vim.current.window, equal_to( different_window ) ) + assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) ) + vim_command.assert_has_exact_calls( [ + call( 'normal! m\'' ), + call( 'normal! zz' ) + ] ) + + @patch( 'ycmd.user_options_store._USER_OPTIONS', { 'goto_buffer_command': 'new-or-existing-tab' } ) @patch( 'vim.command', new_callable = ExtendedMock ) @@ -1748,11 +1846,11 @@ def JumpToLocation_DifferentFile_NewOrExistingTab_NotAlreadyOpened_test( with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): target_name = os.path.realpath( u'different_uni¢𐍈d€' ) - vimsupport.JumpToLocation( target_name, 2, 5 ) + vimsupport.JumpToLocation( target_name, 2, 5, 'aboveleft vertical' ) vim_command.assert_has_exact_calls( [ call( 'normal! m\'' ), - call( u'keepjumps tabedit {0}'.format( target_name ) ), + call( u'keepjumps aboveleft vertical tabedit {0}'.format( target_name ) ), call( 'normal! zz' ) ] ) @@ -1772,7 +1870,7 @@ def JumpToLocation_DifferentFile_NewOrExistingTab_AlreadyOpened_test( with MockVimBuffers( [ current_buffer, different_buffer ], [ current_buffer ] ) as vim: vimsupport.JumpToLocation( os.path.realpath( u'different_uni¢𐍈d€' ), - 2, 5 ) + 2, 5, 'belowright tab' ) assert_that( vim.current.tabpage, equal_to( current_tab ) ) assert_that( vim.current.window, equal_to( different_window ) ) diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py index 0346b47b..1d02d27f 100644 --- a/python/ycm/vimsupport.py +++ b/python/ycm/vimsupport.py @@ -34,6 +34,9 @@ from ycmd.utils import ( ByteOffsetToCodepointOffset, GetCurrentDirectory, from ycmd import user_options_store BUFFER_COMMAND_MAP = { 'same-buffer' : 'edit', + 'split' : 'split', + # These commands are obsolete. :vertical or :tab should + # be used with the 'split' command instead. 'horizontal-split' : 'split', 'vertical-split' : 'vsplit', 'new-tab' : 'tabedit' } @@ -439,19 +442,25 @@ def EscapeFilepathForVimCommand( filepath ): # Both |line| and |column| need to be 1-based -def TryJumpLocationInOpenedTab( filename, line, column ): - filepath = os.path.realpath( filename ) +def TryJumpLocationInTab( tab, filename, line, column ): + for win in tab.windows: + if GetBufferFilepath( win.buffer ) == filename: + vim.current.tabpage = tab + vim.current.window = win + vim.current.window.cursor = ( line, column - 1 ) + # Center the screen on the jumped-to location + vim.command( 'normal! zz' ) + return True + # 'filename' is not opened in this tab page + return False + + +# Both |line| and |column| need to be 1-based +def TryJumpLocationInTabs( filename, line, column ): for tab in vim.tabpages: - for win in tab.windows: - if GetBufferFilepath( win.buffer ) == filepath: - vim.current.tabpage = tab - vim.current.window = win - vim.current.window.cursor = ( line, column - 1 ) - - # Center the screen on the jumped-to location - vim.command( 'normal! zz' ) - return True + if TryJumpLocationInTab( tab, filename, line, column ): + return True # 'filename' is not opened in any tab pages return False @@ -464,8 +473,30 @@ def GetVimCommand( user_command, default = 'edit' ): return vim_command +def JumpToFile( filename, command, modifiers ): + vim_command = GetVimCommand( command ) + try: + escaped_filename = EscapeFilepathForVimCommand( filename ) + vim.command( 'keepjumps {} {} {}'.format( modifiers, + vim_command, + escaped_filename ) ) + # When the file we are trying to jump to has a swap file + # Vim opens swap-exists-choices dialog and throws vim.error with E325 error, + # or KeyboardInterrupt after user selects one of the options. + except vim.error as e: + if 'E325' not in str( e ): + raise + # Do nothing if the target file is still not opened (user chose (Q)uit). + if filename != GetCurrentBufferFilepath(): + return False + # Thrown when user chooses (A)bort in .swp message box. + except KeyboardInterrupt: + return False + return True + + # Both |line| and |column| need to be 1-based -def JumpToLocation( filename, line, column ): +def JumpToLocation( filename, line, column, modifiers ): # Add an entry to the jumplist vim.command( "normal! m'" ) @@ -478,27 +509,24 @@ def JumpToLocation( filename, line, column ): # jumplist. user_command = user_options_store.Value( 'goto_buffer_command' ) + if user_command == 'split-or-existing-window': + if 'tab' in modifiers: + if TryJumpLocationInTabs( filename, line, column ): + return + elif TryJumpLocationInTab( vim.current.tabpage, filename, line, column ): + return + user_command = 'split' + + # This command is kept for backward compatibility. :tab should be used with + # the 'split-or-existing-window' command instead. if user_command == 'new-or-existing-tab': - if TryJumpLocationInOpenedTab( filename, line, column ): + if TryJumpLocationInTabs( filename, line, column ): return user_command = 'new-tab' - vim_command = GetVimCommand( user_command ) - try: - escaped_filename = EscapeFilepathForVimCommand( filename ) - vim.command( 'keepjumps {0} {1}'.format( vim_command, escaped_filename ) ) - # When the file we are trying to jump to has a swap file - # Vim opens swap-exists-choices dialog and throws vim.error with E325 error, - # or KeyboardInterrupt after user selects one of the options. - except vim.error as e: - if 'E325' not in str( e ): - raise - # Do nothing if the target file is still not opened (user chose (Q)uit) - if filename != GetCurrentBufferFilepath(): - return - # Thrown when user chooses (A)bort in .swp message box - except KeyboardInterrupt: + if not JumpToFile( filename, user_command, modifiers ): return + vim.current.window.cursor = ( line, column - 1 ) # Center the screen on the jumped-to location diff --git a/python/ycm/youcompleteme.py b/python/ycm/youcompleteme.py index 31f9473f..7231d129 100644 --- a/python/ycm/youcompleteme.py +++ b/python/ycm/youcompleteme.py @@ -322,6 +322,7 @@ class YouCompleteMe( object ): def SendCommandRequest( self, arguments, completer, + modifiers, has_range, start_line, end_line ): @@ -334,7 +335,7 @@ class YouCompleteMe( object ): if has_range: extra_data.update( vimsupport.BuildRange( start_line, end_line ) ) self._AddExtraConfDataIfNeeded( extra_data ) - return SendCommandRequest( arguments, completer, extra_data ) + return SendCommandRequest( arguments, completer, modifiers, extra_data ) def GetDefinedSubcommands( self ):