From 6fd472418985d1599afc01e1d5db068a0003f1d7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 23 Sep 2020 12:29:40 +0100 Subject: [PATCH 1/3] Launch remote commands in a termianl This allows stdin and mans you don't need the output window visible to see the useful process output when debugging a remote-Launch. --- python3/vimspector/code.py | 89 +++++------------------------ python3/vimspector/debug_session.py | 25 ++++++-- python3/vimspector/terminal.py | 82 ++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 80 deletions(-) create mode 100644 python3/vimspector/terminal.py diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index c50d602..977ecec 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, settings, signs +from vimspector import utils, terminal, signs class CodeView( object ): @@ -26,8 +26,7 @@ class CodeView( object ): self._window = window self._api_prefix = api_prefix - self._terminal_window = None - self._terminal_buffer_number = None + self._terminal = None self.current_syntax = None self._logger = logging.getLogger( __name__ ) @@ -63,7 +62,9 @@ class CodeView( object ): linehl = 'CursorLine' ) - def _UndisplayPC( self ): + def _UndisplayPC( self, clear_pc = True ): + if clear_pc: + self._current_frame = None if self._signs[ 'vimspectorPC' ]: signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' ) self._signs[ 'vimspectorPC' ] = None @@ -74,7 +75,7 @@ class CodeView( object ): if not frame: return - self._UndisplayPC() + self._UndisplayPC( clear_pc = False ) # FIXME: Do we relly need to keep using up IDs ? self._signs[ 'vimspectorPC' ] = self._next_sign_id @@ -152,7 +153,7 @@ class CodeView( object ): signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' ) self._signs[ 'vimspectorPC' ] = None - self._current_frame = None + self._UndisplayPC() self._UndisplaySigns() self.current_syntax = None @@ -241,73 +242,11 @@ class CodeView( object ): def LaunchTerminal( self, params ): - # kind = params.get( 'kind', 'integrated' ) + self._terminal = terminal.LaunchTerminal( self._api_prefix, + params, + window_for_start = self._window, + existing_term = self._terminal ) - # FIXME: We don't support external terminals, and only open in the - # integrated one. - - cwd = params[ 'cwd' ] - args = params[ 'args' ] - env = params.get( 'env', {} ) - - term_options = { - 'vertical': 1, - 'norestore': 1, - 'cwd': cwd, - 'env': env, - } - - if self._window.valid: - window_for_start = self._window - else: - # 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: - 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 ) ) ): - term_options[ 'curwin' ] = 1 - else: - term_options[ 'vertical' ] = 0 - - buffer_number = None - terminal_window = None - 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 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' ), - settings.Int( 'terminal_maxwidth' ) ), - settings.Int( 'terminal_minwidth' ) - ) - - buffer_number = int( - utils.Call( - 'vimspector#internal#{}term#Start'.format( self._api_prefix ), - args, - term_options ) ) - terminal_window = vim.current.window - - if buffer_number is None or buffer_number <= 0: - # TODO: Do something better like reject the request? - raise ValueError( "Unable to start terminal" ) - - 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 + # FIXME: Change this tor return the PID rather than having debug_session + # work that out + return self._terminal.buffer_number diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 3956d7f..e072a23 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -31,6 +31,7 @@ from vimspector import ( breakpoints, utils, variables, settings, + terminal, installer ) from vimspector.vendor.json_minify import minify @@ -61,6 +62,7 @@ class DebugSession( object ): self._outputView = None self._breakpoints = breakpoints.ProjectBreakpoints() self._splash_screen = None + self._remote_term = None self._run_on_server_exit = None @@ -360,7 +362,6 @@ class DebugSession( object ): def OnServerStderr( self, data ): - self._logger.info( "Server stderr: %s", data ) if self._outputView: self._outputView.Print( 'server', data ) @@ -404,6 +405,7 @@ class DebugSession( object ): self._variablesView = None self._outputView = None self._codeView = None + self._remote_term = None self._uiTab = None # make sure that we're displaying signs in any still-open buffers @@ -760,13 +762,20 @@ class DebugSession( object ): commands = self._GetCommands( remote, 'attach' ) for command in commands: - cmd = remote_exec_cmd + command[ : ] + cmd = remote_exec_cmd + command for index, item in enumerate( cmd ): cmd[ index ] = item.replace( '%PID%', pid ) self._logger.debug( 'Running remote app: %s', cmd ) - self._outputView.RunJobWithOutput( 'Remote', cmd ) + self._remote_term = terminal.LaunchTerminal( + self._api_prefix, + { + 'args': cmd, + 'cwd': os.getcwd() + }, + self._codeView._window, + self._remote_term ) else: if atttach_config[ 'pidSelect' ] == 'ask': prop = atttach_config[ 'pidProperty' ] @@ -805,8 +814,14 @@ class DebugSession( object ): full_cmd.append( item.replace( '%CMD%', command_line ) ) self._logger.debug( 'Running remote app: %s', full_cmd ) - self._outputView.RunJobWithOutput( 'Remote{}'.format( index ), - full_cmd ) + self._remote_term = terminal.LaunchTerminal( + self._api_prefix, + { + 'args': full_cmd, + 'cwd': os.getcwd() + }, + self._codeView._window, + self._remote_term ) def _GetSSHCommand( self, remote ): diff --git a/python3/vimspector/terminal.py b/python3/vimspector/terminal.py new file mode 100644 index 0000000..a2ed264 --- /dev/null +++ b/python3/vimspector/terminal.py @@ -0,0 +1,82 @@ +from vimspector import utils, settings + +import vim + + +class Terminal: + window = None + buffer_number: int = None + + +def LaunchTerminal( api_prefix, + params, + window_for_start, + existing_term ): + if not existing_term: + term = Terminal() + else: + term = existing_term + + cwd = params[ 'cwd' ] + args = params[ 'args' ] + env = params.get( 'env', {} ) + + term_options = { + 'vertical': 1, + 'norestore': 1, + 'cwd': cwd, + 'env': env, + } + + if not window_for_start or not window_for_start.valid: + # TOOD: Where? Maybe we should just use botright vertical ... + window_for_start = vim.current.window + + if term.window is not None and term.window.valid: + assert term.buffer_number + window_for_start = term.window + if ( term.window.buffer.number == term.buffer_number + and int( utils.Call( 'vimspector#internal#{}term#IsFinished'.format( + api_prefix ), + term.buffer_number ) ) ): + term_options[ 'curwin' ] = 1 + else: + term_options[ 'vertical' ] = 0 + + buffer_number = None + terminal_window = None + 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 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' ), + settings.Int( 'terminal_maxwidth' ) ), + settings.Int( 'terminal_minwidth' ) + ) + + buffer_number = int( + utils.Call( + 'vimspector#internal#{}term#Start'.format( api_prefix ), + args, + term_options ) ) + terminal_window = vim.current.window + + if buffer_number is None or buffer_number <= 0: + # TODO: Do something better like reject the request? + raise ValueError( "Unable to start terminal" ) + + term.window = terminal_window + term.buffer_number = buffer_number + + vim.vars[ 'vimspector_session_windows' ][ 'terminal' ] = utils.WindowID( + term.window, + vim.current.tabpage ) + with utils.RestoreCursorPosition(): + with utils.RestoreCurrentWindow(): + with utils.RestoreCurrentBuffer( vim.current.window ): + vim.command( 'doautocmd User VimspectorTerminalOpened' ) + + return term From 069224e28d4b6eae04c11f17502b87b7aebca861 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 24 Sep 2020 15:56:41 +0100 Subject: [PATCH 2/3] Allow defaults for variables with multiple underscore characters --- python3/vimspector/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 2874635..879e910 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -452,7 +452,7 @@ VAR_MATCH = re.compile( (?P[_a-z][_a-z0-9]*) | # or An identifier - named param {(?P[_a-z][_a-z0-9]*)} | # or An {identifier} - braced param {(?P # or An {id:default} - default param, as - (?P[_a-z][a-z0-9]*) # an ID + (?P[_a-z][_a-z0-9]*) # an ID : # then a colon (?P(?:[^}]|\})*) # then anything up to }, or a \} )} | # From b34ccd679d3589dc2220d1c2a939c529c147aa13 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 25 Sep 2020 16:28:54 +0100 Subject: [PATCH 3/3] Fix ambiwidth=double again and make sure it doesn't break again --- python3/vimspector/breakpoints.py | 3 + python3/vimspector/code.py | 2 + python3/vimspector/signs.py | 5 +- tests/breakpoints_doublewidth.test.vim | 726 +++++++++++++++++++++++++ 4 files changed, 735 insertions(+), 1 deletion(-) create mode 100644 tests/breakpoints_doublewidth.test.vim diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 6e59149..617ae6c 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -55,16 +55,19 @@ class ProjectBreakpoints( object ): if not signs.SignDefined( 'vimspectorBP' ): signs.DefineSign( 'vimspectorBP', text = '●', + double_text = '●', texthl = 'WarningMsg' ) if not signs.SignDefined( 'vimspectorBPCond' ): signs.DefineSign( 'vimspectorBPCond', text = '◆', + double_text = '◆', texthl = 'WarningMsg' ) if not signs.SignDefined( 'vimspectorBPDisabled' ): signs.DefineSign( 'vimspectorBPDisabled', text = '●', + double_text = '●', texthl = 'LineNr' ) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 977ecec..c462272 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -53,11 +53,13 @@ class CodeView( object ): if not signs.SignDefined( 'vimspectorPC' ): signs.DefineSign( 'vimspectorPC', text = '▶', + double_text = '▶', texthl = 'MatchParen', linehl = 'CursorLine' ) if not signs.SignDefined( 'vimspectorPCBP' ): signs.DefineSign( 'vimspectorPCBP', text = '●▶', + double_text = '▷', texthl = 'MatchParen', linehl = 'CursorLine' ) diff --git a/python3/vimspector/signs.py b/python3/vimspector/signs.py index 0c5e704..c51bd76 100644 --- a/python3/vimspector/signs.py +++ b/python3/vimspector/signs.py @@ -12,7 +12,10 @@ def SignDefined( name ): return False -def DefineSign( name, text, texthl, col = 'right', **kwargs ): +def DefineSign( name, text, double_text, texthl, col = 'right', **kwargs ): + if utils.GetVimValue( vim.options, 'ambiwidth', '' ) == 'double': + text = double_text + if col == 'right': if int( utils.Call( 'strdisplaywidth', text ) ) < 2: text = ' ' + text diff --git a/tests/breakpoints_doublewidth.test.vim b/tests/breakpoints_doublewidth.test.vim new file mode 100644 index 0000000..12756da --- /dev/null +++ b/tests/breakpoints_doublewidth.test.vim @@ -0,0 +1,726 @@ +function! SetUp() + set ambiwidth=double + call vimspector#test#setup#SetUpWithMappings( v:none ) +endfunction + +function! ClearDown() + call vimspector#test#setup#ClearDown() +endfunction + +function! SetUp_Test_Mappings_Are_Added_HUMAN() + let g:vimspector_enable_mappings = 'HUMAN' +endfunction + +function! Test_Mappings_Are_Added_HUMAN() + call assert_true( hasmapto( 'vimspector#Continue()' ) ) + call assert_false( hasmapto( 'vimspector#Launch()' ) ) + call assert_true( hasmapto( 'vimspector#Stop()' ) ) + call assert_true( hasmapto( 'vimspector#Restart()' ) ) + call assert_true( hasmapto( 'vimspector#ToggleBreakpoint()' ) ) + call assert_true( hasmapto( 'vimspector#AddFunctionBreakpoint' ) ) + call assert_true( hasmapto( 'vimspector#StepOver()' ) ) + call assert_true( hasmapto( 'vimspector#StepInto()' ) ) + call assert_true( hasmapto( 'vimspector#StepOut()' ) ) +endfunction + +function! SetUp_Test_Mappings_Are_Added_VISUAL_STUDIO() + let g:vimspector_enable_mappings = 'VISUAL_STUDIO' +endfunction + +function! Test_Mappings_Are_Added_VISUAL_STUDIO() + call assert_true( hasmapto( 'vimspector#Continue()' ) ) + call assert_false( hasmapto( 'vimspector#Launch()' ) ) + call assert_true( hasmapto( 'vimspector#Stop()' ) ) + call assert_true( hasmapto( 'vimspector#Restart()' ) ) + call assert_true( hasmapto( 'vimspector#ToggleBreakpoint()' ) ) + call assert_true( hasmapto( 'vimspector#AddFunctionBreakpoint' ) ) + call assert_true( hasmapto( 'vimspector#StepOver()' ) ) + call assert_true( hasmapto( 'vimspector#StepInto()' ) ) + call assert_true( hasmapto( 'vimspector#StepOut()' ) ) +endfunction + +function! SetUp_Test_Signs_Placed_Using_API_Are_Shown() + let g:vimspector_enable_mappings = 'VISUAL_STUDIO' +endfunction + +function! Test_Signs_Placed_Using_API_Are_Shown() + " We need a real file + edit testdata/cpp/simple/simple.cpp + call feedkeys( "/printf\", 'xt' ) + + " Set breakpoint + call vimspector#ToggleBreakpoint() + + call assert_true( exists( '*vimspector#ToggleBreakpoint' ) ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', + \ line( '.' ), + \ 'vimspectorBP', + \ 9 ) + + " Disable breakpoint + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ line( '.' ), + \ 'vimspectorBPDisabled', + \ 9 ) + + " Remove breakpoint + call vimspector#ToggleBreakpoint() + + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', + \ line( '.' ) ) + + call vimspector#ClearBreakpoints() + call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorBP' ) + call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorCode' ) + + call vimspector#test#setup#Reset() + %bwipeout! +endfunction + +function! SetUp_Test_Use_Mappings_HUMAN() + let g:vimspector_enable_mappings = 'HUMAN' +endfunction + +function! Test_Use_Mappings_HUMAN() + lcd testdata/cpp/simple + edit simple.cpp + call setpos( '.', [ 0, 15, 1 ] ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 15 ) + + " Add the breakpoint + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', + \ 15, + \ 'vimspectorBP', + \ 9 ) + + " Disable the breakpoint + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 15, + \ 'vimspectorBPDisabled', + \ 9 ) + + " Delete the breakpoint + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 15 ) + + " Add it again + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 15, + \ 'vimspectorBP', + \ 9 ) + + " Here we go. Start Debugging + call feedkeys( "\", 'xt' ) + + call assert_equal( 2, len( gettabinfo() ) ) + let cur_tabnr = tabpagenr() + call assert_equal( 5, len( gettabinfo( cur_tabnr )[ 0 ].windows ) ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + + " Step + call feedkeys( "\", 'xt' ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cp', 16 ) + \ } ) + + call vimspector#test#setup#Reset() + + lcd - + %bwipeout! +endfunction + +function! SetUp_Test_StopAtEntry() + let g:vimspector_enable_mappings = 'HUMAN' +endfunction + +function Test_StopAtEntry() + lcd testdata/cpp/simple + edit simple.cpp + call setpos( '.', [ 0, 1, 1 ] ) + + " Test stopAtEntry behaviour + call feedkeys( "\", 'xt' ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 15 ) + \ } ) + + call vimspector#test#setup#Reset() + + lcd - + %bwipeout! +endfunction + +function! SetUp_Test_DisableBreakpointWhileDebugging() + let g:vimspector_enable_mappings = 'HUMAN' +endfunction + +function Test_DisableBreakpointWhileDebugging() + lcd testdata/cpp/simple + edit simple.cpp + call setpos( '.', [ 0, 15, 1 ] ) + + " Test stopAtEntry behaviour + call feedkeys( "\", 'xt' ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 15 ) + \ } ) + call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorBP' ) + + call setpos( '.', [ 0, 16, 1 ] ) + + " Add the breakpoint + call feedkeys( "\", 'xt' ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorCode', + \ 16, + \ 'vimspectorBP', + \ 9 ) + \ } ) + + " Remove the breakpoint + call feedkeys( "\", 'xt' ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', + \ 16 ) + \ } ) + + " Add the breakpoint + call feedkeys( "\", 'xt' ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorCode', + \ 16, + \ 'vimspectorBP', + \ 9 ) + \ } ) + + " Run to breakpoint + call setpos( '.', [ 0, 15, 1 ] ) + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 16 ) + \ } ) + + call vimspector#Reset() + call WaitForAssert( {-> + \ assert_true ( pyxeval( '_vimspector_session._connection is None' ) ) + \ } ) + call WaitForAssert( {-> + \ assert_true( pyxeval( '_vimspector_session._uiTab is None' ) ) + \ } ) + + " Check breakpoint is now a user breakpoint + call setpos( '.', [ bufnr( 'simple.cpp' ), 1, 1 ] ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 16, + \ 'vimspectorBP', + \ 9 ) + + " Disable the breakpoint + call setpos( '.', [ bufnr( 'simple.cpp' ), 16, 1 ] ) + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 16, + \ 'vimspectorBPDisabled', + \ 9 ) + + " And delete it + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( + \ 'VimspectorBP', + \ 16 ) + + call vimspector#ClearBreakpoints() + call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorBP' ) + call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorCode' ) + + lcd - + call vimspector#test#setup#Reset() + %bwipeout! +endfunction + +function! SetUp_Test_Insert_Code_Above_Breakpoint() + let g:vimspector_enable_mappings = 'HUMAN' +endfunction + +function! Test_Insert_Code_Above_Breakpoint() + let fn='main.py' + lcd ../support/test/python/simple_python + exe 'edit ' . fn + call setpos( '.', [ 0, 25, 5 ] ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 25, 5 ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 25 ) + + " Add the breakpoint + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', + \ 25, + \ 'vimspectorBP', + \ 9 ) + + " Insert a line above the breakpoint + call append( 22, ' # Test' ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 5 ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', + \ 26, + \ 'vimspectorBP', + \ 9 ) + + " CHeck that we break at the right point + call setpos( '.', [ 0, 1, 1 ] ) + call vimspector#LaunchWithSettings( { 'configuration': 'run' } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) + call vimspector#Reset() + call vimspector#test#setup#WaitForReset() + + " Toggle the breakpoint + call setpos( '.', [ 0, 26, 1 ] ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', + \ 26, + \ 'vimspectorBP', + \ 9 ) + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 26, + \ 'vimspectorBPDisabled', + \ 9 ) + " Delete it + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 26 ) + +endfunction + +function! SetUp_Test_Conditional_Line_Breakpoint() + let g:vimspector_enable_mappings = 'HUMAN' +endfunction + +function! Test_Conditional_Line_Breakpoint() + lcd testdata/cpp/simple + edit simple.cpp + call setpos( '.', [ 0, 16, 1 ] ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 16 ) + + " Add the conditional breakpoint + call feedkeys( "\\\argc==0\\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', + \ 16, + \ 'vimspectorBPCond', + \ 9 ) + + " Disable the breakpoint + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 16, + \ 'vimspectorBPDisabled', + \ 9 ) + + " Delete the breakpoint + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 16 ) + + " Add breakpoint using API: + " - on line 16 condition which doesn't match + " - then an unconditional one on line 9, unconditional + " - then on line 17, condition which matches + call vimspector#ToggleBreakpoint( { 'condition': 'argc == 0' } ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 16, + \ 'vimspectorBPCond', + \ 9 ) + call setpos( '.', [ 0, 9, 1 ] ) + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 9, + \ 'vimspectorBP', + \ 9 ) + + call setpos( '.', [ 0, 17, 1 ] ) + call vimspector#ToggleBreakpoint( { 'condition': 'argc == 1' } ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 17, + \ 'vimspectorBPCond', + \ 9 ) + + call setpos( '.', [ 0, 1, 1 ] ) + + " Start debugging + call vimspector#Continue() + " break on main + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + + " Ignore non-matching on line 16, break on line 9 + call vimspector#Continue() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 9, 1 ) + + " Condition matches on line 17 + call vimspector#Continue() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 17, 1 ) + + call vimspector#test#setup#Reset() + + lcd - + %bwipeout! +endfunction + +function! SetUp_Test_Conditional_Line_Breakpoint_Hit() + let g:vimspector_enable_mappings = 'HUMAN' +endfunction + +function! Test_Conditional_Line_Breakpoint_Hit() + call ThisTestIsFlaky() + + let fn = '../support/test/python/simple_python/main.py' + exe 'edit' fn + call setpos( '.', [ 0, 14, 1 ] ) + + " Add the conditional breakpoint (3 times) + call feedkeys( "\\\\3\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 14, + \ 'vimspectorBPCond', + \ 9 ) + + call vimspector#LaunchWithSettings( { 'configuration': 'run' } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 14, 1 ) + + " difficult to check if we really did run 3 times, so just use the watch + " window (also, tests the watch window!) + call vimspector#AddWatch( 'i' ) + call WaitForAssert( {-> + \ assert_equal( [ ' *- Result: 2' ], + \ getbufline( 'vimspector.Watches', '$' ) ) + \ } ) + + + call vimspector#test#setup#Reset() + %bwipeout! +endfunction + +function! Test_Function_Breakpoint() + lcd testdata/cpp/simple + edit simple.cpp + call vimspector#AddFunctionBreakpoint( 'foo' ) + call vimspector#Launch() + " break on main + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + call vimspector#Continue() + " break on func + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 9, 1 ) + call vimspector#test#setup#Reset() + %bwipeout! +endfunction + +function! Test_Function_Breakpoint_Condition() + lcd testdata/cpp/simple + edit simple.cpp + call vimspector#AddFunctionBreakpoint( 'foo', { 'condition': '1' } ) + call vimspector#Launch() + " break on main + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + call vimspector#Continue() + " break on func + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 9, 1 ) + call vimspector#test#setup#Reset() + %bwipeout! +endfunction + +" Can't find an adapter that supports conditional function breakpoints which are +" probably pretty niche anyway +" +" function! Test_Function_Breakpoint_Condition_False() +" lcd testdata/cpp/simple +" edit simple.cpp +" +" call vimspector#AddFunctionBreakpoint( 'foo', { 'condition': '0' } ) +" call setpos( '.', [ 0, 17, 1 ] ) +" call vimspector#ToggleBreakpoint() +" call vimspector#Launch() +" " break on main +" call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) +" call vimspector#Continue() +" +" " doesn't break in func, break on line 17 +" call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 17, 1 ) +" call vimspector#test#setup#Reset() +" %bwipeout! +" throw "xfail cpptools doesn't seem to honour conditions on function bps" +" endfunction + +function! s:CheckQuickFixEntries( entries ) + let qf = getqflist() + let i = 0 + for entry in a:entries + if i >= len( qf ) + call assert_report( 'Expected more quickfix entries' ) + endif + for key in keys( entry ) + call assert_equal( entry[ key ], + \ qf[ i ][ key ], + \ key . ' in ' . string( qf[ i ] ) + \ . ' expected ' . entry[ key ] ) + endfor + let i = i+1 + endfor +endfunction + +function! Test_ListBreakpoints() + lcd testdata/cpp/simple + edit simple.cpp + call setpos( '.', [ 0, 15, 1 ] ) + + call vimspector#ListBreakpoints() + wincmd p + cclose + call s:CheckQuickFixEntries( [] ) + + call vimspector#ToggleBreakpoint() + call assert_equal( [], getqflist() ) + + call vimspector#ListBreakpoints() + call s:CheckQuickFixEntries( [ + \ { 'lnum': 15, 'col': 1, 'bufnr': bufnr( 'simple.cpp', 0 ) } + \ ] ) + + " Cursor jumps to the quickfix window + call assert_equal( 'quickfix', &buftype ) + cclose + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + + call vimspector#Launch() + " break on main + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + + call vimspector#ListBreakpoints() + call s:CheckQuickFixEntries( [ + \ { 'lnum': 15, 'col': 1, 'bufnr': bufnr( 'simple.cpp', 0 ) } + \ ] ) + call assert_equal( 'quickfix', &buftype ) + wincmd p + cclose + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + + " Add a breakpoint that moves (from line 5 to line 9) + call cursor( [ 5, 1 ] ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 5, 1 ) + call vimspector#ToggleBreakpoint() + + function! Check() + call vimspector#ListBreakpoints() + wincmd p + return assert_equal( 2, len( getqflist() ) ) + endfunction + call WaitForAssert( function( 'Check' ) ) + + call s:CheckQuickFixEntries( [ + \ { 'lnum': 15, 'col': 1, 'bufnr': bufnr( 'simple.cpp', 0 ) }, + \ { 'lnum': 9, 'col': 1, 'bufnr': bufnr( 'simple.cpp', 0 ) }, + \ ] ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_Custom_Breakpoint_Priority() + let g:vimspector_sign_priority = { + \ 'vimspectorPC': 1, + \ 'vimspectorPCBP': 1, + \ 'vimspectorBP': 2, + \ 'vimspectorBPCond': 3, + \ 'vimspectorBPDisabled': 4 + \ } + + " While not debugging + lcd testdata/cpp/simple + edit simple.cpp + + call setpos( '.', [ 0, 15, 1 ] ) + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', + \ 15, + \ 'vimspectorBP', + \ 2 ) + call setpos( '.', [ 0, 16, 1 ] ) + call vimspector#ToggleBreakpoint() + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 16, + \ 'vimspectorBPDisabled', + \ 4 ) + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 15 ) + + call setpos( '.', [ 0, 17, 1 ] ) + call vimspector#ToggleBreakpoint( { 'condition': '1' } ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 17, + \ 'vimspectorBPCond', + \ 3 ) + + " While debugging + call vimspector#Launch() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 15 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 15, + \ 'vimspectorBP', + \ 2 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 15, + \ 'vimspectorPCBP', + \ 1 ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorCode', + \ 17, + \ 'vimspectorBP', + \ 2 ) + + call vimspector#StepOver() + " No sign as disabled + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 16 ) + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 17, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 17 ) + + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorCode', + \ 15, + \ 'vimspectorBP', + \ 2 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 17, + \ 'vimspectorBP', + \ 2 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 17, + \ 'vimspectorPCBP', + \ 1 ) + + + call vimspector#test#setup#Reset() + lcd - + %bwipeout! + unlet! g:vimspector_sign_priority +endfunction + +function! Test_Custom_Breakpoint_Priority_Partial() + let g:vimspector_sign_priority = { + \ 'vimspectorBP': 2, + \ 'vimspectorBPCond': 3, + \ 'vimspectorBPDisabled': 4 + \ } + + " While not debugging + lcd testdata/cpp/simple + edit simple.cpp + + call setpos( '.', [ 0, 15, 1 ] ) + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', + \ 15, + \ 'vimspectorBP', + \ 2 ) + call setpos( '.', [ 0, 16, 1 ] ) + call vimspector#ToggleBreakpoint() + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 16, + \ 'vimspectorBPDisabled', + \ 4 ) + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 15 ) + + call setpos( '.', [ 0, 17, 1 ] ) + call vimspector#ToggleBreakpoint( { 'condition': '1' } ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 17, + \ 'vimspectorBPCond', + \ 3 ) + + " While debugging + call vimspector#Launch() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 15 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 15, + \ 'vimspectorBP', + \ 2 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 15, + \ 'vimspectorPCBP', + \ 200 ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorCode', + \ 17, + \ 'vimspectorBP', + \ 2 ) + + call vimspector#StepOver() + " No sign as disabled + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 16 ) + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 17, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 17 ) + + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorCode', + \ 15, + \ 'vimspectorBP', + \ 2 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 17, + \ 'vimspectorBP', + \ 2 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 17, + \ 'vimspectorPCBP', + \ 200 ) + + + call vimspector#test#setup#Reset() + lcd - + %bwipeout! + unlet! g:vimspector_sign_priority +endfunction