diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 6fb0b16..4a89cdf 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -655,6 +655,37 @@ class DebugSession( object ): vim.command( 'tab split' ) self._uiTab = vim.current.tabpage + mode = settings.Get( 'ui_mode' ) + + self._logger.debug( 'ui_mode = %s', mode ) + + if mode == 'auto': + # Go vertical if there isn't enough horizontal space for at least: + # the left bar width + # + the code min width + # + the terminal min width + # + enough space for a sign column and number column? + min_width = ( settings.Int( 'sidebar_width' ) + + 1 + 2 + 3 + + settings.Int( 'code_minwidth' ) + + 1 + settings.Int( 'terminal_minwidth' ) ) + + mode = ( 'vertical' + if vim.options[ 'columns' ] < min_width + else 'horizontal' ) + + self._logger.debug( 'min_width: %s, actual: %s - result: %s', + min_width, + vim.options[ 'columns' ], + mode ) + + if mode == 'vertical': + self._SetUpUIVertical() + else: + self._SetUpUIHorizontal() + + + def _SetUpUIHorizontal( self ): # Code window code_window = vim.current.window self._codeView = code.CodeView( code_window, self._api_prefix ) @@ -695,6 +726,66 @@ class DebugSession( object ): # 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' ] = { + 'mode': 'horizontal', + '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 ), + 'eval': None # this is going to be updated every time eval popup is opened + } + with utils.RestoreCursorPosition(): + with utils.RestoreCurrentWindow(): + with utils.RestoreCurrentBuffer( vim.current.window ): + vim.command( 'doautocmd User VimspectorUICreated' ) + + + def _SetUpUIVertical( self ): + # Code window + code_window = vim.current.window + self._codeView = code.CodeView( code_window, self._api_prefix ) + + # Call stack + vim.command( + f'topleft { settings.Int( "topbar_height" ) }new' ) + stack_trace_window = vim.current.window + one_third = int( vim.eval( 'winwidth( 0 )' ) ) / 3 + self._stackTraceView = stack_trace.StackTraceView( self, + stack_trace_window ) + + + # Watches + vim.command( 'leftabove vertical new' ) + watch_window = vim.current.window + + # Variables + vim.command( 'leftabove vertical new' ) + vars_window = vim.current.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( vars_window, + watch_window ) + + + # Output/logging + vim.current.window = code_window + vim.command( f'rightbelow { settings.Int( "bottombar_height" ) }new' ) + output_window = vim.current.window + self._outputView = output.DAPOutputView( 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' ] = { + 'mode': 'vertical', 'tabpage': self._uiTab.number, 'code': utils.WindowID( code_window, self._uiTab ), 'stack_trace': utils.WindowID( stack_trace_window, self._uiTab ), diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index 9ad9e41..e9e76ea 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -20,11 +20,20 @@ from vimspector import utils DEFAULTS = { # UI - 'bottombar_height': 10, - 'sidebar_width': 50, - 'code_minwidth': 82, - 'terminal_maxwidth': 80, - 'terminal_minwidth': 10, + 'ui_mode': 'auto', + 'bottombar_height': 10, + + # For ui_mode = 'horizontal': + 'sidebar_width': 50, + 'code_minwidth': 82, + 'terminal_maxwidth': 80, + 'terminal_minwidth': 10, + + # For ui_mode = 'vertical': + 'topbar_height': 15, + 'code_minheight': 20, + 'terminal_maxheight': 15, + 'terminal_minheight': 5, # Signs 'sign_priority': { diff --git a/python3/vimspector/terminal.py b/python3/vimspector/terminal.py index 6ffd56a..0c84704 100644 --- a/python3/vimspector/terminal.py +++ b/python3/vimspector/terminal.py @@ -23,12 +23,53 @@ def LaunchTerminal( api_prefix, env = params.get( 'env' ) or {} term_options = { - 'vertical': 1, 'norestore': 1, 'cwd': cwd, 'env': env, } + if settings.Get( 'ui_mode' ) == 'horizontal': + # force-horizontal + term_options[ 'vertical' ] = 1 + elif utils.GetVimValue( vim.vars[ 'vimspector_session_windows' ], + 'mode' ) == 'horizontal': + # horizontal, which means that we should have enough space for: + # - sidebar + # - code min + # - term min width + # - + 2 vertical spaders + # - + 3 columns for signs + term_options[ 'vertical' ] = 1 + + # if we don't have enough space for terminal_maxwidth, then see if we have + # enough vertically for terminal_maxheight, in which case, + # that seems a better fit + term_horiz_max = ( settings.Int( 'sidebar_width' ) + + 1 + 2 + 3 + + settings.Int( 'code_minwidth' ) + + 1 + settings.Int( 'terminal_maxwidth' ) ) + term_vert_max = ( settings.Int( 'bottombar_height' ) + 1 + + settings.Int( 'code_minheight' ) + 1 + + settings.Int( 'terminal_minheight' ) ) + + if ( vim.options[ 'columns' ] < term_horiz_max and + vim.options[ 'lines' ] >= term_vert_max ): + # Looks like it, let's try that layout + term_options[ 'vertical' ] = 0 + + + else: + # vertical - we need enough space horizontally for the code+terminal, but we + # may fit better with code above terminal + term_options[ 'vertical' ] = 0 + + term_horiz_max = ( settings.Int( 'code_minwidth' ) + 3 + + settings.Int( 'terminal_maxwidth' ) + 1 ) + + if vim.options[ 'columns' ] > term_horiz_max: + term_options[ 'vertical' ] = 1 + + 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 @@ -50,13 +91,23 @@ def LaunchTerminal( api_prefix, # 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 ): + if term_options.get( 'curwin', 0 ): + pass + elif term_options[ 'vertical' ]: term_options[ 'term_cols' ] = max( min ( int( vim.eval( 'winwidth( 0 )' ) ) - settings.Int( 'code_minwidth' ), settings.Int( 'terminal_maxwidth' ) ), settings.Int( 'terminal_minwidth' ) ) + else: + term_options[ 'term_rows' ] = max( + min ( int( vim.eval( 'winheight( 0 )' ) ) + - settings.Int( 'code_minheight' ), + settings.Int( 'terminal_maxheight' ) ), + settings.Int( 'terminal_minheight' ) + ) + buffer_number = int( utils.Call( diff --git a/tests/lib/autoload/vimspector/test/setup.vim b/tests/lib/autoload/vimspector/test/setup.vim index b0296e0..6ab220c 100644 --- a/tests/lib/autoload/vimspector/test/setup.vim +++ b/tests/lib/autoload/vimspector/test/setup.vim @@ -50,3 +50,59 @@ function! vimspector#test#setup#Reset() abort call popup_clear() endfunction +let s:g_stack = {} + +function! vimspector#test#setup#PushGlobal( name, value ) abort + if !has_key( s:g_stack, a:name ) + let s:g_stack[ a:name ] = [] + endif + + let old_value = get( g:, a:name, v:null ) + call add( s:g_stack[ a:name ], old_value ) + let g:[ a:name ] = a:value + + return old_value +endfunction + +function! vimspector#test#setup#PopGlobal( name ) abort + if !has_key( s:g_stack, a:name ) || len( s:g_stack[ a:name ] ) == 0 + return v:null + endif + + let old_value = s:g_stack[ a:name ][ -1 ] + call remove( s:g_stack[ a:name ], -1 ) + + if old_value is v:null + silent! call remove( g:, a:name ) + else + let g:[ a:name ] = old_value + endif + + return old_value +endfunction + +let s:o_stack = {} + +function! vimspector#test#setup#PushOption( name, value ) abort + if !has_key( s:o_stack, a:name ) + let s:o_stack[ a:name ] = [] + endif + + let old_value = v:null + execute 'let old_value = &' . a:name + call add( s:o_stack[ a:name ], old_value ) + execute 'set ' . a:name . '=' . a:value + return old_value +endfunction + +function! vimspector#test#setup#PopOption( name ) abort + if !has_key( s:o_stack, a:name ) || len( s:o_stack[ a:name ] ) == 0 + return v:null + endif + + let old_value = s:o_stack[ a:name ][ -1 ] + call remove( s:o_stack[ a:name ], -1 ) + + execute 'set ' . a:name . '=' . old_value + return old_value +endfunction diff --git a/tests/ui.test.vim b/tests/ui.test.vim index f251539..1f7789e 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -1,6 +1,7 @@ let s:fn='../support/test/python/simple_python/main.py' function! SetUp() + let g:vimspector_ui_mode = get( s:, 'vimspector_ui_mode', 'horizontal' ) call vimspector#test#setup#SetUpWithMappings( 'HUMAN' ) endfunction @@ -16,12 +17,17 @@ function! s:StartDebugging() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 23, 1 ) endfunction +function! SetUp_Test_StandardLayout() + call vimspector#test#setup#PushOption( 'columns', 200 ) +endfunction + function! Test_StandardLayout() call s:StartDebugging() call vimspector#StepOver() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + call assert_equal( 'horizontal', g:vimspector_session_windows.mode ) call assert_equal( \ [ 'row', [ \ [ 'col', [ @@ -43,6 +49,247 @@ function! Test_StandardLayout() %bwipe! endfunction +function! TearDown_Test_StandardLayout() + call vimspector#test#setup#PopOption( 'columns' ) +endfunction + +function! SetUp_Test_NarrowLayout() + call vimspector#test#setup#PushOption( 'columns', 100 ) + let s:vimspector_ui_mode = 'vertical' +endfunction + +function! Test_NarrowLayout() + call s:StartDebugging() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + call assert_equal( 'vertical', g:vimspector_session_windows.mode ) + call assert_equal( + \ [ 'col', [ + \ [ 'row', [ + \ [ '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 ) ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! TearDown_Test_NarrowLayout() + unlet s:vimspector_ui_mode + call vimspector#test#setup#PopOption( 'columns' ) +endfunction + +function! SetUp_Test_AutoLayoutTerminalVert() + let s:vimspector_ui_mode = 'auto' + call vimspector#test#setup#PushOption( 'columns', 250 ) + call vimspector#test#setup#PushOption( 'lines', 30 ) +endfunction + +function! Test_AutoLayoutTerminalVert() + call s:StartDebugging() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + call assert_equal( 'horizontal', g:vimspector_session_windows.mode ) + 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! TearDown_Test_AutoLayoutTerminalVert() + unlet s:vimspector_ui_mode + call vimspector#test#setup#PopOption( 'lines' ) + call vimspector#test#setup#PopOption( 'columns' ) +endfunction + +function! SetUp_Test_AutoLayoutTerminalHorizVert() + let s:vimspector_ui_mode = 'auto' + " Wide enough to be horizontal layout, but not wide enough to fully fit the + " terminal, with enough rows to fit the max terminal below + call vimspector#test#setup#PushOption( 'columns', + \ 50 + 82 + 3 + 2 + 12 ) + call vimspector#test#setup#PushOption( 'lines', 50 ) +endfunction + +function! Test_AutoLayoutTerminalHorizVert() + call s:StartDebugging() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + call assert_equal( 'horizontal', g:vimspector_session_windows.mode ) + 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', [ + \ [ '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! TearDown_Test_AutoLayoutTerminalHorizVert() + unlet s:vimspector_ui_mode + call vimspector#test#setup#PopOption( 'lines' ) + call vimspector#test#setup#PopOption( 'columns' ) +endfunction + +function! SetUp_Test_AutoLayoutTerminalHorizVertButNotEnoughLines() + let s:vimspector_ui_mode = 'auto' + " Wide enough to be horizontal layout, but not wide enough to fully fit the + " terminal, with enough rows to fit the max terminal below, but there are not + " enough lines to do this + call vimspector#test#setup#PushOption( 'columns', + \ 50 + 82 + 3 + 2 + 12 ) + call vimspector#test#setup#PushOption( 'lines', 20 ) +endfunction + +function! Test_AutoLayoutTerminalHorizVertButNotEnoughLines() + call s:StartDebugging() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + call assert_equal( 'horizontal', g:vimspector_session_windows.mode ) + 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! TearDown_Test_AutoLayoutTerminalHorizVertButNotEnoughLines() + unlet s:vimspector_ui_mode + call vimspector#test#setup#PopOption( 'lines' ) + call vimspector#test#setup#PopOption( 'columns' ) +endfunction + +function! SetUp_Test_AutoLayoutTerminalHoriz() + let s:vimspector_ui_mode = 'vertical' + " Vertical layout, but we split the terminal horizonally + call vimspector#test#setup#PushOption( 'columns', 200 ) + call vimspector#test#setup#PushOption( 'lines', 50 ) +endfunction + +function! Test_AutoLayoutTerminalHoriz() + call s:StartDebugging() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + call assert_equal( 'vertical', g:vimspector_session_windows.mode ) + call assert_equal( + \ [ 'col', [ + \ [ 'row', [ + \ [ 'leaf', g:vimspector_session_windows.variables ], + \ [ 'leaf', g:vimspector_session_windows.watches ], + \ [ 'leaf', g:vimspector_session_windows.stack_trace ], + \ ] ], + \ [ '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! TearDown_Test_AutoLayoutTerminalHoriz() + unlet s:vimspector_ui_mode + call vimspector#test#setup#PopOption( 'lines' ) + call vimspector#test#setup#PopOption( 'columns' ) +endfunction + +function! SetUp_Test_AutoLayoutTerminalVertVert() + let s:vimspector_ui_mode = 'auto' + " Not wide enough to go horizontal, but wide enough to put the terminal and + " code vertically split + call vimspector#test#setup#PushOption( 'columns', 80 ) + call vimspector#test#setup#PushOption( 'lines', 30 ) +endfunction + +function! Test_AutoLayoutTerminalVertVert() + call s:StartDebugging() + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 25, 1 ) + + call assert_equal( 'vertical', g:vimspector_session_windows.mode ) + call assert_equal( + \ [ 'col', [ + \ [ 'row', [ + \ [ '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 ) ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! TearDown_Test_AutoLayoutTerminalVertVert() + unlet s:vimspector_ui_mode + call vimspector#test#setup#PopOption( 'lines' ) + call vimspector#test#setup#PopOption( 'columns' ) +endfunction + + function! Test_CloseVariables() call s:StartDebugging()