From de2a924c3883a3bbbcbeba1e772661694cc5bcd1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 15:12:28 +0100 Subject: [PATCH 01/23] use simpler UI setup commands --- python3/vimspector/debug_session.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 1302ab6..eb3c701 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -479,8 +479,7 @@ class DebugSession( object ): # Call stack with utils.TemporaryVimOptions( { 'splitright': False, 'equalalways': False, } ): - vim.command( 'topleft 50vspl' ) - vim.command( 'enew' ) + vim.command( 'topleft vertical 50new' ) self._stackTraceView = stack_trace.StackTraceView( self, self._connection, vim.current.buffer ) @@ -489,13 +488,11 @@ class DebugSession( object ): 'eadirection': 'ver', 'equalalways': True } ): # Watches - vim.command( 'spl' ) - vim.command( 'enew' ) + vim.command( 'new' ) watch_win = vim.current.window # Variables - vim.command( 'spl' ) - vim.command( 'enew' ) + vim.command( 'new' ) vars_win = vim.current.window self._variablesView = variables.VariablesView( self._connection, @@ -507,8 +504,7 @@ class DebugSession( object ): vim.current.window = self._codeView._window # Output/logging - vim.command( '10spl' ) - vim.command( 'enew' ) + vim.command( '10new' ) self._outputView = output.OutputView( self._connection, vim.current.window, self._api_prefix ) From 2c5937c2c17dae0a922f0fba8d146f5fa80ea8c6 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 16:49:48 +0100 Subject: [PATCH 02/23] Support basic UI customisation via a User autocommand --- plugin/vimspector.vim | 6 +++++ python3/vimspector/debug_session.py | 38 ++++++++++++++++++++++------- python3/vimspector/stack_trace.py | 8 +++--- python3/vimspector/utils.py | 4 +++ support/custom_ui_vimrc | 23 +++++++++++++++++ support/minimal_vimrc | 4 +++ 6 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 support/custom_ui_vimrc create mode 100644 support/minimal_vimrc diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index cb921eb..1a6ffdc 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -97,6 +97,12 @@ command! -bar \ VimspectorReset \ call vimspector#Reset() +" Dummy autocommands so that we can call this whenever +augroup VimspectorUserAutoCmds + au! + au User VimspectorUICreated silent +augroup END + " boilerplate {{{ call s:restore_cpo() " }}} diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index eb3c701..61d7a6c 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -335,6 +335,8 @@ class DebugSession( object ): self._logger.info( "Debugging complete." ) if self._uiTab: self._logger.debug( "Clearing down UI" ) + + del vim.vars[ 'vimspector_session_windows' ] vim.current.tabpage = self._uiTab self._splash_screen = utils.HideSplash( self._api_prefix, @@ -473,42 +475,60 @@ class DebugSession( object ): self._uiTab = vim.current.tabpage # Code window - self._codeView = code.CodeView( vim.current.window, - self._api_prefix ) + code_window = vim.current.window + self._codeView = code.CodeView( code_window, self._api_prefix ) # Call stack with utils.TemporaryVimOptions( { 'splitright': False, 'equalalways': False, } ): vim.command( 'topleft vertical 50new' ) + stack_trace_window = vim.current.window self._stackTraceView = stack_trace.StackTraceView( self, self._connection, - vim.current.buffer ) + stack_trace_window ) with utils.TemporaryVimOptions( { 'splitbelow': False, 'eadirection': 'ver', 'equalalways': True } ): # Watches vim.command( 'new' ) - watch_win = vim.current.window + watch_window = vim.current.window # Variables vim.command( 'new' ) - vars_win = vim.current.window + vars_window = vim.current.window self._variablesView = variables.VariablesView( self._connection, - vars_win, - watch_win ) + vars_window, + watch_window ) with utils.TemporaryVimOption( 'splitbelow', True ): - vim.current.window = self._codeView._window + vim.current.window = code_window # Output/logging vim.command( '10new' ) + output_window = vim.current.window self._outputView = output.OutputView( self._connection, - vim.current.window, + output_window, self._api_prefix ) + # TODO: If/when we support multiple sessions, we'll need some way to + # indicate which tab was created and store all the tabs + vim.vars[ 'vimspector_session_windows' ] = { + 'tabpage': self._uiTab.number, + 'code': utils.WindowID( code_window, self._uiTab ), + 'stack_trace': utils.WindowID( stack_trace_window, self._uiTab ), + 'variables': utils.WindowID( vars_window, self._uiTab ), + 'watches': utils.WindowID( watch_window, self._uiTab ), + 'output': utils.WindowID( output_window, self._uiTab ), + } + with utils.RestoreCursorPosition(): + with utils.RestoreCurrentWindow(): + with utils.RestoreCurrentBuffer( vim.current.window ): + vim.command( 'doautocmd User VimspectorUICreated' ) + + def ClearCurrentFrame( self ): self.SetCurrentFrame( None ) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 0afeb81..d5ea0b5 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -21,11 +21,11 @@ from vimspector import utils class StackTraceView( object ): - def __init__( self, session, connection, buf ): + def __init__( self, session, connection, win ): self._logger = logging.getLogger( __name__ ) utils.SetUpLogging( self._logger ) - self._buf = buf + self._buf = win.buffer self._session = session self._connection = connection @@ -38,9 +38,7 @@ class StackTraceView( object ): self._scratch_buffers = [] utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' ) - - vim.current.buffer = self._buf - utils.SetUpUIWindow( vim.current.window ) + utils.SetUpUIWindow( win ) vim.command( 'nnoremap :call vimspector#GoToFrame()' ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 48991f7..d4a7749 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -639,3 +639,7 @@ def GetUnusedLocalPort(): port = sock.getsockname()[ 1 ] sock.close() return port + + +def WindowID( window, tab ): + return int( Call( 'win_getid', window.number, tab.number ) ) diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc new file mode 100644 index 0000000..1f30aeb --- /dev/null +++ b/support/custom_ui_vimrc @@ -0,0 +1,23 @@ +execute 'source' expand( ':p:h' ) . '/minimal_vimrc' +set noequalalways + +function! s:CustomiseUI() + let wins = g:vimspector_session_windows + + " Close the Variables window + call win_execute( wins.variables, 'q' ) + + " Put the stack trace at the top of the "left bar" (rotate) + call win_gotoid( wins.stack_trace ) + wincmd r + + " Make the output window 10 lines high and right at the top of the screen + call win_gotoid( wins.output ) + 10wincmd _ + wincmd K +endfunction + +augroup TestUICustomistaion + autocmd! + autocmd User VimspectorUICreated call s:CustomiseUI() +augroup END diff --git a/support/minimal_vimrc b/support/minimal_vimrc new file mode 100644 index 0000000..3626d39 --- /dev/null +++ b/support/minimal_vimrc @@ -0,0 +1,4 @@ +let s:vimspector_path = expand( ':p:h:h' ) +let g:vimspector_enable_mappings = 'HUMAN' +exe 'source ' . s:vimspector_path . '/tests/vimrc' + From ebc0b3607a06cca20b4c01490d3e79b474903e43 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 17:05:39 +0100 Subject: [PATCH 03/23] Neovim sigh --- support/custom_ui_vimrc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index 1f30aeb..8e3746a 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -5,7 +5,13 @@ function! s:CustomiseUI() let wins = g:vimspector_session_windows " Close the Variables window - call win_execute( wins.variables, 'q' ) + if has( 'nvim' ) + " No win_execute in neovim + call win_gotoid( wins.variables ) + quit + else + call win_execute( wins.variables, 'q' ) + endif " Put the stack trace at the top of the "left bar" (rotate) call win_gotoid( wins.stack_trace ) From 727214c599749cffe30bcaabff368ec9af4d1194 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 21:59:28 +0100 Subject: [PATCH 04/23] Add a bunch of tests for the ui customisation --- azure-pipelines.yml | 4 +- compiler/vimspector_test.vim | 6 +- python3/vimspector/code.py | 3 + run_tests | 27 ++- tests/ui.test.vim | 326 +++++++++++++++++++++++++++++++++++ tests/vimrc | 1 + 6 files changed, 354 insertions(+), 13 deletions(-) create mode 100644 tests/ui.test.vim diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5dc944a..1eeb1c4 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -62,7 +62,7 @@ stages: displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: gdb - VIMSPECTOR_TEST_STDOUT: true + VIMSPECTOR_TEST_STDOUT: all - bash: ./make_package linux $(Build.SourceVersion) displayName: 'Package' @@ -107,7 +107,7 @@ stages: displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: lldb - VIMSPECTOR_TEST_STDOUT: true + VIMSPECTOR_TEST_STDOUT: all - bash: ./make_package macos $(Build.SourceVersion) displayName: 'Package' diff --git a/compiler/vimspector_test.vim b/compiler/vimspector_test.vim index 8ccd734..5264b4b 100644 --- a/compiler/vimspector_test.vim +++ b/compiler/vimspector_test.vim @@ -92,7 +92,7 @@ function! s:RunTestUnderCursor() let l:cwd = getcwd() execute 'lcd ' . s:root_dir try - execute s:make_cmd . ' ' + execute s:make_cmd . ' --report messages ' \ . get( g:, 'vimspector_test_args', '' ) . ' ' \ . l:test_arg finally @@ -105,7 +105,7 @@ function! s:RunTest() let l:cwd = getcwd() execute 'lcd ' . s:root_dir try - execute s:make_cmd . ' ' + execute s:make_cmd . ' --report messages ' \ . get( g:, 'vimspector_test_args', '' ) \ . ' %:p:t' finally @@ -118,7 +118,7 @@ function! s:RunAllTests() let l:cwd = getcwd() execute 'lcd ' . s:root_dir try - execute s:make_cmd . ' ' + execute s:make_cmd . ' --report messages ' \ . get( g:, 'vimspector_test_args', '' ) finally execute 'lcd ' . l:cwd diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index b826d21..cadd50b 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -258,5 +258,8 @@ class CodeView( object ): else: self._terminal_window = terminal_window self._terminal_buffer_number = buffer_number + vim.vars[ 'vimspector_session_windows' ][ 'terminal' ] = utils.WindowID( + self._terminal_window, + vim.current.tabpage ) return buffer_number diff --git a/run_tests b/run_tests index 7ff2ebd..7c7fc59 100755 --- a/run_tests +++ b/run_tests @@ -3,8 +3,9 @@ if [ "$1" == "--help" ]; then echo "$(basename $0) [--basedir ] [--install] " echo "" - echo " --basedir path to runtime directory like the optino to install_gadget.py" - echo " --install run install_gadget.py, useful with --basedir" + echo " --basedir path to runtime directory like the optino to install_gadget.py" + echo " --install run install_gadget.py, useful with --basedir" + echo " --report how verbose to be with stdout" echo "e.g.: " echo " - run all tests: $0" echo " - run specific tests script: $0 signature_help.test.vim" @@ -23,19 +24,24 @@ BASEDIR_CMD='py3 pass' while [ -n "$1" ]; do case "$1" in "--basedir") - BASEDIR=$2 + shift + BASEDIR=$1 + shift if [[ ! $BASEDIR = /* ]]; then # Relative BASEDIR=$(pwd)/${BASEDIR} fi BASEDIR_CMD="let g:vimspector_base_dir='${BASEDIR}'" - shift - shift ;; "--install") INSTALL=1 shift ;; + "--report") + shift + VIMSPECTOR_TEST_STDOUT=$1 + shift + ;; "--") shift break @@ -109,10 +115,16 @@ for t in ${TESTS}; do rm -rf $TESTLOGDIR mkdir -p $TESTLOGDIR ${RUN_VIM} --version > ${TESTLOGDIR}/vimversion + if [ "$VIMSPECTOR_TEST_STDOUT" = "messages" ]; then + if [ -f messages ]; then + cat messages + fi + fi + 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 + if [ "$VIMSPECTOR_TEST_STDOUT" = "all" ]; then echo "" echo "" echo "*** START: $l ***" @@ -130,7 +142,6 @@ echo "Done running tests" popd > /dev/null echo "" -echo "All done." - +echo "All done. Exit with ${RESULT}" exit $RESULT diff --git a/tests/ui.test.vim b/tests/ui.test.vim new file mode 100644 index 0000000..ec1bc4c --- /dev/null +++ b/tests/ui.test.vim @@ -0,0 +1,326 @@ +let s:fn='main.py' + +function! SetUp() + call vimspector#test#setup#SetUpWithMappings( 'HUMAN' ) +endfunction + +function! ClearDown() + call vimspector#test#setup#ClearDown() +endfunction + +function! s:StartDebugging() + lcd ../support/test/python/simple_python + exe 'edit ' . s:fn + call setpos( '.', [ 0, 23, 1 ] ) + call vimspector#ToggleBreakpoint() + call vimspector#LaunchWithSettings( { 'configuration': 'run' } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 23, 1 ) +endfunction + +function! Test_StandardLayout() + call s:StartDebugging() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + call assert_equal( + \ [ 'row', [ + \ [ 'col', [ + \ [ 'leaf', g:vimspector_session_windows.variables ], + \ [ 'leaf', g:vimspector_session_windows.watches ], + \ [ 'leaf', g:vimspector_session_windows.stack_trace ], + \ ] ], + \ [ 'col', [ + \ [ 'row', [ + \ [ 'leaf', g:vimspector_session_windows.code ], + \ [ 'leaf', g:vimspector_session_windows.terminal ], + \ ] ], + \ [ 'leaf', g:vimspector_session_windows.output ], + \ ] ] + \ ] ], + \ winlayout( g:vimspector_session_windows.tabpage ) ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_CloseVariables() + call s:StartDebugging() + + call win_execute( g:vimspector_session_windows.variables, 'q' ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + call assert_equal( + \ [ 'row', [ + \ [ 'col', [ + \ [ 'leaf', g:vimspector_session_windows.watches ], + \ [ 'leaf', g:vimspector_session_windows.stack_trace ], + \ ] ], + \ [ 'col', [ + \ [ 'row', [ + \ [ 'leaf', g:vimspector_session_windows.code ], + \ [ 'leaf', g:vimspector_session_windows.terminal ], + \ ] ], + \ [ 'leaf', g:vimspector_session_windows.output ], + \ ] ] + \ ] ], + \ winlayout( g:vimspector_session_windows.tabpage ) ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_CloseWatches() + call s:StartDebugging() + + call win_execute( g:vimspector_session_windows.watches, 'q' ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + " Add a wtch + call vimspector#AddWatch( 't' ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 26, 1 ) + + call assert_equal( + \ [ 'row', [ + \ [ 'col', [ + \ [ 'leaf', g:vimspector_session_windows.variables ], + \ [ 'leaf', g:vimspector_session_windows.stack_trace ], + \ ] ], + \ [ 'col', [ + \ [ 'row', [ + \ [ 'leaf', g:vimspector_session_windows.code ], + \ [ 'leaf', g:vimspector_session_windows.terminal ], + \ ] ], + \ [ 'leaf', g:vimspector_session_windows.output ], + \ ] ] + \ ] ], + \ winlayout( g:vimspector_session_windows.tabpage ) ) + + " Replace the variables view with a watches view! + call win_execute( g:vimspector_session_windows.variables, + \ 'bu vimspector.Watches' ) + + " Delete a watch expression + call win_gotoid( g:vimspector_session_windows.variables ) + call setpos( '.', [ 0, 3, 1 ] ) + call feedkeys( "\", 'xt' ) + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + + call vimspector#StepInto() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 13, 1 ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 14, 1 ) + call vimspector#AddWatch( 'i' ) + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i', + \ ' *- Result: 0', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#AddWatch( 'i+1' ) + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i', + \ ' - Result: 0', + \ 'Expression: i+1', + \ ' *- Result: 1', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#AddWatch( 'i+2' ) + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i', + \ ' - Result: 0', + \ 'Expression: i+1', + \ ' - Result: 1', + \ 'Expression: i+2', + \ ' *- Result: 2', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Delete that middle watch + call win_gotoid( g:vimspector_session_windows.variables ) + call setpos( '.', [ 0, 4, 1 ] ) + call vimspector#DeleteWatch() + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i', + \ ' - Result: 0', + \ 'Expression: i+2', + \ ' *- Result: 2', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 15, 1 ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i', + \ ' - Result: 0', + \ 'Expression: i+2', + \ ' - Result: 2', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Delete the top watch + call win_gotoid( g:vimspector_session_windows.variables ) + call setpos( '.', [ 0, 3, 1 ] ) + call vimspector#DeleteWatch() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 13, 1 ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 14, 1 ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i+2', + \ ' *- Result: 3', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_CloseStackTrace() + call s:StartDebugging() + + call win_execute( g:vimspector_session_windows.stack_trace, 'q' ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + call assert_equal( + \ [ 'row', [ + \ [ 'col', [ + \ [ 'leaf', g:vimspector_session_windows.variables ], + \ [ 'leaf', g:vimspector_session_windows.watches ], + \ ] ], + \ [ 'col', [ + \ [ 'row', [ + \ [ 'leaf', g:vimspector_session_windows.code ], + \ [ 'leaf', g:vimspector_session_windows.terminal ], + \ ] ], + \ [ 'leaf', g:vimspector_session_windows.output ], + \ ] ] + \ ] ], + \ winlayout( g:vimspector_session_windows.tabpage ) ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_CloseOutput() + call s:StartDebugging() + + call win_execute( g:vimspector_session_windows.output, 'q' ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + call assert_equal( + \ [ 'row', [ + \ [ 'col', [ + \ [ 'leaf', g:vimspector_session_windows.variables ], + \ [ 'leaf', g:vimspector_session_windows.watches ], + \ [ 'leaf', g:vimspector_session_windows.stack_trace ], + \ ] ], + \ [ 'leaf', g:vimspector_session_windows.code ], + \ [ 'leaf', g:vimspector_session_windows.terminal ], + \ ] ], + \ winlayout( g:vimspector_session_windows.tabpage ) ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_CustomUI() + augroup TestCustomUI + au! + au User VimspectorUICreated + \ call win_execute( g:vimspector_session_windows.watches, 'q' ) + augroup END + + call s:StartDebugging() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + " Add a watch + call vimspector#AddWatch( 't' ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 26, 1 ) + + call assert_equal( + \ [ 'row', [ + \ [ 'col', [ + \ [ 'leaf', g:vimspector_session_windows.variables ], + \ [ 'leaf', g:vimspector_session_windows.stack_trace ], + \ ] ], + \ [ 'col', [ + \ [ 'row', [ + \ [ 'leaf', g:vimspector_session_windows.code ], + \ [ 'leaf', g:vimspector_session_windows.terminal ], + \ ] ], + \ [ 'leaf', g:vimspector_session_windows.output ], + \ ] ] + \ ] ], + \ winlayout( g:vimspector_session_windows.tabpage ) ) + + au! TestCustomUI + call vimspector#test#setup#Reset() + %bwipe! +endfunction diff --git a/tests/vimrc b/tests/vimrc index e845f78..e362824 100644 --- a/tests/vimrc +++ b/tests/vimrc @@ -1,5 +1,6 @@ let g:vimspector_test_plugin_path = expand( ':p:h:h' ) set mouse=a +set noequalalways let &rtp = &rtp . ',' . g:vimspector_test_plugin_path From cd796d1500bad4c6c8b1979d811a2f019a99cfc4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 22:17:40 +0100 Subject: [PATCH 05/23] Just print messages to azure too --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1eeb1c4..938e74e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -62,7 +62,7 @@ stages: displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: gdb - VIMSPECTOR_TEST_STDOUT: all + VIMSPECTOR_TEST_STDOUT: messsages - bash: ./make_package linux $(Build.SourceVersion) displayName: 'Package' @@ -107,7 +107,7 @@ stages: displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: lldb - VIMSPECTOR_TEST_STDOUT: all + VIMSPECTOR_TEST_STDOUT: messsages - bash: ./make_package macos $(Build.SourceVersion) displayName: 'Package' From 3beb25f949ab2be47ed8e295f7e01c9736de47a7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 22:33:02 +0100 Subject: [PATCH 06/23] Raise an autocommand for the terminal too --- plugin/vimspector.vim | 3 ++- python3/vimspector/code.py | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 1a6ffdc..fa8312c 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -100,7 +100,8 @@ command! -bar " Dummy autocommands so that we can call this whenever augroup VimspectorUserAutoCmds au! - au User VimspectorUICreated silent + au User VimspectorUICreated silent + au User VimspectorTerminalOpened silent augroup END " boilerplate {{{ diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index cadd50b..f91f768 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -228,7 +228,7 @@ class CodeView( object ): if self._window.valid: window_for_start = self._window else: - # TOOD: Where? + # TOOD: Where? Maybe we should just use botright vertical ... window_for_start = vim.current.window if self._terminal_window is not None and self._terminal_window.valid: @@ -255,11 +255,16 @@ class CodeView( object ): if buffer_number is None or buffer_number <= 0: # TODO: Do something better like reject the request? raise ValueError( "Unable to start terminal" ) - else: - self._terminal_window = terminal_window - self._terminal_buffer_number = buffer_number - vim.vars[ 'vimspector_session_windows' ][ 'terminal' ] = utils.WindowID( - self._terminal_window, - vim.current.tabpage ) + + self._terminal_window = terminal_window + self._terminal_buffer_number = buffer_number + + vim.vars[ 'vimspector_session_windows' ][ 'terminal' ] = utils.WindowID( + self._terminal_window, + vim.current.tabpage ) + with utils.RestoreCursorPosition(): + with utils.RestoreCurrentWindow(): + with utils.RestoreCurrentBuffer( vim.current.window ): + vim.command( 'doautocmd User VimspectorTerminalOpened' ) return buffer_number From db344148eaea204399394268fe3e4407ef354107 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 23:31:08 +0100 Subject: [PATCH 07/23] Example of setting terminal window size sensibly --- support/custom_ui_vimrc | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index 8e3746a..bf1f4ec 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -17,13 +17,37 @@ function! s:CustomiseUI() call win_gotoid( wins.stack_trace ) wincmd r + " Make the left column at least 70 chars + 70wincmd | + + " Make the code window at least 80 chars + call win_gotoid( wins.code ) + 80wincmd | + " Make the output window 10 lines high and right at the top of the screen call win_gotoid( wins.output ) 10wincmd _ wincmd K endfunction +function s:SetUpTerminal() + let terminal_win = g:vimspector_session_windows.terminal + + " Make the terminal window at most 80 columns wide, ensuring there is enough + " sapce for our code window (80 columns) and the left bar (70 columns) + + " Padding is 2 for the 2 vertical split markers and 2 for the sign column in + " the code window. + let left_bar = 70 + let code = 80 + let padding = 4 + let cols = max( [ min([ &columns - left_bar - code - padding, 80 ] ), 10 ] ) + call win_gotoid( terminal_win ) + execute cols . 'wincmd |' +endfunction + augroup TestUICustomistaion autocmd! autocmd User VimspectorUICreated call s:CustomiseUI() + autocmd User VimspectorTerminalOpened call s:SetUpTerminal() augroup END From 9df680089bd3451e246ea5ebcfccd443adfe3fa3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 12 Jul 2020 16:44:18 +0100 Subject: [PATCH 08/23] Allow default configuraiton to be specified; document selection --- README.md | 14 +++++ docs/configuration.md | 60 +++++++++++++++++++++- python3/vimspector/debug_session.py | 14 +++-- tests/testdata/cpp/simple/.vimspector.json | 29 +++++++++-- 4 files changed, 108 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 15e9872..65c4a6b 100644 --- a/README.md +++ b/README.md @@ -601,6 +601,19 @@ See [our YouCompleteMe integration guide](#usage-with-youcompleteme) for another example where it can be used to specify the port to connect the [java debugger](#java---partially-supported) +### Debug configuration selection + +Vimspector uses the following logic to choose a configuration to launch: + +1. If a configuration was specified in the launch options (as above), use that. +2. Otherwise if there's only one configuration and it doesn't have `autoselect` + set to `false`, use that. +3. Otherwise if there's exactly one configuration with `default` set to `true` + and without `autoselect` set to `false`, use that. +4. Otherwise, prompt the user to select a configuration. + +See [the reference guide][vimspector-ref-config-selection] for details. + ## Breakpoints * Use `vimspector#ToggleBreakpoint([ { 'condition': '' } ])` @@ -1366,6 +1379,7 @@ Copyright © 2018 Ben Jackson [vimspector-ref]: https://puremourning.github.io/vimspector/configuration.html [vimspector-ref-var]: https://puremourning.github.io/vimspector/configuration.html#replacements-and-variables [vimspector-ref-exception]: https://puremourning.github.io/vimspector/configuration.html#exception-breakpoints +[vimspector-ref-config-selection]: https://puremourning.github.io/vimspector/configuration.html#configuration-selection [debugpy]: https://github.com/microsoft/debugpy [YouCompleteMe]: https://github.com/ycm-core/YouCompleteMe#java-semantic-completion [remote-debugging]: https://puremourning.github.io/vimspector/configuration.html#remote-debugging-support diff --git a/docs/configuration.md b/docs/configuration.md index 14f5047..da7a120 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -321,6 +321,62 @@ typical example looks like this: } ``` +### Configuration selection + +When starting debugging, you can specify which debug configuration to launch +with `call vimspector#LaunchWithSettings( #{ configuration: 'name here' } )`. + +Otherwise, if there's only one configuration found, Vimspector will use that +configuration, unless it contains a key `"autoselect": false`. + +If multiple debug configurations are found, and no explicit configuration was +selected on Launch, the user is prompted to select a configuration, unless a +single debug configuration is found with a key `"default": true`. + +#### Specifying a default configuration + +As noted, you can specify a default configuration with `"default": true`: + +```json +{ + "configurations": { + "use this one": { + "default": true, + "adapter": " ... ", + "configuation": { + // ... + } + }, + "don't use this one": { + // ... + } + } +} +``` + +If multiple conifigurations are found with `default` set to `true`, then the +user is prompted anyway. + +#### Preventing automatic selection + +If you don't want a configuration to be selected automatically, then set +`"autoselect": false`. This particularly useful for configurations in the +central (as opposed to project-local) directory. For example: + +```json + "configurations": { + "Don't use this by default!": { + "autoselect": false, + "adapter": " ... ", + "configuation": { + // ... + } + } + } +``` + +Setting `autoselect` to `false` overrides setting `default` to `true`. + ### Exception breakpionts Debug adapters have arbitrary configuration for exception breakpoints. Normally @@ -426,12 +482,12 @@ where the development is done on one host and the runtime is some other host, account, container, etc. In order for it to work, you have to set up passwordless SSH between the local -and remote machines/accounts. Then just tell Vimsector how to remotely launch +and remote machines/accounts. Then just tell Vimspector how to remotely launch and/or attach to the app. This is presented as examples with commentary, as it's a fairly advanced/niche case. If you're not already familiar with remote debugging tools (such as -gdbserver) or not familar with ssh or such, you might need to independently +gdbserver) or not familiar with ssh or such, you might need to independently research that. Vimspector's tools are intended to automate your existing process for setting diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 61d7a6c..8d3e5c1 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -125,9 +125,17 @@ class DebugSession( object ): next( iter( configurations.values() ) ).get( "autoselect", True ) ): configuration_name = next( iter( configurations.keys() ) ) else: - configuration_name = utils.SelectFromList( - 'Which launch configuration?', - sorted( configurations.keys() ) ) + # Find a single configuration with 'default' True and autoselect not False + defaults = { n: c for n, c in configurations.items() + if c.get( 'default', False ) is True + and c.get( 'autoselect', True ) is not False } + + if len( defaults ) == 1: + configuration_name = next( iter( defaults.keys() ) ) + else: + configuration_name = utils.SelectFromList( + 'Which launch configuration?', + sorted( configurations.keys() ) ) if not configuration_name or configuration_name not in configurations: return diff --git a/tests/testdata/cpp/simple/.vimspector.json b/tests/testdata/cpp/simple/.vimspector.json index 5eb06e1..93a7a60 100644 --- a/tests/testdata/cpp/simple/.vimspector.json +++ b/tests/testdata/cpp/simple/.vimspector.json @@ -1,17 +1,38 @@ { "configurations": { - "cpptools-run": { + "run-to-entry": { "adapter": "vscode-cpptools", + // This makes this configuration the default. Only one default can be set + // (having two is the same as having none) + "default": true, "configuration": { "request": "launch", "program": "${workspaceRoot}/${fileBasenameNoExtension}", "externalConsole": false, "stopAtEntry": true, "stopOnEntry": true, - "MImode": "${VIMSPECTOR_MIMODE}", - "logging": { - "engineLogging": true + "MImode": "${VIMSPECTOR_MIMODE}" + }, + "breakpoints": { + "exception": { + "cpp_catch": "", + "cpp_throw": "", + "objc_catch": "", + "objc_throw": "", + "swift_catch": "", + "swift_throw": "" } + } + }, + "run-to-breakpoint": { + "adapter": "vscode-cpptools", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/${fileBasenameNoExtension}", + "externalConsole": false, + "stopAtEntry": false, + "stopOnEntry": false, + "MImode": "${VIMSPECTOR_MIMODE}" }, "breakpoints": { "exception": { From 97f6dd29a6aad7cff54dc8f75f9839cccf7da340 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 12 Jul 2020 16:45:43 +0100 Subject: [PATCH 09/23] Add some tests for expand/collapse variables; todo - fails on gdb/linux --- run_tests | 2 +- tests/lib/autoload/vimspector/test/setup.vim | 6 + tests/lib/autoload/vimspector/test/signs.vim | 4 +- tests/testdata/cpp/simple/.gitignore | 1 + tests/testdata/cpp/simple/Makefile | 2 +- tests/testdata/cpp/simple/struct.cpp | 33 ++ tests/ui.test.vim | 3 +- tests/variables.test.vim | 343 +++++++++++++++++++ 8 files changed, 389 insertions(+), 5 deletions(-) create mode 100644 tests/testdata/cpp/simple/struct.cpp create mode 100644 tests/variables.test.vim diff --git a/run_tests b/run_tests index 7c7fc59..d4b8218 100755 --- a/run_tests +++ b/run_tests @@ -77,7 +77,7 @@ echo " * BASEDIR_CMD=$BASEDIR_CMD" echo "%SETUP - Building test programs..." set -e pushd tests/testdata/cpp/simple - make clean simple + make clean all popd set +e echo "%DONE - built test programs" diff --git a/tests/lib/autoload/vimspector/test/setup.vim b/tests/lib/autoload/vimspector/test/setup.vim index d69acef..b0296e0 100644 --- a/tests/lib/autoload/vimspector/test/setup.vim +++ b/tests/lib/autoload/vimspector/test/setup.vim @@ -11,6 +11,12 @@ function! vimspector#test#setup#SetUpWithMappings( mappings ) abort " This is a bit of a hack runtime! plugin/**/*.vim + + augroup VimspectorTestSwap + au! + au SwapExists * let v:swapchoice = 'e' + augroup END + endfunction function! vimspector#test#setup#ClearDown() abort diff --git a/tests/lib/autoload/vimspector/test/signs.vim b/tests/lib/autoload/vimspector/test/signs.vim index 1ef7fc9..bd7a463 100644 --- a/tests/lib/autoload/vimspector/test/signs.vim +++ b/tests/lib/autoload/vimspector/test/signs.vim @@ -2,7 +2,9 @@ function! vimspector#test#signs#AssertCursorIsAtLineInBuffer( buffer, \ line, \ column ) abort call WaitForAssert( {-> - \ assert_equal( a:buffer, bufname( '%' ), 'Current buffer' ) + \ assert_equal( fnamemodify( a:buffer, ':p' ), + \ fnamemodify( bufname( '%' ), ':p' ), + \ 'Current buffer' ) \ }, 10000 ) call WaitForAssert( {-> \ assert_equal( a:line, line( '.' ), 'Current line' ) diff --git a/tests/testdata/cpp/simple/.gitignore b/tests/testdata/cpp/simple/.gitignore index ac54bdc..fec4c0b 100644 --- a/tests/testdata/cpp/simple/.gitignore +++ b/tests/testdata/cpp/simple/.gitignore @@ -1,2 +1,3 @@ simple variables +struct diff --git a/tests/testdata/cpp/simple/Makefile b/tests/testdata/cpp/simple/Makefile index 8583202..0e8d8c8 100644 --- a/tests/testdata/cpp/simple/Makefile +++ b/tests/testdata/cpp/simple/Makefile @@ -2,7 +2,7 @@ CXXFLAGS=-g -O0 -std=c++17 .PHONY: all -TARGETS=simple variables +TARGETS=simple variables struct all: $(TARGETS) diff --git a/tests/testdata/cpp/simple/struct.cpp b/tests/testdata/cpp/simple/struct.cpp new file mode 100644 index 0000000..ea55efc --- /dev/null +++ b/tests/testdata/cpp/simple/struct.cpp @@ -0,0 +1,33 @@ +struct AnotherTest +{ + char choo; + int ints[5]; +}; + +struct Test +{ + int i; + char c; + float fffff; + + AnotherTest another_test; +}; + +static Test SetUp( Test t ) +{ + t.another_test.choo = 'p'; + t.another_test.ints[ 0 ] = t.i; return t; +} + +int main( int , char ** ) +{ + Test t = {}; + + t.i = 1; + t.c = 'c'; + t.fffff = 3.14; + + t = SetUp( t ); + + return t.another_test.ints[ 0 ]; +} diff --git a/tests/ui.test.vim b/tests/ui.test.vim index ec1bc4c..07639e5 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -1,4 +1,4 @@ -let s:fn='main.py' +let s:fn='../support/test/python/simple_python/main.py' function! SetUp() call vimspector#test#setup#SetUpWithMappings( 'HUMAN' ) @@ -9,7 +9,6 @@ function! ClearDown() endfunction function! s:StartDebugging() - lcd ../support/test/python/simple_python exe 'edit ' . s:fn call setpos( '.', [ 0, 23, 1 ] ) call vimspector#ToggleBreakpoint() diff --git a/tests/variables.test.vim b/tests/variables.test.vim new file mode 100644 index 0000000..ad6f34d --- /dev/null +++ b/tests/variables.test.vim @@ -0,0 +1,343 @@ +let s:fn='../support/test/python/simple_python/main.py' + +function! SetUp() + call vimspector#test#setup#SetUpWithMappings( 'HUMAN' ) +endfunction + +function! ClearDown() + call vimspector#test#setup#ClearDown() +endfunction + +function! s:StartDebugging( ... ) + if a:0 == 0 + let config = #{ + \ fn: s:fn, + \ line: 23, + \ col: 1, + \ launch: #{ configuration: 'run' } + \ } + else + let config = a:1 + endif + + execute 'edit' config.fn + call setpos( '.', [ 0, config.line, config.col ] ) + call vimspector#ToggleBreakpoint() + call vimspector#LaunchWithSettings( config.launch ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( + \ config.fn, + \ config.line, + \ config.col ) +endfunction + +function! Test_SimpleWatches() + call s:StartDebugging() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + " Add a wtch + call vimspector#AddWatch( 't' ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 26, 1 ) + + " Delete a watch expression + call win_gotoid( g:vimspector_session_windows.watches ) + call setpos( '.', [ 0, 3, 1 ] ) + call feedkeys( "\", 'xt' ) + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call assert_equal( 'python', + \ getbufvar( + \ winbufnr( g:vimspector_session_windows.watches ), + \ '&syntax' ) ) + + call vimspector#StepInto() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 13, 1 ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 14, 1 ) + call vimspector#AddWatch( 'i' ) + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i', + \ ' *- Result: 0', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#AddWatch( 'i+1' ) + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i', + \ ' - Result: 0', + \ 'Expression: i+1', + \ ' *- Result: 1', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#AddWatch( 'i+2' ) + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i', + \ ' - Result: 0', + \ 'Expression: i+1', + \ ' - Result: 1', + \ 'Expression: i+2', + \ ' *- Result: 2', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Delete that middle watch + call win_gotoid( g:vimspector_session_windows.watches ) + call setpos( '.', [ 0, 4, 1 ] ) + call vimspector#DeleteWatch() + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i', + \ ' - Result: 0', + \ 'Expression: i+2', + \ ' *- Result: 2', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 15, 1 ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i', + \ ' - Result: 0', + \ 'Expression: i+2', + \ ' - Result: 2', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Delete the top watch + call win_gotoid( g:vimspector_session_windows.watches ) + call setpos( '.', [ 0, 3, 1 ] ) + call vimspector#DeleteWatch() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 13, 1 ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 14, 1 ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: i+2', + \ ' *- Result: 3', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_ExpandWatch() + let fn = 'testdata/cpp/simple/struct.cpp' + " TODO: This stops at a different point on linux/gdb + call s:StartDebugging( #{ fn: fn, line: 24, col: 1, launch: #{ + \ configuration: 'run-to-breakpoint' + \ } } ) + " Make sure the Test t is initialised + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '- Scope: Locals', + \ ' *+ t (Test): {...}', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + call assert_equal( 'cpp', + \ getbufvar( + \ winbufnr( g:vimspector_session_windows.variables ), + \ '&syntax' ) ) + + " Expand + call win_gotoid( g:vimspector_session_windows.variables ) + call setpos( '.', [ 0, 2, 1 ] ) + call feedkeys( "\", 'xt' ) + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '- Scope: Locals', + \ ' *- t (Test): {...}', + \ ' *- i (int): 0', + \ " *- c (char): 0 '\\0'", + \ ' *- fffff (float): 0', + \ ' *+ another_test (AnotherTest): {...}', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Step - stays expanded + call vimspector#StepOver() + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '- Scope: Locals', + \ ' - t (Test): {...}', + \ ' *- i (int): 1', + \ " - c (char): 0 '\\0'", + \ ' - fffff (float): 0', + \ ' + another_test (AnotherTest): {...}', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Collapse + call win_gotoid( g:vimspector_session_windows.variables ) + call setpos( '.', [ 0, 2, 1 ] ) + call feedkeys( "\", 'xt' ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '- Scope: Locals', + \ ' + t (Test): {...}', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#StepOver() + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '- Scope: Locals', + \ ' + t (Test): {...}', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Exapand - see that the changed value is highlighted + call win_gotoid( g:vimspector_session_windows.variables ) + call setpos( '.', [ 0, 2, 1 ] ) + call feedkeys( "\", 'xt' ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '- Scope: Locals', + \ ' - t (Test): {...}', + \ ' - i (int): 1', + \ " *- c (char): 99 'c'", + \ ' - fffff (float): 0', + \ ' + another_test (AnotherTest): {...}', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Collapse the 'inexpensive' scope and see that it stays collapsed + " Exapand - see that the changed value is highlighted + call win_gotoid( g:vimspector_session_windows.variables ) + call setpos( '.', [ 0, 1, 1 ] ) + call feedkeys( "\", 'xt' ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '+ Scope: Locals', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Stays collpased through step + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 30, 1 ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '+ Scope: Locals', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Cpptools keeps the same "Locals" scope, so it stays collapsed even throught + " step-in + call vimspector#StepInto() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 18, 1 ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '+ Scope: Locals', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction From 82ec19fff96c7636fbfafa088ac6c80a279e59fe Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 12 Jul 2020 16:47:04 +0100 Subject: [PATCH 10/23] Update tocs --- README.md | 125 +++++++++++++++++++++--------------------- docs/configuration.md | 5 +- 2 files changed, 67 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 65c4a6b..fe0f66f 100644 --- a/README.md +++ b/README.md @@ -6,69 +6,70 @@ For a tutorial and usage overview, take a look at the [![Build Status](https://dev.azure.com/puremouron/Vimspector/_apis/build/status/puremourning.vimspector?branchName=master)](https://dev.azure.com/puremouron/Vimspector/_build/latest?definitionId=1&branchName=master) [![Gitter](https://badges.gitter.im/vimspector/Lobby.svg)](https://gitter.im/vimspector/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - * [Features and Usage](#features-and-usage) - * [Supported debugging features](#supported-debugging-features) - * [Supported languages:](#supported-languages) - * [Languages known to work](#languages-known-to-work) - * [Languages known not to work](#languages-known-not-to-work) - * [Other languages](#other-languages) - * [Installation](#installation) - * [Dependencies](#dependencies) - * [Neovim differences](#neovim-differences) - * [Windows differences](#windows-differences) - * [Language dependencies](#language-dependencies) - * [Clone the plugin](#clone-the-plugin) - * [Install some gadgets](#install-some-gadgets) - * [Manual gadget installation](#manual-gadget-installation) - * [The gadget directory](#the-gadget-directory) - * [Trying it out](#trying-it-out) - * [About](#about) - * [Background](#background) - * [Status](#status) - * [Experimental](#experimental) - * [Mappings](#mappings) - * [Visual Studio / VSCode](#visual-studio--vscode) - * [Human Mode](#human-mode) - * [Usage](#usage) - * [Launch and attach by PID:](#launch-and-attach-by-pid) - * [Launch with options](#launch-with-options) - * [Breakpoints](#breakpoints) - * [Exception breakpoints](#exception-breakpoints) - * [Clear breakpoints](#clear-breakpoints) - * [Stepping](#stepping) - * [Variables and scopes](#variables-and-scopes) - * [Watches](#watches) - * [Stack Traces](#stack-traces) - * [Program Output](#program-output) - * [Console](#console) - * [Closing debugger](#closing-debugger) - * [Debug adapter configuration](#debug-adapter-configuration) - * [C, C , Rust, etc.](#c-c-rust-etc) - * [Remote debugging](#remote-debugging) - * [Remote launch and attach](#remote-launch-and-attach) - * [Python](#python) - * [Remote Debugging](#remote-debugging-1) - * [Remote launch and attach](#remote-launch-and-attach-1) - * [Legacy: vscode-python](#legacy-vscode-python) - * [TCL](#tcl) - * [C♯](#c) - * [Go](#go) - * [PHP](#php) - * [Debug web application](#debug-web-application) - * [Debug cli application](#debug-cli-application) - * [JavaScript, TypeScript, etc.](#javascript-typescript-etc) - * [Java](#java) - * [Usage with YouCompleteMe](#usage-with-youcompleteme) - * [Other LSP clients](#other-lsp-clients) - * [Rust](#rust) - * [Other servers](#other-servers) - * [Customisation](#customisation) - * [Changing the default signs](#changing-the-default-signs) - * [FAQ](#faq) - * [Motivation](#motivation) - * [License](#license) + * [Features and Usage](#features-and-usage) + * [Supported debugging features](#supported-debugging-features) + * [Supported languages:](#supported-languages) + * [Languages known to work](#languages-known-to-work) + * [Languages known not to work](#languages-known-not-to-work) + * [Other languages](#other-languages) + * [Installation](#installation) + * [Dependencies](#dependencies) + * [Neovim differences](#neovim-differences) + * [Windows differences](#windows-differences) + * [Language dependencies](#language-dependencies) + * [Clone the plugin](#clone-the-plugin) + * [Install some gadgets](#install-some-gadgets) + * [Manual gadget installation](#manual-gadget-installation) + * [The gadget directory](#the-gadget-directory) + * [Trying it out](#trying-it-out) + * [About](#about) + * [Background](#background) + * [Status](#status) + * [Experimental](#experimental) + * [Mappings](#mappings) + * [Visual Studio / VSCode](#visual-studio--vscode) + * [Human Mode](#human-mode) + * [Usage](#usage) + * [Launch and attach by PID:](#launch-and-attach-by-pid) + * [Launch with options](#launch-with-options) + * [Debug configuration selection](#debug-configuration-selection) + * [Breakpoints](#breakpoints) + * [Exception breakpoints](#exception-breakpoints) + * [Clear breakpoints](#clear-breakpoints) + * [Stepping](#stepping) + * [Variables and scopes](#variables-and-scopes) + * [Watches](#watches) + * [Stack Traces](#stack-traces) + * [Program Output](#program-output) + * [Console](#console) + * [Closing debugger](#closing-debugger) + * [Debug adapter configuration](#debug-adapter-configuration) + * [C, C , Rust, etc.](#c-c-rust-etc) + * [Remote debugging](#remote-debugging) + * [Remote launch and attach](#remote-launch-and-attach) + * [Python](#python) + * [Remote Debugging](#remote-debugging-1) + * [Remote launch and attach](#remote-launch-and-attach-1) + * [Legacy: vscode-python](#legacy-vscode-python) + * [TCL](#tcl) + * [C♯](#c) + * [Go](#go) + * [PHP](#php) + * [Debug web application](#debug-web-application) + * [Debug cli application](#debug-cli-application) + * [JavaScript, TypeScript, etc.](#javascript-typescript-etc) + * [Java](#java) + * [Usage with YouCompleteMe](#usage-with-youcompleteme) + * [Other LSP clients](#other-lsp-clients) + * [Rust](#rust) + * [Other servers](#other-servers) + * [Customisation](#customisation) + * [Changing the default signs](#changing-the-default-signs) + * [FAQ](#faq) + * [Motivation](#motivation) + * [License](#license) - + diff --git a/docs/configuration.md b/docs/configuration.md index da7a120..134b049 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -16,6 +16,9 @@ for Vimspector. * [Files and locations](#files-and-locations) * [Adapter configurations](#adapter-configurations) * [Debug configurations](#debug-configurations) + * [Configuration selection](#configuration-selection) + * [Specifying a default configuration](#specifying-a-default-configuration) + * [Preventing automatic selection](#preventing-automatic-selection) * [Exception breakpionts](#exception-breakpionts) * [Predefined Variables](#predefined-variables) * [Remote Debugging Support](#remote-debugging-support) @@ -25,7 +28,7 @@ for Vimspector. * [Appendix: Configuration file format](#appendix-configuration-file-format) * [Appendix: Editor configuration](#appendix-editor-configuration) - + From 79a8ad40a4e409fe9b8327cfa2c9763a06fffd80 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 12 Jul 2020 17:20:46 +0100 Subject: [PATCH 11/23] Use my fork of vint; fix some lint --- dev_requirements.txt | 4 +++- support/custom_ui_vimrc | 2 +- tests/lib/plugin/screendump.vim | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index e65675c..c7a9ec1 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,4 +1,6 @@ flake8==3.8.3 -vim-vint==0.3.21 flake8-comprehensions==3.2.3 flake8-ycm>= 0.1.0 + +# Use fork of vint which is up to date +git+https://github.com/puremourning/vint diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index bf1f4ec..d319b28 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -41,7 +41,7 @@ function s:SetUpTerminal() let left_bar = 70 let code = 80 let padding = 4 - let cols = max( [ min([ &columns - left_bar - code - padding, 80 ] ), 10 ] ) + let cols = max( [ min( [ &columns - left_bar - code - padding, 80 ] ), 10 ] ) call win_gotoid( terminal_win ) execute cols . 'wincmd |' endfunction diff --git a/tests/lib/plugin/screendump.vim b/tests/lib/plugin/screendump.vim index 9b823c3..157febd 100644 --- a/tests/lib/plugin/screendump.vim +++ b/tests/lib/plugin/screendump.vim @@ -94,7 +94,7 @@ func StopVimInTerminal(buf) " 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))}) + call WaitForAssert({-> assert_equal('finished', term_getstatus(a:buf))}) only! endfunc From 231720b5b50f8fa99c93856d1b30f9710878bb60 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 12 Jul 2020 20:12:31 +0100 Subject: [PATCH 12/23] Make the messages clearer --- azure-pipelines.yml | 6 ++---- run_tests | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 938e74e..5fb2b08 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -58,11 +58,10 @@ stages: - bash: | eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) export GOPATH=$HOME/go - ./run_tests + ./run_tests --report messages --quiet displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: gdb - VIMSPECTOR_TEST_STDOUT: messsages - bash: ./make_package linux $(Build.SourceVersion) displayName: 'Package' @@ -103,11 +102,10 @@ stages: - bash: vim --version displayName: 'Print vim version information' - - bash: ./run_tests + - bash: ./run_tests --report messages --quiet displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: lldb - VIMSPECTOR_TEST_STDOUT: messsages - bash: ./make_package macos $(Build.SourceVersion) displayName: 'Package' diff --git a/run_tests b/run_tests index d4b8218..0fb9c65 100755 --- a/run_tests +++ b/run_tests @@ -5,7 +5,8 @@ if [ "$1" == "--help" ]; then echo "" echo " --basedir path to runtime directory like the optino to install_gadget.py" echo " --install run install_gadget.py, useful with --basedir" - echo " --report how verbose to be with stdout" + echo " --report which logs to dump to stdout after a test" + echo " --quiet suppress vim's stdout" echo "e.g.: " echo " - run all tests: $0" echo " - run specific tests script: $0 signature_help.test.vim" @@ -21,6 +22,9 @@ RUN_VIM="vim -N --clean --not-a-term" RUN_TEST="${RUN_VIM} -S lib/run_test.vim" BASEDIR_CMD='py3 pass' +# 1 is stdout +out_fd=1 + while [ -n "$1" ]; do case "$1" in "--basedir") @@ -42,6 +46,15 @@ while [ -n "$1" ]; do VIMSPECTOR_TEST_STDOUT=$1 shift ;; + "--quiet") + shift + # send the output to /dev/null + # https://stackoverflow.com/a/47553900 + # Note we can't use {out_fd} here because the bash version in CI is too + # old on macOS + out_fd=3 + exec 3>/dev/null + ;; "--") shift break @@ -52,6 +65,13 @@ while [ -n "$1" ]; do esac done +# We use fd 3 for vim's output. Send it to stdout if not already redirected +# Note: can't use ${out_fd} in a redirect because redirects happen before +# variable substitution +if [ "${out_fd}" = "1" ]; then + exec 3>&1 +fi + if [ $INSTALL = 1 ]; then python3 $(dirname $0)/install_gadget.py --basedir ${BASEDIR} --all fi @@ -105,6 +125,7 @@ for t in ${TESTS}; do if ${RUN_TEST} --cmd "${BASEDIR_CMD}" \ --cmd 'au SwapExists * let v:swapchoice = "e"' $t $T \ + >&3\ && [ -f $t.res ]; then echo "%PASS: $t PASSED" else @@ -117,7 +138,13 @@ for t in ${TESTS}; do ${RUN_VIM} --version > ${TESTLOGDIR}/vimversion if [ "$VIMSPECTOR_TEST_STDOUT" = "messages" ]; then if [ -f messages ]; then + echo "%MESSAGES" cat messages + echo "%END" + else + echo "%MESSAGES" + echo "No messages found" + echo "%END" fi fi @@ -138,6 +165,10 @@ for t in ${TESTS}; do rm -f $t.res done +# close out_fd if it's not stdout/stderr/ +(( out_fd > 2 )) && exec 3>&- + + echo "Done running tests" popd > /dev/null From 6d020a50de74be6db0a9f27d947746b96cfa1e93 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 12 Jul 2020 22:12:08 +0100 Subject: [PATCH 13/23] Fix removing all assert errors, properly this time --- tests/lib/plugin/shared.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/plugin/shared.vim b/tests/lib/plugin/shared.vim index d682810..2d656d7 100644 --- a/tests/lib/plugin/shared.vim +++ b/tests/lib/plugin/shared.vim @@ -73,7 +73,7 @@ func s:WaitForCommon(expr, assert, timeout) " Remove the errors added by the assert function. let errors_added = len( v:errors ) - errors_before if errors_added > 0 - call remove(v:errors, -1 * errors_added ) + call remove( v:errors, -1 * errors_added, -1 ) endif endif From 4f0847bcf88455e689d91e56fa84ffb58261b6e9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 12 Jul 2020 22:16:59 +0100 Subject: [PATCH 14/23] Match the output on linux and mac --- tests/variables.test.vim | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index ad6f34d..b6e604d 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -8,6 +8,17 @@ function! ClearDown() call vimspector#test#setup#ClearDown() endfunction +function! s:assert_match_list( expected, actual ) abort + let ret = assert_equal( len( a:expected ), len( a:actual ) ) + let len = min( [ len( a:expected ), len( a:actual ) ] ) + let idx = 0 + while idx < len + let ret += assert_match( a:expected[ idx ], a:actual[ idx ] ) + let idx += 1 + endwhile + return ret +endfunction + function! s:StartDebugging( ... ) if a:0 == 0 let config = #{ @@ -181,10 +192,10 @@ endfunction function! Test_ExpandWatch() let fn = 'testdata/cpp/simple/struct.cpp' - " TODO: This stops at a different point on linux/gdb call s:StartDebugging( #{ fn: fn, line: 24, col: 1, launch: #{ \ configuration: 'run-to-breakpoint' \ } } ) + " Make sure the Test t is initialised call vimspector#StepOver() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) @@ -211,14 +222,14 @@ function! Test_ExpandWatch() call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ assert_equal( + \ s:assert_match_list( \ [ \ '- Scope: Locals', - \ ' *- t (Test): {...}', - \ ' *- i (int): 0', - \ " *- c (char): 0 '\\0'", - \ ' *- fffff (float): 0', - \ ' *+ another_test (AnotherTest): {...}', + \ ' \*- t (Test): {...}', + \ ' \*- i (int): 0', + \ ' \*- c (char): 0 ''\\0\{1,3}''', + \ ' \*- fffff (float): 0', + \ ' \*+ another_test (AnotherTest):\( {...}\)\?', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -229,14 +240,14 @@ function! Test_ExpandWatch() " Step - stays expanded call vimspector#StepOver() call WaitForAssert( {-> - \ assert_equal( + \ s:assert_match_list( \ [ \ '- Scope: Locals', \ ' - t (Test): {...}', - \ ' *- i (int): 1', - \ " - c (char): 0 '\\0'", + \ ' \*- i (int): 1', + \ ' - c (char): 0 ''\\0\{1,3}''', \ ' - fffff (float): 0', - \ ' + another_test (AnotherTest): {...}', + \ ' + another_test (AnotherTest):\( {...}\)\?', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -278,14 +289,14 @@ function! Test_ExpandWatch() call setpos( '.', [ 0, 2, 1 ] ) call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ assert_equal( + \ s:assert_match_list( \ [ \ '- Scope: Locals', \ ' - t (Test): {...}', \ ' - i (int): 1', - \ " *- c (char): 99 'c'", + \ ' \*- c (char): 99 ''c''', \ ' - fffff (float): 0', - \ ' + another_test (AnotherTest): {...}', + \ ' + another_test (AnotherTest):\( {...}\)\?', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, From e884a5f71cb6c1b21e7efa5a39fdbaf3bf652caf Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 14 Jul 2020 20:36:53 +0100 Subject: [PATCH 15/23] run_tests: add --help properly --- run_tests | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/run_tests b/run_tests index 0fb9c65..c03c7b9 100755 --- a/run_tests +++ b/run_tests @@ -1,21 +1,5 @@ #!/usr/bin/env bash -if [ "$1" == "--help" ]; then - echo "$(basename $0) [--basedir ] [--install] " - echo "" - echo " --basedir path to runtime directory like the optino to install_gadget.py" - echo " --install run install_gadget.py, useful with --basedir" - echo " --report which logs to dump to stdout after a test" - echo " --quiet suppress vim's stdout" - 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\(\)" - echo " - run all tests in a clean env: $0 --basedir \$(mktemp -d) --install" - exit 0 -fi - - BASEDIR=$(dirname $0) INSTALL=0 RUN_VIM="vim -N --clean --not-a-term" @@ -59,6 +43,21 @@ while [ -n "$1" ]; do shift break ;; + "--help") + shift + echo "$(basename $0) [--basedir ] [--report output] [--quiet] [--install] " + echo "" + echo " --basedir path to runtime directory like the optino to install_gadget.py" + echo " --install run install_gadget.py, useful with --basedir" + echo " --report which logs to dump to stdout after a test" + echo " --quiet suppress vim's stdout" + 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\(\)" + echo " - run all tests in a clean env: $0 --basedir \$(mktemp -d) --install" + exit 0 + ;; *) break ;; From 9faa8aa6f761d8b57afa26beecb5c06431f6da5d Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 14 Jul 2020 20:37:17 +0100 Subject: [PATCH 16/23] Fix test timing issue, so it passes on llvm and gdb --- tests/variables.test.vim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index b6e604d..b511d1e 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -190,7 +190,7 @@ function! Test_SimpleWatches() %bwipe! endfunction -function! Test_ExpandWatch() +function! Test_ExpandVariables() let fn = 'testdata/cpp/simple/struct.cpp' call s:StartDebugging( #{ fn: fn, line: 24, col: 1, launch: #{ \ configuration: 'run-to-breakpoint' @@ -272,6 +272,7 @@ function! Test_ExpandWatch() \ } ) call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 28, 1 ) call WaitForAssert( {-> \ assert_equal( \ [ @@ -284,7 +285,6 @@ function! Test_ExpandWatch() \ ) \ } ) - " Exapand - see that the changed value is highlighted call win_gotoid( g:vimspector_session_windows.variables ) call setpos( '.', [ 0, 2, 1 ] ) call feedkeys( "\", 'xt' ) @@ -293,10 +293,10 @@ function! Test_ExpandWatch() \ [ \ '- Scope: Locals', \ ' - t (Test): {...}', - \ ' - i (int): 1', + \ ' \*- i (int): 1', \ ' \*- c (char): 99 ''c''', - \ ' - fffff (float): 0', - \ ' + another_test (AnotherTest):\( {...}\)\?', + \ ' \*- fffff (float): 0', + \ ' \*+ another_test (AnotherTest):\( {...}\)\?', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, From b1fd15c56ad1043ef367d28c80a4efa8c0fd4b80 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 14 Jul 2020 22:07:57 +0100 Subject: [PATCH 17/23] Add a test for expanding watches --- tests/variables.test.vim | 127 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index b511d1e..1b10809 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -352,3 +352,130 @@ function! Test_ExpandVariables() call vimspector#test#setup#Reset() %bwipe! endfunction + +function! Test_ExpandWatch() + let fn = 'testdata/cpp/simple/struct.cpp' + call s:StartDebugging( #{ fn: fn, line: 24, col: 1, launch: #{ + \ configuration: 'run-to-breakpoint' + \ } } ) + + " Make sure the Test t is initialised + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) + + call win_gotoid( g:vimspector_session_windows.watches ) + call feedkeys( "it\", 'xt' ) + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: t', + \ ' *+ Result: {...}', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + call assert_equal( 'cpp', + \ getbufvar( + \ winbufnr( g:vimspector_session_windows.watches ), + \ '&syntax' ) ) + + " Expand + call win_gotoid( g:vimspector_session_windows.watches ) + call setpos( '.', [ 0, 3, 1 ] ) + call feedkeys( "\", 'xt' ) + + call WaitForAssert( {-> + \ s:assert_match_list( + \ [ + \ 'Watches: ----', + \ 'Expression: t', + \ ' \*- Result: {...}', + \ ' \*- i (int): 0', + \ ' \*- c (char): 0 ''\\0\{1,3}''', + \ ' \*- fffff (float): 0', + \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Step - stays expanded + call vimspector#StepOver() + call WaitForAssert( {-> + \ s:assert_match_list( + \ [ + \ 'Watches: ----', + \ 'Expression: t', + \ ' - Result: {...}', + \ ' \*- i (int): 1', + \ ' - c (char): 0 ''\\0\{1,3}''', + \ ' - fffff (float): 0', + \ ' + another_test (AnotherTest):\( {...}\)\?', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Collapse + call win_gotoid( g:vimspector_session_windows.watches ) + call setpos( '.', [ 0, 3, 1 ] ) + call feedkeys( "\", 'xt' ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: t', + \ ' + Result: {...}', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 28, 1 ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Watches: ----', + \ 'Expression: t', + \ ' + Result: {...}', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call win_gotoid( g:vimspector_session_windows.watches ) + call setpos( '.', [ 0, 3, 1 ] ) + call feedkeys( "\", 'xt' ) + call WaitForAssert( {-> + \ s:assert_match_list( + \ [ + \ 'Watches: ----', + \ 'Expression: t', + \ ' - Result: {...}', + \ ' - i (int): 1', + \ ' - c (char): 99 ''c''', + \ ' - fffff (float): 0', + \ ' + another_test (AnotherTest):\( {...}\)\?', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction From 7a9f75a06e48753f3a226947ee75e1b25390b966 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 16 Jul 2020 15:25:28 +0100 Subject: [PATCH 18/23] Don't change the value of equalalways We were trying to avoid equalalways from changing the UI layout by unsetting it and resetting it after changes. However, re-setting equalalways actually resizes all the windows, so this never worked. Instead we judiciously use rightbelow, leftabove, etc. and specify the exact window sizes we want. As a side-effect we make the terminal sizing a little more pleasant by default, ensuring that it is no wider than 80 chars, and tries to use any remianing vertical space after reserving 80 chars for the code window. --- autoload/vimspector/internal/neoterm.vim | 2 +- autoload/vimspector/internal/term.vim | 2 +- python3/vimspector/code.py | 29 +++++++----- python3/vimspector/debug_session.py | 56 ++++++++++++------------ 4 files changed, 49 insertions(+), 40 deletions(-) diff --git a/autoload/vimspector/internal/neoterm.vim b/autoload/vimspector/internal/neoterm.vim index c6e798e..3f5bf66 100644 --- a/autoload/vimspector/internal/neoterm.vim +++ b/autoload/vimspector/internal/neoterm.vim @@ -48,7 +48,7 @@ endfunction function! vimspector#internal#neoterm#Start( cmd, opts ) abort " Prepare current buffer to be turned into a term if curwin is not set if ! get( a:opts, 'curwin', 0 ) - let mods = '' + let mods = 'rightbelow ' if get( a:opts, 'vertical', 0 ) let mods .= 'vertical ' let mods .= get( a:opts, 'term_cols', '' ) diff --git a/autoload/vimspector/internal/term.vim b/autoload/vimspector/internal/term.vim index 5a570fc..aa394ba 100644 --- a/autoload/vimspector/internal/term.vim +++ b/autoload/vimspector/internal/term.vim @@ -20,7 +20,7 @@ set cpoptions&vim " }}} function! vimspector#internal#term#Start( cmd, opts ) abort - return term_start( a:cmd, a:opts ) + rightbelow return term_start( a:cmd, a:opts ) endfunction function! vimspector#internal#term#IsFinished( bufno ) abort diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index f91f768..29eaa0b 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -233,24 +233,33 @@ class CodeView( object ): if self._terminal_window is not None and self._terminal_window.valid: assert self._terminal_buffer_number + window_for_start = self._terminal_window if ( self._terminal_window.buffer.number == 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 + else: + options[ 'vertical' ] = 0 buffer_number = None terminal_window = None - with utils.TemporaryVimOptions( { 'splitright': True, - 'equalalways': False } ): - with utils.LetCurrentWindow( window_for_start ): - buffer_number = int( - utils.Call( - 'vimspector#internal#{}term#Start'.format( self._api_prefix ), - args, - options ) ) - terminal_window = vim.current.window + with utils.LetCurrentWindow( window_for_start ): + # If we're making a vertical split from the code window, make it no more + # than 80 columns and no fewer than 10. Also try and keep the code window + # at least 82 columns + if options[ 'vertical' ] and not options.get( 'curwin', 0 ): + options[ 'term_cols' ] = max( + min ( int( vim.eval( 'winwidth( 0 ) - 82' ) ), 80 ), + 10 + ) + + 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: # TODO: Do something better like reject the request? diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 8d3e5c1..640a92e 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -487,39 +487,39 @@ class DebugSession( object ): self._codeView = code.CodeView( code_window, self._api_prefix ) # Call stack - with utils.TemporaryVimOptions( { 'splitright': False, - 'equalalways': False, } ): - vim.command( 'topleft vertical 50new' ) - stack_trace_window = vim.current.window - self._stackTraceView = stack_trace.StackTraceView( self, - self._connection, - stack_trace_window ) + vim.command( 'topleft vertical 50new' ) + stack_trace_window = vim.current.window + one_third = int( vim.eval( 'winheight( 0 )' ) ) / 3 + self._stackTraceView = stack_trace.StackTraceView( self, + self._connection, + stack_trace_window ) - with utils.TemporaryVimOptions( { 'splitbelow': False, - 'eadirection': 'ver', - 'equalalways': True } ): - # Watches - vim.command( 'new' ) - watch_window = vim.current.window + # Watches + vim.command( 'leftabove new' ) + watch_window = vim.current.window - # Variables - vim.command( 'new' ) - vars_window = vim.current.window + # Variables + vim.command( 'leftabove new' ) + vars_window = vim.current.window - self._variablesView = variables.VariablesView( self._connection, - vars_window, - watch_window ) + with utils.LetCurrentWindow( vars_window ): + vim.command( f'{ one_third }wincmd _' ) + with utils.LetCurrentWindow( watch_window ): + vim.command( f'{ one_third }wincmd _' ) + with utils.LetCurrentWindow( stack_trace_window ): + vim.command( f'{ one_third }wincmd _' ) + self._variablesView = variables.VariablesView( self._connection, + vars_window, + watch_window ) - with utils.TemporaryVimOption( 'splitbelow', True ): - vim.current.window = code_window - - # Output/logging - vim.command( '10new' ) - output_window = vim.current.window - self._outputView = output.OutputView( self._connection, - output_window, - self._api_prefix ) + # Output/logging + vim.current.window = code_window + vim.command( 'rightbelow 10new' ) + output_window = vim.current.window + self._outputView = output.OutputView( self._connection, + output_window, + self._api_prefix ) # TODO: If/when we support multiple sessions, we'll need some way to # indicate which tab was created and store all the tabs From f8cbb7c5b60e3aca40ef10f780e0cd62c099d290 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 17 Jul 2020 16:52:41 +0100 Subject: [PATCH 19/23] Add options to control window sizes This adds the following options, allowing the default sizes to be overridden: - g:vimspector_sidebar_width: Controls the width of the left utility windows (variables, watches, stack trace) - g:vimspector_bottombar_height: Controls the height of the output window below the code window The terminal is typically created as a vertical split of the code window. The following control the sizing of the terminal window used for debuggee input/output when using Vim's built-in terminal. - g:vimspector_code_minwidth: Minimum number of columns to try and maintain for the code window. - g:vimspector_terminal_maxwidth: Maximum number of columns to use for th terminal, when vertically splitting the code window. - g:vimspector_terminal_minwidth: Minimum number of columns to use when it is not possible to fit g:vimspector_terminal_maxwidth columns next to the code window with g:vimspector_code_minwidth columns. --- python3/vimspector/code.py | 20 +++++++++++--------- python3/vimspector/debug_session.py | 8 +++++--- python3/vimspector/settings.py | 26 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 python3/vimspector/settings.py diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 29eaa0b..a4f2514 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -18,7 +18,7 @@ import logging import json from collections import defaultdict -from vimspector import utils +from vimspector import utils, settings class CodeView( object ): @@ -218,7 +218,7 @@ class CodeView( object ): args = params[ 'args' ] env = params.get( 'env', {} ) - options = { + term_options = { 'vertical': 1, 'norestore': 1, 'cwd': cwd, @@ -238,9 +238,9 @@ class CodeView( object ): and int( utils.Call( 'vimspector#internal#{}term#IsFinished'.format( self._api_prefix ), self._terminal_buffer_number ) ) ): - options[ 'curwin' ] = 1 + term_options[ 'curwin' ] = 1 else: - options[ 'vertical' ] = 0 + term_options[ 'vertical' ] = 0 buffer_number = None terminal_window = None @@ -248,17 +248,19 @@ class CodeView( object ): # If we're making a vertical split from the code window, make it no more # than 80 columns and no fewer than 10. Also try and keep the code window # at least 82 columns - if options[ 'vertical' ] and not options.get( 'curwin', 0 ): - options[ 'term_cols' ] = max( - min ( int( vim.eval( 'winwidth( 0 ) - 82' ) ), 80 ), - 10 + if term_options[ 'vertical' ] and not term_options.get( 'curwin', 0 ): + term_options[ 'term_cols' ] = max( + min ( int( vim.eval( 'winwidth( 0 )' ) ) + - settings.Int( 'code_minwidth', 82 ), + settings.Int( 'terminal_maxwidth', 80 ) ), + settings.Int( 'terminal_minwidth' , 10 ) ) buffer_number = int( utils.Call( 'vimspector#internal#{}term#Start'.format( self._api_prefix ), args, - options ) ) + term_options ) ) terminal_window = vim.current.window if buffer_number is None or buffer_number <= 0: diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 640a92e..f553850 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -29,7 +29,8 @@ from vimspector import ( breakpoints, output, stack_trace, utils, - variables ) + variables, + settings ) from vimspector.vendor.json_minify import minify # We cache this once, and don't allow it to change (FIXME?) @@ -487,7 +488,8 @@ class DebugSession( object ): self._codeView = code.CodeView( code_window, self._api_prefix ) # Call stack - vim.command( 'topleft vertical 50new' ) + vim.command( + f'topleft vertical { settings.Int( "sidebar_width", 50 ) }new' ) stack_trace_window = vim.current.window one_third = int( vim.eval( 'winheight( 0 )' ) ) / 3 self._stackTraceView = stack_trace.StackTraceView( self, @@ -515,7 +517,7 @@ class DebugSession( object ): # Output/logging vim.current.window = code_window - vim.command( 'rightbelow 10new' ) + vim.command( f'rightbelow { settings.Int( "bottombar_height", 10 ) }new' ) output_window = vim.current.window self._outputView = output.OutputView( self._connection, output_window, diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py new file mode 100644 index 0000000..c365499 --- /dev/null +++ b/python3/vimspector/settings.py @@ -0,0 +1,26 @@ +# 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. + + +import vim +import builtins + + +def Get( option: str, default=None, cls=str ): + return cls( vim.vars.get( f'vimspector_{ option }', default ) ) + + +def Int( option: str, default=0 ): + return Get( option, default=default, cls=builtins.int ) From 80afb153b96e4e033b6c25fa6d5143deccbf5ee9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 18 Jul 2020 13:15:06 +0100 Subject: [PATCH 20/23] FixUp: Closing the output window causes errors on output --- python3/vimspector/output.py | 10 ++++++---- python3/vimspector/utils.py | 13 ++++++++----- tests/ui.test.vim | 30 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 4599e04..5b6e32a 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -157,11 +157,13 @@ class OutputView( object ): def _CreateBuffer( self, category, file_name = None, cmd = None ): - if not self._window.valid: - return + win = self._window + if not win.valid: + # We need to borrow the current window + win = vim.current.window - with utils.LetCurrentWindow( self._window ): - with utils.RestoreCurrentBuffer( self._window ): + with utils.LetCurrentWindow( win ): + with utils.RestoreCurrentBuffer( win ): if file_name is not None: assert cmd is None diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index d4a7749..7cd9266 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -184,8 +184,10 @@ def RestoreCurrentWindow(): try: yield finally: - vim.current.tabpage = old_tabpage - vim.current.window = old_window + if old_tabpage.valid: + vim.current.tabpage = old_tabpage + if old_window.valid: + vim.current.window = old_window @contextlib.contextmanager @@ -194,9 +196,10 @@ def RestoreCurrentBuffer( window ): try: yield finally: - with RestoreCurrentWindow(): - vim.current.window = window - vim.current.buffer = old_buffer + if window.valid: + with RestoreCurrentWindow(): + vim.current.window = window + vim.current.buffer = old_buffer @contextlib.contextmanager diff --git a/tests/ui.test.vim b/tests/ui.test.vim index 07639e5..82667e2 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -286,6 +286,36 @@ function! Test_CloseOutput() %bwipe! endfunction +function! Test_CloseOutput_Early() + augroup TestCustomUI + au! + au User VimspectorUICreated + \ call win_execute( g:vimspector_session_windows.output, 'q' ) + augroup END + + call s:StartDebugging() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + call assert_equal( + \ [ 'row', [ + \ [ 'col', [ + \ [ 'leaf', g:vimspector_session_windows.variables ], + \ [ 'leaf', g:vimspector_session_windows.watches ], + \ [ 'leaf', g:vimspector_session_windows.stack_trace ], + \ ] ], + \ [ 'leaf', g:vimspector_session_windows.code ], + \ [ 'leaf', g:vimspector_session_windows.terminal ], + \ ] ], + \ winlayout( g:vimspector_session_windows.tabpage ) ) + + au! TestCustomUI + call vimspector#test#setup#Reset() + %bwipe! +endfunction + + function! Test_CustomUI() augroup TestCustomUI au! From 37267666942643e3bc268411f913260b0cd734eb Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 18 Jul 2020 13:41:22 +0100 Subject: [PATCH 21/23] Documentation for the UI customisation --- README.md | 158 +++++++++++++++++++++++++++- python3/vimspector/debug_session.py | 5 + python3/vimspector/output.py | 11 ++ python3/vimspector/utils.py | 7 ++ 4 files changed, 180 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fe0f66f..9813538 100644 --- a/README.md +++ b/README.md @@ -65,11 +65,15 @@ For a tutorial and usage overview, take a look at the * [Other servers](#other-servers) * [Customisation](#customisation) * [Changing the default signs](#changing-the-default-signs) + * [Changing the default window sizes](#changing-the-default-window-sizes) + * [Changing the terminal size](#changing-the-terminal-size) + * [Advanced UI customisation](#advanced-ui-customisation) + * [Example](#example) * [FAQ](#faq) * [Motivation](#motivation) * [License](#license) - + @@ -673,6 +677,8 @@ You can configure your choices in the `.vimspector.json`. See ![locals window](https://puremourning.github.io/vimspector-web/img/vimspector-locals-window.png) +Scopes and variables are represented by the buffer `vimspector.Variables`. + ## Watches The watches window is a prompt buffer, where that's available. Enter insert mode @@ -687,6 +693,8 @@ to add a new watch expression. ![watch window](https://puremourning.github.io/vimspector-web/img/vimspector-watch-window.png) +The watches are represented by the buffer `vimspector.StackTrace`. + ## Stack Traces * In the threads window, use `` to expand/collapse. @@ -694,6 +702,8 @@ to add a new watch expression. ![stack trace](https://puremourning.github.io/vimspector-web/img/vimspector-callstack-window.png) +The stack trace is represented by the buffer `vimspector.StackTrace`. + ## Program Output * In the outputs window use the WinBar to select the output channel. @@ -704,6 +714,10 @@ to add a new watch expression. ![output window](https://puremourning.github.io/vimspector-web/img/vimspector-output-window.png) +If the output window is closed, a new one can be opened with +`:VimspectorShowOutput ` (use tab-completion - `wildmenu` to see the +options). + ### Console The console window is a prompt buffer, where that's available, and can be used @@ -718,6 +732,9 @@ adapters. NOTE: See also [Watches](#watches) above. +If the output window is closed, a new one can be opened with +`:VimspectorShowOutput Console`. + ## Closing debugger To close the debugger, use: @@ -1298,6 +1315,145 @@ sign define vimspectorBPDisabled text=🔵 texthl=Normal sign define vimspectorPC text=🔶 texthl=SpellBad ``` +## Changing the default window sizes + +> ***Please Note***: This cusomiation API is ***unstable***, meaning that it may +change at any time. I will endeavour to reduce the impact of this and annouce +changes in Gitter. + +The following options control the default sizes of the UI windows (all of them +are numbers) + +- `g:vimspector_sidebar_width` (default: 50 columns): + The width in columns of the left utility windows (variables, watches, stack + trace) +- `g:vimspector_bottombar_height` (default 10 lines): + The height in rows of the output window below the code window. + +Example: + +```viml +let g:vimspector_sidebar_width = 75 +let g:vimspector_bottombar_height = 15 +``` + +## Changing the terminal size + +The terminal is typically created as a vertical split to the righ of the code +window, and that window is re-used for subsequent terminal buffers. +The following control the sizing of the terminal window used +for debuggee input/output when using Vim's built-in terminal. + +- `g:vimspector_code_minwidth` (default: 82 columns): + Minimum number of columns to try and maintain for the code window when + splitting to create the terminal window. +- `g:vimspector_terminal_maxwidth` (default: 80 columns): + Maximum number of columns to use for the terminal. +- `g:vimspector_terminal_minwidth` (default: 10 columns): + Minimum number of columns to use when it is not possible to fit + `g:vimspector_terminal_maxwidth` columns for the terminal. + +That's a lot of options, but essentially we try to make sure that there are at +least `g:vimspector_code_minwidth` columns for the main code window and that the +terminal is no wider than `g:vimspector_terminal_maxwidth` columns. +`g:vimspector_terminal_minwidth` is there to ensure that there's a reasonable +number of columns for the terminal even when there isn't enough horizontal space +to satisfy the other contraints. + +Example: + +```viml +let g:vimspector_code_minwidth = 90 +let g:vimspector_terminal_maxwidth = 75 +let g:vimspector_terminal_minwidth = 20 +``` + +## Advanced UI customisation + +> ***Please Note***: This cusomiation API is ***unstable***, meaning that it may +change at any time. I will endeavour to reduce the impact of this and annouce +changes in Gitter. + +The above customisation of window sizes is limited intentionally to keep things +simple. Vimspector also provides a way for you to customise the UI without +restrictions, by running a `User` autocommand just after creating the UI or +opening the terminal. This requires you to write some vimscript, but allows you +to do things like: + +* Hide a particular window or windows +* Move a particular window or windows +* Resize windows +* Have multiple windows for a particular buffer (say, you want 2 watch windows) +* etc. + +You can essentially do anything you could do manually by writing a little +vimscript code. + +The `User` autocommand is raised with `pattern` set with the following values: + +* `VimspectorUICreated`: Just after setting up the UI for a debug session +* `VimspectorTerminalOpened`: Just after opening the terminal window for program + input/output. + +The following global variable is set up for you to get access to the UI +elements: `g:vimspector_session_windows`. This is a `dict` with the following +keys: + +* `g:vimspector_session_windows.tagpage`: The tab page for the session +* `g:vimspector_session_windows.variables`: Window ID of the variables window, + containing the `vimspector.Variables` buffer. +* `g:vimspector_session_windows.watches`: Window ID of the watches window, + containng the `vimspector.Watches` buffer. +* `g:vimspector_session_windows.stack_trace`: Window ID of the stack trade + window containing the `vimspector.StackTrace` buffer. +* `g:vimspector_session_windows.code`: Window ID of the code window. +* `g:vimspector_session_windows.output`: Window ID of the output window. + +In addition, the following key is added when triggering the +`VimspectorTerminalOpened` event: + +* `g:vimspector_session_windows.terminal`: Window ID of the terminal window + +## Example + +There is some example code in `support/custom_ui_vimrc` showing how you can use +the window IDs to modify various aspects of the UI using some basic vim +commands, primarily `win_gotoid` funciton and the `wincmd` ex command. + +To try this out `vim -Nu support/custom_ui_vimrc `. + +Here's a rather smaller example. A simple way to use this is to drop it into a +file named `my_vimspector_ui.vim` in `~/.vim/plugin` (or paste into your +`vimrc`): + +```viml +" Set the basic sizes +let g:vimspector_sidebar_width = 80 +let g:vimspector_code_minwidth = 85 +let g:vimspector_terminal_minwidth = 75 + +function! s:CustomiseUI() + " Customise the basic UI... + + " Close the output window + call win_gotoid( g:vimspector_session_windows.output ) + q +endfunction + +function s:SetUpTerminal() + " Customise the terminal window size/position + " For some reasons terminal buffers in Neovim have line numbers + call win_gotoid( g:vimspector_session_windows.terminal ) + set norelativenumber nonumber +endfunction + +augroup MyVimspectorUICustomistaion + autocmd! + autocmd User VimspectorUICreated call s:CustomiseUI() + autocmd User VimspectorTerminalOpened call s:SetUpTerminal() +augroup END +``` + # FAQ 1. Q: Does it work? A: Yeah. It's a bit unpolished. diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index f553850..61713a5 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -453,6 +453,11 @@ class DebugSession( object ): self._stackTraceView.ExpandFrameOrThread() def ShowOutput( self, category ): + if not self._outputView.WindowIsValid(): + with utils.LetCurrentTabpage( self._uiTab ): + vim.command( f'botright { settings.Int( "bottombar_height", 10 ) }new' ) + self._outputView.UseWindow( vim.current.window ) + self._outputView.ShowOutput( category ) def GetOutputBuffers( self ): diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 5b6e32a..c21f8f1 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -102,6 +102,17 @@ class OutputView( object ): # FIXME: nunmenu the WinBar ? self._buffers = {} + def WindowIsValid( self ): + return self._window.valid + + def UseWindow( self, win ): + assert not self._window.valid + self._window = win + # TODO: Sorting of the WinBar ? + for category, _ in self._buffers.items(): + self._RenderWinBar( category ) + + def _ShowOutput( self, category ): if not self._window.valid: return diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 7cd9266..0049a1b 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -214,6 +214,13 @@ def AnyWindowForBuffer( buf ): yield +@contextlib.contextmanager +def LetCurrentTabpage( tabpage ): + with RestoreCurrentWindow(): + vim.current.tabpage = tabpage + yield + + @contextlib.contextmanager def LetCurrentWindow( window ): with RestoreCurrentWindow(): From 47ace823644f1db6c0ab48a61e3855004787020e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 18 Jul 2020 13:51:05 +0100 Subject: [PATCH 22/23] FixUp: output window should set the global win id --- python3/vimspector/debug_session.py | 8 ++++++++ tests/ui.test.vim | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 61713a5..4110d85 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -454,9 +454,17 @@ class DebugSession( object ): def ShowOutput( self, category ): if not self._outputView.WindowIsValid(): + # TODO: The UI code is too scattered. Re-organise into a UI class that + # just deals with these thigns like window layout and custmisattion. + # currently, this class and the CodeView share some responsiblity for this + # and poking into each View class to check its window is valid also feels + # wrong. with utils.LetCurrentTabpage( self._uiTab ): vim.command( f'botright { settings.Int( "bottombar_height", 10 ) }new' ) self._outputView.UseWindow( vim.current.window ) + vim.vars[ 'vimspector_session_windows' ][ 'output' ] = utils.WindowID( + vim.current.window, + self._uiTab ) self._outputView.ShowOutput( category ) diff --git a/tests/ui.test.vim b/tests/ui.test.vim index 82667e2..3bd9707 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -310,6 +310,28 @@ function! Test_CloseOutput_Early() \ ] ], \ winlayout( g:vimspector_session_windows.tabpage ) ) + " Open it again! + let g:vimspector_bottombar_height = 5 + VimspectorShowOutput Console + call assert_equal( + \ [ 'col', [ + \ [ 'row', [ + \ [ 'col', [ + \ [ 'leaf', g:vimspector_session_windows.variables ], + \ [ 'leaf', g:vimspector_session_windows.watches ], + \ [ 'leaf', g:vimspector_session_windows.stack_trace ], + \ ] ], + \ [ 'leaf', g:vimspector_session_windows.code ], + \ [ 'leaf', g:vimspector_session_windows.terminal ], + \ ] ], + \ [ 'leaf', g:vimspector_session_windows.output ] + \ ] ], + \ winlayout( g:vimspector_session_windows.tabpage ) ) + + " The actual height reported is the number of lines visible. The WinBar takes + " 1 screen row, so g:vimspector_bottombar_height -1 + call assert_equal( 4, winheight( g:vimspector_session_windows.output ) ) + au! TestCustomUI call vimspector#test#setup#Reset() %bwipe! From 99b582378a496473d380fd4252b0742eae5afe87 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 18 Jul 2020 14:39:54 +0100 Subject: [PATCH 23/23] Allow ctrl-c to cancel when asked for a variable --- autoload/vimspector.vim | 6 +- python3/vimspector/breakpoints.py | 13 ++-- python3/vimspector/debug_session.py | 112 ++++++++++++++++++---------- python3/vimspector/utils.py | 6 +- tests/ui.test.vim | 3 + 5 files changed, 91 insertions(+), 49 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index f37f27e..a7c6e4b 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -137,14 +137,16 @@ function! vimspector#ListBreakpoints() abort endfunction function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort - let buffers = py3eval( '_vimspector_session.GetOutputBuffers()' ) + let buffers = py3eval( '_vimspector_session.GetOutputBuffers() ' + \ . ' if _vimspector_session else []' ) 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" ) ) )' ), + \.' int( vim.eval( "a:CursorPos" ) ) )' + \. ' if _vimspector_session else []' ), \ "\n" ) endfunction diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index b6a975d..10b89e8 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -342,11 +342,14 @@ class ProjectBreakpoints( object ): f"Invalid value for exception breakpoint filter '{f}': " f"'{result}'. Must be boolean, 'Y', 'N' or '' (default)" ) else: - result = utils.AskForInput( - "{}: Break on {} (Y/N/default: {})? ".format( f[ 'filter' ], - f[ 'label' ], - default_value ), - default_value ) + try: + result = utils.AskForInput( + "{}: Break on {} (Y/N/default: {})? ".format( f[ 'filter' ], + f[ 'label' ], + default_value ), + default_value ) + except KeyboardInterrupt: + result = '' if result == 'Y': exception_filters.append( f[ 'filter' ] ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 4110d85..9ff680a 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -215,26 +215,30 @@ class DebugSession( object ): USER_CHOICES.update( launch_variables ) variables.update( launch_variables ) - variables.update( - utils.ParseVariables( adapter.get( 'variables', {} ), - variables, - calculus, - USER_CHOICES ) ) - variables.update( - utils.ParseVariables( configuration.get( 'variables', {} ), - variables, - calculus, - USER_CHOICES ) ) + try: + variables.update( + utils.ParseVariables( adapter.get( 'variables', {} ), + variables, + calculus, + USER_CHOICES ) ) + variables.update( + utils.ParseVariables( configuration.get( 'variables', {} ), + variables, + calculus, + USER_CHOICES ) ) - utils.ExpandReferencesInDict( configuration, - variables, - calculus, - USER_CHOICES ) - utils.ExpandReferencesInDict( adapter, - variables, - calculus, - USER_CHOICES ) + utils.ExpandReferencesInDict( configuration, + variables, + calculus, + USER_CHOICES ) + utils.ExpandReferencesInDict( adapter, + variables, + calculus, + USER_CHOICES ) + except KeyboardInterrupt: + self._Reset() + return if not adapter: utils.UserMessage( 'No adapter configured for {}'.format( @@ -294,18 +298,35 @@ class DebugSession( object ): self._StartWithConfiguration( self._configuration, self._adapter ) - def IfConnected( fct ): + def IfConnected( otherwise=None ): + def decorator( fct ): + """Decorator, call fct if self._connected else echo warning""" + @functools.wraps( fct ) + def wrapper( self, *args, **kwargs ): + if not self._connection: + utils.UserMessage( + 'Vimspector not connected, start a debug session first', + persist=False, + error=True ) + return otherwise + return fct( self, *args, **kwargs ) + return wrapper + return decorator + + def RequiresUI( otherwise=None ): """Decorator, call fct if self._connected else echo warning""" - @functools.wraps( fct ) - def wrapper( self, *args, **kwargs ): - if not self._connection: - utils.UserMessage( - 'Vimspector not connected, start a debug session first', - persist=True, - error=True ) - return - return fct( self, *args, **kwargs ) - return wrapper + def decorator( fct ): + @functools.wraps( fct ) + def wrapper( self, *args, **kwargs ): + if not self._uiTab or not self._uiTab.valid: + utils.UserMessage( + 'Vimspector is not active', + persist=False, + error=True ) + return otherwise + return fct( self, *args, **kwargs ) + return wrapper + return decorator def OnChannelData( self, data ): if self._connection is None: @@ -328,7 +349,7 @@ class DebugSession( object ): # TODO: Not calld self._connection = None - @IfConnected + @IfConnected() def Stop( self ): self._logger.debug( "Stop debug adapter with no callback" ) self._StopDebugAdapter() @@ -365,7 +386,7 @@ class DebugSession( object ): # make sure that we're displaying signs in any still-open buffers self._breakpoints.UpdateUI() - @IfConnected + @IfConnected() def StepOver( self ): if self._stackTraceView.GetCurrentThreadId() is None: return @@ -377,7 +398,7 @@ class DebugSession( object ): }, } ) - @IfConnected + @IfConnected() def StepInto( self ): if self._stackTraceView.GetCurrentThreadId() is None: return @@ -389,7 +410,7 @@ class DebugSession( object ): }, } ) - @IfConnected + @IfConnected() def StepOut( self ): if self._stackTraceView.GetCurrentThreadId() is None: return @@ -407,29 +428,29 @@ class DebugSession( object ): else: self.Start() - @IfConnected + @IfConnected() def Pause( self ): self._stackTraceView.Pause() - @IfConnected + @IfConnected() def ExpandVariable( self ): self._variablesView.ExpandVariable() - @IfConnected + @IfConnected() def AddWatch( self, expression ): self._variablesView.AddWatch( self._stackTraceView.GetCurrentFrame(), expression ) - @IfConnected + @IfConnected() def EvaluateConsole( self, expression ): self._outputView.Evaluate( self._stackTraceView.GetCurrentFrame(), expression ) - @IfConnected + @IfConnected() def DeleteWatch( self ): self._variablesView.DeleteWatch() - @IfConnected + @IfConnected() def ShowBalloon( self, winnr, expression ): """Proxy: ballonexpr -> variables.ShowBallon""" frame = self._stackTraceView.GetCurrentFrame() @@ -448,10 +469,11 @@ class DebugSession( object ): # Return variable aware function return self._variablesView.ShowBalloon( frame, expression ) - @IfConnected + @IfConnected() def ExpandFrameOrThread( self ): self._stackTraceView.ExpandFrameOrThread() + @RequiresUI() def ShowOutput( self, category ): if not self._outputView.WindowIsValid(): # TODO: The UI code is too scattered. Re-organise into a UI class that @@ -468,10 +490,11 @@ class DebugSession( object ): self._outputView.ShowOutput( category ) + @RequiresUI( otherwise=[] ) def GetOutputBuffers( self ): return self._outputView.GetCategories() - @IfConnected + @IfConnected( otherwise=[] ) def GetCompletionsSync( self, text_line, column_in_bytes ): if not self._server_capabilities.get( 'supportsCompletionsRequest' ): return [] @@ -552,9 +575,11 @@ class DebugSession( object ): vim.command( 'doautocmd User VimspectorUICreated' ) + @RequiresUI() def ClearCurrentFrame( self ): self.SetCurrentFrame( None ) + @RequiresUI() def SetCurrentFrame( self, frame ): if not frame: self._stackTraceView.Clear() @@ -596,6 +621,9 @@ class DebugSession( object ): if self._adapter[ 'port' ] == 'ask': port = utils.AskForInput( 'Enter port to connect to: ' ) + if port is None: + self._Reset() + return self._adapter[ 'port' ] = port self._connection_type = self._api_prefix + self._connection_type @@ -705,6 +733,8 @@ class DebugSession( object ): prop = atttach_config[ 'pidProperty' ] if prop not in launch_config: pid = utils.AskForInput( 'Enter PID to attach to: ' ) + if pid is None: + return launch_config[ prop ] = pid return elif atttach_config[ 'pidSelect' ] == 'none': diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 0049a1b..bfdddcc 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -339,7 +339,7 @@ def AskForInput( prompt, default_value = None ): return vim.eval( "input( '{}' {} )".format( Escape( prompt ), default_option ) ) except KeyboardInterrupt: - return '' + return None def AppendToBuffer( buf, line_or_lines, modified=False ): @@ -447,6 +447,10 @@ def ExpandReferencesInString( orig_s, default_value = user_choices.get( key, None ) mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ), default_value ) + + if mapping[ key ] is None: + raise KeyboardInterrupt + user_choices[ key ] = mapping[ key ] _logger.debug( "Value for %s not set in %s (from %s): set to %s", key, diff --git a/tests/ui.test.vim b/tests/ui.test.vim index 3bd9707..02bde3e 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -332,6 +332,9 @@ function! Test_CloseOutput_Early() " 1 screen row, so g:vimspector_bottombar_height -1 call assert_equal( 4, winheight( g:vimspector_session_windows.output ) ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 26, 1 ) + au! TestCustomUI call vimspector#test#setup#Reset() %bwipe!