Support modifiers for GoTo commands

This commit is contained in:
micbou 2018-07-24 17:12:50 +02:00
parent 459b3e620e
commit 87702559f3
No known key found for this signature in database
GPG Key ID: C7E8FD1F3BDA1E05
9 changed files with 242 additions and 74 deletions

View File

@ -2868,13 +2868,30 @@ let g:ycm_use_ultisnips_completer = 1
### The `g:ycm_goto_buffer_command` option ### The `g:ycm_goto_buffer_command` option
Defines where `GoTo*` commands result should be opened. Defines where `GoTo*` commands result should be opened. Can take one of the
Can take one of the following values: following values:
`[ 'same-buffer', 'horizontal-split', 'vertical-split', 'new-tab', `[ 'same-buffer', 'split', 'split-or-existing-window' ]`
'new-or-existing-tab' ]`
If this option is set to the `'same-buffer'` but current buffer can not 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), 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'` Default: `'same-buffer'`

View File

@ -845,7 +845,8 @@ function! s:SetUpCommands()
command! -nargs=* -complete=custom,youcompleteme#LogsComplete command! -nargs=* -complete=custom,youcompleteme#LogsComplete
\ YcmToggleLogs call s:ToggleLogs(<f-args>) \ YcmToggleLogs call s:ToggleLogs(<f-args>)
command! -nargs=* -complete=custom,youcompleteme#SubCommandsComplete -range command! -nargs=* -complete=custom,youcompleteme#SubCommandsComplete -range
\ YcmCompleter call s:CompleterCommand(<count>, \ YcmCompleter call s:CompleterCommand(<q-mods>,
\ <count>,
\ <line1>, \ <line1>,
\ <line2>, \ <line2>,
\ <f-args>) \ <f-args>)
@ -889,7 +890,7 @@ function! youcompleteme#LogsComplete( arglead, cmdline, cursorpos )
endfunction endfunction
function! s:CompleterCommand( count, line1, line2, ... ) function! s:CompleterCommand( mods, count, line1, line2, ... )
" CompleterCommand will call the OnUserCommand function of a completer. If " 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 " 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 " 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(" . exec s:python_command "ycm_state.SendCommandRequest(" .
\ "vim.eval( 'l:arguments' )," . \ "vim.eval( 'l:arguments' )," .
\ "vim.eval( 'l:completer' )," . \ "vim.eval( 'l:completer' )," .
\ "vim.eval( 'a:mods' )," .
\ "vimsupport.GetBoolValue( 'a:count != -1' )," . \ "vimsupport.GetBoolValue( 'a:count != -1' )," .
\ "vimsupport.GetIntValue( 'a:line1' )," . \ "vimsupport.GetIntValue( 'a:line1' )," .
\ "vimsupport.GetIntValue( 'a:line2' ) )" \ "vimsupport.GetIntValue( 'a:line2' ) )"

View File

@ -3107,11 +3107,27 @@ Default: '1'
The *g:ycm_goto_buffer_command* option The *g:ycm_goto_buffer_command* option
Defines where 'GoTo*' commands result should be opened. Can take one of the Defines where 'GoTo*' commands result should be opened. Can take one of the
following values: "[ 'same-buffer', 'horizontal-split', 'vertical-split', 'new- following values: "[ 'same-buffer', 'split', 'split-or-existing-window' ]" If
tab', 'new-or-existing-tab' ]" If this option is set to the "'same-buffer'" but this option is set to the "'same-buffer'" but current buffer can not be
current buffer can not be switched (when buffer is modified and 'nohidden' switched (when buffer is modified and 'nohidden' option is set), then result
option is set), then result will be opened in horizontal split. 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'" Default: "'same-buffer'"
> >
let g:ycm_goto_buffer_command = 'same-buffer' let g:ycm_goto_buffer_command = 'same-buffer'

View File

@ -60,7 +60,7 @@ class CommandRequest( BaseRequest ):
return self._response return self._response
def RunPostCommandActionsIfNeeded( self ): def RunPostCommandActionsIfNeeded( self, modifiers ):
if not self.Done() or self._response is None: if not self.Done() or self._response is None:
return return
@ -82,10 +82,10 @@ class CommandRequest( BaseRequest ):
# The only other type of response we understand is GoTo, and that is the # 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 # only one that we can't detect just by inspecting the response (it should
# either be a single location or a list) # 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 ): if isinstance( self._response, list ):
vimsupport.SetQuickFixList( vimsupport.SetQuickFixList(
[ _BuildQfListItem( x ) for x in self._response ] ) [ _BuildQfListItem( x ) for x in self._response ] )
@ -93,7 +93,8 @@ class CommandRequest( BaseRequest ):
else: else:
vimsupport.JumpToLocation( self._response[ 'filepath' ], vimsupport.JumpToLocation( self._response[ 'filepath' ],
self._response[ 'line_num' ], self._response[ 'line_num' ],
self._response[ 'column_num' ] ) self._response[ 'column_num' ],
modifiers )
def _HandleFixitResponse( self ): def _HandleFixitResponse( self ):
@ -131,11 +132,11 @@ class CommandRequest( BaseRequest ):
vimsupport.WriteToPreviewWindow( self._response[ 'detailed_info' ] ) 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 ) request = CommandRequest( arguments, completer, extra_data )
# This is a blocking call. # This is a blocking call.
request.Start() request.Start()
request.RunPostCommandActionsIfNeeded() request.RunPostCommandActionsIfNeeded( modifiers )
return request.Response() return request.Response()

View File

@ -100,7 +100,7 @@ class GoToResponse_QuickFix_test( object ):
variable_exists ): variable_exists ):
self._request._response = completer_response self._request._response = completer_response
self._request.RunPostCommandActionsIfNeeded() self._request.RunPostCommandActionsIfNeeded( 'aboveleft' )
vim_eval.assert_has_exact_calls( [ vim_eval.assert_has_exact_calls( [
call( 'setqflist( {0} )'.format( json.dumps( expected_qf_list ) ) ) 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: with patch( 'vim.command' ) as vim_command:
request = CommandRequest( [ command ] ) request = CommandRequest( [ command ] )
request._response = response request._response = response
request.RunPostCommandActionsIfNeeded() request.RunPostCommandActionsIfNeeded( 'belowright' )
vim_command.assert_called_with( "echo '{0}'".format( response ) ) vim_command.assert_called_with( "echo '{0}'".format( response ) )
tests = [ tests = [
@ -144,7 +144,7 @@ class Response_Detection_test( object ):
request._response = { request._response = {
'fixits': [] 'fixits': []
} }
request.RunPostCommandActionsIfNeeded() request.RunPostCommandActionsIfNeeded( 'botright' )
post_vim_message.assert_called_with( post_vim_message.assert_called_with(
'No fixits found for current line', warning = False ) 'No fixits found for current line', warning = False )
@ -163,7 +163,7 @@ class Response_Detection_test( object ):
return_value = selection ): return_value = selection ):
request = CommandRequest( [ command ] ) request = CommandRequest( [ command ] )
request._response = response request._response = response
request.RunPostCommandActionsIfNeeded() request.RunPostCommandActionsIfNeeded( 'leftabove' )
replace_chunks.assert_called_with( chunks, silent = silent ) replace_chunks.assert_called_with( chunks, silent = silent )
post_vim_message.assert_not_called() post_vim_message.assert_not_called()
@ -222,7 +222,7 @@ class Response_Detection_test( object ):
with patch( 'ycm.vimsupport.PostVimMessage' ) as post_vim_message: with patch( 'ycm.vimsupport.PostVimMessage' ) as post_vim_message:
request = CommandRequest( [ command ] ) request = CommandRequest( [ command ] )
request._response = { 'message': message } request._response = { 'message': message }
request.RunPostCommandActionsIfNeeded() request.RunPostCommandActionsIfNeeded( 'rightbelow' )
post_vim_message.assert_called_with( message, warning = False ) post_vim_message.assert_called_with( message, warning = False )
tests = [ tests = [
@ -243,7 +243,7 @@ class Response_Detection_test( object ):
with patch( 'ycm.vimsupport.WriteToPreviewWindow' ) as write_to_preview: with patch( 'ycm.vimsupport.WriteToPreviewWindow' ) as write_to_preview:
request = CommandRequest( [ command ] ) request = CommandRequest( [ command ] )
request._response = { 'detailed_info': info } request._response = { 'detailed_info': info }
request.RunPostCommandActionsIfNeeded() request.RunPostCommandActionsIfNeeded( 'topleft' )
write_to_preview.assert_called_with( info ) write_to_preview.assert_called_with( info )
tests = [ tests = [
@ -263,11 +263,12 @@ class Response_Detection_test( object ):
with patch( 'ycm.vimsupport.JumpToLocation' ) as jump_to_location: with patch( 'ycm.vimsupport.JumpToLocation' ) as jump_to_location:
request = CommandRequest( [ command ] ) request = CommandRequest( [ command ] )
request._response = response request._response = response
request.RunPostCommandActionsIfNeeded() request.RunPostCommandActionsIfNeeded( 'rightbelow' )
jump_to_location.assert_called_with( jump_to_location.assert_called_with(
response[ 'filepath' ], response[ 'filepath' ],
response[ 'line_num' ], response[ 'line_num' ],
response[ 'column_num' ] ) response[ 'column_num' ],
'rightbelow' )
def GoToListTest( command, response ): def GoToListTest( command, response ):
# Note: the detail of these called are tested by # 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: with patch( 'ycm.vimsupport.OpenQuickFixList' ) as open_qf_list:
request = CommandRequest( [ command ] ) request = CommandRequest( [ command ] )
request._response = response request._response = response
request.RunPostCommandActionsIfNeeded() request.RunPostCommandActionsIfNeeded( 'tab' )
ok_( set_qf_list.called ) ok_( set_qf_list.called )
ok_( open_qf_list.called ) ok_( open_qf_list.called )

View File

@ -36,13 +36,14 @@ def SendCommandRequest_ExtraConfVimData_Works_test( ycm ):
current_buffer = VimBuffer( 'buffer' ) current_buffer = VimBuffer( 'buffer' )
with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: 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( assert_that(
# Positional arguments passed to SendCommandRequest. # Positional arguments passed to SendCommandRequest.
send_request.call_args[ 0 ], send_request.call_args[ 0 ],
contains( contains(
contains( 'GoTo' ), contains( 'GoTo' ),
'python', 'python',
'aboveleft',
has_entries( { has_entries( {
'options': has_entries( { 'options': has_entries( {
'tab_size': 2, 'tab_size': 2,
@ -61,13 +62,14 @@ def SendCommandRequest_ExtraConfData_UndefinedValue_test( ycm ):
current_buffer = VimBuffer( 'buffer' ) current_buffer = VimBuffer( 'buffer' )
with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: 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( assert_that(
# Positional arguments passed to SendCommandRequest. # Positional arguments passed to SendCommandRequest.
send_request.call_args[ 0 ], send_request.call_args[ 0 ],
contains( contains(
contains( 'GoTo' ), contains( 'GoTo' ),
'python', 'python',
'belowright',
has_entries( { has_entries( {
'options': has_entries( { 'options': has_entries( {
'tab_size': 2, 'tab_size': 2,
@ -84,10 +86,11 @@ def SendCommandRequest_BuildRange_NoVisualMarks_test( ycm, *args ):
'second line' ] ) 'second line' ] )
with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: 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( send_request.assert_called_once_with(
[ 'GoTo' ], [ 'GoTo' ],
'python', 'python',
'',
{ {
'options': { 'options': {
'tab_size': 2, 'tab_size': 2,
@ -116,10 +119,11 @@ def SendCommandRequest_BuildRange_VisualMarks_test( ycm, *args ):
visual_end = [ 2, 8 ] ) visual_end = [ 2, 8 ] )
with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
with patch( 'ycm.youcompleteme.SendCommandRequest' ) as send_request: 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( send_request.assert_called_once_with(
[ 'GoTo' ], [ 'GoTo' ],
'python', 'python',
'tab',
{ {
'options': { 'options': {
'tab_size': 2, 'tab_size': 2,

View File

@ -1619,7 +1619,10 @@ def JumpToLocation_SameFile_SameBuffer_NoSwapFile_test( vim_command ):
# bytes on Python 2 but unicode on Python 3. # bytes on Python 2 but unicode on Python 3.
current_buffer = VimBuffer( 'uni¢𐍈d€' ) current_buffer = VimBuffer( 'uni¢𐍈d€' )
with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: 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 ) ) ) assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) )
vim_command.assert_has_exact_calls( [ 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: with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim:
target_name = os.path.realpath( u'different_uni¢𐍈d€' ) 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 ) ) ) assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) )
vim_command.assert_has_exact_calls( [ vim_command.assert_has_exact_calls( [
call( 'normal! m\'' ), call( 'normal! m\'' ),
call( u'keepjumps edit {0}'.format( target_name ) ), call( u'keepjumps belowright edit {0}'.format( target_name ) ),
call( 'normal! zz' ) call( 'normal! zz' )
] ) ] )
@ -1656,12 +1659,12 @@ def JumpToLocation_DifferentFile_SameBuffer_Modified_CannotHide_test(
with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim:
target_name = os.path.realpath( u'different_uni¢𐍈d€' ) 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 ) ) ) assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) )
vim_command.assert_has_exact_calls( [ vim_command.assert_has_exact_calls( [
call( 'normal! m\'' ), call( 'normal! m\'' ),
call( u'keepjumps split {0}'.format( target_name ) ), call( u'keepjumps botright split {0}'.format( target_name ) ),
call( 'normal! zz' ) call( 'normal! zz' )
] ) ] )
@ -1676,12 +1679,12 @@ def JumpToLocation_DifferentFile_SameBuffer_Modified_CanHide_test(
with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim: with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim:
target_name = os.path.realpath( u'different_uni¢𐍈d€' ) 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 ) ) ) assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) )
vim_command.assert_has_exact_calls( [ vim_command.assert_has_exact_calls( [
call( 'normal! m\'' ), call( 'normal! m\'' ),
call( u'keepjumps edit {0}'.format( target_name ) ), call( u'keepjumps leftabove edit {0}'.format( target_name ) ),
call( 'normal! zz' ) call( 'normal! zz' )
] ) ] )
@ -1697,7 +1700,7 @@ def JumpToLocation_DifferentFile_SameBuffer_SwapFile_Unexpected_test(
with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
assert_that( assert_that(
calling( vimsupport.JumpToLocation ).with_args( 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' ) raises( VimError, 'Unknown code' )
) )
@ -1712,11 +1715,11 @@ def JumpToLocation_DifferentFile_SameBuffer_SwapFile_Quit_test( vim_command ):
with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
target_name = os.path.realpath( u'different_uni¢𐍈d€' ) 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( [ vim_command.assert_has_exact_calls( [
call( 'normal! m\'' ), 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 ] ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
target_name = os.path.realpath( u'different_uni¢𐍈d€' ) 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( [ vim_command.assert_has_exact_calls( [
call( 'normal! m\'' ), 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', @patch( 'ycmd.user_options_store._USER_OPTIONS',
{ 'goto_buffer_command': 'new-or-existing-tab' } ) { 'goto_buffer_command': 'new-or-existing-tab' } )
@patch( 'vim.command', new_callable = ExtendedMock ) @patch( 'vim.command', new_callable = ExtendedMock )
@ -1748,11 +1846,11 @@ def JumpToLocation_DifferentFile_NewOrExistingTab_NotAlreadyOpened_test(
with MockVimBuffers( [ current_buffer ], [ current_buffer ] ): with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
target_name = os.path.realpath( u'different_uni¢𐍈d€' ) 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( [ vim_command.assert_has_exact_calls( [
call( 'normal! m\'' ), call( 'normal! m\'' ),
call( u'keepjumps tabedit {0}'.format( target_name ) ), call( u'keepjumps aboveleft vertical tabedit {0}'.format( target_name ) ),
call( 'normal! zz' ) call( 'normal! zz' )
] ) ] )
@ -1772,7 +1870,7 @@ def JumpToLocation_DifferentFile_NewOrExistingTab_AlreadyOpened_test(
with MockVimBuffers( [ current_buffer, different_buffer ], with MockVimBuffers( [ current_buffer, different_buffer ],
[ current_buffer ] ) as vim: [ current_buffer ] ) as vim:
vimsupport.JumpToLocation( os.path.realpath( u'different_uni¢𐍈d€' ), 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.tabpage, equal_to( current_tab ) )
assert_that( vim.current.window, equal_to( different_window ) ) assert_that( vim.current.window, equal_to( different_window ) )

View File

@ -34,6 +34,9 @@ from ycmd.utils import ( ByteOffsetToCodepointOffset, GetCurrentDirectory,
from ycmd import user_options_store from ycmd import user_options_store
BUFFER_COMMAND_MAP = { 'same-buffer' : 'edit', 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', 'horizontal-split' : 'split',
'vertical-split' : 'vsplit', 'vertical-split' : 'vsplit',
'new-tab' : 'tabedit' } 'new-tab' : 'tabedit' }
@ -439,19 +442,25 @@ def EscapeFilepathForVimCommand( filepath ):
# Both |line| and |column| need to be 1-based # Both |line| and |column| need to be 1-based
def TryJumpLocationInOpenedTab( filename, line, column ): def TryJumpLocationInTab( tab, filename, line, column ):
filepath = os.path.realpath( filename ) 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 tab in vim.tabpages:
for win in tab.windows: if TryJumpLocationInTab( tab, filename, line, column ):
if GetBufferFilepath( win.buffer ) == filepath: return True
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 any tab pages # 'filename' is not opened in any tab pages
return False return False
@ -464,8 +473,30 @@ def GetVimCommand( user_command, default = 'edit' ):
return vim_command 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 # 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 # Add an entry to the jumplist
vim.command( "normal! m'" ) vim.command( "normal! m'" )
@ -478,27 +509,24 @@ def JumpToLocation( filename, line, column ):
# jumplist. # jumplist.
user_command = user_options_store.Value( 'goto_buffer_command' ) 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 user_command == 'new-or-existing-tab':
if TryJumpLocationInOpenedTab( filename, line, column ): if TryJumpLocationInTabs( filename, line, column ):
return return
user_command = 'new-tab' user_command = 'new-tab'
vim_command = GetVimCommand( user_command ) if not JumpToFile( filename, user_command, modifiers ):
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:
return return
vim.current.window.cursor = ( line, column - 1 ) vim.current.window.cursor = ( line, column - 1 )
# Center the screen on the jumped-to location # Center the screen on the jumped-to location

View File

@ -322,6 +322,7 @@ class YouCompleteMe( object ):
def SendCommandRequest( self, def SendCommandRequest( self,
arguments, arguments,
completer, completer,
modifiers,
has_range, has_range,
start_line, start_line,
end_line ): end_line ):
@ -334,7 +335,7 @@ class YouCompleteMe( object ):
if has_range: if has_range:
extra_data.update( vimsupport.BuildRange( start_line, end_line ) ) extra_data.update( vimsupport.BuildRange( start_line, end_line ) )
self._AddExtraConfDataIfNeeded( extra_data ) self._AddExtraConfDataIfNeeded( extra_data )
return SendCommandRequest( arguments, completer, extra_data ) return SendCommandRequest( arguments, completer, modifiers, extra_data )
def GetDefinedSubcommands( self ): def GetDefinedSubcommands( self ):