From d458d06bc599ea14a97e59e6f08234528285030a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 1 Jun 2019 15:04:53 +0100 Subject: [PATCH] Use the same version as the current macvim --- autoload/vimspector/internal/channel.vim | 4 + autoload/vimspector/internal/job.vim | 11 + compiler/vimspector_test.vim | 115 ++++++ python3/vimspector/code.py | 3 +- python3/vimspector/utils.py | 1 + run_tests | 2 +- tests/breakpoints.test.vim | 59 +++ tests/ci/image/Dockerfile | 6 +- tests/{ => lib}/run_test.vim | 0 tests/lib/screendump.vim | 171 +++++++++ tests/lib/shared.vim | 358 +++++++++++++++++++ tests/testdata/cpp/simple/.ycm_extra_conf.py | 7 + 12 files changed, 733 insertions(+), 4 deletions(-) create mode 100644 compiler/vimspector_test.vim rename tests/{ => lib}/run_test.vim (100%) create mode 100644 tests/lib/screendump.vim create mode 100644 tests/lib/shared.vim create mode 100644 tests/testdata/cpp/simple/.ycm_extra_conf.py diff --git a/autoload/vimspector/internal/channel.vim b/autoload/vimspector/internal/channel.vim index 9630e0b..15e2a20 100644 --- a/autoload/vimspector/internal/channel.vim +++ b/autoload/vimspector/internal/channel.vim @@ -27,14 +27,17 @@ endfunction function! s:_OnServerError( channel, data ) abort echom 'Channel received error: ' . a:data + redraw endfunction function! s:_OnExit( channel, status ) abort echom 'Channel exit with status ' . a:status + redraw endfunction function! s:_OnClose( channel ) abort echom 'Channel closed' + redraw " py3 _vimspector_session.OnChannelClosed() endfunction @@ -68,6 +71,7 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort if ch_status( s:ch ) !=# 'open' echom 'Unable to connect to debug adapter' + redraw return v:none endif diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index 15fa1c5..26ba117 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -29,28 +29,33 @@ endfunction function! s:_OnExit( channel, status ) abort echom 'Channel exit with status ' . a:status + redraw unlet s:job py3 _vimspector_session.OnServerExit( vim.eval( 'a:status' ) ) endfunction function! s:_OnClose( channel ) abort echom 'Channel closed' + redraw endfunction function! s:_Send( msg ) abort if ! exists( 's:job' ) echom "Can't send message: Job was not initialised correctly" + redraw return 0 endif if job_status( s:job ) !=# 'run' echom "Can't send message: Job is not running" + redraw return 0 endif let ch = job_getchannel( s:job ) if ch ==# 'channel fail' echom 'Channel was closed unexpectedly!' + redraw return 0 endif @@ -61,6 +66,7 @@ endfunction function! vimspector#internal#job#StartDebugSession( config ) abort if exists( 's:job' ) echom 'Not starging: Job is already running' + redraw return v:none endif @@ -80,9 +86,11 @@ function! vimspector#internal#job#StartDebugSession( config ) abort \ ) echom 'Started job, status is: ' . job_status( s:job ) + redraw if job_status( s:job ) !=# 'run' echom 'Unable to start job, status is: ' . job_status( s:job ) + redraw return v:none endif @@ -92,11 +100,13 @@ endfunction function! vimspector#internal#job#StopDebugSession() abort if !exists( 's:job' ) echom "Not stopping session: Job doesn't exist" + redraw return endif if job_status( s:job ) ==# 'run' echom 'Terminating job' + redraw call job_stop( s:job, 'kill' ) endif endfunction @@ -140,6 +150,7 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort if job_status( s:commands[ a:category ][ index ] ) !=# 'run' echom 'Unable to start job for ' . a:cmd + redraw return v:none endif diff --git a/compiler/vimspector_test.vim b/compiler/vimspector_test.vim new file mode 100644 index 0000000..a522489 --- /dev/null +++ b/compiler/vimspector_test.vim @@ -0,0 +1,115 @@ +" vimspector - A multi-language debugging system for Vim +" Copyright 2018 Ben Jackson +" +" Licensed under the Apache License, Version 2.0 (the "License"); +" you may not use this file except in compliance with the License. +" You may obtain a copy of the License at +" +" http://www.apache.org/licenses/LICENSE-2.0 +" +" Unless required by applicable law or agreed to in writing, software +" distributed under the License is distributed on an "AS IS" BASIS, +" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +" See the License for the specific language governing permissions and +" limitations under the License. + +" Compiler plugin to help running vimspector tests + +if exists("current_compiler") + finish +endif +let current_compiler = "vimspector_test" + +setlocal errorformat= + \Found\ errors\ in\ %f:%.%#: + +let &l:makeprg=fnamemodify( findfile( 'run_tests', '.;' ), ':p' ) + \ . ' $* 2>&1' + +let s:make_cmd = get( g:, 'vimspector_test_make_cmd', 'Make' ) + +" If :Make doesn't exist, then use :make +if ! exists( ':' . s:make_cmd ) + let s:make_cmd = 'make' +endif + +function! VimGetCurrentFunction() + echom s:GetCurrentFunction() +endfunction + +function! s:GetCurrentFunction() + " Store the cursor position; we'll need to reset it + let [ l:buf, l:row, l:col, l:offset ] = getpos( '.' ) + + let l:test_function = '' + + let l:pattern = '\V\C\s\*function!\?\s\+\(\<\w\+\>\)\.\*\$' + + let l:lnum = prevnonblank( '.' ) + + " Find the top-level method and class + while l:lnum > 0 + call cursor( l:lnum, 1 ) + let l:lnum = search( l:pattern, 'bcnWz' ) + + if l:lnum <= 0 + call cursor( l:row, l:col ) + return l:test_function + endif + + let l:this_decl = substitute( getline( l:lnum ), l:pattern, '\1', '' ) + let l:this_decl_is_test = match( l:this_decl, '\V\C\^Test_' ) >= 0 + + if l:this_decl_is_test + let l:test_function = l:this_decl + + if indent( l:lnum ) == 0 + call cursor( l:row, l:col ) + return l:test_function + endif + endif + + let l:lnum = prevnonblank( l:lnum - 1 ) + endwhile + +endfunction + +function! s:RunTestUnderCursor() + update + let l:test_func_name = s:GetCurrentFunction() + + if l:test_func_name ==# '' + echo "No test method found" + return + endif + + echo "Running test '" . l:test_func_name . "'" + + let l:test_arg = expand( '%:p:t' ) . ':' . l:test_func_name + execute s:make_cmd . ' ' . l:test_arg +endfunction + +function! s:RunTest() + update + execute s:make_cmd . ' %:p:t' +endfunction + +function! s:RunAllTests() + update + execute s:make_cmd +endfunction + +if ! has( 'gui_running' ) + " ® is right-option+r + nnoremap ® :call RunTest() + " ® is right-option+r + nnoremap  :call RunAllTests() + " † is right-option+t + nnoremap † :call RunTestUnderCursor() + " å is the right-option+q + nnoremap å :cfirst + " å is the right-option+a + nnoremap œ :cnext + " Ω is the right-option+z + nnoremap Ω :cprevious +endif diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index b8084c6..d8f1eac 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -70,7 +70,8 @@ class CodeView( object ): frame[ 'source' ][ 'path' ] ) ) return False - self._window.cursor = ( frame[ 'line' ], frame[ 'column' ] ) + # SIC: column is 0-based, line is 1-based in vim. Why? Nobody knows. + self._window.cursor = ( frame[ 'line' ], frame[ 'column' ] - 1 ) self._signs[ 'vimspectorPC' ] = self._next_sign_id self._next_sign_id += 1 diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 6aad9e7..6ef51e1 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -213,6 +213,7 @@ def UserMessage( msg, persist=False ): cmd = 'echom' if persist else 'echo' for line in msg.split( '\n' ): vim.command( "{0} '{1}'".format( cmd, Escape( line ) ) ) + vim.command( 'redraw' ) @contextlib.contextmanager diff --git a/run_tests b/run_tests index fd83465..f2f8022 100755 --- a/run_tests +++ b/run_tests @@ -1,7 +1,7 @@ #!/usr/bin/env bash RUN_VIM="vim --clean --not-a-term" -RUN_TEST="${RUN_VIM} -S run_test.vim" +RUN_TEST="${RUN_VIM} -S lib/run_test.vim" if [ -z "$VIMSPECTOR_MIMODE" ]; then if which -s lldb; then diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index a9043a6..94705e7 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -1,3 +1,5 @@ +source lib/shared.vim + function! SetUp() if exists ( 'g:loaded_vimpector' ) unlet g:loaded_vimpector @@ -160,6 +162,63 @@ function! Test_Use_Mappings_HUMAN() let cur_tabnr = tabpagenr() call assert_equal( 5, len( gettabinfo( cur_tabnr )[ 0 ].windows ) ) + call WaitForAssert( {-> + \ assert_equal( 'simple.cpp', bufname( '%' ), 'Current buffer' ) + \ } ) + call assert_equal( 15, line( '.' ), 'Current line' ) + call assert_equal( 1, col( '.' ), 'Current column' ) + + let signs = sign_getplaced( '%', { + \ 'group': 'VimspectorCode', + \ } ) + + call assert_equal( 1, len( signs ), 'Sign-buffers' ) + call assert_equal( 2, len( signs[ 0 ].signs ), 'Signs in buffer' ) + + let pc_index = -1 + let index = 0 + while index < len( signs[ 0 ].signs ) + let s = signs[ 0 ].signs[ index ] + if s.name ==# 'vimspectorPC' + if pc_index >= 0 + call assert_report( 'Found too many PC signs!' ) + endif + let pc_index = index + endif + let index = index + 1 + endwhile + call assert_true( pc_index >= 0 ) + call assert_equal( 15, signs[ 0 ].signs[ pc_index ].lnum ) + + " Step + call feedkeys( "\", 'xt' ) + + call WaitForAssert( {-> assert_equal( 16, line( '.' ), 'Current line' ) } ) + call assert_equal( 'simple.cpp', bufname( '%' ), 'Current buffer' ) + call assert_equal( 1, col( '.' ), 'Current column' ) + + let signs = sign_getplaced( '%', { + \ 'group': 'VimspectorCode', + \ } ) + + call assert_equal( 1, len( signs ), 'Sign-buffers' ) + call assert_equal( 2, len( signs[ 0 ].signs ), 'Signs in buffer' ) + + let pc_index = -1 + let index = 0 + while index < len( signs[ 0 ].signs ) + let s = signs[ 0 ].signs[ index ] + if s.name ==# 'vimspectorPC' + if pc_index >= 0 + call assert_report( 'Found too many PC signs!' ) + endif + let pc_index = index + endif + let index = index + 1 + endwhile + call assert_true( pc_index >= 0 ) + call assert_equal( 16, signs[ 0 ].signs[ pc_index ].lnum ) + call vimspector#Reset() call vimspector#ClearBreakpoints() diff --git a/tests/ci/image/Dockerfile b/tests/ci/image/Dockerfile index 327ad46..71656ee 100644 --- a/tests/ci/image/Dockerfile +++ b/tests/ci/image/Dockerfile @@ -16,15 +16,17 @@ RUN apt-get update && \ RUN ln -fs /usr/share/zoneinfo/Europe/London /etc/localtime && \ dpkg-reconfigure --frontend noninteractive tzdata +ARG VIM_VERSION=v8.1.1270 + RUN mkdir -p $HOME/vim && \ cd $HOME/vim && \ git clone https://github.com/vim/vim && \ cd vim && \ - git checkout v8.1.1302 && \ + git checkout ${VIM_VERSION} && \ ./configure --with-features=huge \ --enable-python3interp \ --enable-terminal \ --enable-multibyte \ --enable-fail-if-missing && \ - make -j 8 install + make -j 4 install diff --git a/tests/run_test.vim b/tests/lib/run_test.vim similarity index 100% rename from tests/run_test.vim rename to tests/lib/run_test.vim diff --git a/tests/lib/screendump.vim b/tests/lib/screendump.vim new file mode 100644 index 0000000..ac8ec4b --- /dev/null +++ b/tests/lib/screendump.vim @@ -0,0 +1,171 @@ +" Functions shared by tests making screen dumps. + +" Only load this script once. +if exists('*CanRunVimInTerminal') + finish +endif + +" For most tests we need to be able to run terminal Vim with 256 colors. On +" MS-Windows the console only has 16 colors and the GUI can't run in a +" terminal. +func CanRunVimInTerminal() + return has('terminal') && !has('win32') +endfunc + +" Skip the rest if there is no terminal feature at all. +if !has('terminal') + finish +endif + +source lib/shared.vim + +" Run Vim with "arguments" in a new terminal window. +" By default uses a size of 20 lines and 75 columns. +" Returns the buffer number of the terminal. +" +" Options is a dictionary, these items are recognized: +" "rows" - height of the terminal window (max. 20) +" "cols" - width of the terminal window (max. 78) +" "statusoff" - number of lines the status is offset from default +func RunVimInTerminal(arguments, options) + " If Vim doesn't exit a swap file remains, causing other tests to fail. + " Remove it here. + call delete(".swp") + + if exists('$COLORFGBG') + " Clear $COLORFGBG to avoid 'background' being set to "dark", which will + " only be corrected if the response to t_RB is received, which may be too + " late. + let $COLORFGBG = '' + endif + + " Make a horizontal and vertical split, so that we can get exactly the right + " size terminal window. Works only when the current window is full width. + call assert_equal(&columns, winwidth(0)) + split + vsplit + + " Always do this with 256 colors and a light background. + set t_Co=256 background=light + hi Normal ctermfg=NONE ctermbg=NONE + + " Make the window 20 lines high and 75 columns, unless told otherwise. + let rows = get(a:options, 'rows', 20) + let cols = get(a:options, 'cols', 75) + let statusoff = get(a:options, 'statusoff', 1) + + let cmd = GetVimCommandClean() + + " Add -v to have gvim run in the terminal (if possible) + let cmd .= ' -v ' . a:arguments + let buf = term_start(cmd, { + \ 'curwin': 1, + \ 'term_rows': rows, + \ 'term_cols': cols, + \ }) + if &termwinsize == '' + " in the GUI we may end up with a different size, try to set it. + if term_getsize(buf) != [rows, cols] + call term_setsize(buf, rows, cols) + endif + call assert_equal([rows, cols], term_getsize(buf)) + else + let rows = term_getsize(buf)[0] + let cols = term_getsize(buf)[1] + endif + + " Wait for "All" or "Top" of the ruler to be shown in the last line or in + " the status line of the last window. This can be quite slow (e.g. when + " using valgrind). + " If it fails then show the terminal contents for debugging. + try + call WaitFor({-> len(term_getline(buf, rows)) >= cols - 1 || len(term_getline(buf, rows - statusoff)) >= cols - 1}) + catch /timed out after/ + let lines = map(range(1, rows), {key, val -> term_getline(buf, val)}) + call assert_report('RunVimInTerminal() failed, screen contents: ' . join(lines, "")) + endtry + + return buf +endfunc + +" Stop a Vim running in terminal buffer "buf". +func StopVimInTerminal(buf) + call assert_equal("running", term_getstatus(a:buf)) + + " CTRL-O : works both in Normal mode and Insert mode to start a command line. + " In Command-line it's inserted, the CTRL-U removes it again. + call term_sendkeys(a:buf, "\:\qa!\") + + call WaitForAssert({-> assert_equal("finished", term_getstatus(a:buf))}) + only! +endfunc + +" Verify that Vim running in terminal buffer "buf" matches the screen dump. +" "options" is passed to term_dumpwrite(). +" The file name used is "dumps/{filename}.dump". +" Optionally an extra argument can be passed which is prepended to the error +" message. Use this when using the same dump file with different options. +" Will wait for up to a second for the screen dump to match. +" Returns non-zero when verification fails. +func VerifyScreenDump(buf, filename, options, ...) + let reference = 'dumps/' . a:filename . '.dump' + let testfile = 'failed/' . a:filename . '.dump' + + " Redraw to execute the code that updates the screen. Otherwise we get the + " text and attributes only from the internal buffer. + redraw + + let did_mkdir = 0 + if !isdirectory('failed') + let did_mkdir = 1 + call mkdir('failed') + endif + + let i = 0 + while 1 + " leave some time for updating the original window + sleep 10m + call delete(testfile) + call term_dumpwrite(a:buf, testfile, a:options) + let testdump = readfile(testfile) + if filereadable(reference) + let refdump = readfile(reference) + else + " Must be a new screendump, always fail + let refdump = [] + endif + if refdump == testdump + call delete(testfile) + if did_mkdir + call delete('failed', 'd') + endif + break + endif + if i == 100 + " Leave the failed dump around for inspection. + if filereadable(reference) + let msg = 'See dump file difference: call term_dumpdiff("' . testfile . '", "' . reference . '")' + if a:0 == 1 + let msg = a:1 . ': ' . msg + endif + if len(testdump) != len(refdump) + let msg = msg . '; line count is ' . len(testdump) . ' instead of ' . len(refdump) + endif + else + let msg = 'See new dump file: call term_dumpload("' . testfile . '")' + endif + for i in range(len(refdump)) + if i >= len(testdump) + break + endif + if testdump[i] != refdump[i] + let msg = msg . '; difference in line ' . (i + 1) . ': "' . testdump[i] . '"' + endif + endfor + call assert_report(msg) + return 1 + endif + let i += 1 + endwhile + return 0 +endfunc diff --git a/tests/lib/shared.vim b/tests/lib/shared.vim new file mode 100644 index 0000000..b71901f --- /dev/null +++ b/tests/lib/shared.vim @@ -0,0 +1,358 @@ + +" Functions shared by several tests. + +" Only load this script once. +if exists('*WaitFor') + finish +endif + +" Get the name of the Python executable. +" Also keeps it in s:python. +func PythonProg() + " This test requires the Python command to run the test server. + " This most likely only works on Unix and Windows. + if has('unix') + " We also need the job feature or the pkill command to make sure the server + " can be stopped. + if !(executable('python') && (has('job') || executable('pkill'))) + return '' + endif + let s:python = 'python' + elseif has('win32') + " Use Python Launcher for Windows (py.exe) if available. + if executable('py.exe') + let s:python = 'py.exe' + elseif executable('python.exe') + let s:python = 'python.exe' + else + return '' + endif + else + return '' + endif + return s:python +endfunc + +" Run "cmd". Returns the job if using a job. +func RunCommand(cmd) + let job = 0 + if has('job') + let job = job_start(a:cmd, {"stoponexit": "hup"}) + call job_setoptions(job, {"stoponexit": "kill"}) + elseif has('win32') + exe 'silent !start cmd /c start "test_channel" ' . a:cmd + else + exe 'silent !' . a:cmd . '&' + endif + return job +endfunc + +" Read the port number from the Xportnr file. +func GetPort() + let l = [] + " with 200 it sometimes failed + for i in range(400) + try + let l = readfile("Xportnr") + catch + endtry + if len(l) >= 1 + break + endif + sleep 10m + endfor + call delete("Xportnr") + + if len(l) == 0 + " Can't make the connection, give up. + return 0 + endif + return l[0] +endfunc + +" Run a Python server for "cmd" and call "testfunc". +" Always kills the server before returning. +func RunServer(cmd, testfunc, args) + " The Python program writes the port number in Xportnr. + call delete("Xportnr") + + if len(a:args) == 1 + let arg = ' ' . a:args[0] + else + let arg = '' + endif + let pycmd = s:python . " " . a:cmd . arg + + try + let g:currentJob = RunCommand(pycmd) + + " Wait for up to 2 seconds for the port number to be there. + let port = GetPort() + if port == 0 + call assert_false(1, "Can't start " . a:cmd) + return + endif + + call call(function(a:testfunc), [port]) + catch + call assert_false(1, 'Caught exception: "' . v:exception . '" in ' . v:throwpoint) + finally + call s:kill_server(a:cmd) + endtry +endfunc + +func s:kill_server(cmd) + if has('job') + if exists('g:currentJob') + call job_stop(g:currentJob) + unlet g:currentJob + endif + elseif has('win32') + let cmd = substitute(a:cmd, ".py", '', '') + call system('taskkill /IM ' . s:python . ' /T /F /FI "WINDOWTITLE eq ' . cmd . '"') + else + call system("pkill -f " . a:cmd) + endif +endfunc + +" Wait for up to five seconds for "expr" to become true. "expr" can be a +" stringified expression to evaluate, or a funcref without arguments. +" Using a lambda works best. Example: +" call WaitFor({-> status == "ok"}) +" +" A second argument can be used to specify a different timeout in msec. +" +" When successful the time slept is returned. +" When running into the timeout an exception is thrown, thus the function does +" not return. +func WaitFor(expr, ...) + let timeout = get(a:000, 0, 5000) + let slept = s:WaitForCommon(a:expr, v:null, timeout) + if slept < 0 + throw 'WaitFor() timed out after ' . timeout . ' msec' + endif + return slept +endfunc + +" Wait for up to five seconds for "assert" to return zero. "assert" must be a +" (lambda) function containing one assert function. Example: +" call WaitForAssert({-> assert_equal("dead", job_status(job)}) +" +" A second argument can be used to specify a different timeout in msec. +" +" Return zero for success, one for failure (like the assert function). +func WaitForAssert(assert, ...) + let timeout = get(a:000, 0, 5000) + if s:WaitForCommon(v:null, a:assert, timeout) < 0 + return 1 + endif + return 0 +endfunc + +" Common implementation of WaitFor() and WaitForAssert(). +" Either "expr" or "assert" is not v:null +" Return the waiting time for success, -1 for failure. +func s:WaitForCommon(expr, assert, timeout) + " using reltime() is more accurate, but not always available + let slept = 0 + if has('reltime') + let start = reltime() + endif + + while 1 + if type(a:expr) == v:t_func + let success = a:expr() + elseif type(a:assert) == v:t_func + let success = a:assert() == 0 + else + let success = eval(a:expr) + endif + if success + return slept + endif + + if slept >= a:timeout + break + endif + if type(a:assert) == v:t_func + " Remove the error added by the assert function. + call remove(v:errors, -1) + endif + + sleep 10m + if has('reltime') + let slept = float2nr(reltimefloat(reltime(start)) * 1000) + else + let slept += 10 + endif + endwhile + + return -1 " timed out +endfunc + + +" Wait for up to a given milliseconds. +" With the +timers feature this waits for key-input by getchar(), Resume() +" feeds key-input and resumes process. Return time waited in milliseconds. +" Without +timers it uses simply :sleep. +func Standby(msec) + if has('timers') + let start = reltime() + let g:_standby_timer = timer_start(a:msec, function('s:feedkeys')) + call getchar() + return float2nr(reltimefloat(reltime(start)) * 1000) + else + execute 'sleep ' a:msec . 'm' + return a:msec + endif +endfunc + +func Resume() + if exists('g:_standby_timer') + call timer_stop(g:_standby_timer) + call s:feedkeys(0) + unlet g:_standby_timer + endif +endfunc + +func s:feedkeys(timer) + call feedkeys('x', 'nt') +endfunc + +" Get $VIMPROG to run Vim executable. +" The Makefile writes it as the first line in the "vimcmd" file. +func GetVimProg() + if !filereadable('vimcmd') + " Assume the script was sourced instead of running "make". + return '../vim' + endif + return readfile('vimcmd')[0] +endfunc + +let g:valgrind_cnt = 1 + +" Get the command to run Vim, with -u NONE and --not-a-term arguments. +" If there is an argument use it instead of "NONE". +func GetVimCommand(...) + if !filereadable('vimcmd') + echo 'Cannot read the "vimcmd" file, falling back to ../vim.' + let lines = ['../vim'] + else + let lines = readfile('vimcmd') + endif + if a:0 == 0 + let name = 'NONE' + else + let name = a:1 + endif + " For Unix Makefile writes the command to use in the second line of the + " "vimcmd" file, including environment options. + " Other Makefiles just write the executable in the first line, so fall back + " to that if there is no second line or it is empty. + if len(lines) > 1 && lines[1] != '' + let cmd = lines[1] + else + let cmd = lines[0] + endif + + let cmd = substitute(cmd, '-u \f\+', '-u ' . name, '') + if cmd !~ '-u '. name + let cmd = cmd . ' -u ' . name + endif + let cmd .= ' --not-a-term' + let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '') + + " If using valgrind, make sure every run uses a different log file. + if cmd =~ 'valgrind.*--log-file=' + let cmd = substitute(cmd, '--log-file=\(^\s*\)', '--log-file=\1.' . g:valgrind_cnt, '') + let g:valgrind_cnt += 1 + endif + + return cmd +endfunc + +" Get the command to run Vim, with --clean. +func GetVimCommandClean() + let cmd = GetVimCommand() + let cmd = substitute(cmd, '-u NONE', '--clean', '') + let cmd = substitute(cmd, '--not-a-term', '', '') + + " Optionally run Vim under valgrind + " let cmd = 'valgrind --tool=memcheck --leak-check=yes --num-callers=25 --log-file=valgrind ' . cmd + + return cmd +endfunc + +" Run Vim, using the "vimcmd" file and "-u NORC". +" "before" is a list of Vim commands to be executed before loading plugins. +" "after" is a list of Vim commands to be executed after loading plugins. +" Plugins are not loaded, unless 'loadplugins' is set in "before". +" Return 1 if Vim could be executed. +func RunVim(before, after, arguments) + return RunVimPiped(a:before, a:after, a:arguments, '') +endfunc + +func RunVimPiped(before, after, arguments, pipecmd) + let cmd = GetVimCommand() + let args = '' + if len(a:before) > 0 + call writefile(a:before, 'Xbefore.vim') + let args .= ' --cmd "so Xbefore.vim"' + endif + if len(a:after) > 0 + call writefile(a:after, 'Xafter.vim') + let args .= ' -S Xafter.vim' + endif + + exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments + + if len(a:before) > 0 + call delete('Xbefore.vim') + endif + if len(a:after) > 0 + call delete('Xafter.vim') + endif + return 1 +endfunc + +func CanRunGui() + return has('gui') && ($DISPLAY != "" || has('gui_running')) +endfunc + +func WorkingClipboard() + if !has('clipboard') + return 0 + endif + if has('x11') + return $DISPLAY != "" + endif + return 1 +endfunc + +" Get line "lnum" as displayed on the screen. +" Trailing white space is trimmed. +func! Screenline(lnum) + let chars = [] + for c in range(1, winwidth(0)) + call add(chars, nr2char(screenchar(a:lnum, c))) + endfor + let line = join(chars, '') + return matchstr(line, '^.\{-}\ze\s*$') +endfunc + +" Stops the shell running in terminal "buf". +func Stop_shell_in_terminal(buf) + call term_sendkeys(a:buf, "exit\r") + let job = term_getjob(a:buf) + call WaitFor({-> job_status(job) == "dead"}) +endfunc + +" Gets the text of a terminal line, using term_scrape() +func Get_terminal_text(bufnr, row) + let list = term_scrape(a:bufnr, a:row) + let text = '' + for item in list + let text .= item.chars + endfor + return text +endfunc diff --git a/tests/testdata/cpp/simple/.ycm_extra_conf.py b/tests/testdata/cpp/simple/.ycm_extra_conf.py new file mode 100644 index 0000000..78a71f0 --- /dev/null +++ b/tests/testdata/cpp/simple/.ycm_extra_conf.py @@ -0,0 +1,7 @@ +def Settings( **kwargs ): + return { + 'flags': [ + '-x', 'c++', + '-Wextra', '-Werror', '-Wall' + ] + }