1
0
Эх сурвалжийг харах

Support modifiers for GoTo commands

micbou 6 жил өмнө
parent
commit
87702559f3

+ 22 - 5
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'`
 

+ 4 - 2
autoload/youcompleteme.vim

@@ -845,7 +845,8 @@ function! s:SetUpCommands()
   command! -nargs=* -complete=custom,youcompleteme#LogsComplete
         \ YcmToggleLogs call s:ToggleLogs(<f-args>)
   command! -nargs=* -complete=custom,youcompleteme#SubCommandsComplete -range
-        \ YcmCompleter call s:CompleterCommand(<count>,
+        \ YcmCompleter call s:CompleterCommand(<q-mods>,
+        \                                      <count>,
         \                                      <line1>,
         \                                      <line2>,
         \                                      <f-args>)
@@ -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' ) )"

+ 20 - 4
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'

+ 7 - 6
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()
 
 

+ 10 - 9
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 )
 

+ 8 - 4
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,

+ 113 - 15
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 ) )

+ 56 - 28
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

+ 2 - 1
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 ):