commit
4318e567f2
18 changed files with 760 additions and 193 deletions
|
|
@ -96,6 +96,11 @@ function! vimspector#AddWatchPrompt( expr ) abort
|
|||
call vimspector#AddWatch( a:expr )
|
||||
endfunction
|
||||
|
||||
function! vimspector#Evaluate( expr ) abort
|
||||
py3 _vimspector_session.ShowOutput( 'Console' )
|
||||
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) )
|
||||
endfunction
|
||||
|
||||
function! vimspector#EvaluateConsole( expr ) abort
|
||||
stopinsert
|
||||
setlocal nomodified
|
||||
|
|
@ -110,6 +115,18 @@ function! vimspector#ListBreakpoints() abort
|
|||
py3 _vimspector_session.ListBreakpoints()
|
||||
endfunction
|
||||
|
||||
function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort
|
||||
let buffers = py3eval( '_vimspector_session.GetOutputBuffers()' )
|
||||
return join( buffers, "\n" )
|
||||
endfunction
|
||||
|
||||
function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort
|
||||
return join( py3eval( '_vimspector_session.GetCompletionsSync( '
|
||||
\.' vim.eval( "a:CmdLine" ),'
|
||||
\.' int( vim.eval( "a:CursorPos" ) ) )' ),
|
||||
\ "\n" )
|
||||
endfunction
|
||||
|
||||
" Boilerplate {{{
|
||||
let &cpoptions=s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
|
|
|||
|
|
@ -25,11 +25,6 @@ _vimspector_session.OnChannelData( vim.eval( 'a:data' ) )
|
|||
EOF
|
||||
endfunction
|
||||
|
||||
function! s:_OnServerError( channel, data ) abort
|
||||
echom 'Channel received error: ' . a:data
|
||||
redraw
|
||||
endfunction
|
||||
|
||||
function! s:_OnClose( channel ) abort
|
||||
echom 'Channel closed'
|
||||
redraw
|
||||
|
|
@ -37,22 +32,11 @@ function! s:_OnClose( channel ) abort
|
|||
py3 _vimspector_session.OnServerExit( 0 )
|
||||
endfunction
|
||||
|
||||
function! s:_Send( msg ) abort
|
||||
call ch_sendraw( s:ch, a:msg )
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#channel#Timeout( id ) abort
|
||||
py3 << EOF
|
||||
_vimspector_session.OnRequestTimeout( vim.eval( 'a:id' ) )
|
||||
EOF
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#channel#StartDebugSession( config ) abort
|
||||
|
||||
if exists( 's:ch' )
|
||||
echo 'Channel is already running'
|
||||
return v:none
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let l:addr = 'localhost:' . a:config[ 'port' ]
|
||||
|
|
@ -70,10 +54,21 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort
|
|||
if ch_status( s:ch ) !=# 'open'
|
||||
echom 'Unable to connect to debug adapter'
|
||||
redraw
|
||||
return v:none
|
||||
return v:false
|
||||
endif
|
||||
|
||||
return funcref( 's:_Send' )
|
||||
return v:true
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#channel#Send( msg ) abort
|
||||
call ch_sendraw( s:ch, a:msg )
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#channel#Timeout( id ) abort
|
||||
py3 << EOF
|
||||
_vimspector_session.OnRequestTimeout( vim.eval( 'a:id' ) )
|
||||
EOF
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#channel#StopDebugSession() abort
|
||||
|
|
@ -97,15 +92,6 @@ function! vimspector#internal#channel#Reset() abort
|
|||
endif
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#channel#ForceRead() abort
|
||||
if exists( 's:ch' )
|
||||
let data = ch_readraw( s:ch, { 'timeout': 1000 } )
|
||||
if data !=# ''
|
||||
call s:_OnServerData( s:ch, data )
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Boilerplate {{{
|
||||
let &cpoptions=s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ endfunction
|
|||
function! s:_OnExit( channel, status ) abort
|
||||
echom 'Channel exit with status ' . a:status
|
||||
redraw
|
||||
unlet s:job
|
||||
unlet s:job
|
||||
py3 _vimspector_session.OnServerExit( vim.eval( 'a:status' ) )
|
||||
endfunction
|
||||
|
||||
|
|
@ -39,35 +39,11 @@ function! s:_OnClose( channel ) abort
|
|||
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
|
||||
|
||||
call ch_sendraw( ch, a:msg )
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#StartDebugSession( config ) abort
|
||||
if exists( 's:job' )
|
||||
echom 'Not starging: Job is already running'
|
||||
redraw
|
||||
return v:none
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let s:job = job_start( a:config[ 'command' ],
|
||||
|
|
@ -91,10 +67,34 @@ function! vimspector#internal#job#StartDebugSession( config ) abort
|
|||
if job_status( s:job ) !=# 'run'
|
||||
echom 'Unable to start job, status is: ' . job_status( s:job )
|
||||
redraw
|
||||
return v:none
|
||||
return v:false
|
||||
endif
|
||||
|
||||
return funcref( 's:_Send' )
|
||||
return v:true
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#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
|
||||
|
||||
call ch_sendraw( ch, a:msg )
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#StopDebugSession() abort
|
||||
|
|
@ -115,15 +115,6 @@ function! vimspector#internal#job#Reset() abort
|
|||
call vimspector#internal#job#StopDebugSession()
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#ForceRead() abort
|
||||
if exists( 's:job' )
|
||||
let data = ch_readraw( job_getchannel( s:job ), { 'timeout': 1000 } )
|
||||
if data !=# ''
|
||||
call s:_OnServerData( job_getchannel( s:job ), data )
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
|
||||
if ! exists( 's:commands' )
|
||||
let s:commands = {}
|
||||
|
|
@ -136,7 +127,7 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
|
|||
let l:index = len( s:commands[ a:category ] )
|
||||
|
||||
call add( s:commands[ a:category ], job_start(
|
||||
\ a:cmd,
|
||||
\ a:cmd,
|
||||
\ {
|
||||
\ 'out_io': 'buffer',
|
||||
\ 'in_io': 'null',
|
||||
|
|
|
|||
85
autoload/vimspector/internal/neochannel.vim
Normal file
85
autoload/vimspector/internal/neochannel.vim
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
" vimspector - A multi-language debugging system for Vim
|
||||
" Copyright 2020 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.
|
||||
|
||||
|
||||
" Boilerplate {{{
|
||||
let s:save_cpo = &cpoptions
|
||||
set cpoptions&vim
|
||||
" }}}
|
||||
|
||||
|
||||
|
||||
function! s:_OnEvent( chan_id, data, event ) abort
|
||||
if a:data == ['']
|
||||
echom 'Channel closed'
|
||||
redraw
|
||||
unlet s:ch
|
||||
py3 _vimspector_session.OnServerExit( 0 )
|
||||
else
|
||||
py3 _vimspector_session.OnChannelData( '\n'.join( vim.eval( 'a:data' ) ) )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neochannel#StartDebugSession( config ) abort
|
||||
if exists( 's:ch' )
|
||||
echom 'Not starging: Channel is already running'
|
||||
redraw
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let addr = 'localhost:' . a:config[ 'port' ]
|
||||
|
||||
let s:ch = sockconnect( 'tcp', addr, { 'on_data': funcref( 's:_OnEvent' ) } )
|
||||
if s:ch <= 0
|
||||
unlet s:ch
|
||||
return v:false
|
||||
endif
|
||||
|
||||
return v:true
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neochannel#Send( msg ) abort
|
||||
if ! exists( 's:ch' )
|
||||
echom "Can't send message: Channel was not initialised correctly"
|
||||
redraw
|
||||
return 0
|
||||
endif
|
||||
|
||||
call chansend( s:ch, a:msg )
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neochannel#StopDebugSession() abort
|
||||
if !exists( 's:ch' )
|
||||
echom "Not stopping session: Channel doesn't exist"
|
||||
redraw
|
||||
return
|
||||
endif
|
||||
|
||||
call chanclose( s:ch )
|
||||
" It doesn't look like we get a callback after chanclos. Who knows if we will
|
||||
" subsequently receive data callbacks.
|
||||
call s:_OnEvent( s:ch, [ '' ], 'data' )
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neochannel#Reset() abort
|
||||
call vimspector#internal#neochannel#StopDebugSession()
|
||||
endfunction
|
||||
|
||||
" Boilerplate {{{
|
||||
let &cpoptions=s:save_cpo
|
||||
unlet s:save_cpo
|
||||
" }}}
|
||||
|
||||
212
autoload/vimspector/internal/neojob.vim
Normal file
212
autoload/vimspector/internal/neojob.vim
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
" vimspector - A multi-language debugging system for Vim
|
||||
" Copyright 2020 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.
|
||||
|
||||
|
||||
" Boilerplate {{{
|
||||
let s:save_cpo = &cpoptions
|
||||
set cpoptions&vim
|
||||
" }}}
|
||||
|
||||
|
||||
|
||||
function! s:_OnEvent( chan_id, data, event ) abort
|
||||
" In neovim, the data argument is a list.
|
||||
if a:event ==# 'stdout'
|
||||
py3 _vimspector_session.OnChannelData( '\n'.join( vim.eval( 'a:data' ) ) )
|
||||
elseif a:event ==# 'stderr'
|
||||
py3 _vimspector_session.OnServerStderr( '\n'.join( vim.eval( 'a:data' ) ) )
|
||||
elseif a:event ==# 'exit'
|
||||
echom 'Channel exit with status ' . a:data
|
||||
redraw
|
||||
unlet s:job
|
||||
" This causes terminal spam in neovim due to
|
||||
" https://github.com/neovim/neovim/issues/11725
|
||||
py3 _vimspector_session.OnServerExit( vim.eval( 'a:data' ) )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#StartDebugSession( config ) abort
|
||||
if exists( 's:job' )
|
||||
echom 'Not starging: Job is already running'
|
||||
redraw
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let s:job = jobstart( a:config[ 'command' ],
|
||||
\ {
|
||||
\ 'on_stdout': funcref( 's:_OnEvent' ),
|
||||
\ 'on_stderr': funcref( 's:_OnEvent' ),
|
||||
\ 'on_exit': funcref( 's:_OnEvent' ),
|
||||
\ 'cwd': a:config[ 'cwd' ],
|
||||
\ 'env': a:config[ 'env' ],
|
||||
\ }
|
||||
\ )
|
||||
|
||||
" FIXME: env might not work: Missing in neovim 0.4. But in master:
|
||||
return v:true
|
||||
endfunction
|
||||
|
||||
function! s:JobIsRunning( job ) abort
|
||||
return jobwait( [ s:job ], 0 )[ 0 ] == -1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#Send( msg ) abort
|
||||
if ! exists( 's:job' )
|
||||
echom "Can't send message: Job was not initialised correctly"
|
||||
redraw
|
||||
return 0
|
||||
endif
|
||||
|
||||
if !s:JobIsRunning( s:job )
|
||||
echom "Can't send message: Job is not running"
|
||||
redraw
|
||||
return 0
|
||||
endif
|
||||
|
||||
call chansend( s:job, a:msg )
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#StopDebugSession() abort
|
||||
if !exists( 's:job' )
|
||||
return
|
||||
endif
|
||||
|
||||
if s:JobIsRunning( s:job )
|
||||
echom 'Terminating job'
|
||||
redraw
|
||||
call jobstop( s:job )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#Reset() abort
|
||||
call vimspector#internal#neojob#StopDebugSession()
|
||||
endfunction
|
||||
|
||||
function! s:_OnCommandEvent( category, id, data, event ) abort
|
||||
if a:data == ['']
|
||||
return
|
||||
endif
|
||||
|
||||
if a:event ==# 'stdout'
|
||||
let buffer = s:commands[ a:category ][ a:id ].stdout
|
||||
elseif a:event ==# 'stderr'
|
||||
let buffer = s:commands[ a:category ][ a:id ].stderr
|
||||
endif
|
||||
|
||||
try
|
||||
call bufload( buffer )
|
||||
catch /E325/
|
||||
" Ignore E325/ATTENTION
|
||||
endtry
|
||||
|
||||
|
||||
let numlines = py3eval( "len( vim.buffers[ int( vim.eval( 'buffer' ) ) ] )" )
|
||||
let last_line = getbufline( buffer, '$' )[ 0 ]
|
||||
|
||||
call s:MakeBufferWritable( buffer )
|
||||
try
|
||||
if numlines == 1 && last_line ==# ''
|
||||
call setbufline( buffer, 1, a:data[ 0 ] )
|
||||
else
|
||||
call setbufline( buffer, '$', last_line . a:data[ 0 ] )
|
||||
endif
|
||||
|
||||
call appendbufline( buffer, '$', a:data[ 1: ] )
|
||||
finally
|
||||
call s:MakeBufferReadOnly( buffer )
|
||||
call setbufvar( buffer, '&modified', 0 )
|
||||
endtry
|
||||
|
||||
" if the buffer is visible, scroll it
|
||||
let w = bufwinnr( buffer )
|
||||
if w > 0
|
||||
let cw = winnr()
|
||||
try
|
||||
execute w . 'wincmd w'
|
||||
normal! Gz.
|
||||
finally
|
||||
execute cw . 'wincmd w'
|
||||
endtry
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:SetUpHiddenBuffer( buffer ) abort
|
||||
call setbufvar( a:buffer, '&hidden', 1 )
|
||||
call setbufvar( a:buffer, '&bufhidden', 'hide' )
|
||||
call setbufvar( a:buffer, '&wrap', 0 )
|
||||
call setbufvar( a:buffer, '&swapfile', 0 )
|
||||
call s:MakeBufferReadOnly( a:buffer )
|
||||
endfunction
|
||||
|
||||
function! s:MakeBufferReadOnly( buffer ) abort
|
||||
call setbufvar( a:buffer, '&modifiable', 0 )
|
||||
call setbufvar( a:buffer, '&readonly', 1 )
|
||||
endfunction
|
||||
|
||||
function! s:MakeBufferWritable( buffer ) abort
|
||||
call setbufvar( a:buffer, '&readonly', 0 )
|
||||
call setbufvar( a:buffer, '&modifiable', 1 )
|
||||
endfunction
|
||||
|
||||
|
||||
let s:commands = {}
|
||||
|
||||
function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort
|
||||
if ! has_key( s:commands, a:category )
|
||||
let s:commands[ a:category ] = {}
|
||||
endif
|
||||
|
||||
let stdout_buf = bufnr( '_vimspector_log_' . a:category . '_out', v:true )
|
||||
let stderr_buf = bufnr( '_vimspector_log_' . a:category . '_err', v:true )
|
||||
|
||||
" FIXME: This largely duplicates the same stuff in the python layer, but we
|
||||
" don't want to potentially mess up Vim behaviour where the job output is
|
||||
" attached to a buffer set up by Vim. So we sort o mimic that here.
|
||||
call s:SetUpHiddenBuffer( stdout_buf )
|
||||
call s:SetUpHiddenBuffer( stderr_buf )
|
||||
|
||||
let id = jobstart(a:cmd,
|
||||
\ {
|
||||
\ 'on_stdout': funcref( 's:_OnCommandEvent',
|
||||
\ [ a:category ] ),
|
||||
\ 'on_stderr': funcref( 's:_OnCommandEvent',
|
||||
\ [ a:category ] )
|
||||
\ } )
|
||||
|
||||
let s:commands[ a:category ][ id ] = {
|
||||
\ 'stdout': stdout_buf,
|
||||
\ 'stderr': stderr_buf
|
||||
\ }
|
||||
|
||||
return [ stdout_buf, stderr_buf ]
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#CleanUpCommand( category ) abort
|
||||
if ! has_key( s:commands, a:category )
|
||||
return
|
||||
endif
|
||||
|
||||
for id in keys( s:commands[ a:category ] )
|
||||
call jobstop( id )
|
||||
call jobwait( id )
|
||||
endfor
|
||||
unlet! s:commands[ a:category ]
|
||||
endfunction
|
||||
|
||||
" Boilerplate {{{
|
||||
let &cpoptions=s:save_cpo
|
||||
unlet s:save_cpo
|
||||
" }}}
|
||||
67
autoload/vimspector/internal/neoterm.vim
Normal file
67
autoload/vimspector/internal/neoterm.vim
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
" 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.
|
||||
|
||||
|
||||
" Boilerplate {{{
|
||||
let s:save_cpo = &cpoptions
|
||||
set cpoptions&vim
|
||||
" }}}
|
||||
|
||||
" Ids are unique throughtout the life of neovim, but obviously buffer numbers
|
||||
" aren't
|
||||
"
|
||||
" FIXME: Tidy this map when buffers are closed ?
|
||||
let s:buffer_to_id = {}
|
||||
|
||||
function! vimspector#internal#neoterm#Start( cmd, opts ) abort
|
||||
if ! get( a:opts, 'curwin', 0 )
|
||||
if get( a:opts, 'vertical', 0 )
|
||||
vsplit
|
||||
else
|
||||
split
|
||||
endif
|
||||
endif
|
||||
|
||||
" FIXME: 'env' doesn't work
|
||||
let id = termopen( a:cmd, { 'cwd': a:opts[ 'cwd' ] } )
|
||||
let bufnr = bufnr()
|
||||
let s:buffer_to_id[ bufnr ] = id
|
||||
return bufnr
|
||||
endfunction
|
||||
|
||||
function! s:JobIsRunning( job ) abort
|
||||
return jobwait( [ a:job ], 0 )[ 0 ] == -1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neoterm#IsFinished( bufno ) abort
|
||||
if !has_key( s:buffer_to_id, a:bufno )
|
||||
return v:true
|
||||
endif
|
||||
|
||||
return !s:JobIsRunning( s:buffer_to_id[ a:bufno ] )
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neoterm#GetPID( bufno ) abort
|
||||
if !has_key( s:buffer_to_id, a:bufno )
|
||||
return -1
|
||||
endif
|
||||
|
||||
return jobpid( s:buffer_to_id[ a:bufno ] )
|
||||
endfunction
|
||||
|
||||
" Boilerplate {{{
|
||||
let &cpoptions=s:save_cpo
|
||||
unlet s:save_cpo
|
||||
" }}}
|
||||
|
|
@ -19,10 +19,16 @@ let s:save_cpo = &cpoptions
|
|||
set cpoptions&vim
|
||||
" }}}
|
||||
|
||||
let s:is_neovim = has( 'nvim' )
|
||||
|
||||
function! vimspector#internal#state#Reset() abort
|
||||
let prefix = ''
|
||||
if s:is_neovim
|
||||
let prefix='neo'
|
||||
endif
|
||||
py3 << EOF
|
||||
from vimspector import debug_session
|
||||
_vimspector_session = debug_session.DebugSession()
|
||||
_vimspector_session = debug_session.DebugSession( vim.eval( 'prefix' ) )
|
||||
EOF
|
||||
endfunction
|
||||
|
||||
|
|
|
|||
37
autoload/vimspector/internal/term.vim
Normal file
37
autoload/vimspector/internal/term.vim
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
" 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.
|
||||
|
||||
|
||||
" Boilerplate {{{
|
||||
let s:save_cpo = &cpoptions
|
||||
set cpoptions&vim
|
||||
" }}}
|
||||
|
||||
function! vimspector#internal#term#Start( cmd, opts ) abort
|
||||
return term_start( a:cmd, a:opts )
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#term#IsFinished( bufno ) abort
|
||||
return index( split( term_getstatus( a:bufno ), ',' ), 'finished' ) >= 0
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#term#GetPID( bufno ) abort
|
||||
return job_info( term_getjob( a:bufno ) ).process
|
||||
endfunction
|
||||
|
||||
" Boilerplate {{{
|
||||
let &cpoptions=s:save_cpo
|
||||
unlet s:save_cpo
|
||||
" }}}
|
||||
|
|
@ -26,6 +26,7 @@ if exists( 'g:loaded_vimpector' )
|
|||
call s:restore_cpo()
|
||||
finish
|
||||
endif
|
||||
"}}}
|
||||
|
||||
" TODO:
|
||||
" - Check Vim version (for jobs)
|
||||
|
|
@ -36,28 +37,54 @@ let g:loaded_vimpector = 1
|
|||
|
||||
let s:mappings = get( g:, 'vimspector_enable_mappings', '' )
|
||||
|
||||
nnoremap <Plug>VimspectorContinue :<c-u>call vimspector#Continue()<CR>
|
||||
nnoremap <Plug>VimspectorStop :<c-u>call vimspector#Stop()<CR>
|
||||
nnoremap <Plug>VimspectorRestart :<c-u>call vimspector#Restart()<CR>
|
||||
nnoremap <Plug>VimspectorPause :<c-u>call vimspector#Pause()<CR>
|
||||
nnoremap <Plug>VimspectorToggleBreakpoint
|
||||
\ :<c-u>call vimspector#ToggleBreakpoint()<CR>
|
||||
nnoremap <Plug>VimspectorAddFunctionBreakpoint
|
||||
\ :<c-u>call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
|
||||
nnoremap <Plug>VimspectorStepOver :<c-u>call vimspector#StepOver()<CR>
|
||||
nnoremap <Plug>VimspectorStepInto :<c-u>call vimspector#StepInto()<CR>
|
||||
nnoremap <Plug>VimspectorStepOut :<c-u>call vimspector#StepOut()<CR>
|
||||
|
||||
if s:mappings ==# 'VISUAL_STUDIO'
|
||||
nnoremap <F5> :call vimspector#Continue()<CR>
|
||||
nnoremap <S-F5> :call vimspector#Stop()<CR>
|
||||
nnoremap <C-S-F5> :call vimspector#Restart()<CR>
|
||||
nnoremap <F6> :call vimspector#Pause()<CR>
|
||||
nnoremap <F9> :call vimspector#ToggleBreakpoint()<CR>
|
||||
nnoremap <S-F9> :call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
|
||||
nnoremap <F10> :call vimspector#StepOver()<CR>
|
||||
nnoremap <F11> :call vimspector#StepInto()<CR>
|
||||
nnoremap <S-F11> :call vimspector#StepOut()<CR>
|
||||
nmap <F5> <Plug>VimspectorContinue
|
||||
nmap <S-F5> <Plug>VimspectorStop
|
||||
nmap <C-S-F5> <Plug>VimspectorRestart
|
||||
nmap <F6> <Plug>VimspectorPause
|
||||
nmap <F9> <Plug>VimspectorToggleBreakpoint
|
||||
nmap <S-F9> <Plug>VimspectorAddFunctionBreakpoint
|
||||
nmap <F10> <Plug>VimspectorStepOver
|
||||
nmap <F11> <Plug>VimspectorStepInto
|
||||
nmap <S-F11> <Plug>VimspectorStepOut
|
||||
elseif s:mappings ==# 'HUMAN'
|
||||
nnoremap <F5> :call vimspector#Continue()<CR>
|
||||
nnoremap <F3> :call vimspector#Stop()<CR>
|
||||
nnoremap <F4> :call vimspector#Restart()<CR>
|
||||
nnoremap <F6> :call vimspector#Pause()<CR>
|
||||
nnoremap <F9> :call vimspector#ToggleBreakpoint()<CR>
|
||||
nnoremap <F8> :call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
|
||||
nnoremap <F10> :call vimspector#StepOver()<CR>
|
||||
nnoremap <F11> :call vimspector#StepInto()<CR>
|
||||
nnoremap <F12> :call vimspector#StepOut()<CR>
|
||||
nmap <F5> <Plug>VimspectorContinue
|
||||
nmap <F3> <Plug>VimspectorStop
|
||||
nmap <F4> <Plug>VimspectorRestart
|
||||
nmap <F6> <Plug>VimspectorPause
|
||||
nmap <F9> <Plug>VimspectorToggleBreakpoint
|
||||
nmap <F8> <Plug>VimspectorAddFunctionBreakpoint
|
||||
nmap <F10> <Plug>VimspectorStepOver
|
||||
nmap <F11> <Plug>VimspectorStepInto
|
||||
nmap <F12> <Plug>VimspectorStepOut
|
||||
endif
|
||||
"}}}
|
||||
|
||||
command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr
|
||||
\ VimspectorWatch
|
||||
\ call vimspector#AddWatch( <f-args> )
|
||||
command! -bar -nargs=1 -complete=custom,vimspector#CompleteOutput
|
||||
\ VimspectorShowOutput
|
||||
\ call vimspector#ShowOutput( <f-args> )
|
||||
command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr
|
||||
\ VimspectorEval
|
||||
\ call vimspector#Evaluate( <f-args> )
|
||||
command! -bar
|
||||
\ VimspectorReset
|
||||
\ call vimspector#Reset()
|
||||
|
||||
" boilerplate {{{
|
||||
call s:restore_cpo()
|
||||
" }}}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ from vimspector import utils
|
|||
|
||||
|
||||
class CodeView( object ):
|
||||
def __init__( self, window ):
|
||||
def __init__( self, window, api_prefix ):
|
||||
self._window = window
|
||||
self._api_prefix = api_prefix
|
||||
|
||||
self._terminal_window = None
|
||||
self._terminal_buffer_number = None
|
||||
|
|
@ -214,8 +215,9 @@ class CodeView( object ):
|
|||
if self._terminal_window is not None:
|
||||
assert self._terminal_buffer_number
|
||||
if ( self._terminal_window.buffer.number == self._terminal_buffer_number
|
||||
and 'finished' in vim.eval( 'term_getstatus( {} )'.format(
|
||||
self._terminal_buffer_number ) ) ):
|
||||
and int( utils.Call( 'vimspector#internal#{}term#IsFinished'.format(
|
||||
self._api_prefix ),
|
||||
self._terminal_buffer_number ) ) ):
|
||||
window_for_start = self._terminal_window
|
||||
options[ 'curwin' ] = 1
|
||||
|
||||
|
|
@ -224,13 +226,11 @@ class CodeView( object ):
|
|||
with utils.TemporaryVimOptions( { 'splitright': True,
|
||||
'equalalways': False } ):
|
||||
with utils.LetCurrentWindow( window_for_start ):
|
||||
# TODO/FIXME: Do something about closing this when we reset ?
|
||||
vim_cmd = 'term_start( {}, {} )'.format( json.dumps( args ),
|
||||
json.dumps( options ) )
|
||||
|
||||
self._logger.debug( 'Start terminal: {}'.format( vim_cmd ) )
|
||||
|
||||
buffer_number = int( vim.eval( vim_cmd ) )
|
||||
buffer_number = int(
|
||||
utils.Call(
|
||||
'vimspector#internal#{}term#Start'.format( self._api_prefix ),
|
||||
args,
|
||||
options ) )
|
||||
terminal_window = vim.current.window
|
||||
|
||||
if buffer_number is None or buffer_number <= 0:
|
||||
|
|
|
|||
|
|
@ -65,6 +65,33 @@ class DebugAdapterConnection( object ):
|
|||
if not self._SendMessage( msg ):
|
||||
self._AbortRequest( request, 'Unable to send message' )
|
||||
|
||||
|
||||
def DoRequestSync( self, msg, timeout = 5000 ):
|
||||
result = {}
|
||||
|
||||
def handler( msg ):
|
||||
result[ 'response' ] = msg
|
||||
|
||||
def failure_handler( reason, msg ):
|
||||
result[ 'response' ] = msg
|
||||
result[ 'exception' ] = RuntimeError( reason )
|
||||
|
||||
self.DoRequest( handler, msg, failure_handler, timeout )
|
||||
|
||||
bug_catcher = 1000
|
||||
while not result and bug_catcher >= 0:
|
||||
vim.command( 'sleep 10m' )
|
||||
bug_catcher -= 10
|
||||
|
||||
if result.get( 'exception' ) is not None:
|
||||
raise result[ 'exception' ]
|
||||
|
||||
if result.get( 'response' ) is None:
|
||||
raise RuntimeError( "No response" )
|
||||
|
||||
return result[ 'response' ]
|
||||
|
||||
|
||||
def OnRequestTimeout( self, timer_id ):
|
||||
request_id = None
|
||||
for seq, request in self._outstanding_requests.items():
|
||||
|
|
|
|||
|
|
@ -41,11 +41,14 @@ USER_CHOICES = {}
|
|||
|
||||
|
||||
class DebugSession( object ):
|
||||
def __init__( self ):
|
||||
def __init__( self, api_prefix ):
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
|
||||
self._api_prefix = api_prefix
|
||||
|
||||
self._logger.info( "**** INITIALISING NEW VIMSPECTOR SESSION ****" )
|
||||
self._logger.info( "API is: {}".format( api_prefix ) )
|
||||
self._logger.info( 'VIMSPECTOR_HOME = %s', VIMSPECTOR_HOME )
|
||||
self._logger.info( 'gadgetDir = %s',
|
||||
install.GetGadgetDir( VIMSPECTOR_HOME,
|
||||
|
|
@ -400,12 +403,35 @@ class DebugSession( object ):
|
|||
def ShowOutput( self, category ):
|
||||
self._outputView.ShowOutput( category )
|
||||
|
||||
def GetOutputBuffers( self ):
|
||||
return self._outputView.GetCategories()
|
||||
|
||||
def GetCompletionsSync( self, text_line, column_in_bytes ):
|
||||
if not self._server_capabilities.get( 'supportsCompletionsRequest' ):
|
||||
return []
|
||||
|
||||
response = self._connection.DoRequestSync( {
|
||||
'command': 'completions',
|
||||
'arguments': {
|
||||
'frameId': self._stackTraceView.GetCurrentFrame()[ 'id' ],
|
||||
# TODO: encoding ? bytes/codepoints
|
||||
'text': text_line,
|
||||
'column': column_in_bytes
|
||||
}
|
||||
} )
|
||||
# TODO:
|
||||
# - start / length
|
||||
# - sortText
|
||||
return [ i.get( 'text' ) or i[ 'label' ]
|
||||
for i in response[ 'body' ][ 'targets' ] ]
|
||||
|
||||
|
||||
def _SetUpUI( self ):
|
||||
vim.command( 'tabnew' )
|
||||
self._uiTab = vim.current.tabpage
|
||||
|
||||
# Code window
|
||||
self._codeView = code.CodeView( vim.current.window )
|
||||
self._codeView = code.CodeView( vim.current.window, self._api_prefix )
|
||||
|
||||
# Call stack
|
||||
with utils.TemporaryVimOptions( { 'splitright': False,
|
||||
|
|
@ -441,7 +467,8 @@ class DebugSession( object ):
|
|||
vim.command( '10spl' )
|
||||
vim.command( 'enew' )
|
||||
self._outputView = output.OutputView( self._connection,
|
||||
vim.current.window )
|
||||
vim.current.window,
|
||||
self._api_prefix )
|
||||
|
||||
def ClearCurrentFrame( self ):
|
||||
self.SetCurrentFrame( None )
|
||||
|
|
@ -482,6 +509,8 @@ class DebugSession( object ):
|
|||
port = utils.AskForInput( 'Enter port to connect to: ' )
|
||||
self._adapter[ 'port' ] = port
|
||||
|
||||
self._connection_type = self._api_prefix + self._connection_type
|
||||
|
||||
# TODO: Do we actually need to copy and update or does Vim do that?
|
||||
env = os.environ.copy()
|
||||
if 'env' in self._adapter:
|
||||
|
|
@ -492,19 +521,18 @@ class DebugSession( object ):
|
|||
self._adapter[ 'cwd' ] = os.getcwd()
|
||||
|
||||
vim.vars[ '_vimspector_adapter_spec' ] = self._adapter
|
||||
channel_send_func = vim.bindeval(
|
||||
"vimspector#internal#{}#StartDebugSession( "
|
||||
" g:_vimspector_adapter_spec "
|
||||
")".format( self._connection_type ) )
|
||||
|
||||
if channel_send_func is None:
|
||||
if not vim.eval( "vimspector#internal#{}#StartDebugSession( "
|
||||
" g:_vimspector_adapter_spec "
|
||||
")".format( self._connection_type ) ):
|
||||
self._logger.error( "Unable to start debug server" )
|
||||
else:
|
||||
self._connection = debug_adapter_connection.DebugAdapterConnection(
|
||||
self,
|
||||
channel_send_func )
|
||||
lambda msg: utils.Call(
|
||||
"vimspector#internal#{}#Send".format( self._connection_type ),
|
||||
msg ) )
|
||||
|
||||
self._logger.info( 'Debug Adapter Started' )
|
||||
self._logger.info( 'Debug Adapter Started' )
|
||||
|
||||
def _StopDebugAdapter( self, callback = None ):
|
||||
def handler( *args ):
|
||||
|
|
@ -796,11 +824,12 @@ class DebugSession( object ):
|
|||
self._logger.debug( 'Defaulting working directory to %s',
|
||||
params[ 'cwd' ] )
|
||||
|
||||
buffer_number = self._codeView.LaunchTerminal( params )
|
||||
term_id = self._codeView.LaunchTerminal( params )
|
||||
|
||||
response = {
|
||||
'processId': vim.eval( 'job_info( term_getjob( {} ) )'
|
||||
'.process'.format( buffer_number ) )
|
||||
'processId': int( utils.Call(
|
||||
'vimspector#internal#{}term#GetPID'.format( self._api_prefix ),
|
||||
term_id ) )
|
||||
}
|
||||
|
||||
self._connection.DoResponse( message, None, response )
|
||||
|
|
|
|||
|
|
@ -40,10 +40,11 @@ def CategoryToBuffer( category ):
|
|||
|
||||
|
||||
class OutputView( object ):
|
||||
def __init__( self, connection, window ):
|
||||
def __init__( self, connection, window, api_prefix ):
|
||||
self._window = window
|
||||
self._connection = connection
|
||||
self._buffers = {}
|
||||
self._api_prefix = api_prefix
|
||||
|
||||
for b in set( BUFFER_MAP.values() ):
|
||||
self._CreateBuffer( b )
|
||||
|
|
@ -95,12 +96,12 @@ class OutputView( object ):
|
|||
def Clear( self ):
|
||||
for category, tab_buffer in self._buffers.items():
|
||||
if tab_buffer.is_job:
|
||||
utils.CleanUpCommand( category )
|
||||
utils.CleanUpCommand( category, self._api_prefix )
|
||||
try:
|
||||
vim.command( 'bdelete! {0}'.format( tab_buffer.buf.number ) )
|
||||
except vim.error as e:
|
||||
# FIXME: For now just ignore the "no buffers were deleted" error
|
||||
if 'E516' not in e:
|
||||
if 'E516' not in str( e ):
|
||||
raise
|
||||
|
||||
self._buffers = {}
|
||||
|
|
@ -116,17 +117,19 @@ class OutputView( object ):
|
|||
|
||||
def Evaluate( self, frame, expression ):
|
||||
console = self._buffers[ 'Console' ].buf
|
||||
utils.AppendToBuffer( console, 'Evaluating: ' + expression )
|
||||
with utils.ModifiableScratchBuffer( console ):
|
||||
utils.AppendToBuffer( console, 'Evaluating: ' + expression )
|
||||
|
||||
def print_result( message ):
|
||||
utils.AppendToBuffer( console,
|
||||
'Evaluated: ' + expression )
|
||||
with utils.ModifiableScratchBuffer( console ):
|
||||
utils.AppendToBuffer( console,
|
||||
'Evaluated: ' + expression )
|
||||
|
||||
result = message[ 'body' ][ 'result' ]
|
||||
if result is None:
|
||||
result = 'null'
|
||||
result = message[ 'body' ][ 'result' ]
|
||||
if result is None:
|
||||
result = 'null'
|
||||
|
||||
utils.AppendToBuffer( console, ' Result: ' + result )
|
||||
utils.AppendToBuffer( console, ' Result: ' + result )
|
||||
|
||||
request = {
|
||||
'command': 'evaluate',
|
||||
|
|
@ -161,7 +164,7 @@ class OutputView( object ):
|
|||
cmd = [ 'tail', '-F', '-n', '+1', '--', file_name ]
|
||||
|
||||
if cmd is not None:
|
||||
out, err = utils.SetUpCommandBuffer( cmd, category )
|
||||
out, err = utils.SetUpCommandBuffer( cmd, category, self._api_prefix )
|
||||
self._buffers[ category + '-out' ] = TabBuffer( out,
|
||||
len( self._buffers ) )
|
||||
self._buffers[ category + '-out' ].is_job = True
|
||||
|
|
@ -205,3 +208,6 @@ class OutputView( object ):
|
|||
tab_buffer.index,
|
||||
utils.Escape( category ),
|
||||
'*' if tab_buffer.flag else '' ) )
|
||||
|
||||
def GetCategories( self ):
|
||||
return [ category for category in self._buffers.keys() ]
|
||||
|
|
|
|||
|
|
@ -56,24 +56,26 @@ def OpenFileInCurrentWindow( file_name ):
|
|||
return vim.buffers[ buffer_number ]
|
||||
|
||||
|
||||
def SetUpCommandBuffer( cmd, name ):
|
||||
bufs = vim.bindeval(
|
||||
'vimspector#internal#job#StartCommandWithLog( {}, "{}" )'.format(
|
||||
def SetUpCommandBuffer( cmd, name, api_prefix ):
|
||||
bufs = vim.eval(
|
||||
'vimspector#internal#{}job#StartCommandWithLog( {}, "{}" )'.format(
|
||||
api_prefix,
|
||||
json.dumps( cmd ),
|
||||
name ) )
|
||||
|
||||
if bufs is None:
|
||||
raise RuntimeError( "Unable to start job {}: {}".format( cmd, name ) )
|
||||
elif not all( b > 0 for b in bufs ):
|
||||
elif not all( int( b ) > 0 for b in bufs ):
|
||||
raise RuntimeError( "Unable to get all streams for job {}: {}".format(
|
||||
name,
|
||||
cmd ) )
|
||||
|
||||
return [ vim.buffers[ b ] for b in bufs ]
|
||||
return [ vim.buffers[ int( b ) ] for b in bufs ]
|
||||
|
||||
|
||||
def CleanUpCommand( name ):
|
||||
return vim.eval( 'vimspector#internal#job#CleanUpCommand( "{}" )'.format(
|
||||
def CleanUpCommand( name, api_prefix ):
|
||||
return vim.eval( 'vimspector#internal#{}job#CleanUpCommand( "{}" )'.format(
|
||||
api_prefix,
|
||||
name ) )
|
||||
|
||||
|
||||
|
|
@ -102,7 +104,7 @@ def SetUpHiddenBuffer( buf, name ):
|
|||
def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ):
|
||||
# This feature is _super_ new, so only enable when available
|
||||
if not int( vim.eval( "exists( '*prompt_setprompt' )" ) ):
|
||||
return SetUpScratchBuffer( buf, name )
|
||||
return SetUpHiddenBuffer( buf, name )
|
||||
|
||||
buf.options[ 'buftype' ] = 'prompt'
|
||||
buf.options[ 'swapfile' ] = False
|
||||
|
|
@ -449,3 +451,19 @@ def ToUnicode( b ):
|
|||
if isinstance( b, bytes ):
|
||||
return b.decode( 'utf-8' )
|
||||
return b
|
||||
|
||||
|
||||
# Call a vimscript function with suplied arguments.
|
||||
def Call( vimscript_function, *args ):
|
||||
call = vimscript_function + '('
|
||||
for index, arg in enumerate( args ):
|
||||
if index > 0:
|
||||
call += ', '
|
||||
|
||||
arg_name = 'vimspector_internal_arg_{}'.format( index )
|
||||
vim.vars[ arg_name ] = arg
|
||||
call += 'g:' + arg_name
|
||||
|
||||
call += ')'
|
||||
_logger.debug( 'Calling: {}'.format( call ) )
|
||||
return vim.eval( call )
|
||||
|
|
|
|||
|
|
@ -105,6 +105,9 @@ class VariablesView( object ):
|
|||
for k, v in self._oldoptions.items():
|
||||
vim.options[ k ] = v
|
||||
|
||||
vim.command( 'bdelete! ' + str( self._watch.win.buffer.number ) )
|
||||
vim.command( 'bdelete! ' + str( self._vars.win.buffer.number ) )
|
||||
|
||||
def LoadScopes( self, frame ):
|
||||
def scopes_consumer( message ):
|
||||
old_scopes = self._scopes
|
||||
|
|
|
|||
51
run_tests
51
run_tests
|
|
@ -1,8 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if [ "$1" == "--help" ]; then
|
||||
echo "$(basename $0) <optional list of tests in form file:func>"
|
||||
echo "e.g.: "
|
||||
echo " - run all tests: $0"
|
||||
echo " - run specific tests script: $0 signature_help.test.vim"
|
||||
echo " - run specific tests fun: $0 signature_help.test.vim:Test_signatures_TopLine\(\)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
RUN_VIM="vim --clean --not-a-term"
|
||||
RUN_TEST="${RUN_VIM} -S lib/run_test.vim"
|
||||
|
||||
NODE_VERSION_MAJ=$(node --version \
|
||||
| awk 'BEGIN { FS="." } { print substr( $1, 2 ) }')
|
||||
if [ ${NODE_VERSION_MAJ} -gt "10" ]; then
|
||||
echo "ERROR: Tests must be run against node 10 or earlier because python." >&2
|
||||
echo "You have $(node --version)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$VIMSPECTOR_MIMODE" ]; then
|
||||
if which lldb >/dev/null 2>&1; then
|
||||
export VIMSPECTOR_MIMODE=lldb
|
||||
|
|
@ -24,8 +41,8 @@ set -e
|
|||
set +e
|
||||
echo "%DONE - built test programs"
|
||||
|
||||
pushd tests > /dev/null
|
||||
|
||||
# Start
|
||||
pushd $(dirname $0)/tests > /dev/null
|
||||
echo "Running Vimspector Vim tests"
|
||||
|
||||
RESULT=0
|
||||
|
|
@ -39,23 +56,45 @@ fi
|
|||
for t in ${TESTS}; do
|
||||
echo ""
|
||||
echo "%RUN: $t"
|
||||
rm -f messages debuglog
|
||||
|
||||
# split on : into fileName and testName
|
||||
IFS=: read -s t T <<< "$t"
|
||||
|
||||
if ${RUN_TEST} --cmd 'au SwapExists * let v:swapchoice = "e"' $t $T; then
|
||||
TESTLOGDIR=$(pwd)/logs/$t
|
||||
|
||||
if ${RUN_TEST} --cmd 'au SwapExists * let v:swapchoice = "e"' $t $T \
|
||||
&& [ -f $t.res ]; then
|
||||
echo "%PASS: $t PASSED"
|
||||
else
|
||||
cat messages
|
||||
echo "%FAIL: $t FAILED"
|
||||
echo "%FAIL: $t FAILED - see $TESTLOGDIR"
|
||||
RESULT=1
|
||||
fi
|
||||
|
||||
rm -rf $TESTLOGDIR
|
||||
mkdir -p $TESTLOGDIR
|
||||
${RUN_VIM} --version > ${TESTLOGDIR}/vimversion
|
||||
for l in messages debuglog test.log *.testlog; do
|
||||
# In CI we can't view the output files, so we just have to cat them
|
||||
if [ -f $l ]; then
|
||||
if [ "$VIMSPECTOR_TEST_STDOUT" ]; then
|
||||
echo ""
|
||||
echo ""
|
||||
echo "*** START: $l ***"
|
||||
cat $l
|
||||
echo "*** END: $l ***"
|
||||
fi
|
||||
mv $l $TESTLOGDIR
|
||||
fi
|
||||
done
|
||||
|
||||
rm -f $t.res
|
||||
done
|
||||
|
||||
echo "Done running tests"
|
||||
popd > /dev/null
|
||||
|
||||
echo ""
|
||||
echo "All done."
|
||||
|
||||
|
||||
exit $RESULT
|
||||
|
|
|
|||
1
tests/.gitignore
vendored
Normal file
1
tests/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
logs/
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
"
|
||||
" To execute only specific test functions, add a second argument. It will be
|
||||
" matched against the names of the Test_ funtion. E.g.:
|
||||
" ../vim -u NONE -S runtest.vim test_channel.vim open_delay
|
||||
" ../vim -Nu NONE vimrc -S lib/run_test.vim test_channel.vim open_delay
|
||||
" The output can be found in the "messages" file.
|
||||
"
|
||||
" The test script may contain anything, only functions that start with
|
||||
|
|
@ -26,8 +26,16 @@
|
|||
" It will be called after each Test_ function.
|
||||
"
|
||||
" When debugging a test it can be useful to add messages to v:errors:
|
||||
" call add(v:errors, "this happened")
|
||||
" call add(v:errors, "this happened")
|
||||
"
|
||||
" But for real debug logging:
|
||||
" call ch_log( ",,,message..." )
|
||||
" Then view it in 'debuglog'
|
||||
|
||||
" Let a test take up to 1 minute
|
||||
let s:single_test_timeout = 60000
|
||||
|
||||
" Restrict the runtimepath to the exact minimum needed for testing
|
||||
set rtp=$PWD/lib,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after
|
||||
if has('packages')
|
||||
let &packpath = &rtp
|
||||
|
|
@ -37,7 +45,7 @@ call ch_logfile( 'debuglog', 'w' )
|
|||
|
||||
" For consistency run all tests with 'nocompatible' set.
|
||||
" This also enables use of line continuation.
|
||||
set nocp viminfo+=nviminfo
|
||||
set nocp
|
||||
|
||||
" Use utf-8 by default, instead of whatever the system default happens to be.
|
||||
" Individual tests can overrule this at the top of the file.
|
||||
|
|
@ -47,11 +55,23 @@ set encoding=utf-8
|
|||
set nomore
|
||||
|
||||
" Output all messages in English.
|
||||
lang mess C
|
||||
lang messages C
|
||||
|
||||
" Always use forward slashes.
|
||||
set shellslash
|
||||
|
||||
func s:TestFailed()
|
||||
let log = readfile( expand( '~/.vimspector.log' ) )
|
||||
let logfile = s:testid_filesafe . '_vimspector.log.testlog'
|
||||
call writefile( log, logfile, 's' )
|
||||
call add( s:messages, 'Wrote log for failed test: ' . logfile )
|
||||
endfunc
|
||||
|
||||
func! Abort( timer_id )
|
||||
call assert_report( 'Test timed out!!!' )
|
||||
qa!
|
||||
endfunc
|
||||
|
||||
func RunTheTest(test)
|
||||
echo 'Executing ' . a:test
|
||||
|
||||
|
|
@ -107,40 +127,38 @@ func RunTheTest(test)
|
|||
|
||||
call add(s:messages, 'Executing ' . a:test)
|
||||
let s:done += 1
|
||||
let timer = timer_start( s:single_test_timeout, funcref( 'Abort' ) )
|
||||
|
||||
if a:test =~ 'Test_nocatch_'
|
||||
" Function handles errors itself. This avoids skipping commands after the
|
||||
" error.
|
||||
try
|
||||
let s:test = a:test
|
||||
let s:testid = g:testpath . ':' . a:test
|
||||
|
||||
let test_filesafe = substitute( a:test, '[)(,:]', '_', 'g' )
|
||||
let s:testid_filesafe = g:testpath . '_' . test_filesafe
|
||||
|
||||
au VimLeavePre * call EarlyExit(s:test)
|
||||
exe 'call ' . a:test
|
||||
else
|
||||
try
|
||||
let s:test = a:test
|
||||
let s:testid = g:testpath . ':' . a:test
|
||||
let test_filesafe = substitute( a:test, ')', '_', 'g' )
|
||||
let test_filesafe = substitute( test_filesafe, '(', '_', 'g' )
|
||||
let test_filesafe = substitute( test_filesafe, ',', '_', 'g' )
|
||||
let test_filesafe = substitute( test_filesafe, ':', '_', 'g' )
|
||||
let s:testid_filesafe = g:testpath . '_' . test_filesafe
|
||||
au VimLeavePre * call EarlyExit(s:test)
|
||||
exe 'call ' . a:test
|
||||
au! VimLeavePre
|
||||
catch /^\cskipped/
|
||||
call add(s:messages, ' Skipped')
|
||||
call add(s:skipped,
|
||||
\ 'SKIPPED ' . a:test
|
||||
\ . ': '
|
||||
\ . substitute(v:exception, '^\S*\s\+', '', ''))
|
||||
catch
|
||||
call add(v:errors,
|
||||
\ 'Caught exception in ' . a:test
|
||||
\ . ': '
|
||||
\ . v:exception
|
||||
\ . ' @ '
|
||||
\ . g:testpath
|
||||
\ . ':'
|
||||
\ . v:throwpoint)
|
||||
endtry
|
||||
endif
|
||||
au! VimLeavePre
|
||||
catch /^\cskipped/
|
||||
call add(s:messages, ' Skipped')
|
||||
call add(s:skipped,
|
||||
\ 'SKIPPED ' . a:test
|
||||
\ . ': '
|
||||
\ . substitute(v:exception, '^\S*\s\+', '', ''))
|
||||
catch
|
||||
call add(v:errors,
|
||||
\ 'Caught exception in ' . a:test
|
||||
\ . ': '
|
||||
\ . v:exception
|
||||
\ . ' @ '
|
||||
\ . g:testpath
|
||||
\ . ':'
|
||||
\ . v:throwpoint)
|
||||
|
||||
call s:TestFailed()
|
||||
endtry
|
||||
|
||||
call timer_stop( timer )
|
||||
|
||||
" In case 'insertmode' was set and something went wrong, make sure it is
|
||||
" reset to avoid trouble with anything else.
|
||||
|
|
@ -203,24 +221,16 @@ endfunc
|
|||
func AfterTheTest()
|
||||
if len(v:errors) > 0
|
||||
let s:fail += 1
|
||||
call s:TestFailed()
|
||||
call add(s:errors, 'Found errors in ' . s:testid . ':')
|
||||
call extend(s:errors, v:errors)
|
||||
let v:errors = []
|
||||
|
||||
let log = readfile( expand( '~/.vimspector.log' ) )
|
||||
let logfile = s:testid_filesafe . '.vimspector.log'
|
||||
call writefile( log, logfile, 's' )
|
||||
call add( s:messages, 'Wrote log for failed test: ' . logfile )
|
||||
call extend( s:messages, log )
|
||||
endif
|
||||
endfunc
|
||||
|
||||
func EarlyExit(test)
|
||||
" It's OK for the test we use to test the quit detection.
|
||||
if a:test != 'Test_zz_quit_detected()'
|
||||
call add(v:errors, 'Test caused Vim to exit: ' . a:test)
|
||||
endif
|
||||
|
||||
call add(v:errors, 'Test caused Vim to exit: ' . a:test)
|
||||
call FinishTesting()
|
||||
endfunc
|
||||
|
||||
|
|
@ -233,17 +243,19 @@ func FinishTesting()
|
|||
|
||||
if s:fail == 0
|
||||
" Success, create the .res file so that make knows it's done.
|
||||
exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
|
||||
write
|
||||
call writefile( [], g:testname . '.res', 's' )
|
||||
endif
|
||||
|
||||
if len(s:errors) > 0
|
||||
" Append errors to test.log
|
||||
split test.log
|
||||
call append(line('$'), '')
|
||||
call append(line('$'), 'From ' . g:testpath . ':')
|
||||
call append(line('$'), s:errors)
|
||||
write
|
||||
let l = []
|
||||
if filereadable( 'test.log' )
|
||||
let l = readfile( 'test.log' )
|
||||
endif
|
||||
call writefile( l->extend( [ '', 'From ' . g:testpath . ':' ] )
|
||||
\ ->extend( s:errors ),
|
||||
\ 'test.log',
|
||||
\ 's' )
|
||||
endif
|
||||
|
||||
if s:done == 0
|
||||
|
|
@ -264,11 +276,14 @@ func FinishTesting()
|
|||
call extend(s:messages, s:skipped)
|
||||
|
||||
" Append messages to the file "messages"
|
||||
split messages
|
||||
call append(line('$'), '')
|
||||
call append(line('$'), 'From ' . g:testpath . ':')
|
||||
call append(line('$'), s:messages)
|
||||
write
|
||||
let l = []
|
||||
if filereadable( 'messages' )
|
||||
let l = readfile( 'messages' )
|
||||
endif
|
||||
call writefile( l->extend( [ '', 'From ' . g:testpath . ':' ] )
|
||||
\ ->extend( s:messages ),
|
||||
\ 'messages',
|
||||
\ 's' )
|
||||
|
||||
if s:fail > 0
|
||||
cquit!
|
||||
|
|
@ -317,6 +332,7 @@ endif
|
|||
for s:test in sort(s:tests)
|
||||
" Silence, please!
|
||||
set belloff=all
|
||||
|
||||
call RunTheTest(s:test)
|
||||
call AfterTheTest()
|
||||
endfor
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue