Commit 6b940637 authored by Onur Aslan's avatar Onur Aslan

Imported Upstream version 0+20160109+git21b6ced

parent 00af0381
......@@ -47,23 +47,21 @@ Here are the things you should do when creating an issue:
2. Put the following options in your vimrc:
```viml
let g:ycm_server_use_vim_stdout = 1
let g:ycm_server_keep_logfiles = 1
let g:ycm_server_log_level = 'debug'
```
Then, if possible, start gvim/macvim (not console vim) from the console.
As you use Vim, you'll see the `ycmd` debug output stream in the console.
If you can not use gvim/macvim, run `:YcmDebugInfo` in vim to see what
temporary files (listed under "Server logfiles") the debug output streams
are written to. Attach the debug output stream to your issue.
3. **Create a test case for your issue**. This is critical. Don't talk about how
Run `:YcmToggleLogs stderr` in vim to open the logfile. Attach the contents
of this file to your issue.
3. Add the output of the `:YcmDebugInfo` command.
4. **Create a test case for your issue**. This is critical. Don't talk about how
"when I have X in my file" or similar, _create a file with X in it_ and put
the contents inside code blocks in your issue description. Try to make this
test file _as small as possible_. Don't just paste a huge, 500 line source
file you were editing and present that as a test. _Minimize_ the file so that
the problem is reproduced with the smallest possible amount of test data.
4. **Include your OS and OS version.**
5. **Include the output of `vim --version`.**
5. **Include your OS and OS version.**
6. **Include the output of `vim --version`.**
Creating good pull requests
......
This diff is collapsed.
......@@ -11,6 +11,6 @@ install:
- python C:\get-pip.py
- pip install -r python\test_requirements.txt
build_script:
- run_tests.py
- python run_tests.py
# Disable automatic tests
test: off
......@@ -22,6 +22,7 @@ set cpo&vim
" This needs to be called outside of a function
let s:script_folder_path = escape( expand( '<sfile>:p:h' ), '\' )
let s:omnifunc_mode = 0
let s:defer_omnifunc = 1
let s:old_cursor_position = []
let s:cursor_moved = 0
......@@ -88,6 +89,21 @@ function! youcompleteme#Enable()
autocmd CompleteDone * call s:OnCompleteDone()
augroup END
" Setting the omnifunc require us to ask the server if it has a Native
" Semantic Completer for the current buffer's filetype. When vim first start
" this mean that we have to wait for the server to be up and running which
" would block vim's GUI. To avoid this we defer setting the omnifunc the
" first time to when we enter Insert mode and then update it on every
" BufferVisit as normal.
if s:defer_omnifunc
augroup ycm_defer_omnifunc
autocmd!
autocmd InsertEnter * call s:SetOmnicompleteFunc()
\ | let s:defer_omnifunc = 0
\ | autocmd! ycm_defer_omnifunc
augroup END
endif
" Calling these once solves the problem of BufReadPre/BufRead/BufEnter not
" triggering for the first loaded file. This should be the last commands
" executed in this function!
......@@ -111,6 +127,16 @@ function! youcompleteme#DisableCursorMovedAutocommands()
endfunction
function! youcompleteme#GetErrorCount()
return pyeval( 'ycm_state.GetErrorCount()' )
endfunction
function! youcompleteme#GetWarningCount()
return pyeval( 'ycm_state.GetWarningCount()' )
endfunction
function! s:SetUpPython() abort
python << EOF
import sys
......@@ -310,6 +336,8 @@ function! s:SetUpCommands()
command! YcmRestartServer call s:RestartServer()
command! YcmShowDetailedDiagnostic call s:ShowDetailedDiagnostic()
command! YcmDebugInfo call s:DebugInfo()
command! -nargs=? -complete=custom,youcompleteme#LogsComplete
\ YcmToggleLogs call s:ToggleLogs(<f-args>)
command! -nargs=* -complete=custom,youcompleteme#SubCommandsComplete
\ YcmCompleter call s:CompleterCommand(<f-args>)
command! YcmForceCompileAndDiagnostics call s:ForceCompileAndDiagnostics()
......@@ -400,6 +428,11 @@ function! s:OnBufferVisit()
call s:SetUpCompleteopt()
call s:SetCompleteFunc()
if !s:defer_omnifunc
call s:SetOmnicompleteFunc()
endif
py ycm_state.OnBufferVisit()
call s:OnFileReadyToParse()
endfunction
......@@ -532,11 +565,6 @@ function! s:OnInsertEnter()
return
endif
if !get( b:, 'ycm_omnicomplete', 0 )
let b:ycm_omnicomplete = 1
call s:SetOmnicompleteFunc()
endif
let s:old_cursor_position = []
endfunction
......@@ -589,6 +617,7 @@ function! s:UpdateDiagnosticNotifications()
\ s:DiagnosticUiSupportedForCurrentFiletype()
if !should_display_diagnostics
py ycm_state.ValidateParseRequest()
return
endif
......@@ -763,6 +792,14 @@ function! s:DebugInfo()
endfunction
function! s:ToggleLogs(...)
let stderr = a:0 == 0 || a:1 !=? 'stdout'
let stdout = a:0 == 0 || a:1 !=? 'stderr'
py ycm_state.ToggleLogs( stdout = vimsupport.GetBoolValue( 'l:stdout' ),
\ stderr = vimsupport.GetBoolValue( 'l:stderr' ) )
endfunction
function! s:CompleterCommand(...)
" 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
......@@ -796,6 +833,11 @@ function! youcompleteme#OpenGoToList()
endfunction
function! youcompleteme#LogsComplete( arglead, cmdline, cursorpos )
return "stdout\nstderr"
endfunction
function! youcompleteme#SubCommandsComplete( arglead, cmdline, cursorpos )
return join( pyeval( 'ycm_state.GetDefinedSubcommands()' ),
\ "\n")
......
This diff is collapsed.
......@@ -18,7 +18,7 @@ def Main():
'to run:\n\tgit submodule update --init --recursive\n\n' )
python_binary = sys.executable
subprocess.call( [ python_binary, build_file ] + sys.argv[1:] )
subprocess.check_call( [ python_binary, build_file ] + sys.argv[1:] )
# Remove old YCM libs if present so that YCM can start.
old_libs = (
......
......@@ -121,9 +121,6 @@ let g:ycm_key_detailed_diagnostics =
let g:ycm_cache_omnifunc =
\ get( g:, 'ycm_cache_omnifunc', 1 )
let g:ycm_server_use_vim_stdout =
\ get( g:, 'ycm_server_use_vim_stdout', 0 )
let g:ycm_server_log_level =
\ get( g:, 'ycm_server_log_level', 'info' )
......
......@@ -2,4 +2,3 @@ flake8>=2.0
mock>=1.0.1
nose>=1.3.0
PyHamcrest>=1.8.0
......@@ -60,17 +60,26 @@ class CommandRequest( BaseRequest ):
def RunPostCommandActionsIfNeeded( self ):
if not self.Done() or not self._response:
if not self.Done() or self._response is None:
return
if self._is_goto_command:
self._HandleGotoResponse()
elif self._is_fixit_command:
self._HandleFixitResponse()
elif 'message' in self._response:
self._HandleMessageResponse()
elif 'detailed_info' in self._response:
self._HandleDetailedInfoResponse()
return self._HandleGotoResponse()
if self._is_fixit_command:
return self._HandleFixitResponse()
# If not a dictionary or a list, the response is necessarily a
# scalar: boolean, number, string, etc. In this case, we print
# it to the user.
if not isinstance( self._response, ( dict, list ) ):
return self._HandleBasicResponse()
if 'message' in self._response:
return self._HandleMessageResponse()
if 'detailed_info' in self._response:
return self._HandleDetailedInfoResponse()
def _HandleGotoResponse( self ):
......@@ -97,6 +106,10 @@ class CommandRequest( BaseRequest ):
+ " changes" )
def _HandleBasicResponse( self ):
vimsupport.EchoText( self._response )
def _HandleMessageResponse( self ):
vimsupport.EchoText( self._response[ 'message' ] )
......@@ -122,5 +135,13 @@ def _BuildQfListItem( goto_data_item ):
if 'line_num' in goto_data_item:
qf_item[ 'lnum' ] = goto_data_item[ 'line_num' ]
if 'column_num' in goto_data_item:
qf_item[ 'col' ] = goto_data_item[ 'column_num' ] - 1
# ycmd returns columns 1-based, and QuickFix lists require "byte offsets".
# See :help getqflist and equivalent comment in
# vimsupport.ConvertDiagnosticsToQfList.
#
# When the Vim help says "byte index", it really means "1-based column
# number" (which is somewhat confusing). :help getqflist states "first
# column is 1".
qf_item[ 'col' ] = goto_data_item[ 'column_num' ]
return qf_item
#
# Copyright (C) 2016 YouCompleteMe Contributors
#
# This file is part of YouCompleteMe.
#
# YouCompleteMe is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# YouCompleteMe is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from ycm.test_utils import MockVimModule
MockVimModule()
from mock import patch, call
from ycm.client.command_request import CommandRequest
class GoToResponse_QuickFix_test:
"""This class tests the generation of QuickFix lists for GoTo responses which
return multiple locations, such as the Python completer and JavaScript
completer. It mostly proves that we use 1-based indexing for the column
number."""
def setUp( self ):
self._request = CommandRequest( [ 'GoToTest' ] )
def tearDown( self ):
self._request = None
def GoTo_EmptyList_test( self ):
self._CheckGoToList( [], [] )
def GoTo_SingleItem_List_test( self ):
self._CheckGoToList( [ {
'filepath': 'dummy_file',
'line_num': 10,
'column_num': 1,
'description': 'this is some text',
} ], [ {
'filename': 'dummy_file',
'text': 'this is some text',
'lnum': 10,
'col': 1
} ] )
def GoTo_MultiItem_List_test( self ):
self._CheckGoToList( [ {
'filepath': 'dummy_file',
'line_num': 10,
'column_num': 1,
'description': 'this is some other text',
}, {
'filepath': 'dummy_file2',
'line_num': 1,
'column_num': 21,
'description': 'this is some text',
} ], [ {
'filename': 'dummy_file',
'text': 'this is some other text',
'lnum': 10,
'col': 1
}, {
'filename': 'dummy_file2',
'text': 'this is some text',
'lnum': 1,
'col': 21
} ] )
@patch( 'vim.eval' )
def _CheckGoToList( self, completer_response, expected_qf_list, vim_eval ):
self._request._response = completer_response
self._request.RunPostCommandActionsIfNeeded()
vim_eval.assert_has_calls( [
call( 'setqflist( {0} )'.format( repr( expected_qf_list ) ) ),
call( 'youcompleteme#OpenGoToList()' ),
] )
......@@ -43,6 +43,15 @@ class DiagnosticInterface( object ):
if self._user_options[ 'echo_current_diagnostic' ]:
self._EchoDiagnosticForLine( line )
def GetErrorCount( self ):
return len( self._FilterDiagnostics( _DiagnosticIsError ) )
def GetWarningCount( self ):
return len( self._FilterDiagnostics( _DiagnosticIsWarning ) )
def UpdateWithNewDiagnostics( self, diags ):
normalized_diags = [ _NormalizeDiagnostic( x ) for x in diags ]
self._buffer_number_to_line_to_diags = _ConvertDiagListToDict(
......@@ -79,6 +88,16 @@ class DiagnosticInterface( object ):
self._diag_message_needs_clearing = True
def _FilterDiagnostics( self, predicate ):
matched_diags = []
line_to_diags = self._buffer_number_to_line_to_diags[
vim.current.buffer.number ]
for diags in line_to_diags.itervalues():
matched_diags.extend( filter( predicate, diags ) )
return matched_diags
def _UpdateSquiggles( buffer_number_to_line_to_diags ):
vimsupport.ClearYcmSyntaxMatches()
line_to_diags = buffer_number_to_line_to_diags[ vim.current.buffer.number ]
......@@ -209,6 +228,10 @@ def _DiagnosticIsError( diag ):
return diag[ 'kind' ] == 'ERROR'
def _DiagnosticIsWarning( diag ):
return diag[ 'kind' ] == 'WARNING'
def _NormalizeDiagnostic( diag ):
def ClampToOne( value ):
return value if value > 0 else 1
......
......@@ -18,18 +18,77 @@
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from mock import MagicMock
import re
import sys
BUFNR_REGEX = re.compile( r"^bufnr\('(.+)', ([0-9]+)\)$" )
BUFWINNR_REGEX = re.compile( r"^bufwinnr\(([0-9]+)\)$" )
BWIPEOUT_REGEX = re.compile( r"^(?:silent! )bwipeout!? ([0-9]+)$" )
# One-and only instance of mocked Vim object. The first 'import vim' that is
# executed binds the vim module to the instance of MagicMock that is created,
# and subsquent assignments to sys.modules[ 'vim' ] don't retrospectively update
# them. The result is that while running the tests, we must assign only one
# instance of MagicMock to sys.modules[ 'vim' ] and always return it.
# and subsquent assignments to sys.modules[ 'vim' ] don't retrospectively
# update them. The result is that while running the tests, we must assign only
# one instance of MagicMock to sys.modules[ 'vim' ] and always return it.
#
# More explanation is available:
# https://github.com/Valloric/YouCompleteMe/pull/1694
VIM_MOCK = MagicMock()
def MockGetBufferNumber( buffer_filename ):
for buffer in VIM_MOCK.buffers:
if buffer[ 'filename' ] == buffer_filename:
return buffer[ 'number' ]
return -1
def MockGetBufferWindowNumber( buffer_number ):
for buffer in VIM_MOCK.buffers:
if buffer[ 'number' ] == buffer_number and 'window' in buffer:
return buffer[ 'window' ]
return -1
def MockVimEval( value ):
if value == "g:ycm_min_num_of_chars_for_completion":
return 0
if value == "g:ycm_path_to_python_interpreter":
return ''
if value == "tempname()":
return '_TEMP_FILE_'
if value == "&previewheight":
# Default value from Vim
return 12
match = BUFNR_REGEX.search( value )
if match:
return MockGetBufferNumber( match.group( 1 ) )
match = BUFWINNR_REGEX.search( value )
if match:
return MockGetBufferWindowNumber( int( match.group( 1 ) ) )
raise ValueError( 'Unexpected evaluation: ' + value )
def MockWipeoutBuffer( buffer_number ):
buffers = VIM_MOCK.buffers
for index, buffer in enumerate( buffers ):
if buffer[ 'number' ] == buffer_number:
return buffers.pop( index )
def MockVimCommand( command ):
match = BWIPEOUT_REGEX.search( command )
if match:
return MockWipeoutBuffer( int( match.group( 1 ) ) )
raise RuntimeError( 'Unexpected command: ' + command )
def MockVimModule():
"""The 'vim' module is something that is only present when running inside the
Vim Python interpreter, so we replace it with a MagicMock for tests. If you
......@@ -52,12 +111,8 @@ def MockVimModule():
Failure to use this approach may lead to unexpected failures in other
tests."""
def VimEval( value ):
if value == "g:ycm_min_num_of_chars_for_completion":
return 0
return ''
VIM_MOCK.eval = MagicMock( side_effect = VimEval )
VIM_MOCK.buffers = {}
VIM_MOCK.eval = MagicMock( side_effect = MockVimEval )
sys.modules[ 'vim' ] = VIM_MOCK
return VIM_MOCK
This diff is collapsed.
This diff is collapsed.
......@@ -17,12 +17,14 @@
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from ycm.test_utils import MockVimModule
from ycm.test_utils import MockVimModule, MockVimCommand
MockVimModule()
from ycm import vimsupport
from nose.tools import eq_
from hamcrest import assert_that, calling, raises, none
from mock import MagicMock, call, patch
import os
def ReplaceChunk_SingleLine_Repl_1_test():
......@@ -582,17 +584,9 @@ def _BuildChunk( start_line, start_column, end_line, end_column,
}
def _Mock_tempname( arg ):
if arg == 'tempname()':
return '_TEMP_FILE_'
raise ValueError( 'Unexpected evaluation: ' + arg )
@patch( 'vim.eval', side_effect=_Mock_tempname )
@patch( 'vim.command' )
@patch( 'vim.current' )
def WriteToPreviewWindow_test( vim_current, vim_command, vim_eval ):
def WriteToPreviewWindow_test( vim_current, vim_command ):
vim_current.window.options.__getitem__ = MagicMock( return_value = True )
vimsupport.WriteToPreviewWindow( "test" )
......@@ -615,9 +609,8 @@ def WriteToPreviewWindow_test( vim_current, vim_command, vim_eval ):
], any_order = True )
@patch( 'vim.eval', side_effect=_Mock_tempname )
@patch( 'vim.current' )
def WriteToPreviewWindow_MultiLine_test( vim_current, vim_eval ):
def WriteToPreviewWindow_MultiLine_test( vim_current ):
vim_current.window.options.__getitem__ = MagicMock( return_value = True )
vimsupport.WriteToPreviewWindow( "test\ntest2" )
......@@ -625,10 +618,9 @@ def WriteToPreviewWindow_MultiLine_test( vim_current, vim_eval ):
slice( None, None, None ), [ 'test', 'test2' ] )
@patch( 'vim.eval', side_effect=_Mock_tempname )
@patch( 'vim.command' )
@patch( 'vim.current' )
def WriteToPreviewWindow_JumpFail_test( vim_current, vim_command, vim_eval ):
def WriteToPreviewWindow_JumpFail_test( vim_current, vim_command ):
vim_current.window.options.__getitem__ = MagicMock( return_value = False )
vimsupport.WriteToPreviewWindow( "test" )
......@@ -644,12 +636,9 @@ def WriteToPreviewWindow_JumpFail_test( vim_current, vim_command, vim_eval ):
vim_current.buffer.options.__setitem__.assert_not_called()
@patch( 'vim.eval', side_effect=_Mock_tempname )
@patch( 'vim.command' )
@patch( 'vim.current' )
def WriteToPreviewWindow_JumpFail_MultiLine_test( vim_current,
vim_command,
vim_eval ):
def WriteToPreviewWindow_JumpFail_MultiLine_test( vim_current, vim_command ):
vim_current.window.options.__getitem__ = MagicMock( return_value = False )
......@@ -665,3 +654,94 @@ def WriteToPreviewWindow_JumpFail_MultiLine_test( vim_current,
vim_current.buffer.__setitem__.assert_not_called()
vim_current.buffer.options.__setitem__.assert_not_called()
def CheckFilename_test():
assert_that(
calling( vimsupport.CheckFilename ).with_args( None ),
raises( RuntimeError, "'None' is not a valid filename" )
)
assert_that(
calling( vimsupport.CheckFilename ).with_args( 'nonexistent_file' ),
raises( RuntimeError,
"filename 'nonexistent_file' cannot be opened. "
"\[Errno 2\] No such file or directory: 'nonexistent_file'" )
)
assert_that( vimsupport.CheckFilename( __file__ ), none() )
def BufferIsVisibleForFilename_test():
buffers = [
{
'number': 1,
'filename': os.path.realpath( 'visible_filename' ),
'window': 1
},
{
'number': 2,
'filename': os.path.realpath( 'hidden_filename' ),
}
]
with patch( 'vim.buffers', buffers ):
eq_( vimsupport.BufferIsVisibleForFilename( 'visible_filename' ), True )
eq_( vimsupport.BufferIsVisibleForFilename( 'hidden_filename' ), False )
eq_( vimsupport.BufferIsVisibleForFilename( 'another_filename' ), False )
@patch( 'vim.command', side_effect = MockVimCommand )
def CloseBuffersForFilename_test( vim_command ):
buffers = [
{
'number': 2,
'filename': os.path.realpath( 'some_filename' ),
},
{
'number': 5,
'filename': os.path.realpath( 'some_filename' ),
},
{
'number': 1,
'filename': os.path.realpath( 'another_filename' )
}
]
with patch( 'vim.buffers', buffers ):
vimsupport.CloseBuffersForFilename( 'some_filename' )
vim_command.assert_has_calls( [
call( 'silent! bwipeout! 2' ),
call( 'silent! bwipeout! 5' )
], any_order = True )
@patch( 'vim.command' )
@patch( 'vim.current' )
def OpenFilename_test( vim_current, vim_command ):
# Options used to open a logfile
options = {
'size': vimsupport.GetIntValue( '&previewheight' ),
'fix': True,
'watch': True,
'position': 'end'
}
vimsupport.OpenFilename( __file__, options )
vim_command.assert_has_calls( [
call( 'silent! 12split {0}'.format( __file__ ) ),
call( "exec "
"'au BufEnter <buffer> :silent! checktime {0}'".format( __file__ ) ),
call( 'silent! normal G zz' ),
call( 'silent! wincmd p' )
] )
vim_current.buffer.options.__setitem__.assert_has_calls( [
call( 'autoread', True ),
] )
vim_current.window.options.__setitem__.assert_has_calls( [
call( 'winfixheight', True )
] )
......@@ -240,10 +240,12 @@ def SetLocationList( diagnostics ):
def ConvertDiagnosticsToQfList( diagnostics ):
def ConvertDiagnosticToQfFormat( diagnostic ):
# see :h getqflist for a description of the dictionary fields
# See :h getqflist for a description of the dictionary fields.
# Note that, as usual, Vim is completely inconsistent about whether
# line/column numbers are 1 or 0 based in its various APIs. Here, it wants
# them to be 1-based.
# them to be 1-based. The documentation states quite clearly that it
# expects a byte offset, by which it means "1-based column number" as
# described in :h getqflist ("the first column is 1").
location = diagnostic[ 'location' ]
line_num = location[ 'line_num' ]
......@@ -331,9 +333,9 @@ def TryJumpLocationInOpenedTab( filename, line, column ):
return False
# Maps User jump command to vim jump command
def GetVimJumpCommand( user_command ):
vim_command = BUFFER_COMMAND_MAP.get( user_command, 'edit' )
# Maps User command to vim command
def GetVimCommand( user_command, default = 'edit' ):
vim_command = BUFFER_COMMAND_MAP.get( user_command, default )
if vim_command == 'edit' and not BufferIsUsable( vim.current.buffer ):
vim_command = 'split'
return vim_command
......@@ -358,7 +360,7 @@ def JumpToLocation( filename, line, column ):
return
user_command = 'new-tab'
vim_command = GetVimJumpCommand( user_command )
vim_command = GetVimCommand( user_command )
try:
vim.command( 'keepjumps {0} {1}'.format( vim_command,
EscapedFilepath( filename ) ) )
......@@ -610,6 +612,11 @@ def JumpToPreviousWindow():
vim.command( 'silent! wincmd p' )
def JumpToTab( tab_number ):
"""Jump to Vim tab with corresponding number """
vim.command( 'silent! tabn {0}'.format( tab_number ) )
def OpenFileInPreviewWindow( filename ):
""" Open the supplied filename in the preview window """
vim.command( 'silent! pedit! ' + filename )
......@@ -653,3 +660,88 @@ def WriteToPreviewWindow( message ):
# the information we have. The only remaining option is to echo to the
# status area.
EchoText( message )
def CheckFilename( filename ):
"""Check if filename is openable."""
try:
open( filename ).close()
except TypeError:
raise RuntimeError( "'{0}' is not a valid filename".format( filename ) )
except IOError as error:
raise RuntimeError(
"filename '{0}' cannot be opened. {1}".format( filename, error ) )
def BufferIsVisibleForFilename( filename ):
"""Check if a buffer exists for a specific file."""
buffer_number = GetBufferNumberForFilename( filename, False )
return BufferIsVisible( buffer_number )
def CloseBuffersForFilename( filename ):
"""Close all buffers for a specific file."""
buffer_number = GetBufferNumberForFilename( filename, False )
while buffer_number is not -1:
vim.command( 'silent! bwipeout! {0}'.format( buffer_number ) )
new_buffer_number = GetBufferNumberForFilename( filename, False )
if buffer_number == new_buffer_number:
raise RuntimeError( "Buffer {0} for filename '{1}' should already be "
"wiped out.".format( buffer_number, filename ) )
buffer_number = new_buffer_number
def OpenFilename( filename, options = {} ):
"""Open a file in Vim. Following options are available:
- command: specify which Vim command is used to open the file. Choices
are same-buffer, horizontal-split, vertical-split, and new-tab (default:
horizontal-split);
- size: set the height of the window for a horizontal split or the width for
a vertical one (default: '');
- fix: set the winfixheight option for a horizontal split or winfixwidth for
a vertical one (default: False). See :h winfix for details;
- focus: focus the opened file (default: False);
- watch: automatically watch for changes (default: False). This is useful
for logs;
- position: set the position where the file is opened (default: start).
Choices are start and end."""