From cf44cae98cb013ec6fe67abcad54704ec2867dea Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 2 Nov 2019 23:09:54 +0000 Subject: [PATCH 001/609] Add powershell demo Doeesn't work due to debugger crashing on stack trace request. Related #69 --- install_gadget.py | 29 +++++++++++++++++++++++- support/test/powershell/.vimspector.json | 17 ++++++++++++++ support/test/powershell/connect | 6 +++++ support/test/powershell/run | 18 +++++++++++++++ support/test/powershell/test.ps1 | 9 ++++++++ 5 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 support/test/powershell/.vimspector.json create mode 100755 support/test/powershell/connect create mode 100755 support/test/powershell/run create mode 100644 support/test/powershell/test.ps1 diff --git a/install_gadget.py b/install_gadget.py index 6da3b35..a1d9484 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -260,6 +260,32 @@ GADGETS = { }, }, }, + 'PowerShellEditorServices': { + 'language': 'powershell', + 'enabled': False, + 'download': { + 'url': 'https://github.com/PowerShell/PowerShellEditorServices/releases/' + 'download/v${version}/PowerShellEditorServices.zip', + 'format': 'zip', + }, + 'do': ( lambda name, root: + MakeSymlink( gadget_dir, + name, + os.path.join( root, 'PowerShellEditorServices' ) ) ), + 'all': { + 'version': '1.13.0', + 'file_name': 'PowerShellEditorServices.zip', + 'checksum': + 'd10bda5c7d36795bb8f860b2d09a637f8f98ee401c60ac4c40a636f8606565b9' + }, + 'adapters': { + 'PowerShell': { + 'name': 'PowerShell', + 'type': 'PowerShell', + 'port': 'ask', + }, + }, + } } @@ -453,7 +479,8 @@ class ModePreservingZipFile( zipfile.ZipFile ): ret_val = self._extract_member( member, path, pwd ) attr = member.external_attr >> 16 - os.chmod( ret_val, attr ) + if attr: + os.chmod( ret_val, attr ) return ret_val diff --git a/support/test/powershell/.vimspector.json b/support/test/powershell/.vimspector.json new file mode 100644 index 0000000..eac5ed1 --- /dev/null +++ b/support/test/powershell/.vimspector.json @@ -0,0 +1,17 @@ +{ + "configurations": { + "launch": { + "adapter": "PowerShell", + "configuration": { + "request": "launch", + "Script": "${file}", + "NoDebug": false, + "StopOnEntry": true, + "Args": [], + "Cwd": "${workspaceRoot}", + "CreateTemporaryIntegratedConsole": true, + "Env": {} + } + } + } +} diff --git a/support/test/powershell/connect b/support/test/powershell/connect new file mode 100755 index 0000000..e9c3fae --- /dev/null +++ b/support/test/powershell/connect @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +PIPE=$(python -c 'import json; print( json.load( open( "session.json" ))["debugServicePipeName"] )') + +ssh -L 4321:$PIPE localhost + diff --git a/support/test/powershell/run b/support/test/powershell/run new file mode 100755 index 0000000..98382db --- /dev/null +++ b/support/test/powershell/run @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +PSES_BUNDLE_PATH=$HOME/.vim/bundle/vimspector/gadgets/macos/PowerShellEditorServices +SESSION_TEMP_PATH=$(pwd) + +pwsh -NoProfile \ + -Command \ + "$PSES_BUNDLE_PATH/PowerShellEditorServices/Start-EditorServices.ps1 \ + -BundledModulesPath $PSES_BUNDLE_PATH \ + -LogPath $SESSION_TEMP_PATH/logs.log \ + -SessionDetailsPath $SESSION_TEMP_PATH/session.json \ + -FeatureFlags @() \ + -AdditionalModules @() \ + -HostName 'My Client' \ + -HostProfileId 'myclient' \ + -HostVersion 1.0.0 \ + -EnableConsoleRepl \ + -LogLevel Verbose" diff --git a/support/test/powershell/test.ps1 b/support/test/powershell/test.ps1 new file mode 100644 index 0000000..d3708d4 --- /dev/null +++ b/support/test/powershell/test.ps1 @@ -0,0 +1,9 @@ +echo "HELLO" +echo "HELLO" +echo "HELLO" +echo "HELLO" +echo "HELLO" +echo "HELLO" +echo "HELLO" +echo "HELLO" +echo "HELLO" From f173a043be5690a96f73261671c43cf73aaecd55 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 5 Nov 2019 18:35:23 +0000 Subject: [PATCH 002/609] Find the vimspector config from the parent of the open file not the current working directory --- python3/vimspector/debug_session.py | 12 ++++++++---- python3/vimspector/utils.py | 8 ++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 8bcb3a5..a4a154d 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -73,11 +73,15 @@ class DebugSession( object ): self._configuration = None self._adapter = None - launch_config_file = utils.PathToConfigFile( '.vimspector.json' ) + current_file = utils.GetBufferFilepath( vim.current.buffer ) + + launch_config_file = utils.PathToConfigFile( + '.vimspector.json', + os.path.dirname( current_file ) ) if not launch_config_file: utils.UserMessage( 'Unable to find .vimspector.json. You need to tell ' - 'vimspector how to launch your application' ) + 'vimspector how to launch your application.' ) return with open( launch_config_file, 'r' ) as f: @@ -134,8 +138,6 @@ class DebugSession( object ): # ${selectedText} - the current selected text in the active file # ${execPath} - the path to the running VS Code executable - current_file = utils.GetBufferFilepath( vim.current.buffer ) - def relpath( p, relative_to ): if not p: return '' @@ -158,6 +160,8 @@ class DebugSession( object ): splitext( os.path.basename( current_file ) )[ 0 ], 'fileDirname': os.path.dirname( current_file ), 'fileExtname': splitext( os.path.basename( current_file ) )[ 1 ], + # NOTE: this is the window-local cwd for the current window, *not* Vim's + # working directory. 'cwd': os.getcwd(), } self._variables.update( diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index a3835b5..73e2fea 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -207,8 +207,12 @@ def TemporaryVimOption( opt, value ): vim.options[ opt ] = old_value -def PathToConfigFile( file_name ): - p = os.getcwd() +def PathToConfigFile( file_name, from_directory = None ): + if not from_directory: + p = os.getcwd() + else: + p = os.path.abspath( os.path.realpath( from_directory ) ) + while True: candidate = os.path.join( p, file_name ) if os.path.exists( candidate ): From 5586d9e6947594cdb00528f496fe42dfbac388ff Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 5 Nov 2019 18:33:41 +0000 Subject: [PATCH 003/609] Allow setting exception breakpoints in debug config --- python3/vimspector/breakpoints.py | 37 ++++++++++++++++++---- python3/vimspector/debug_session.py | 2 ++ tests/testdata/cpp/simple/.vimspector.json | 11 +++++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index e511b09..a24d1de 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -44,6 +44,7 @@ class ProjectBreakpoints( object ): self._line_breakpoints = defaultdict( list ) self._func_breakpoints = [] self._exception_breakpoints = None + self._configured_breakpoints = {} # FIXME: Remove this. Remove breakpoints nonesense from code.py self._breakpoints_handler = None @@ -73,6 +74,8 @@ class ProjectBreakpoints( object ): # NOTE: we don't reset self._exception_breakpoints because we don't want to # re-ask the user every time for the sane info. + # FIXME: If the adapter type changes, we should probably forget this ? + def ListBreakpoints( self ): # FIXME: Handling of breakpoints is a mess, split between _codeView and this @@ -185,6 +188,10 @@ class ProjectBreakpoints( object ): self._breakpoints_handler = handler + def SetConfiguredBreakpoints( self, configured_breakpoints ): + self._configured_breakpoints = configured_breakpoints + + def SendBreakpoints( self, doneHandler = None ): assert self._breakpoints_handler is not None @@ -202,6 +209,9 @@ class ProjectBreakpoints( object ): doneHandler() + # TODO: add the _configured_breakpoints to line_breakpoints + # TODO: the line numbers might have changed since pressing the F9 key! + for file_name, line_breakpoints in self._line_breakpoints.items(): breakpoints = [] for bp in line_breakpoints: @@ -233,6 +243,8 @@ class ProjectBreakpoints( object ): } ) + # TODO: Add the _configured_breakpoints to function breakpoints + if self._server_capabilities.get( 'supportsFunctionBreakpoints' ): awaiting = awaiting + 1 self._connection.DoRequest( @@ -249,7 +261,7 @@ class ProjectBreakpoints( object ): ) if self._exception_breakpoints is None: - self._SetUpExceptionBreakpoints() + self._SetUpExceptionBreakpoints( self._configured_breakpoints ) if self._exception_breakpoints: awaiting = awaiting + 1 @@ -265,7 +277,7 @@ class ProjectBreakpoints( object ): doneHandler() - def _SetUpExceptionBreakpoints( self ): + def _SetUpExceptionBreakpoints( self, configured_breakpoints ): exception_breakpoint_filters = self._server_capabilities.get( 'exceptionBreakpointFilters', [] ) @@ -278,14 +290,27 @@ class ProjectBreakpoints( object ): # trigger requesting threads etc.). See the note in # debug_session.py:_Initialise for more detials exception_filters = [] + configured_filter_options = configured_breakpoints.get( 'exception', {} ) if exception_breakpoint_filters: for f in exception_breakpoint_filters: default_value = 'Y' if f.get( 'default' ) else 'N' - result = utils.AskForInput( - "Break on {} (Y/N/default: {})? ".format( f[ 'label' ], - default_value ), - default_value ) + if f[ 'filter' ] in configured_filter_options: + result = configured_filter_options[ f[ 'filter' ] ] + + if isinstance( result, bool ): + result = 'Y' if result else 'N' + + if not isinstance( result, str) or result not in ( 'Y', 'N', '' ): + raise ValueError( + 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 ) if result == 'Y': exception_filters.append( f[ 'filter' ] ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index a4a154d..25b1d5b 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -739,6 +739,8 @@ class DebugSession( object ): self._OnInitializeComplete() self._codeView.ClearBreakpoints() + self._breakpoints.SetConfiguredBreakpoints( + self._configuration.get( 'breakpoints', {} ) ) self._breakpoints.SendBreakpoints( onBreakpointsDone ) def OnEvent_thread( self, message ): diff --git a/tests/testdata/cpp/simple/.vimspector.json b/tests/testdata/cpp/simple/.vimspector.json index 9fa71d2..d7a4377 100644 --- a/tests/testdata/cpp/simple/.vimspector.json +++ b/tests/testdata/cpp/simple/.vimspector.json @@ -11,7 +11,18 @@ "environment": [], "externalConsole": true, "stopAtEntry": true, + "stopOnEntry": true, "MImode": "${VIMSPECTOR_MIMODE}" + }, + "breakpoints": { + "exception": { + "cpp_catch": "", + "cpp_throw": "", + "objc_catch": "", + "objc_throw": "", + "swift_catch": "", + "swift_throw": "" + } } } } From 0057094179f16add8773913537256997eac4b7eb Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 5 Nov 2019 18:34:07 +0000 Subject: [PATCH 004/609] Allow gadget config in .gadgets.d to override the installed config --- python3/vimspector/debug_session.py | 27 +++++++++++++++++++-------- python3/vimspector/install.py | 5 +++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 25b1d5b..647002a 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -13,13 +13,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging -import vim +import glob import json +import logging import os -import subprocess import shlex +import subprocess import traceback +import vim from vimspector import ( breakpoints, code, @@ -90,8 +91,10 @@ class DebugSession( object ): configurations = database.get( 'configurations' ) adapters = {} - for gadget_config_file in [ install.GetGadgetConfigFile( VIMSPECTOR_HOME ), - utils.PathToConfigFile( '.gadgets.json' ) ]: + glob.glob( install.GetGadgetDir( VIMSPECTOR_HOME, install.GetOS() ) ) + for gadget_config_file in PathsToAllGadgetConfigs( VIMSPECTOR_HOME, + current_file ): + self._logger.debug( f'Reading gadget config: {gadget_config_file}' ) if gadget_config_file and os.path.exists( gadget_config_file ): with open( gadget_config_file, 'r' ) as f: adapters.update( json.load( f ).get( 'adapters' ) or {} ) @@ -196,9 +199,6 @@ class DebugSession( object ): def _StartWithConfiguration( self, configuration, adapter ): def start(): - self._logger.debug( "Starting debugger from stack context: %s", - traceback.format_stack() ) - self._configuration = configuration self._adapter = adapter @@ -860,3 +860,14 @@ class DebugSession( object ): def AddFunctionBreakpoint( self, function ): return self._breakpoints.AddFunctionBreakpoint( function ) + + +def PathsToAllGadgetConfigs( vimspector_base, current_file ): + yield install.GetGadgetConfigFile( vimspector_base ) + for p in sorted( glob.glob( + os.path.join( install.GetGadgetConfigDir( vimspector_base ), + '*.json' ) ) ): + yield p + + yield utils.PathToConfigFile( '.gadgets.json', + os.path.dirname( current_file ) ) diff --git a/python3/vimspector/install.py b/python3/vimspector/install.py index df03a11..2adafcd 100644 --- a/python3/vimspector/install.py +++ b/python3/vimspector/install.py @@ -33,3 +33,8 @@ def GetGadgetDir( vimspector_base, OS ): def GetGadgetConfigFile( vimspector_base ): return os.path.join( GetGadgetDir( vimspector_base, GetOS() ), '.gadgets.json' ) + + +def GetGadgetConfigDir( vimspector_base ): + return os.path.join( GetGadgetDir( vimspector_base, GetOS() ), + '.gadgets.d' ) From 25b22d2a9eb5e9d189e9ed51c2dd918835818c04 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 5 Nov 2019 18:34:26 +0000 Subject: [PATCH 005/609] Allow gadget config to set up default values for launch configuration --- python3/vimspector/debug_session.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 647002a..5e98491 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -659,7 +659,9 @@ class DebugSession( object ): def _Launch( self ): self._logger.debug( "LAUNCH!" ) adapter_config = self._adapter - launch_config = self._configuration[ 'configuration' ] + launch_config = {} + launch_config.update( self._adapter.get( 'configuration', {} ) ) + launch_config.update( self._configuration[ 'configuration' ] ) request = self._configuration.get( 'remote-request', From 7297e12605645a973fd2791e0381068ed0b736d7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 5 Nov 2019 18:30:27 +0000 Subject: [PATCH 006/609] Add schema definition for the config --- .ycm_extra_conf.py | 35 ++++ docs/CONFIGURATION.md | 318 +++++++++++++++++++++++++++++ docs/index.md | 17 ++ docs/schema/gadgets.schema.json | 11 + docs/schema/index.md | 19 ++ docs/schema/vimspector.schema.json | 256 +++++++++++++++++++++++ 6 files changed, 656 insertions(+) create mode 100644 .ycm_extra_conf.py create mode 100644 docs/CONFIGURATION.md create mode 100644 docs/index.md create mode 100644 docs/schema/gadgets.schema.json create mode 100644 docs/schema/index.md create mode 100644 docs/schema/vimspector.schema.json diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py new file mode 100644 index 0000000..77feee6 --- /dev/null +++ b/.ycm_extra_conf.py @@ -0,0 +1,35 @@ +try: + from ycmd.extra_conf_support import IgnoreExtraConf +except ImportError: + IgnoreExtraConf = None + +import os.path as p + +PATH_TO_THIS_DIR = p.dirname( p.abspath( __file__ ) ) + + +def Settings( **kwargs ): + if kwargs[ 'language' ] == 'json': + return { + 'ls': { + 'json': { + 'schemas': [ + { + 'fileMatch': [ '.vimspector.json' ], + 'url': + f'file://{PATH_TO_THIS_DIR}/docs/schema/vimspector.schema.json' + }, + { + 'fileMatch': [ '.gadgets.json', '.gadgets.d/*.json' ], + 'url': + f'file://{PATH_TO_THIS_DIR}/docs/schema/gadgets.schema.json' + } + ] + } + } + } + + if IgnoreExtraConf: + raise IgnoreExtraConf() + + return None diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md new file mode 100644 index 0000000..72b07c8 --- /dev/null +++ b/docs/CONFIGURATION.md @@ -0,0 +1,318 @@ +# Vimspector project configuration + +This document defines the supported format for project and adapter configuration +for Vimspector. + +# Concepts + +As Vimspector supports debugging arbitrary projects, you need to tell it a few +deatils about what you want to debug, and how to go about doing that. + +In order to debug things, Vimspector requires a Debug Adapter which bridges +between Vimspector and the actual debugger tool. Vimspector can be used with any +debug adapter that implemnents the [Debug Adapter Protocol][dap]. + +For each debugging session, you provide a _debug configuration_ which includes +things like: + +- The debug adapter to use (and possibly how to launch and configure it). +- How to connect to the remote host, if remote debugging. +- How to launch or attach to your process. + +Along with optional additional configuration for things like: + +- Function breakpoints +- Exception breakpoints + +## Debug adapter configuration + +The adapter to use for a particular debug session can be specified inline within +the _debug configuration_, but more usually the debug adapter is defined +separately and just referenced from the _debug configuration_. + +The adapter configuration includes things like: + +* How to launch or connect to the debeg adapter +* How to configure it for PID attachment +* How to set up remote debugging, such as how to launch the process remotely + (for example, under `gdbserver`, `ptvsd`, etc.) + +## Debug profile configuration + +Projects can have many different debug profiles. For example you might have all +of the following, for a given source tree: + +* Remotely launch c++ the process, and break on `main` +* Locally Python test and break exception +* Remotely attach to a c++ process +* Locally launchg a bash script +* Attach to a JVM listening on a port + +Each of these represents a different use case and a different _debug +configuration_. As mentioned above, a _debug configuration_ is essentially: + +* The adapter to use +* The type of session (launch or attach), and whether or not to do it remotely +* The configuration to pass to the adapter in order to launch or attach to the + process. + +The bulk of the configuration is the last of these, which comprises +adapter-specific opions, as the Debug Adapter Protocol does not specify any +standard for launch or attach configuration. + +## Replacements and variables + +Vimspector _debug configuration_ is intended to be as general as possible, and +to be committed to source control so that debugging your applications becomes a +simple, quick and pain-free habit (e.g. answering questions like "what happens +if..." with "just hit F5 and step through!"). + +Therefore it's important to abstract certain details, like runtime and +build-time paths, and to parameterise the _debug configuration_. Vimspector +provides a simple mechanism to do this with `${replacement}` style replacments. + +The values avaiable within the `${...}` are defined below, but in summary the +following are supported: + +* Environment variables, such as `${PATH}` +* Predefined variables, such as `${workspaceRoot}`, `${file}` etc. +* Configuration-defined variables, either provided by the adapter configuration + or debug configuration, or from running a simple shell command. +* Anything else you like - the user will be asked to provide a value. + +If the latter 2 are confusing, for now, suffice to say that they are how +Vimspector allows parameterisation of debug sessions. The [Vimspector +website][website-getting-started] has a good example of where this sort of thing +is useful: accepting the name of a test to run. + +But for now, consider the following example snippet: + +```json +{ + "configurations": { + "example-debug-configuration": { + "adapter": "example-adapter-name", + "variables": { + "SecretToken": { + "shell" : [ "cat", "${HOME}/.secret_token" ] + } + }, + "configuration": { + "request": "launch", + "program": [ + "${fileBasenameNoExtension}", + "-c", "configuration_file.cfg", + "-u", "${USER}", + "--test-identifier", "${TestIdentifier}", + "--secret-token", "${SecretToken}" + ] + } + } + } +} +``` + +In this (ficticious) example the `program` launch configuration item contains +the following variable substiutions: + +* `${fileBasenameNoExtension}` - this is a [Predefined + Variable](#predefined-variables), set by Vimspector to the base name of the + file that's opened in Vim, with its extension removed (`/path/to/xyz.cc` -> + `xyz`). +* `${USER}` - this refers to the Environment Variable `USER`. +* `${TestIdentifier}` - this variable is not defined, so the user is asked to + provide a value interractively when starting debugging. Vimspector remembers + what they said and provides it as the default should they debug again. +* `${SecretToken}` - this variable is provided by the configuration's + `variables` block. Its value is taken from the `strip`ped result of running + the shell command. Note these variables can be supplied by both the debug and + adapter configurations and can be either static strings or shell commands. + +# Configuration Format + +All Vimspector configuration is defined in a JSON object. The complete +specification of this object is available in the [JSON Schema][schema], but +the basic format for the configuation object is: + +``` +{ + "adapters": { }, + "configurations": { } +} +``` + +The `adapters` key is actually optional, as `` can be +embedded within ``, though this is not recommended usage. + +# Files and locations + +The above configuration object is constructed from a number of configuration +files, by merging objects i specified order. + +In a minimal sense, the only file required is a `.vimspector.json` file in the +root of your project which deinfes the [full configuration object][schema], but +it is usually useful to split the `adapters` configuration into a separate file +(or indeed one file per debug adapter). + +The following sections describe the files that are read and use the following +abbreviations: + +* `` means the path to the Vimspector installation (such as + `$HOME/.vim/pack/vimspector/start/vimspector`) +* `` is either `macos` or `linux` depending on the host operating system. + +# Adapter configurations + +Vimspector reads a series of files to build the `adapters` object. The +`adapters` objects are merged in such a way that a defintion for an adapter +named `example-adapter` in a later file _completely replaces_ a previous +definition. + +* `/gadgets//.gadgets.json` - the file written by + `install_gadget.py` and not usually edited by users. +* `/gadgets//.gadgets.d/*.json` (sorted alphabetically). + These files are user-supplied and override the above. +* The first such `.gadgets.json` file found in all parent directories of the + file open in Vim. +* The `.vimspector.json` (see below) + +In all cases, the required format is: + +```json +{ + "$schema": "https://puremourning.github.io/vimspector/schema/gadgets.schema.json#", + "adapters": { + "": { + + } + } +} +``` + +Each adapters block can define any number of adapters. As mentioned, if the same +adapter name exists in multiple files, the last one read takes precedence and +_completely replaces_ the previous configuration. In particular that means you +can't just override one option, you have to override the whole block. + +Adapter configurations are re-read at the start of each debug session. + +The specification for the gadget object is defined in the [gadget schema][]. + +# Debug configurations + +The debug conifgurations are read from `.vimspector.json`. The file is found +(like `.gadgets.json` above) by recursively searching up the directory hierarchy +from the directory of the file open in Vim. The first file found is read and no +futher searching is done. + +Only a single `.vimspector.json` is read. + +Debug configurations are re-read at the start of each debug session. + +The specification for the gadget object is defined in the [schema][], but a +typical example looks like this: + +``` +{ + "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#", + "configurations": { + "": { + "adapter": "", + "configuration": { + "request": "", + + } + } + } +} +``` + +# Predefined Variables + +The following variables are provided: + +* `${dollar}` - has the value `$`, can be used to enter a literal dollar +* `$$` - a literal dollar +* `${workspaceRoot}` - the path of the folder where `.vimspector.json` was + found +* `${workspaceFolder}` - the path of the folder where `.vimspector.json` was + found +* `${gadgetDir}` - path to the OS-specifc gadget dir (`/gadgets/`) +* `${file}` - the current opened file +* `${relativeFile}` - the current opened file relative to workspaceRoot +* `${fileBasename}` - the current opened file's basename +* `${fileBasenameNoExtension}` - the current opened file's basename with no + file extension +* `${fileDirname}` - the current opened file's dirname +* `${fileExtname}` - the current opened file's extension +* `${cwd}` - the current working directory of the active window on launch + +# Appendix: Editor configuration + +If you would like some assistance with writing the JSON files, and your editor +of choice has a way to use a language server, you can use the +[VSCode JSON language server][vscode-json]. + +It is recommended to include the `$schema` declaration as in the above examples, +but if that isn't present, the following [JSON language server +configuration][json-ls-config] is recommened to load the schema from the +Internet: + +```json +{ + "json": { + "schemas": [ + { + "fileMatch": [ ".vimspector.json" ], + 'url': "https://puremourning.github.io/vimspector/schema/vimspector.schema.json" + }, + { + "fileMatch": [ ".gadgets.json", ".gadgets.d/*.json" ], + "url": "https://puremourning.github.io/vimspector/schema/gadgets.schema.json" + } + ] + } +} +``` + +If your language server client of choice happens to be [YouCompleteMe][], then +the following `.ycm_extra_conf.py` is good enough to get you going, after +following the instructions in the [lsp-examples][] repo to get the server set +up: + +```python +VIMSPECTOR_HOME = '/path/to/vimspector' # TODO: Change this + +def Settings( **kwargs ): + if kwargs[ 'language' ] == 'json': + return { + 'ls': { + 'json': { + 'schemas': [ + { + 'fileMatch': [ '.vimspector.json' ], + 'url': f'file://{VIMSPECTOR_HOME}/docs/schema/vimspector.schema.json' + }, + { + 'fileMatch': [ '.gadgets.json', '.gadgets.d/*.json' ], + 'url': f'file://{VIMSPECTOR_HOME}/docs/schema/gadgets.schema.json' + } + ] + } + } + } + + return None # Or your existing Settings definition.... +``` + +This configuration can be adapted to any other LSP-based editor configuration +and is provided just as an example. + +[dap]: https://microsoft.github.io/debug-adapter-protocol/ +[schema]: http://puremourning.github.io/vimspector/vimspector.schema.json +[gadget-schema]: http://puremourning.github.io/vimspector/gadgets.schema.json +[YouCompleteMe]: https://github.com/ycm-core/YouCompleteMe +[lsp-examples]: https://github.com/ycm-core/lsp-examples +[vscode-json]: https://github.com/vscode-langservers/vscode-json-languageserver +[json-ls-config]: https://github.com/vscode-langservers/vscode-json-languageserver#settings diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..3da138b --- /dev/null +++ b/docs/index.md @@ -0,0 +1,17 @@ +# Vimsepctor + +This section contains reference material for configuring and using +[Vimsepctor][vimspector]. It is intentionally technical in nature and should +serve as a reference guide, rather than a user guide or tutorial. + +It complements the following: + +* The [Vimspector README], which contains a general overview and is the main + go-to for getting started, installation, etc. +* The [Vimspector Website], which contains a step-by-step tutorial and some + detais about the user interface. + +# Contents + +* [Configuring Vimspector](configuration.html) +* [JSON Schema](schema/) diff --git a/docs/schema/gadgets.schema.json b/docs/schema/gadgets.schema.json new file mode 100644 index 0000000..b4aab3b --- /dev/null +++ b/docs/schema/gadgets.schema.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://puremourning.github.io/vimspector/schema/gadgets.schema.json", + "type": "object", + "properties": { + "adapters": { + "type": "object", + "additionalProperties": { "$ref": "vimspector.schema.json#/definitions/adapter" } + } + } +} diff --git a/docs/schema/index.md b/docs/schema/index.md new file mode 100644 index 0000000..f5d00a9 --- /dev/null +++ b/docs/schema/index.md @@ -0,0 +1,19 @@ +# Vimsepctor JSON Schema + +This area contains the [JSON Schema][json-schema] representation of the +configuration objects used for configuring Vimspector. For more information on +JSON Schema, check out [Understanding JSON +Schema](http://json-schema.org/understanding-json-schema) + +Vimsepctor specification is based on [Draft 7][draft-7] of JSON Schema +standard. + +# The schemas + +* [`vimspector.schema.json`](vimspector.schema.json) - contains the full + configuration defnition (`.vimspector.json`). +* [`gadgets.schema.json`](gadgets.schema.json) - contains the specification for + gadget-only objects (`.gadgets.json`). + +[json-schema]: http://json-schema.org +[draft-7]: https://json-schema.org/specification-links.html#draft-7 diff --git a/docs/schema/vimspector.schema.json b/docs/schema/vimspector.schema.json new file mode 100644 index 0000000..ace8589 --- /dev/null +++ b/docs/schema/vimspector.schema.json @@ -0,0 +1,256 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json", + "definitions": { + "variables": { + "description": "A mappoing of name/value pairs to set variables to be used elsewhere in the definition, or a list of such mappings.", + "properties": { + "variables": { + "oneOf": [ + { "$ref": "#/definitions/variables-mapping" }, + { "$ref": "#/definitions/variables-list" } + ] + } + } + }, + "variables-list": { + "type": "array", + "description": "A list of variable mappings. This can be useful where variable definitions rely on each other. By using a list, you can control the sequence in which variables are defined.", + "items": { "$ref": "#/definitions/variables-mapping" } + }, + "variables-mapping": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "required": [ "shell" ], + "properties": { + "shell": { + "type": [ "array", "string" ], + "description": "Command to run. If it's a string, it's split using Python's shelex splitting. Can contain other variable references." + }, + "cwd": { "type": "string" }, + "env": { "type": "object" } + } + } + ] + } + }, + "adapter-common": { + "allOf": [ + { "$ref": "#/definitions/variables" }, + { "$ref": "#/definitions/adapter-remote" }, + { "$ref": "#/definitions/adapter-attach" }, + { + "properties": { + "name": { + "type": "string", + "description": "Passed to the adapter in the initialization request. Some adapters are particularly picky about what value goes here. Usually it can be omitted and Vimspector will send a generic value" + }, + "configuration": { + "type": "object", + "description": "Base debug configuration. Can be used to set default values for all debug configurations. When reading individual debug configurations from 'configurations', those configurations are merged with this object. Definitions in the debug configuration override anything in this object. Typical usage for this is to set the 'type' parameter, which some debug adapters are very picky about, or to set e.g. the path to an underlying debugger." + } + } + } + ] + }, + "adapter-attach": { + "properties": { + "attach": { + "type": "object", + "required": [ "pidSelect" ], + "properties": { + "pidSelect": { + "enum": [ "ask", "none" ] + }, + "pidProperty": { + "type": "string", + "description": "The launch config property which the PID should be injected into. Required when 'pidSelect' is 'ask'." + } + } + } + } + }, + "adapter-remote": { + "properties": { + "remote": { + "type": "object", + "required": [ "host" ], + "description": "Configures how Vimspector will marshal remote debugging requests. When remote debugging, Vimspector will ssh to 'account'@'host' and run 'pidCommand', 'attachCommands', 'runCommands', etc. based on the 'remote-command' option in the debug configuration. If 'remote-command' is 'launch', it runs 'runCommand(s)', otherwise (it's 'attach') vimspector runs 'pidCommand', followed by 'attachCommand(s)'.Then it starts up the debug adapter with the debug configuration as normal. Usually this is configured with an 'attach' request (whether we remotely 'launched' or not). Once the initialization exchange is complete, Vimspector runs the optional 'initCompleteCommand' which can be used to force the application to break, e.g. by sending it SIGINT. This is required on some platforms which have buggy gdbservers (for example)", + "properties": { + "account": { + "type": "string", + "description": "Remote account name used when ssh'ing. Defaults to the current user account." + }, + "host": { + "type": "string", + "description": "Name of the remote host to connect to (via passwordless SSH). " + }, + "pidCommand": { + "type": "array", + "items": { "type": "string" }, + "description": "Required for remote-attach. Remote command to execute to return the PID to attach to." + }, + "initCompleteCommand": { + "type": "array", + "items": { "type": "string" }, + "description": "For remote-attach. Remote command to execute after initialization of the debug adapter. Can be used to work around buggy attach behaviour on certain platforms (advanced usage). Can contain the special token %PID% which is replaced with the PID returned by 'pidCommand'" + }, + "attachCommands": { + "type": [ "array" ], + "items": { "type": "array", "items": { "type": "string" } }, + "description": "For remote-attach. List of commands to execute remotely to set up the attach. Can contain the special token %PID% which is replaced with the PID returned by the remote 'pidCommand'." + }, + "attachCommand": { + "type": "array", + "items": { "type": "string" }, + "description": "A single command to execute for remote-attach. Like attachCommands but for a single command. If attachCommands is supplied, this is not used." + }, + "runCommands": { + "type": [ "array" ], + "items": { "type": "array", "items": { "type": "string" } }, + "description": "For remote-launch. List of commands to execute remotely to set up the launch. An entry in the array can be the special token '%CMD%' which is replaced with the evaluated 'remote-cmdLine' value in the debug configuration. This is useful to parameterize launcging remotely under something like gdbserver." + }, + "runCommand": { + "type": "array", + "items": { "type": "string" }, + "description": "A single command to execute for remote-launch. Like runCommands but for a single command." + } + } + } + } + }, + "adapter": { + "oneOf": [ + { + "allOf": [ + { "$ref": "#/definitions/adapter-common" }, + { + "required": [ "port" ], + "properties": { + "port": { + "oneOf": [ + { + "type": "string", + "enum": [ "ask" ] + }, + { + "type": "integer" + } + ], + "description": "If supplied, indicates that a socket connection should be made to this port on 'localhost'. If the value is 'ask', then the user is asked to enter the port number to connect to." + } + } + } + ] + }, + { + "allOf": [ + { "$ref": "#/definitions/adapter-common" }, + { + "required": [ "command" ], + "properties": { + "command": { + "type": [ "string", "array" ], + "description": "Command line to execute the debug adapter.", + "items": { "type": "string" } + }, + "env": { + "type": "object", + "description": "Name/value pairs to set in the environment when starting the adapter." + }, + "cwd": { + "type": "string", + "description": "Directory from which to start the adapter. Defaults to the working directory of the window on launch" + } + } + } + ] + } + ] + } + }, + "type": "object", + "required": [ "configurations" ], + "properties": { + "adapters": { + "type": "object", + "additionalProperties": { "$ref": "#/definitions/adapter" } + }, + "configurations": { + "type": "object", + "additionalProperties": { + "allOf": [ + { "$ref": "#/definitions/variables" }, + { + "type": "object", + "properties": { + "adapter": { + "description": "Adapter configuration to use for this debug session", + "oneOf": [ + { "$ref": "#/definitions/adapter" }, + { + "type": "string", + "description": "Name of an adapter in the 'adapters' mapping" + } + ] + }, + "remote-request": { + "enum": [ "launch", "attach" ], + "description": "When the 'remote' block is defined in the adapter configuration, this can be used to override the actual action taken (remotely). Usually the actual 'configuration' will contain 'request' of 'attach', but in order to remotely 'launch' the process (e.g. under gdbserver or equivalent), use remote-attach set to 'launch'" + }, + "remote-cmdLine": { + "type": [ "string", "array" ], + "description": "Defines the value of the special token %CMD% in remote-launch 'runCommand(s)'. The value is inserted into the command line where an entry matching '%CMD%' is found in 'runCommand(s)' command array." + }, + "configuration": { + "type": "object", + "required": [ "request" ], + "properties": { + "request": { + "enum": [ "launch", "attach" ], + "description": "Type of session - launch process or attach to process" + } + }, + "additionalProperties": { + "description": "Additional properties are passed to the debug adapter in the 'launch' or 'attach' request and are specific to the debug adapter." + } + }, + "breakpoints": { + "type": "object", + "properties": { + "line": { + "$comment": "TBA: Not supported yet" + }, + "function": { + "$comment": "TBA: Not supported yet" + }, + "exception": { + "type": "object", + "description": "Exception breakpoints configuration, mapping the server's exception filter to enabled/disable/default flag", + "additionalProperties": { + "oneOf": [ + { + "type": "boolean", + "description": "true = enable, false = disable" + }, + { + "type": "string", + "enum": [ "Y", "N", "" ], + "description": "Y = enable, N = disable, '' = default" + } + ] + } + } + } + } + } + } + ] + } + } + } +} From 28a02617eeea56c69dd895e407a2cde38da51704 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Nov 2019 12:07:20 +0000 Subject: [PATCH 007/609] Set theme jekyll-theme-tactile --- _config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..259a24e --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-tactile \ No newline at end of file From 6791b4bb58173ead21fc2488a8f8d893b6eb15b7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Nov 2019 12:13:51 +0000 Subject: [PATCH 008/609] Rename CONFIGURATION.md to configuration.md --- docs/{CONFIGURATION.md => configuration.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{CONFIGURATION.md => configuration.md} (100%) diff --git a/docs/CONFIGURATION.md b/docs/configuration.md similarity index 100% rename from docs/CONFIGURATION.md rename to docs/configuration.md From ccc966f9853e2c0ffa1cfb8b2ca1ab0c1824c415 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Nov 2019 12:14:43 +0000 Subject: [PATCH 009/609] Fix formatting errors --- docs/configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 72b07c8..ef1ba88 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -178,7 +178,7 @@ definition. In all cases, the required format is: -```json +``` { "$schema": "https://puremourning.github.io/vimspector/schema/gadgets.schema.json#", "adapters": { @@ -265,7 +265,7 @@ Internet: "schemas": [ { "fileMatch": [ ".vimspector.json" ], - 'url': "https://puremourning.github.io/vimspector/schema/vimspector.schema.json" + "url": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json" }, { "fileMatch": [ ".gadgets.json", ".gadgets.d/*.json" ], From bed68aa69104c97d32b86efec826b2dd80d44272 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Nov 2019 15:53:03 +0000 Subject: [PATCH 010/609] Fix up the site --- docs/.gitignore | 5 + docs/404.html | 24 +++ docs/Gemfile | 30 ++++ docs/Gemfile.lock | 252 +++++++++++++++++++++++++++++++ docs/_config.yml | 42 ++++++ docs/configuration.md | 53 ++++--- docs/index.md | 24 +-- docs/schema/index.md | 6 +- support/site/jekyll/.gitignore | 2 + support/site/jekyll/Gemfile | 2 + support/site/jekyll/Gemfile.lock | 61 ++++++++ 11 files changed, 465 insertions(+), 36 deletions(-) create mode 100644 docs/.gitignore create mode 100644 docs/404.html create mode 100644 docs/Gemfile create mode 100644 docs/Gemfile.lock create mode 100644 docs/_config.yml create mode 100644 support/site/jekyll/.gitignore create mode 100644 support/site/jekyll/Gemfile create mode 100644 support/site/jekyll/Gemfile.lock diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..1cabae3 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,5 @@ +_site +.sass-cache +.jekyll-metadata +vendor/ +.bundle/ diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 0000000..c472b4e --- /dev/null +++ b/docs/404.html @@ -0,0 +1,24 @@ +--- +layout: default +--- + + + +
+

404

+ +

Page not found :(

+

The requested page could not be found.

+
diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100644 index 0000000..3f49941 --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,30 @@ +source "https://rubygems.org" + +# Hello! This is where you manage which Jekyll version is used to run. +# When you want to use a different version, change it below, save the +# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: +# +# bundle exec jekyll serve +# +# This will help ensure the proper Jekyll version is running. +# Happy Jekylling! +#gem "jekyll", "~> 3.8.5" + +# This is the default theme for new Jekyll sites. You may change this to anything you like. +gem "minima", "~> 2.0" + +# If you want to use GitHub Pages, remove the "gem "jekyll"" above and +# uncomment the line below. To upgrade, run `bundle update github-pages`. +gem "github-pages", group: :jekyll_plugins + +# If you have any plugins, put them here! +group :jekyll_plugins do + gem "jekyll-feed", "~> 0.6" +end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] + +# Performance-booster for watching directories on Windows +gem "wdm", "~> 0.1.0" if Gem.win_platform? + diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock new file mode 100644 index 0000000..6e0170b --- /dev/null +++ b/docs/Gemfile.lock @@ -0,0 +1,252 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (4.2.11.1) + i18n (~> 0.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.11.1) + colorator (1.1.0) + commonmarker (0.17.13) + ruby-enum (~> 0.5) + concurrent-ruby (1.1.5) + dnsruby (1.61.3) + addressable (~> 2.5) + em-websocket (0.5.1) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0.6.0) + ethon (0.12.0) + ffi (>= 1.3.0) + eventmachine (1.2.7) + execjs (2.7.0) + faraday (0.17.0) + multipart-post (>= 1.2, < 3) + ffi (1.11.1) + forwardable-extended (2.6.0) + gemoji (3.0.1) + github-pages (202) + activesupport (= 4.2.11.1) + github-pages-health-check (= 1.16.1) + jekyll (= 3.8.5) + jekyll-avatar (= 0.6.0) + jekyll-coffeescript (= 1.1.1) + jekyll-commonmark-ghpages (= 0.1.6) + jekyll-default-layout (= 0.1.4) + jekyll-feed (= 0.11.0) + jekyll-gist (= 1.5.0) + jekyll-github-metadata (= 2.12.1) + jekyll-mentions (= 1.4.1) + jekyll-optional-front-matter (= 0.3.0) + jekyll-paginate (= 1.1.0) + jekyll-readme-index (= 0.2.0) + jekyll-redirect-from (= 0.14.0) + jekyll-relative-links (= 0.6.0) + jekyll-remote-theme (= 0.4.0) + jekyll-sass-converter (= 1.5.2) + jekyll-seo-tag (= 2.5.0) + jekyll-sitemap (= 1.2.0) + jekyll-swiss (= 0.4.0) + jekyll-theme-architect (= 0.1.1) + jekyll-theme-cayman (= 0.1.1) + jekyll-theme-dinky (= 0.1.1) + jekyll-theme-hacker (= 0.1.1) + jekyll-theme-leap-day (= 0.1.1) + jekyll-theme-merlot (= 0.1.1) + jekyll-theme-midnight (= 0.1.1) + jekyll-theme-minimal (= 0.1.1) + jekyll-theme-modernist (= 0.1.1) + jekyll-theme-primer (= 0.5.3) + jekyll-theme-slate (= 0.1.1) + jekyll-theme-tactile (= 0.1.1) + jekyll-theme-time-machine (= 0.1.1) + jekyll-titles-from-headings (= 0.5.1) + jemoji (= 0.10.2) + kramdown (= 1.17.0) + liquid (= 4.0.0) + listen (= 3.1.5) + mercenary (~> 0.3) + minima (= 2.5.0) + nokogiri (>= 1.10.4, < 2.0) + rouge (= 3.11.0) + terminal-table (~> 1.4) + github-pages-health-check (1.16.1) + addressable (~> 2.3) + dnsruby (~> 1.60) + octokit (~> 4.0) + public_suffix (~> 3.0) + typhoeus (~> 1.3) + html-pipeline (2.12.0) + activesupport (>= 2) + nokogiri (>= 1.4) + http_parser.rb (0.6.0) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + jekyll (3.8.5) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 0.7) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 2.0) + kramdown (~> 1.14) + liquid (~> 4.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (>= 1.7, < 4) + safe_yaml (~> 1.0) + jekyll-avatar (0.6.0) + jekyll (~> 3.0) + jekyll-coffeescript (1.1.1) + coffee-script (~> 2.2) + coffee-script-source (~> 1.11.1) + jekyll-commonmark (1.3.1) + commonmarker (~> 0.14) + jekyll (>= 3.7, < 5.0) + jekyll-commonmark-ghpages (0.1.6) + commonmarker (~> 0.17.6) + jekyll-commonmark (~> 1.2) + rouge (>= 2.0, < 4.0) + jekyll-default-layout (0.1.4) + jekyll (~> 3.0) + jekyll-feed (0.11.0) + jekyll (~> 3.3) + jekyll-gist (1.5.0) + octokit (~> 4.2) + jekyll-github-metadata (2.12.1) + jekyll (~> 3.4) + octokit (~> 4.0, != 4.4.0) + jekyll-mentions (1.4.1) + html-pipeline (~> 2.3) + jekyll (~> 3.0) + jekyll-optional-front-matter (0.3.0) + jekyll (~> 3.0) + jekyll-paginate (1.1.0) + jekyll-readme-index (0.2.0) + jekyll (~> 3.0) + jekyll-redirect-from (0.14.0) + jekyll (~> 3.3) + jekyll-relative-links (0.6.0) + jekyll (~> 3.3) + jekyll-remote-theme (0.4.0) + addressable (~> 2.0) + jekyll (~> 3.5) + rubyzip (>= 1.2.1, < 3.0) + jekyll-sass-converter (1.5.2) + sass (~> 3.4) + jekyll-seo-tag (2.5.0) + jekyll (~> 3.3) + jekyll-sitemap (1.2.0) + jekyll (~> 3.3) + jekyll-swiss (0.4.0) + jekyll-theme-architect (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-cayman (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-dinky (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-hacker (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-leap-day (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-merlot (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-midnight (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-minimal (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-modernist (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-primer (0.5.3) + jekyll (~> 3.5) + jekyll-github-metadata (~> 2.9) + jekyll-seo-tag (~> 2.0) + jekyll-theme-slate (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-tactile (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-time-machine (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-titles-from-headings (0.5.1) + jekyll (~> 3.3) + jekyll-watch (2.2.1) + listen (~> 3.0) + jemoji (0.10.2) + gemoji (~> 3.0) + html-pipeline (~> 2.2) + jekyll (~> 3.0) + kramdown (1.17.0) + liquid (4.0.0) + listen (3.1.5) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + ruby_dep (~> 1.2) + mercenary (0.3.6) + mini_portile2 (2.4.0) + minima (2.5.0) + jekyll (~> 3.5) + jekyll-feed (~> 0.9) + jekyll-seo-tag (~> 2.1) + minitest (5.13.0) + multipart-post (2.1.1) + nokogiri (1.10.5) + mini_portile2 (~> 2.4.0) + octokit (4.14.0) + sawyer (~> 0.8.0, >= 0.5.3) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (3.1.1) + rb-fsevent (0.10.3) + rb-inotify (0.10.0) + ffi (~> 1.0) + rouge (3.11.0) + ruby-enum (0.7.2) + i18n + ruby_dep (1.5.0) + rubyzip (2.0.0) + safe_yaml (1.0.5) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sawyer (0.8.2) + addressable (>= 2.3.5) + faraday (> 0.8, < 2.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + thread_safe (0.3.6) + typhoeus (1.3.1) + ethon (>= 0.9.0) + tzinfo (1.2.5) + thread_safe (~> 0.1) + unicode-display_width (1.6.0) + +PLATFORMS + ruby + +DEPENDENCIES + github-pages + jekyll-feed (~> 0.6) + minima (~> 2.0) + tzinfo-data + +BUNDLED WITH + 2.0.2 diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..bc512c7 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,42 @@ +# Welcome to Jekyll! +# +# This config file is meant for settings that affect your whole blog, values +# which you are expected to set up once and rarely edit after that. If you find +# yourself editing this file very often, consider using Jekyll's data files +# feature for the data you need to update frequently. +# +# For technical reasons, this file is *NOT* reloaded automatically when you use +# 'bundle exec jekyll serve'. If you change this file, please restart the server process. + +# Site settings +# These are used to personalize your new site. If you look in the HTML files, +# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. +# You can create any custom variable you would like, and they will be accessible +# in the templates via {{ site.myvariable }}. +title: Vimspector Documentation +description: | + Reference Documentation for Vimspector: A multi-language debugging front + end for Vim + +# Build settings +markdown: kramdown +theme: minima +plugins: + - jekyll-feed + +header_pages: + - index.md + - configuration.md + - schema/index.md + +# Exclude from processing. +# The following items will not be processed, by default. Create a custom list +# to override the default setting. +# exclude: +# - Gemfile +# - Gemfile.lock +# - node_modules +# - vendor/bundle/ +# - vendor/cache/ +# - vendor/gems/ +# - vendor/ruby/ diff --git a/docs/configuration.md b/docs/configuration.md index ef1ba88..199ebe9 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,16 +1,19 @@ -# Vimspector project configuration +--- +title: Configuration +--- + This document defines the supported format for project and adapter configuration for Vimspector. -# Concepts +## Concepts As Vimspector supports debugging arbitrary projects, you need to tell it a few deatils about what you want to debug, and how to go about doing that. In order to debug things, Vimspector requires a Debug Adapter which bridges between Vimspector and the actual debugger tool. Vimspector can be used with any -debug adapter that implemnents the [Debug Adapter Protocol][dap]. +debug adapter that implements the [Debug Adapter Protocol][dap]. For each debugging session, you provide a _debug configuration_ which includes things like: @@ -24,7 +27,7 @@ Along with optional additional configuration for things like: - Function breakpoints - Exception breakpoints -## Debug adapter configuration +### Debug adapter configuration The adapter to use for a particular debug session can be specified inline within the _debug configuration_, but more usually the debug adapter is defined @@ -32,12 +35,12 @@ separately and just referenced from the _debug configuration_. The adapter configuration includes things like: -* How to launch or connect to the debeg adapter +* How to launch or connect to the debug adapter * How to configure it for PID attachment * How to set up remote debugging, such as how to launch the process remotely (for example, under `gdbserver`, `ptvsd`, etc.) -## Debug profile configuration +### Debug profile configuration Projects can have many different debug profiles. For example you might have all of the following, for a given source tree: @@ -45,7 +48,7 @@ of the following, for a given source tree: * Remotely launch c++ the process, and break on `main` * Locally Python test and break exception * Remotely attach to a c++ process -* Locally launchg a bash script +* Locally launch a bash script * Attach to a JVM listening on a port Each of these represents a different use case and a different _debug @@ -57,10 +60,10 @@ configuration_. As mentioned above, a _debug configuration_ is essentially: process. The bulk of the configuration is the last of these, which comprises -adapter-specific opions, as the Debug Adapter Protocol does not specify any +adapter-specific options, as the Debug Adapter Protocol does not specify any standard for launch or attach configuration. -## Replacements and variables +### Replacements and variables Vimspector _debug configuration_ is intended to be as general as possible, and to be committed to source control so that debugging your applications becomes a @@ -69,9 +72,9 @@ if..." with "just hit F5 and step through!"). Therefore it's important to abstract certain details, like runtime and build-time paths, and to parameterise the _debug configuration_. Vimspector -provides a simple mechanism to do this with `${replacement}` style replacments. +provides a simple mechanism to do this with `${replacement}` style replacements. -The values avaiable within the `${...}` are defined below, but in summary the +The values available within the `${...}` are defined below, but in summary the following are supported: * Environment variables, such as `${PATH}` @@ -112,8 +115,8 @@ But for now, consider the following example snippet: } ``` -In this (ficticious) example the `program` launch configuration item contains -the following variable substiutions: +In this (fictitious) example the `program` launch configuration item contains +the following variable substitutions: * `${fileBasenameNoExtension}` - this is a [Predefined Variable](#predefined-variables), set by Vimspector to the base name of the @@ -121,18 +124,18 @@ the following variable substiutions: `xyz`). * `${USER}` - this refers to the Environment Variable `USER`. * `${TestIdentifier}` - this variable is not defined, so the user is asked to - provide a value interractively when starting debugging. Vimspector remembers + provide a value interactively when starting debugging. Vimspector remembers what they said and provides it as the default should they debug again. * `${SecretToken}` - this variable is provided by the configuration's `variables` block. Its value is taken from the `strip`ped result of running the shell command. Note these variables can be supplied by both the debug and adapter configurations and can be either static strings or shell commands. -# Configuration Format +## Configuration Format All Vimspector configuration is defined in a JSON object. The complete specification of this object is available in the [JSON Schema][schema], but -the basic format for the configuation object is: +the basic format for the configuration object is: ``` { @@ -144,13 +147,13 @@ the basic format for the configuation object is: The `adapters` key is actually optional, as `` can be embedded within ``, though this is not recommended usage. -# Files and locations +## Files and locations The above configuration object is constructed from a number of configuration files, by merging objects i specified order. In a minimal sense, the only file required is a `.vimspector.json` file in the -root of your project which deinfes the [full configuration object][schema], but +root of your project which defines the [full configuration object][schema], but it is usually useful to split the `adapters` configuration into a separate file (or indeed one file per debug adapter). @@ -161,10 +164,10 @@ abbreviations: `$HOME/.vim/pack/vimspector/start/vimspector`) * `` is either `macos` or `linux` depending on the host operating system. -# Adapter configurations +## Adapter configurations Vimspector reads a series of files to build the `adapters` object. The -`adapters` objects are merged in such a way that a defintion for an adapter +`adapters` objects are merged in such a way that a definition for an adapter named `example-adapter` in a later file _completely replaces_ a previous definition. @@ -198,12 +201,12 @@ Adapter configurations are re-read at the start of each debug session. The specification for the gadget object is defined in the [gadget schema][]. -# Debug configurations +## Debug configurations -The debug conifgurations are read from `.vimspector.json`. The file is found +The debug configurations are read from `.vimspector.json`. The file is found (like `.gadgets.json` above) by recursively searching up the directory hierarchy from the directory of the file open in Vim. The first file found is read and no -futher searching is done. +further searching is done. Only a single `.vimspector.json` is read. @@ -227,7 +230,7 @@ typical example looks like this: } ``` -# Predefined Variables +## Predefined Variables The following variables are provided: @@ -248,7 +251,7 @@ The following variables are provided: * `${fileExtname}` - the current opened file's extension * `${cwd}` - the current working directory of the active window on launch -# Appendix: Editor configuration +## Appendix: Editor configuration If you would like some assistance with writing the JSON files, and your editor of choice has a way to use a language server, you can use the diff --git a/docs/index.md b/docs/index.md index 3da138b..b568203 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,23 @@ -# Vimsepctor +--- +title: Vimspector Reference Guide +--- This section contains reference material for configuring and using -[Vimsepctor][vimspector]. It is intentionally technical in nature and should +[Vimspector][vimspector]. It is intentionally technical in nature and should serve as a reference guide, rather than a user guide or tutorial. It complements the following: -* The [Vimspector README], which contains a general overview and is the main - go-to for getting started, installation, etc. -* The [Vimspector Website], which contains a step-by-step tutorial and some - detais about the user interface. +* The [Vimspector README][readme], which contains a general overview and is the + main go-to for getting started, installation, etc. +* The [Vimspector Website][website], which contains a step-by-step tutorial and + some detail about the user interface. -# Contents +## Contents -* [Configuring Vimspector](configuration.html) -* [JSON Schema](schema/) +* [Configuring Vimspector for your projects](configuration.html) +* [Full JSON Schema reference](schema/) + +[vimspector]: https://github.com/puremourning/vimspector +[readme]: https://github.com/puremourning/vimspector/blob/master/README.md +[website]: https://puremourning.github.io/vimspector-web diff --git a/docs/schema/index.md b/docs/schema/index.md index f5d00a9..2b1bb27 100644 --- a/docs/schema/index.md +++ b/docs/schema/index.md @@ -1,4 +1,6 @@ -# Vimsepctor JSON Schema +--- +title: Configuration Schema +--- This area contains the [JSON Schema][json-schema] representation of the configuration objects used for configuring Vimspector. For more information on @@ -8,7 +10,7 @@ Schema](http://json-schema.org/understanding-json-schema) Vimsepctor specification is based on [Draft 7][draft-7] of JSON Schema standard. -# The schemas +## The schemas * [`vimspector.schema.json`](vimspector.schema.json) - contains the full configuration defnition (`.vimspector.json`). diff --git a/support/site/jekyll/.gitignore b/support/site/jekyll/.gitignore new file mode 100644 index 0000000..011d9d8 --- /dev/null +++ b/support/site/jekyll/.gitignore @@ -0,0 +1,2 @@ +vendor/ +.bundle diff --git a/support/site/jekyll/Gemfile b/support/site/jekyll/Gemfile new file mode 100644 index 0000000..2c01300 --- /dev/null +++ b/support/site/jekyll/Gemfile @@ -0,0 +1,2 @@ +source 'https://rubygems.org' +gem 'jekyll', '3.8.5' diff --git a/support/site/jekyll/Gemfile.lock b/support/site/jekyll/Gemfile.lock new file mode 100644 index 0000000..2367501 --- /dev/null +++ b/support/site/jekyll/Gemfile.lock @@ -0,0 +1,61 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + colorator (1.1.0) + concurrent-ruby (1.1.5) + em-websocket (0.5.1) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0.6.0) + eventmachine (1.2.7) + ffi (1.11.1) + forwardable-extended (2.6.0) + http_parser.rb (0.6.0) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + jekyll (3.8.5) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 0.7) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 2.0) + kramdown (~> 1.14) + liquid (~> 4.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (>= 1.7, < 4) + safe_yaml (~> 1.0) + jekyll-sass-converter (1.5.2) + sass (~> 3.4) + jekyll-watch (2.2.1) + listen (~> 3.0) + kramdown (1.17.0) + liquid (4.0.3) + listen (3.2.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.3.6) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (4.0.1) + rb-fsevent (0.10.3) + rb-inotify (0.10.0) + ffi (~> 1.0) + rouge (3.12.0) + safe_yaml (1.0.5) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + +PLATFORMS + ruby + +DEPENDENCIES + jekyll (= 3.8.5) + +BUNDLED WITH + 2.0.2 From 8d025c475d80f9026f38e05c5d4b8a3822483a52 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Nov 2019 15:55:33 +0000 Subject: [PATCH 011/609] Remove old jekyll config --- _config.yml | 1 - 1 file changed, 1 deletion(-) delete mode 100644 _config.yml diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 259a24e..0000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-tactile \ No newline at end of file From 16c3b65cae55cbdee8ae3c247d5f98eff5f49c97 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 14 Dec 2019 15:51:04 +0000 Subject: [PATCH 012/609] If signs are moved by user actions, use the current lnum of the sign for the breakpoint. --- python3/vimspector/breakpoints.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index a24d1de..82f4c6f 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -87,6 +87,7 @@ class ProjectBreakpoints( object ): else: for file_name, breakpoints in self._line_breakpoints.items(): for bp in breakpoints: + self._SignToLine( file_name, bp ) qf.append( { 'filename': file_name, 'lnum': bp[ 'line' ], @@ -113,6 +114,7 @@ class ProjectBreakpoints( object ): # These are the user-entered breakpoints. for file_name, breakpoints in self._line_breakpoints.items(): for bp in breakpoints: + self._SignToLine( file_name, bp ) if 'sign_id' in bp: vim.command( 'sign unplace {0} group=VimspectorBP'.format( bp[ 'sign_id' ] ) ) @@ -132,6 +134,7 @@ class ProjectBreakpoints( object ): found_bp = False action = 'New' for index, bp in enumerate( self._line_breakpoints[ file_name ] ): + self._SignToLine( file_name, bp ) if bp[ 'line' ] == line: found_bp = True if bp[ 'state' ] == 'ENABLED' and not self._connection: @@ -143,7 +146,7 @@ class ProjectBreakpoints( object ): bp[ 'sign_id' ] ) ) del self._line_breakpoints[ file_name ][ index ] action = 'Delete' - break + break self._logger.debug( "Toggle found bp at {}:{} ? {} ({})".format( file_name, @@ -215,6 +218,7 @@ class ProjectBreakpoints( object ): for file_name, line_breakpoints in self._line_breakpoints.items(): breakpoints = [] for bp in line_breakpoints: + self._SignToLine( file_name, bp ) if 'sign_id' in bp: vim.command( 'sign unplace {0} group=VimspectorBP'.format( bp[ 'sign_id' ] ) ) @@ -330,6 +334,7 @@ class ProjectBreakpoints( object ): def _ShowBreakpoints( self ): for file_name, line_breakpoints in self._line_breakpoints.items(): for bp in line_breakpoints: + self._SignToLine( file_name, bp ) if 'sign_id' in bp: vim.command( 'sign unplace {0} group=VimspectorBP '.format( bp[ 'sign_id' ] ) ) @@ -344,3 +349,17 @@ class ProjectBreakpoints( object ): 'vimspectorBP' if bp[ 'state' ] == 'ENABLED' else 'vimspectorBPDisabled', file_name ) ) + + + def _SignToLine( self, file_name, bp ): + if 'sign_id' not in bp: + return bp[ 'line' ] + + signs = vim.eval( "sign_getplaced( '{}', {} )".format( + utils.Escape( file_name ), + json.dumps( { 'id': file_name, 'group': 'VimspectorBP', } ) ) ) + + if len( signs ) == 1 and len( signs[ 0 ][ 'signs' ] ) == 1: + bp[ 'line' ] = int( signs[ 0 ][ 'signs' ][ 0 ][ 'lnum' ] ) + + return bp[ 'line' ] From 7d046574cbb4fe47d8e5d8d681edcef7161e9af9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 14 Dec 2019 18:03:34 +0000 Subject: [PATCH 013/609] Add tests for toggling breakpoint and inserting line --- docs/configuration.md | 7 ++- python3/vimspector/breakpoints.py | 2 +- .../python/simple_python/.vimspector.json | 12 +++++ tests/breakpoints.test.vim | 49 +++++++++++++++++++ tests/language_python.test.vim | 16 ++---- tests/lib/autoload/vimspector/test/setup.vim | 9 +++- 6 files changed, 79 insertions(+), 16 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 199ebe9..97418e1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -24,7 +24,6 @@ things like: Along with optional additional configuration for things like: -- Function breakpoints - Exception breakpoints ### Debug adapter configuration @@ -109,6 +108,12 @@ But for now, consider the following example snippet: "--test-identifier", "${TestIdentifier}", "--secret-token", "${SecretToken}" ] + }, + "breakpoints": { + "exception": { + "caught": "", + "uncaught": "Y" + } } } } diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 82f4c6f..01da54d 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -305,7 +305,7 @@ class ProjectBreakpoints( object ): if isinstance( result, bool ): result = 'Y' if result else 'N' - if not isinstance( result, str) or result not in ( 'Y', 'N', '' ): + if not isinstance( result, str ) or result not in ( 'Y', 'N', '' ): raise ValueError( f"Invalid value for exception breakpoint filter '{f}': " f"'{result}'. Must be boolean, 'Y', 'N' or '' (default)" ) diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 600295c..3de14d8 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -9,6 +9,12 @@ "program": "${file}", "stopOnEntry": true, "console": "integratedTerminal" + }, + "breakpoints": { + "exception": { + "raised": "N", + "uncaught": "" + } } }, "attach": { @@ -18,6 +24,12 @@ "type": "python", "host": "localhost", "port": "5678" + }, + "breakpoints": { + "exception": { + "raised": "N", + "uncaught": "" + } } } } diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index 9faf08e..1c36c08 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -246,3 +246,52 @@ function Test_DisableBreakpointWhileDebugging() lcd - %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' ) + + " 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' ) + + " 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' ) + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 26, + \ 'vimspectorBPDisabled' ) + " Delete it + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 26 ) + +endfunction diff --git a/tests/language_python.test.vim b/tests/language_python.test.vim index 5240fac..7bf95a8 100644 --- a/tests/language_python.test.vim +++ b/tests/language_python.test.vim @@ -6,11 +6,11 @@ function! ClearDown() call vimspector#test#setup#ClearDown() endfunction -function! SetUp_Test_Go_Simple() +function! SetUp_Test_Python_Simple() let g:vimspector_enable_mappings = 'HUMAN' endfunction -function! Test_Go_Simple() +function! Test_Python_Simple() let fn='main.py' lcd ../support/test/python/simple_python exe 'edit ' . fn @@ -28,16 +28,8 @@ function! Test_Go_Simple() call setpos( '.', [ 0, 1, 1 ] ) " Here we go. Start Debugging - pyx << EOF -from unittest.mock import patch -with patch( 'vimspector.utils.SelectFromList', - return_value=None ) as p: - with patch( 'vimspector.utils.AskForInput', - return_value=None ) as p: - vim.eval( 'vimspector#LaunchWithSettings( { "configuration": "run" } )' ) - vim.eval( 'vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 6, 1 )' ) - p.assert_called() -EOF + call vimspector#LaunchWithSettings( { "configuration": "run" } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 6, 1 ) " Step call feedkeys( "\", 'xt' ) diff --git a/tests/lib/autoload/vimspector/test/setup.vim b/tests/lib/autoload/vimspector/test/setup.vim index a1df8d5..6610647 100644 --- a/tests/lib/autoload/vimspector/test/setup.vim +++ b/tests/lib/autoload/vimspector/test/setup.vim @@ -16,8 +16,7 @@ endfunction function! vimspector#test#setup#ClearDown() abort endfunction -function! vimspector#test#setup#Reset() abort - call vimspector#Reset() +function! vimspector#test#setup#WaitForReset() abort call WaitForAssert( {-> \ assert_true( pyxeval( '_vimspector_session._connection is None' ) ) \ } ) @@ -26,6 +25,12 @@ function! vimspector#test#setup#Reset() abort \ }, 10000 ) call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorCode' ) +endfunction + +function! vimspector#test#setup#Reset() abort + call vimspector#Reset() + call vimspector#test#setup#WaitForReset() + call vimspector#ClearBreakpoints() call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorBP' ) From 74ef72b150c546292e26856949bfc92e16dc7d61 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 14 Dec 2019 18:03:53 +0000 Subject: [PATCH 014/609] which -s is mac only --- run_tests | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run_tests b/run_tests index f2f8022..f216c58 100755 --- a/run_tests +++ b/run_tests @@ -4,9 +4,9 @@ RUN_VIM="vim --clean --not-a-term" RUN_TEST="${RUN_VIM} -S lib/run_test.vim" if [ -z "$VIMSPECTOR_MIMODE" ]; then - if which -s lldb; then + if which lldb >/dev/null 2>&1; then export VIMSPECTOR_MIMODE=lldb - elif which -s gdb; then + elif which gdb >/dev/null 2>&1; then export VIMSPECTOR_MIMODE=gdb else echo "Couldn't guess VIMSPECTOR_MIMODE. Need lldb or gdb in path" From 94b8f0a81deea9ccd45a949b06324e6c6941ff33 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 15 Dec 2019 09:15:56 +0000 Subject: [PATCH 015/609] Update cpptools --- install_gadget.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 6da3b35..ab9e1af 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -56,17 +56,17 @@ GADGETS = { }, 'do': lambda name, root: InstallCppTools( name, root ), 'all': { - 'version': '0.23.1', + 'version': '0.26.2', }, 'linux': { 'file_name': 'cpptools-linux.vsix', 'checksum': - 'c0f424bd6d5e016d70126587c80b92d981729c708ce524f2cce4c3f524b41d71' + '767aed01f0c0b5eb9e9eff96aba47b576d153d2b2d9fc28e306722ea45a02ff5' }, 'macos': { 'file_name': 'cpptools-osx.vsix', 'checksum': - '431692395ba243ea20428e083d5df3201a0dbda31a66eab7729da0f377def5fd', + '6fd52562e1e53287c0e9b94813709c6fab487c16ff3054eda6233d6c0241eb0e', }, 'windows': { 'file_name': 'cpptools-win32.vsix', @@ -523,6 +523,10 @@ parser.add_argument( '--force-all', action = 'store_true', help = 'Enable all unsupported completers' ) +parser.add_argument( '--no-gadget-config', + action = 'store_true', + help = "Don't write the .gagets.json, just install" ) + done_languages = set() for name, gadget in GADGETS.items(): lang = gadget[ 'language' ] @@ -616,9 +620,18 @@ for name, gadget in GADGETS.items(): print( "FAILED installing {}: {}".format( name, e ) ) -with open( install.GetGadgetConfigFile( os.path.dirname( __file__ ) ), - 'w' ) as f: - json.dump( { 'adapters': all_adapters }, f, indent=2, sort_keys=True ) +adapter_config = json.dumps ( { 'adapters': all_adapters }, + indent=2, + sort_keys=True ) + +if args.no_gadget_config: + print( "" ) + print( "Would write the following gadgets: " ) + print( adapter_config ) +else: + with open( install.GetGadgetConfigFile( os.path.dirname( __file__ ) ), + 'w' ) as f: + f.write( adapter_config ) if failed: raise RuntimeError( 'Failed to install gadgets: {}'.format( From ed6beff03b99e38ff124e8041cb65d2207649692 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 15 Dec 2019 10:12:19 +0000 Subject: [PATCH 016/609] Enable syntax highlighting for watches and locals --- python3/vimspector/code.py | 5 +++++ python3/vimspector/debug_session.py | 1 + python3/vimspector/utils.py | 6 ++++++ python3/vimspector/variables.py | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index f28a1b5..509008d 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -27,6 +27,7 @@ class CodeView( object ): self._terminal_window = None self._terminal_buffer_number = None + self.current_syntax = None self._logger = logging.getLogger( __name__ ) utils.SetUpLogging( self._logger ) @@ -93,6 +94,9 @@ class CodeView( object ): frame[ 'line' ], frame[ 'source' ][ 'path' ] ) ) + self.current_syntax = utils.ToUnicode( + vim.current.buffer.options[ 'syntax' ] ) + return True def Clear( self ): @@ -102,6 +106,7 @@ class CodeView( object ): self._signs[ 'vimspectorPC' ] = None self._UndisplaySigns() + self.current_syntax = None def Reset( self ): self.ClearBreakpoints() diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 5e98491..42b7cb8 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -427,6 +427,7 @@ class DebugSession( object ): return False if frame: + self._variablesView.SetSyntax( self._codeView.current_syntax ) self._variablesView.LoadScopes( frame ) self._variablesView.EvaluateWatches() else: diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 73e2fea..e4f06bc 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -434,3 +434,9 @@ def GetBufferFilepath( buf ): return '' return os.path.normpath( buf.name ) + + +def ToUnicode( b ): + if isinstance( b, bytes ): + return b.decode( 'utf-8' ) + return b diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index d9882c8..a8a5d0f 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -31,6 +31,7 @@ class VariablesView( object ): self._vars = View( variables_win, {}, self._DrawScopes ) self._watch = View( watches_win, {}, self._DrawWatches ) self._connection = connection + self._current_syntax = '' # Allows us to hit to expand/collapse variables with utils.LetCurrentWindow( self._vars.win ): @@ -91,6 +92,7 @@ class VariablesView( object ): utils.ClearBuffer( self._vars.win.buffer ) with utils.ModifiableScratchBuffer( self._watch.win.buffer ): utils.ClearBuffer( self._watch.win.buffer ) + self._current_syntax = '' def ConnectionUp( self, connection ): self._connection = connection @@ -373,3 +375,19 @@ class VariablesView( object ): 'context': 'hover', } }, failure_handler ) + + + def SetSyntax( self, syntax ): + if not syntax: + syntax = '' + + if self._current_syntax == syntax: + return + + self._current_syntax = syntax + + with utils.LetCurrentWindow( self._vars.win ): + vim.command( 'set syntax={}'.format( utils.Escape( syntax ) ) ) + + with utils.LetCurrentWindow( self._watch.win ): + vim.command( 'set syntax={}'.format( utils.Escape( syntax ) ) ) From 6944cb7c1ff9d73bc90d8ec52d735e21377f7c85 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 22 Dec 2019 21:33:16 +0000 Subject: [PATCH 017/609] Fix tracebnack when no source in stack frame --- install_gadget.py | 2 +- python3/vimspector/stack_trace.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index ab9e1af..1e9e4d4 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -37,7 +37,7 @@ import functools import time try: - from io import BytesIO ## for Python 3 + from io import BytesIO # for Python 3 except ImportError: from BytesIO import BytesIO diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 74bac68..bc150f4 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -169,11 +169,11 @@ class StackTraceView( object ): def _JumpToFrame( self, frame ): def do_jump(): - if 'line' in frame and frame[ 'line' ]: + if 'line' in frame and frame[ 'line' ] > 0: self._currentFrame = frame return self._session.SetCurrentFrame( self._currentFrame ) - source = frame[ 'source' ] + source = frame.get( 'source', {} ) if source.get( 'sourceReference', 0 ) > 0: def handle_resolved_source( resolved_source ): frame[ 'source' ] = resolved_source From e2738de64e32a4a45a4cbde6bf57edd2ce8c84f4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 30 Dec 2019 20:33:37 +0000 Subject: [PATCH 018/609] Update vscode-python --- install_gadget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 1e9e4d4..8de8c16 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -92,10 +92,10 @@ GADGETS = { '${version}/${file_name}', }, 'all': { - 'version': '2019.10.41019', + 'version': '2019.11.50794', 'file_name': 'ms-python-release.vsix', 'checksum': - '38e8bf782fc6d2dc904868add2e1e5dc66197a06a902f6d17e15f96d4e9bf16b', + '6a9edf9ecabed14aac424e6007858068204a3638bf3bb4f235bd6035d823acc6', }, 'adapters': { "vscode-python": { From 144efab83dd34ed9d945254d29aefacd7153c19f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 1 Jan 2020 15:19:13 +0000 Subject: [PATCH 019/609] Add workaround for broken cpptools on macOS --- README.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6a9d8f3..3ad8aa1 100644 --- a/README.md +++ b/README.md @@ -248,7 +248,7 @@ out how to start it, and configure that in an `adapters` entry in either your The simplest way in practice is to install or start Visusal Studio Code and use its extension manager to install the relevant extension. You can then configure the adapter manually in the `adapters` section of your `.vimspector.json` or in -a `gagets.json`. +a `gadgets.json`. PRs are always welcome to add configuration to do this to `install_gadget.py`. @@ -266,13 +266,32 @@ Where os is one of: * `linux` * `windows` (though note: Windows is not supported) -The format is the same as `.vimspector.json`, but only the `gagets` key is used: +The format is the same as `.vimspector.json`, but only the `adapters` key is +used: Example: ```json { "adapters": { + "lldb-vscode": { + "variables": { + "LLVM": { + "shell": "brew --prefix llvm" + } + }, + "attach": { + "pidProperty": "pid", + "pidSelect": "ask" + }, + "command": [ + "${LLVM}/bin/lldb-vscode" + ], + "env": { + "LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY": "YES" + }, + "name": "lldb" + }, "vscode-cpptools": { "attach": { "pidProperty": "processId", @@ -296,6 +315,11 @@ Example: The gadget file is automatically written by `install_gadget.py`. +Vimspector will also load any fies matching: +`/gadgets//.gadgets.d/*.json`. These have the same +format as `.gadgets.json` but are not overwritten when running +`install_gadget.py`. + # About ## Background @@ -477,7 +501,43 @@ Current tested with the following debug adapters. * C++: [vscode-cpptools](https://github.com/Microsoft/vscode-cpptools) -Example `.vimspector.json` +***NOTE FOR macOS USERS***: Currently VSCode cpptools does *not* work on macOS +(see this issue: https://github.com/microsoft/vscode-cpptools/issues/3829). +Therefore it is highly recommended to use `lldb-vscode`, which comes with llvm. +Here's how: + +* Install llvm with HomeBrew: `brew install llvm` +* Create a file named + `/path/to/vimspector/gadgets/macos/.gadgets.d/lldb-vscode.json`: + +```json + +{ + "adapters": { + "lldb-vscode": { + "variables": { + "LLVM": { + "shell": "brew --prefix llvm" + } + }, + "attach": { + "pidProperty": "pid", + "pidSelect": "ask" + }, + "command": [ + "${LLVM}/bin/lldb-vscode" + ], + "env": { + "LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY": "YES" + }, + "name": "lldb" + } + } +} +``` + +Example `.vimspector.json` (works with both `vscode-cpptools` and `lldb-vscode`. +For `lldb-vscode` replace the name of the adapter with `lldb-vscode`): ``` { From c8a99831d67527979ae6ca1cac86fb3934bf4c5d Mon Sep 17 00:00:00 2001 From: yury Date: Tue, 7 Jan 2020 11:59:44 +0200 Subject: [PATCH 020/609] Remove colon for consistency --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3ad8aa1..7f43851 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ For a tutorial and usage overview, take a look at the * [Variables and scopes](#variables-and-scopes) * [Watches](#watches) * [Stack Traces](#stack-traces) - * [Program Output:](#program-output) + * [Program Output](#program-output) * [Console](#console) * [Debug adapter configuration](#debug-adapter-configuration) * [Supported Languages](#supported-languages-1) @@ -473,7 +473,7 @@ new watch expression. * In the threads window, use `` to expand/collapse. * Use `` on a stack frame to jump to it. -## Program Output: +## Program Output * In the outputs window use the WinBar to select the output channel. * The debugee prints to the stdout channel. From a74b9f2c6588a155dbe57c22e354d7134d34b713 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 7 Jan 2020 13:12:06 +0000 Subject: [PATCH 021/609] Make statement about neovim support Currently unsupported for technical reasons, not philosophical ones. --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3ad8aa1..881163c 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ Alternatively, you can clone the repo and select which gadgets are installed: Vimspector requires: -* Vim version 8.1 with at least patch 1264 +* Vim (*not neovim*) version 8.1 with at least patch 1264 * One of the following operating systems: * Linux * macOS Mojave or pater @@ -146,8 +146,17 @@ Why such a new vim ? Well 2 reasons: Why no Windows support? Because it's effort and it's not a priority for the author. PRs are welcome. -Which Linux versions? I only test on Ubuntu 18.04 and later and RHEL 6.5 and -RHEL 7.6. +Which Linux versions? I only test on Ubuntu 18.04 and later and RHEL 7. + +Why not neovim? A few fiddly reasons (not philosophy): + +* neovim doesn't implement some features Vimspector relies on +* neovim's Python API is not compatible with Vim's +* I don't personally use or test with neovim, and doing so doubles the +effort required. + +If enough people +1 [this issue](https://github.com/puremourning/vimspector/issues/29), +I will consider it. ## Language dependencies From 0ecc99a75c8a1fd8e16e82a4e41c8bf23c16818e Mon Sep 17 00:00:00 2001 From: yury Date: Tue, 7 Jan 2020 11:59:59 +0200 Subject: [PATCH 022/609] Add instruction on how to close the debugger Omit `Reset` mapping --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 7f43851..364afec 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ For a tutorial and usage overview, take a look at the * [Stack Traces](#stack-traces) * [Program Output](#program-output) * [Console](#console) + * [Closing debugger](#closing-debugger) * [Debug adapter configuration](#debug-adapter-configuration) * [Supported Languages](#supported-languages-1) * [Partially supported](#partially-supported) @@ -490,6 +491,13 @@ CLI for the debug adapter. Support for this varies amongt adapters. NOTE: See also [Watches][#watches] above. +## Closing debugger + +To close the debugger, use: + +* `Reset` button when mouse support is enabled in vim (`set mouse=a`) +* `call vimspector#Reset()` + # Debug adapter configuration ## Supported Languages From c8334dc30c63bbfc31a4703620525a2c1d14f06e Mon Sep 17 00:00:00 2001 From: yury Date: Tue, 7 Jan 2020 12:00:35 +0200 Subject: [PATCH 023/609] Fix Watches link in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 364afec..ecf1177 100644 --- a/README.md +++ b/README.md @@ -489,7 +489,7 @@ CLI for the debug adapter. Support for this varies amongt adapters. * Commit the request with `` * The request and subsequent result are printed. -NOTE: See also [Watches][#watches] above. +NOTE: See also [Watches](#watches) above. ## Closing debugger From 05d377217d5772fa1fbec11432f6dcba13dd81f3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jan 2020 17:19:35 +0000 Subject: [PATCH 024/609] Run git commands in the right repo --- install_gadget.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 8de8c16..f56d48f 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -498,14 +498,14 @@ def MakeSymlink( in_folder, link, pointing_to ): def CloneRepoTo( url, ref, destination ): RemoveIfExists( destination ) + git_in_repo = [ 'git', '-C', destination ] subprocess.check_call( [ 'git', 'clone', url, destination ] ) - subprocess.check_call( [ 'git', '-C', destination, 'checkout', ref ] ) - subprocess.check_call( [ 'git', 'submodule', 'sync', '--recursive' ] ) - subprocess.check_call( [ 'git', - 'submodule', - 'update', - '--init', - '--recursive' ] ) + subprocess.check_call( git_in_repo + [ 'checkout', ref ] ) + subprocess.check_call( git_in_repo + [ 'submodule', 'sync', '--recursive' ] ) + subprocess.check_call( git_in_repo + [ 'submodule', + 'update', + '--init', + '--recursive' ] ) OS = install.GetOS() From 1e153910fa1ac07952258d3a39852fbc6a128315 Mon Sep 17 00:00:00 2001 From: tinmarino Date: Thu, 9 Jan 2020 15:35:38 -0300 Subject: [PATCH 025/609] Fix: Remove balloon '...' in windows != code_window Problem: In non-code window, user see a '...' balloon even if python knows it does not need to work Solution: use pyeval in vim so that python's knowledge is getting back from the stack as return value --- autoload/vimspector/internal/balloon.vim | 7 ++++--- python3/vimspector/debug_session.py | 18 ++++++++++++------ python3/vimspector/variables.py | 8 ++++++-- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 6ae65b4..360ed08 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -19,13 +19,14 @@ let s:save_cpo = &cpoptions set cpoptions&vim " }}} +" Returns: py.ShowBalloon( winnr, expresssion ) function! vimspector#internal#balloon#BalloonExpr() abort " winnr + 1 because for *no good reason* winnr is 0 based here unlike " everywhere else " int() because for *no good reason* winnr is a string. - py3 _vimspector_session.ShowBalloon( int( vim.eval( 'v:beval_winnr' ) ) + 1, - \ vim.eval( 'v:beval_text' ) ) - return '...' + return py3eval('_vimspector_session.ShowBalloon(' + \ . 'int( vim.eval( "v:beval_winnr" ) ) + 1,' + \ . 'vim.eval( "v:beval_text" ) )' ) endfunction " Boilerplate {{{ diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 42b7cb8..42ca1c2 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -359,16 +359,22 @@ class DebugSession( object ): self._variablesView.DeleteWatch() def ShowBalloon( self, winnr, expression ): - if self._stackTraceView.GetCurrentFrame() is None: - return + """Proxy: ballonexpr -> variables.ShowBallon""" + frame = self._stackTraceView.GetCurrentFrame() + # Check if RIP is in a frame + if frame is None: + self._logger.debug( 'Balloon: Not in a stack frame' ) + return '' - if winnr == int( self._codeView._window.number ): - self._variablesView.ShowBalloon( self._stackTraceView.GetCurrentFrame(), - expression ) - else: + # Check if cursor in code window + if winnr != int( self._codeView._window.number ): self._logger.debug( 'Winnr %s is not the code window %s', winnr, self._codeView._window.number ) + return '' + + # Return variable aware function + return self._variablesView.ShowBalloon( frame, expression ) def ExpandFrameOrThread( self ): self._stackTraceView.ExpandFrameOrThread() diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index a8a5d0f..7260a43 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -346,8 +346,9 @@ class VariablesView( object ): draw() def ShowBalloon( self, frame, expression ): + """Callback to display variable under cursor `:h ballonexpr`""" if not self._connection: - return + return '' def handler( message ): # TODO: this result count be expandable, but we have no way to allow the @@ -366,7 +367,7 @@ class VariablesView( object ): display = [ reason ] utils.DisplayBaloon( self._is_term, display ) - + # Send async request self._connection.DoRequest( handler, { 'command': 'evaluate', 'arguments': { @@ -376,6 +377,9 @@ class VariablesView( object ): } }, failure_handler ) + # Return working (meanwhile) + return '...' + def SetSyntax( self, syntax ): if not syntax: From 8d7de7172a9ec742939affb82131336598021cb0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 14 Oct 2019 13:39:16 +0100 Subject: [PATCH 026/609] Allow arbitrary ssh args --- python3/vimspector/debug_session.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 42ca1c2..be2b359 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -508,8 +508,8 @@ class DebugSession( object ): # TODO: Use the 'tarminate' request if supportsTerminateRequest set + def _PrepareAttach( self, adapter_config, launch_config ): - atttach_config = adapter_config.get( 'attach' ) if not atttach_config: @@ -520,13 +520,9 @@ class DebugSession( object ): # e.g. expand variables when we use them, not all at once. This would # remove the whole %PID% hack. remote = atttach_config[ 'remote' ] - ssh = [ 'ssh' ] - - if 'account' in remote: - ssh.append( remote[ 'account' ] + '@' + remote[ 'host' ] ) - else: - ssh.append( remote[ 'host' ] ) + ssh = self._GetSSHCommand( remote ) + # FIXME: Why does this not use self._GetCommands ? cmd = ssh + remote[ 'pidCommand' ] self._logger.debug( 'Getting PID: %s', cmd ) @@ -574,12 +570,7 @@ class DebugSession( object ): if 'remote' in run_config: remote = run_config[ 'remote' ] - ssh = [ 'ssh' ] - if 'account' in remote: - ssh.append( remote[ 'account' ] + '@' + remote[ 'host' ] ) - else: - ssh.append( remote[ 'host' ] ) - + ssh = self._GetSSHCommand( remote ) commands = self._GetCommands( remote, 'run' ) for index, command in enumerate( commands ): @@ -599,6 +590,16 @@ class DebugSession( object ): full_cmd ) + def _GetSSHCommand( self, remote ): + ssh = [ 'ssh' ] + remote.get( 'ssh', {} ).get( 'args', [] ) + if 'account' in remote: + ssh.append( remote[ 'account' ] + '@' + remote[ 'host' ] ) + else: + ssh.append( remote[ 'host' ] ) + + return ssh + + def _GetCommands( self, remote, pfx ): commands = remote.get( pfx + 'Commands', None ) From b95fe208456d0d206af379bb8bf1e4362600576f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 31 Oct 2019 10:18:23 +0000 Subject: [PATCH 027/609] Frame is not reqiured for evaluation --- python3/vimspector/output.py | 14 +++++++------- python3/vimspector/variables.py | 10 +++++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index be722af..d4cf1af 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -115,10 +115,6 @@ class OutputView( object ): self._ShowOutput( category ) def Evaluate( self, frame, expression ): - if not frame: - self.Print( 'Console', 'There is no current stack frame' ) - return - console = self._buffers[ 'Console' ].buf utils.AppendToBuffer( console, 'Evaluating: ' + expression ) @@ -132,14 +128,18 @@ class OutputView( object ): utils.AppendToBuffer( console, ' Result: ' + result ) - self._connection.DoRequest( print_result, { + request = { 'command': 'evaluate', 'arguments': { 'expression': expression, 'context': 'repl', - 'frameId': frame[ 'id' ], } - } ) + } + + if frame: + request[ 'arguments' ][ 'frameId' ] = frame[ 'id' ] + + self._connection.DoRequest( print_result, request ) def _ToggleFlag( self, category, flag ): if self._buffers[ category ].flag != flag: diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 7260a43..f0ff99e 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -143,10 +143,12 @@ class VariablesView( object ): def AddWatch( self, frame, expression ): watch = { - 'expression': expression, - 'frameId': frame[ 'id' ], - 'context': 'watch', + 'expression': expression, + 'context': 'watch', } + if frame: + watch[ 'frameId' ] = frame[ 'id' ] + self._watches.append( watch ) self.EvaluateWatches() @@ -395,3 +397,5 @@ class VariablesView( object ): with utils.LetCurrentWindow( self._watch.win ): vim.command( 'set syntax={}'.format( utils.Escape( syntax ) ) ) + +# vim: sw=2 From 2c347c79209b1cd32406fdc20b2ac64ea0c2894b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 27 Nov 2019 12:12:34 +0000 Subject: [PATCH 028/609] Enalbe snippets for json --- .ycm_extra_conf.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py index 77feee6..d988326 100644 --- a/.ycm_extra_conf.py +++ b/.ycm_extra_conf.py @@ -26,6 +26,15 @@ def Settings( **kwargs ): } ] } + }, + 'capabilities': { + 'textDocument': { + 'completion': { + 'completionItem': { + 'snippetSupport': True + } + } + } } } From d950352545085e3c1322190a1df8043be19b6bce Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 20 Dec 2019 12:01:44 +0000 Subject: [PATCH 029/609] Install vscode java debugger somewhere --- install_gadget.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/install_gadget.py b/install_gadget.py index f56d48f..8cd8023 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -107,6 +107,26 @@ GADGETS = { } }, }, + 'vscode-java-debug': { + 'language': 'java', + 'enabled': False, + 'download': { + 'url': 'https://github.com/microsoft/vscode-java-debug/releases/download/' + '${version}/${file_name}', + }, + 'all': { + 'version': '0.23.0', + 'file_name': 'vscode-java-debug-0.23.0.vsix', + 'checksum': + '', + }, + 'adapters': { + "vscode-java": { + "name": "vscode-java", + "port": "ask", + } + }, + }, 'tclpro': { 'language': 'tcl', 'repo': { From 8bb7017a49c83cc3b33e80a2b63fcdd71ac442ff Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 20 Dec 2019 12:02:26 +0000 Subject: [PATCH 030/609] Handle failed breakpoint messages --- python3/vimspector/breakpoints.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 01da54d..7e767d0 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -203,14 +203,18 @@ class ProjectBreakpoints( object ): awaiting = 0 - def response_handler( source, msg ): - if msg: - self._breakpoints_handler.AddBreakpoints( source, msg ) + def response_received(): nonlocal awaiting awaiting = awaiting - 1 if awaiting == 0 and doneHandler: doneHandler() + def response_handler( source, msg ): + if msg: + self._breakpoints_handler.AddBreakpoints( source, msg ) + response_received() + + # TODO: add the _configured_breakpoints to line_breakpoints # TODO: the line numbers might have changed since pressing the F9 key! @@ -244,7 +248,8 @@ class ProjectBreakpoints( object ): 'breakpoints': breakpoints, }, 'sourceModified': False, # TODO: We can actually check this - } + }, + failure_handler = lambda *_: response_received() ) # TODO: Add the _configured_breakpoints to function breakpoints @@ -261,7 +266,8 @@ class ProjectBreakpoints( object ): for bp in self._func_breakpoints if bp[ 'state' ] == 'ENABLED' ], } - } + }, + failure_handler = lambda *_: response_received() ) if self._exception_breakpoints is None: @@ -274,7 +280,8 @@ class ProjectBreakpoints( object ): { 'command': 'setExceptionBreakpoints', 'arguments': self._exception_breakpoints - } + }, + failure_handler = lambda *_: response_received() ) if awaiting == 0 and doneHandler: From 55bb50d2962166d8c6115cd09f32b02ed09fc5d3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 20 Dec 2019 12:03:04 +0000 Subject: [PATCH 031/609] line sometiems not supplied; source is optional --- python3/vimspector/stack_trace.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index bc150f4..cf3be50 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -172,8 +172,9 @@ class StackTraceView( object ): if 'line' in frame and frame[ 'line' ] > 0: self._currentFrame = frame return self._session.SetCurrentFrame( self._currentFrame ) + return False - source = frame.get( 'source', {} ) + source = frame.get( 'source' ) or {} if source.get( 'sourceReference', 0 ) > 0: def handle_resolved_source( resolved_source ): frame[ 'source' ] = resolved_source From bbaab0ebc1a2af7dab3110f46cd247c089b2352f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jan 2020 13:20:08 +0000 Subject: [PATCH 032/609] Fix true/false in JSON when setting up adapter --- python3/vimspector/debug_session.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index be2b359..a5a81aa 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -473,10 +473,11 @@ class DebugSession( object ): if 'cwd' not in self._adapter: self._adapter[ 'cwd' ] = os.getcwd() + vim.vars[ '_vimspector_adapter_spec' ] = self._adapter channel_send_func = vim.bindeval( - "vimspector#internal#{}#StartDebugSession( {} )".format( - self._connection_type, - json.dumps( self._adapter ) ) ) + "vimspector#internal#{}#StartDebugSession( " + " g:_vimspector_adapter_spec " + ")".format( self._connection_type ) ) if channel_send_func is None: self._logger.error( "Unable to start debug server" ) From 8a57104a0ede1e01237ffc72dcf2a2e0b4ff220a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jan 2020 14:14:15 +0000 Subject: [PATCH 033/609] Expand references in non-shell variables too --- python3/vimspector/utils.py | 97 +++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index e4f06bc..66e39bb 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -323,58 +323,57 @@ def SetBufferContents( buf, lines, modified=False ): def IsCurrent( window, buf ): return vim.current.window == window and vim.current.window.buffer == buf +def ExpandReferencesInObject( obj, mapping, user_choices ): + if isinstance( obj, dict ): + ExpandReferencesInDict( obj, mapping, user_choices ) + elif isinstance( obj, list ): + for i, _ in enumerate( obj ): + # FIXME: We are assuming that it is a list of string, but could be a + # list of list of a list of dict, etc. + obj[ i ] = ExpandReferencesInObject( obj[ i ], mapping, user_choices ) + elif isinstance( obj, str ): + obj = ExpandReferencesInString( obj, mapping, user_choices ) + + return obj + +def ExpandReferencesInString( orig_s, mapping, user_choices): + s = os.path.expanduser( orig_s ) + s = os.path.expandvars( s ) + + # Parse any variables passed in in mapping, and ask for any that weren't, + # storing the result in mapping + bug_catcher = 0 + while bug_catcher < 100: + ++bug_catcher + + try: + s = string.Template( s ).substitute( mapping ) + break + except KeyError as e: + # HACK: This is seemingly the only way to get the key. str( e ) returns + # the key surrounded by '' for unknowable reasons. + key = e.args[ 0 ] + default_value = user_choices.get( key, None ) + mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ), + default_value ) + user_choices[ key ] = mapping[ key ] + _logger.debug( "Value for %s not set in %s (from %s): set to %s", + key, + s, + orig_s, + mapping[ key ] ) + except ValueError as e: + UserMessage( 'Invalid $ in string {}: {}'.format( s, e ), + persist = True ) + break + + return s # TODO: Should we just run the substitution on the whole JSON string instead? # That woul dallow expansion in bool and number values, such as ports etc. ? def ExpandReferencesInDict( obj, mapping, user_choices ): - def expand_refs_in_string( orig_s ): - s = os.path.expanduser( orig_s ) - s = os.path.expandvars( s ) - - # Parse any variables passed in in mapping, and ask for any that weren't, - # storing the result in mapping - bug_catcher = 0 - while bug_catcher < 100: - ++bug_catcher - - try: - s = string.Template( s ).substitute( mapping ) - break - except KeyError as e: - # HACK: This is seemingly the only way to get the key. str( e ) returns - # the key surrounded by '' for unknowable reasons. - key = e.args[ 0 ] - default_value = user_choices.get( key, None ) - mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ), - default_value ) - user_choices[ key ] = mapping[ key ] - _logger.debug( "Value for %s not set in %s (from %s): set to %s", - key, - s, - orig_s, - mapping[ key ] ) - except ValueError as e: - UserMessage( 'Invalid $ in string {}: {}'.format( s, e ), - persist = True ) - break - - return s - - def expand_refs_in_object( obj ): - if isinstance( obj, dict ): - ExpandReferencesInDict( obj, mapping, user_choices ) - elif isinstance( obj, list ): - for i, _ in enumerate( obj ): - # FIXME: We are assuming that it is a list of string, but could be a - # list of list of a list of dict, etc. - obj[ i ] = expand_refs_in_object( obj[ i ] ) - elif isinstance( obj, str ): - obj = expand_refs_in_string( obj ) - - return obj - for k in obj.keys(): - obj[ k ] = expand_refs_in_object( obj[ k ] ) + obj[ k ] = ExpandReferencesInObject( obj[ k ], mapping, user_choices ) def ParseVariables( variables_list, mapping, user_choices ): @@ -416,7 +415,9 @@ def ParseVariables( variables_list, mapping, user_choices ): raise ValueError( "Unsupported variable defn {}: Missing 'shell'".format( n ) ) else: - new_variables[ n ] = v + new_variables[ n ] = ExpandReferencesInObject( v, + mapping, + user_choices ) return new_variables From 9f0becab7b620948aaeb1ffd05b8210df96473c1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 10 Jan 2020 12:19:58 +0000 Subject: [PATCH 034/609] Flake8 fixes --- python3/vimspector/debug_session.py | 2 +- python3/vimspector/utils.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index a5a81aa..1cf7d52 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -509,7 +509,7 @@ class DebugSession( object ): # TODO: Use the 'tarminate' request if supportsTerminateRequest set - + def _PrepareAttach( self, adapter_config, launch_config ): atttach_config = adapter_config.get( 'attach' ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 66e39bb..d17492b 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -323,6 +323,7 @@ def SetBufferContents( buf, lines, modified=False ): def IsCurrent( window, buf ): return vim.current.window == window and vim.current.window.buffer == buf + def ExpandReferencesInObject( obj, mapping, user_choices ): if isinstance( obj, dict ): ExpandReferencesInDict( obj, mapping, user_choices ) @@ -336,6 +337,7 @@ def ExpandReferencesInObject( obj, mapping, user_choices ): return obj + def ExpandReferencesInString( orig_s, mapping, user_choices): s = os.path.expanduser( orig_s ) s = os.path.expandvars( s ) @@ -369,6 +371,7 @@ def ExpandReferencesInString( orig_s, mapping, user_choices): return s + # TODO: Should we just run the substitution on the whole JSON string instead? # That woul dallow expansion in bool and number values, such as ports etc. ? def ExpandReferencesInDict( obj, mapping, user_choices ): From 9be0f43a5fba576124cd194239293d82b3d583f9 Mon Sep 17 00:00:00 2001 From: tinmarino Date: Wed, 15 Jan 2020 10:28:50 -0300 Subject: [PATCH 035/609] Feature: Silent Errors if not connected --- python3/vimspector/debug_session.py | 26 ++++++++++++++++++++++---- python3/vimspector/utils.py | 13 +++++++++---- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 1cf7d52..8eec9f4 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -20,6 +20,7 @@ import os import shlex import subprocess import traceback +import functools import vim from vimspector import ( breakpoints, @@ -252,9 +253,21 @@ class DebugSession( object ): self._StartWithConfiguration( self._configuration, self._adapter ) + def IfConnected( 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=True, error=True ) + return + return fct(self, *args, **kwargs) + return wrapper + + @IfConnected def OnChannelData( self, data ): - if self._connection: - self._connection.OnData( data ) + self._connection.OnData( data ) def OnServerStderr( self, data ): @@ -263,14 +276,15 @@ class DebugSession( object ): self._outputView.Print( 'server', data ) + @IfConnected def OnRequestTimeout( self, timer_id ): - if self._connection: - self._connection.OnRequestTimeout( timer_id ) + self._connection.OnRequestTimeout( timer_id ) def OnChannelClosed( self ): # TODO: Not calld self._connection = None + @IfConnected def Stop( self ): self._logger.debug( "Stop debug adapter with no callback" ) self._StopDebugAdapter() @@ -302,6 +316,7 @@ class DebugSession( object ): # make sure that we're displaying signs in any still-open buffers self._breakpoints.UpdateUI() + @IfConnected def StepOver( self ): if self._stackTraceView.GetCurrentThreadId() is None: return @@ -313,6 +328,7 @@ class DebugSession( object ): }, } ) + @IfConnected def StepInto( self ): if self._stackTraceView.GetCurrentThreadId() is None: return @@ -324,6 +340,7 @@ class DebugSession( object ): }, } ) + @IfConnected def StepOut( self ): if self._stackTraceView.GetCurrentThreadId() is None: return @@ -341,6 +358,7 @@ class DebugSession( object ): else: self.Start() + @IfConnected def Pause( self ): self._stackTraceView.Pause() diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index d17492b..ba930ba 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -228,16 +228,21 @@ def Escape( msg ): return msg.replace( "'", "''" ) -def UserMessage( msg, persist=False ): +def UserMessage( msg, persist=False, error=False): if persist: _logger.warning( 'User Msg: ' + msg ) else: _logger.info( 'User Msg: ' + msg ) - vim.command( 'redraw' ) cmd = 'echom' if persist else 'echo' - for line in msg.split( '\n' ): - vim.command( "{0} '{1}'".format( cmd, Escape( line ) ) ) + vim.command( 'redraw' ) + try: + if error: + vim.command("echohl WarningMsg") + for line in msg.split( '\n' ): + vim.command( "{0} '{1}'".format( cmd, Escape( line ) ) ) + finally: + vim.command('echohl None') if error else None vim.command( 'redraw' ) From dcdab6351648087ad349ea89937c50f937ecc6f4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jan 2020 13:51:03 +0000 Subject: [PATCH 036/609] Remove use of bindeval() as it is not suported in neovim --- autoload/vimspector/internal/channel.vim | 28 ++++++------ autoload/vimspector/internal/job.vim | 54 ++++++++++++------------ python3/vimspector/debug_session.py | 15 +++---- python3/vimspector/utils.py | 14 ++++++ 4 files changed, 62 insertions(+), 49 deletions(-) diff --git a/autoload/vimspector/internal/channel.vim b/autoload/vimspector/internal/channel.vim index d0cb7d2..3e0d298 100644 --- a/autoload/vimspector/internal/channel.vim +++ b/autoload/vimspector/internal/channel.vim @@ -37,22 +37,11 @@ function! s:_OnClose( channel ) abort py3 _vimspector_session.OnServerExit( 0 ) endfunction -function! s:_Send( msg ) abort - call ch_sendraw( s:ch, a:msg ) - return 1 -endfunction - -function! vimspector#internal#channel#Timeout( id ) abort - py3 << EOF -_vimspector_session.OnRequestTimeout( vim.eval( 'a:id' ) ) -EOF -endfunction - function! vimspector#internal#channel#StartDebugSession( config ) abort if exists( 's:ch' ) echo 'Channel is already running' - return v:none + return v:false endif let l:addr = 'localhost:' . a:config[ 'port' ] @@ -70,10 +59,21 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort if ch_status( s:ch ) !=# 'open' echom 'Unable to connect to debug adapter' redraw - return v:none + return v:false endif - return funcref( 's:_Send' ) + return v:true +endfunction + +function! vimspector#internal#channel#Send( msg ) abort + call ch_sendraw( s:ch, a:msg ) + return 1 +endfunction + +function! vimspector#internal#channel#Timeout( id ) abort + py3 << EOF +_vimspector_session.OnRequestTimeout( vim.eval( 'a:id' ) ) +EOF endfunction function! vimspector#internal#channel#StopDebugSession() abort diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index 26ba117..530acb3 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -39,35 +39,11 @@ function! s:_OnClose( channel ) abort redraw endfunction -function! s:_Send( msg ) abort - if ! exists( 's:job' ) - echom "Can't send message: Job was not initialised correctly" - redraw - return 0 - endif - - if job_status( s:job ) !=# 'run' - echom "Can't send message: Job is not running" - redraw - return 0 - endif - - let ch = job_getchannel( s:job ) - if ch ==# 'channel fail' - echom 'Channel was closed unexpectedly!' - redraw - return 0 - endif - - call ch_sendraw( ch, a:msg ) - return 1 -endfunction - function! vimspector#internal#job#StartDebugSession( config ) abort if exists( 's:job' ) echom 'Not starging: Job is already running' redraw - return v:none + return v:false endif let s:job = job_start( a:config[ 'command' ], @@ -91,10 +67,34 @@ function! vimspector#internal#job#StartDebugSession( config ) abort if job_status( s:job ) !=# 'run' echom 'Unable to start job, status is: ' . job_status( s:job ) redraw - return v:none + return v:false endif - return funcref( 's:_Send' ) + return v:true +endfunction + +function! vimspector#internal#job#Send( msg ) abort + if ! exists( 's:job' ) + echom "Can't send message: Job was not initialised correctly" + redraw + return 0 + endif + + if job_status( s:job ) !=# 'run' + echom "Can't send message: Job is not running" + redraw + return 0 + endif + + let ch = job_getchannel( s:job ) + if ch ==# 'channel fail' + echom 'Channel was closed unexpectedly!' + redraw + return 0 + endif + + call ch_sendraw( ch, a:msg ) + return 1 endfunction function! vimspector#internal#job#StopDebugSession() abort diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 8eec9f4..edaf75c 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -492,19 +492,18 @@ class DebugSession( object ): self._adapter[ 'cwd' ] = os.getcwd() vim.vars[ '_vimspector_adapter_spec' ] = self._adapter - channel_send_func = vim.bindeval( - "vimspector#internal#{}#StartDebugSession( " - " g:_vimspector_adapter_spec " - ")".format( self._connection_type ) ) - - if channel_send_func is None: + if not vim.eval( "vimspector#internal#{}#StartDebugSession( " + " g:_vimspector_adapter_spec " + ")".format( self._connection_type ) ): self._logger.error( "Unable to start debug server" ) else: self._connection = debug_adapter_connection.DebugAdapterConnection( self, - channel_send_func ) + lambda msg: utils.Call( + "vimspector#internal#{}#Send".format( self._connection_type ), + msg ) ) - self._logger.info( 'Debug Adapter Started' ) + self._logger.info( 'Debug Adapter Started' ) def _StopDebugAdapter( self, callback = None ): def handler( *args ): diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index ba930ba..6fea1c5 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -449,3 +449,17 @@ def ToUnicode( b ): if isinstance( b, bytes ): return b.decode( 'utf-8' ) return b + + +# Call a vimscript function with suplied arguments. +def Call( vimscript_function, *args ): + call = vimscript_function + '(' + for index, arg in enumerate( args ): + arg_name = 'vimspector_internal_arg_{}'.format( index ) + vim.vars[ arg_name ] = arg + call += 'g:' + arg_name + if index: + call += ',' + + call += ')' + vim.eval( call ) From d1e3b648d32a07e8fd2dc210267f44b85937174f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jan 2020 14:00:05 +0000 Subject: [PATCH 037/609] Use eval for lists of buffers too --- python3/vimspector/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 6fea1c5..79c8c67 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -57,19 +57,19 @@ def OpenFileInCurrentWindow( file_name ): def SetUpCommandBuffer( cmd, name ): - bufs = vim.bindeval( + bufs = vim.eval( 'vimspector#internal#job#StartCommandWithLog( {}, "{}" )'.format( json.dumps( cmd ), name ) ) if bufs is None: raise RuntimeError( "Unable to start job {}: {}".format( cmd, name ) ) - elif not all( b > 0 for b in bufs ): + elif not all( int( b ) > 0 for b in bufs ): raise RuntimeError( "Unable to get all streams for job {}: {}".format( name, cmd ) ) - return [ vim.buffers[ b ] for b in bufs ] + return [ vim.buffers[ int( b ) ] for b in bufs ] def CleanUpCommand( name ): From ee1bb009ea1e19afe1a3102fadf40830c5aa892b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jan 2020 14:34:13 +0000 Subject: [PATCH 038/609] Wrap the terminal API in vimscript layer --- autoload/vimspector/internal/job.vim | 2 +- autoload/vimspector/internal/term.vim | 37 +++++++++++++++++++++++++++ python3/vimspector/code.py | 14 ++++------ python3/vimspector/debug_session.py | 4 +-- python3/vimspector/utils.py | 8 +++--- 5 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 autoload/vimspector/internal/term.vim diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index 530acb3..dd76652 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -30,7 +30,7 @@ endfunction function! s:_OnExit( channel, status ) abort echom 'Channel exit with status ' . a:status redraw - unlet s:job + unlet s:job py3 _vimspector_session.OnServerExit( vim.eval( 'a:status' ) ) endfunction diff --git a/autoload/vimspector/internal/term.vim b/autoload/vimspector/internal/term.vim new file mode 100644 index 0000000..5a570fc --- /dev/null +++ b/autoload/vimspector/internal/term.vim @@ -0,0 +1,37 @@ +" vimspector - A multi-language debugging system for Vim +" Copyright 2018 Ben Jackson +" +" Licensed under the Apache License, Version 2.0 (the "License"); +" you may not use this file except in compliance with the License. +" You may obtain a copy of the License at +" +" http://www.apache.org/licenses/LICENSE-2.0 +" +" Unless required by applicable law or agreed to in writing, software +" distributed under the License is distributed on an "AS IS" BASIS, +" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +" See the License for the specific language governing permissions and +" limitations under the License. + + +" Boilerplate {{{ +let s:save_cpo = &cpoptions +set cpoptions&vim +" }}} + +function! vimspector#internal#term#Start( cmd, opts ) abort + return term_start( a:cmd, a:opts ) +endfunction + +function! vimspector#internal#term#IsFinished( bufno ) abort + return index( split( term_getstatus( a:bufno ), ',' ), 'finished' ) >= 0 +endfunction + +function! vimspector#internal#term#GetPID( bufno ) abort + return job_info( term_getjob( a:bufno ) ).process +endfunction + +" Boilerplate {{{ +let &cpoptions=s:save_cpo +unlet s:save_cpo +" }}} diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 509008d..6893b85 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -214,8 +214,8 @@ class CodeView( object ): if self._terminal_window is not None: assert self._terminal_buffer_number if ( self._terminal_window.buffer.number == self._terminal_buffer_number - and 'finished' in vim.eval( 'term_getstatus( {} )'.format( - self._terminal_buffer_number ) ) ): + and int( utils.Call( 'vimspector#internal#term#IsFinished', + self._terminal_buffer_number ) ) ): window_for_start = self._terminal_window options[ 'curwin' ] = 1 @@ -224,13 +224,9 @@ class CodeView( object ): with utils.TemporaryVimOptions( { 'splitright': True, 'equalalways': False } ): with utils.LetCurrentWindow( window_for_start ): - # TODO/FIXME: Do something about closing this when we reset ? - vim_cmd = 'term_start( {}, {} )'.format( json.dumps( args ), - json.dumps( options ) ) - - self._logger.debug( 'Start terminal: {}'.format( vim_cmd ) ) - - buffer_number = int( vim.eval( vim_cmd ) ) + buffer_number = int( utils.Call( 'vimspector#internal#term#Start', + args, + 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 edaf75c..fc85c85 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -798,8 +798,8 @@ class DebugSession( object ): buffer_number = self._codeView.LaunchTerminal( params ) response = { - 'processId': vim.eval( 'job_info( term_getjob( {} ) )' - '.process'.format( buffer_number ) ) + 'processId': int( utils.Call( 'vimspector#internal#term#GetPID', + buffer_number ) ) } self._connection.DoResponse( message, None, response ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 79c8c67..850a759 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -455,11 +455,13 @@ def ToUnicode( b ): def Call( vimscript_function, *args ): call = vimscript_function + '(' for index, arg in enumerate( args ): + if index > 0: + call += ', ' + arg_name = 'vimspector_internal_arg_{}'.format( index ) vim.vars[ arg_name ] = arg call += 'g:' + arg_name - if index: - call += ',' call += ')' - vim.eval( call ) + _logger.debug( 'Calling: {}'.format( call ) ) + return vim.eval( call ) From 5aa33c19f7769115caa5168cd3ece970f407b0ec Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jan 2020 17:23:42 +0000 Subject: [PATCH 039/609] Remove unused ForceRead function --- autoload/vimspector/internal/channel.vim | 9 --------- autoload/vimspector/internal/job.vim | 9 --------- 2 files changed, 18 deletions(-) diff --git a/autoload/vimspector/internal/channel.vim b/autoload/vimspector/internal/channel.vim index 3e0d298..9825c5d 100644 --- a/autoload/vimspector/internal/channel.vim +++ b/autoload/vimspector/internal/channel.vim @@ -97,15 +97,6 @@ function! vimspector#internal#channel#Reset() abort endif endfunction -function! vimspector#internal#channel#ForceRead() abort - if exists( 's:ch' ) - let data = ch_readraw( s:ch, { 'timeout': 1000 } ) - if data !=# '' - call s:_OnServerData( s:ch, data ) - endif - endif -endfunction - " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index dd76652..1031f1d 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -115,15 +115,6 @@ function! vimspector#internal#job#Reset() abort call vimspector#internal#job#StopDebugSession() endfunction -function! vimspector#internal#job#ForceRead() abort - if exists( 's:job' ) - let data = ch_readraw( job_getchannel( s:job ), { 'timeout': 1000 } ) - if data !=# '' - call s:_OnServerData( job_getchannel( s:job ), data ) - endif - endif -endfunction - function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort if ! exists( 's:commands' ) let s:commands = {} From 18627b9244afd53221e7409a06bb1061058e4dc2 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jan 2020 17:26:38 +0000 Subject: [PATCH 040/609] Add a way to have multiple vim API layers --- autoload/vimspector/internal/job.vim | 2 +- autoload/vimspector/internal/state.vim | 3 ++- python3/vimspector/code.py | 14 +++++++++----- python3/vimspector/debug_session.py | 19 +++++++++++++------ python3/vimspector/output.py | 7 ++++--- python3/vimspector/utils.py | 10 ++++++---- 6 files changed, 35 insertions(+), 20 deletions(-) diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index 1031f1d..4ed9ea8 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -127,7 +127,7 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort let l:index = len( s:commands[ a:category ] ) call add( s:commands[ a:category ], job_start( - \ a:cmd, + \ a:cmd, \ { \ 'out_io': 'buffer', \ 'in_io': 'null', diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index 6478506..f268164 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -20,9 +20,10 @@ set cpoptions&vim " }}} function! vimspector#internal#state#Reset() abort + let prefix = '' py3 << EOF from vimspector import debug_session -_vimspector_session = debug_session.DebugSession() +_vimspector_session = debug_session.DebugSession( vim.eval( 'prefix' ) ) EOF endfunction diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 6893b85..b1b7698 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -22,8 +22,9 @@ from vimspector import utils class CodeView( object ): - def __init__( self, window ): + def __init__( self, window, api_prefix ): self._window = window + self._api_prefix = api_prefix self._terminal_window = None self._terminal_buffer_number = None @@ -214,7 +215,8 @@ class CodeView( object ): if self._terminal_window is not None: assert self._terminal_buffer_number if ( self._terminal_window.buffer.number == self._terminal_buffer_number - and int( utils.Call( 'vimspector#internal#term#IsFinished', + and int( utils.Call( 'vimspector#internal#{}term#IsFinished'.format( + self._api_prefix ), self._terminal_buffer_number ) ) ): window_for_start = self._terminal_window options[ 'curwin' ] = 1 @@ -224,9 +226,11 @@ class CodeView( object ): with utils.TemporaryVimOptions( { 'splitright': True, 'equalalways': False } ): with utils.LetCurrentWindow( window_for_start ): - buffer_number = int( utils.Call( 'vimspector#internal#term#Start', - args, - options ) ) + 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: diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index fc85c85..11e602a 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -41,11 +41,14 @@ USER_CHOICES = {} class DebugSession( object ): - def __init__( self ): + def __init__( self, api_prefix ): self._logger = logging.getLogger( __name__ ) utils.SetUpLogging( self._logger ) + self._api_prefix = api_prefix + self._logger.info( "**** INITIALISING NEW VIMSPECTOR SESSION ****" ) + self._logger.info( "API is: {}".format( api_prefix ) ) self._logger.info( 'VIMSPECTOR_HOME = %s', VIMSPECTOR_HOME ) self._logger.info( 'gadgetDir = %s', install.GetGadgetDir( VIMSPECTOR_HOME, @@ -405,7 +408,7 @@ class DebugSession( object ): self._uiTab = vim.current.tabpage # Code window - self._codeView = code.CodeView( vim.current.window ) + self._codeView = code.CodeView( vim.current.window, self._api_prefix ) # Call stack with utils.TemporaryVimOptions( { 'splitright': False, @@ -441,7 +444,8 @@ class DebugSession( object ): vim.command( '10spl' ) vim.command( 'enew' ) self._outputView = output.OutputView( self._connection, - vim.current.window ) + vim.current.window, + self._api_prefix ) def ClearCurrentFrame( self ): self.SetCurrentFrame( None ) @@ -482,6 +486,8 @@ class DebugSession( object ): port = utils.AskForInput( 'Enter port to connect to: ' ) self._adapter[ 'port' ] = port + self._connection_type = self._api_prefix + self._connection_type + # TODO: Do we actually need to copy and update or does Vim do that? env = os.environ.copy() if 'env' in self._adapter: @@ -795,11 +801,12 @@ class DebugSession( object ): self._logger.debug( 'Defaulting working directory to %s', params[ 'cwd' ] ) - buffer_number = self._codeView.LaunchTerminal( params ) + term_id = self._codeView.LaunchTerminal( params ) response = { - 'processId': int( utils.Call( 'vimspector#internal#term#GetPID', - buffer_number ) ) + 'processId': int( utils.Call( + 'vimspector#internal#{}term#GetPID'.format( self._api_prefix ), + term_id ) ) } self._connection.DoResponse( message, None, response ) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index d4cf1af..46c2583 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -40,10 +40,11 @@ def CategoryToBuffer( category ): class OutputView( object ): - def __init__( self, connection, window ): + def __init__( self, connection, window, api_prefix ): self._window = window self._connection = connection self._buffers = {} + self._api_prefix = api_prefix for b in set( BUFFER_MAP.values() ): self._CreateBuffer( b ) @@ -95,7 +96,7 @@ class OutputView( object ): def Clear( self ): for category, tab_buffer in self._buffers.items(): if tab_buffer.is_job: - utils.CleanUpCommand( category ) + utils.CleanUpCommand( category, self._api_prefix ) try: vim.command( 'bdelete! {0}'.format( tab_buffer.buf.number ) ) except vim.error as e: @@ -161,7 +162,7 @@ class OutputView( object ): cmd = [ 'tail', '-F', '-n', '+1', '--', file_name ] if cmd is not None: - out, err = utils.SetUpCommandBuffer( cmd, category ) + out, err = utils.SetUpCommandBuffer( cmd, category, self._api_prefix ) self._buffers[ category + '-out' ] = TabBuffer( out, len( self._buffers ) ) self._buffers[ category + '-out' ].is_job = True diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 850a759..809f393 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -56,9 +56,10 @@ def OpenFileInCurrentWindow( file_name ): return vim.buffers[ buffer_number ] -def SetUpCommandBuffer( cmd, name ): +def SetUpCommandBuffer( cmd, name, api_prefix ): bufs = vim.eval( - 'vimspector#internal#job#StartCommandWithLog( {}, "{}" )'.format( + 'vimspector#internal#{}job#StartCommandWithLog( {}, "{}" )'.format( + api_prefix, json.dumps( cmd ), name ) ) @@ -72,8 +73,9 @@ def SetUpCommandBuffer( cmd, name ): return [ vim.buffers[ int( b ) ] for b in bufs ] -def CleanUpCommand( name ): - return vim.eval( 'vimspector#internal#job#CleanUpCommand( "{}" )'.format( +def CleanUpCommand( name, api_prefix ): + return vim.eval( 'vimspector#internal#{}job#CleanUpCommand( "{}" )'.format( + api_prefix, name ) ) From 29c26996fb7da6d47994789db9a4f8dfe88de7ef Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jan 2020 17:27:08 +0000 Subject: [PATCH 041/609] Add neovim job/terminal APIs --- autoload/vimspector/internal/neojob.vim | 163 +++++++++++++++++++++++ autoload/vimspector/internal/neoterm.vim | 67 ++++++++++ autoload/vimspector/internal/state.vim | 5 + python3/vimspector/output.py | 2 +- python3/vimspector/utils.py | 2 +- 5 files changed, 237 insertions(+), 2 deletions(-) create mode 100644 autoload/vimspector/internal/neojob.vim create mode 100644 autoload/vimspector/internal/neoterm.vim diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim new file mode 100644 index 0000000..64ef934 --- /dev/null +++ b/autoload/vimspector/internal/neojob.vim @@ -0,0 +1,163 @@ +" vimspector - A multi-language debugging system for Vim +" Copyright 2018 Ben Jackson +" +" Licensed under the Apache License, Version 2.0 (the "License"); +" you may not use this file except in compliance with the License. +" You may obtain a copy of the License at +" +" http://www.apache.org/licenses/LICENSE-2.0 +" +" Unless required by applicable law or agreed to in writing, software +" distributed under the License is distributed on an "AS IS" BASIS, +" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +" See the License for the specific language governing permissions and +" limitations under the License. + + +" Boilerplate {{{ +let s:save_cpo = &cpoptions +set cpoptions&vim +" }}} + + + +function! s:_OnEvent( chan_id, data, event ) abort + " In neovim, the data argument is a list. + if a:event ==# 'stdout' + py3 _vimspector_session.OnChannelData( '\n'.join( vim.eval( 'a:data' ) ) ) + elseif a:event ==# 'stderr' + py3 _vimspector_session.OnServerStderr( '\n'.join( vim.eval( 'a:data' ) ) ) + elseif a:event ==# 'exit' + echom 'Channel exit with status ' . a:data + redraw + unlet s:job + py3 _vimspector_session.OnServerExit( vim.eval( 'a:data' ) ) + endif +endfunction + +function! vimspector#internal#neojob#StartDebugSession( config ) abort + if exists( 's:job' ) + echom 'Not starging: Job is already running' + redraw + return v:false + endif + + let s:job = jobstart( a:config[ 'command' ], + \ { + \ 'on_stdout': funcref( 's:_OnEvent' ), + \ 'on_stderr': funcref( 's:_OnEvent' ), + \ 'on_exit': funcref( 's:_OnEvent' ), + \ 'cwd': a:config[ 'cwd' ], + \ } + \ ) + + " FIXME: Missing in neovim 0.4. But in master: + " \ 'env': a:config[ 'env' ], + " + + " FIXME: error handling ? + return v:true +endfunction + +function! s:JobIsRunning( job ) abort + return jobwait( [ s:job ], 0 )[ 0 ] == -1 +endfunction + +function! vimspector#internal#neojob#Send( msg ) abort + if ! exists( 's:job' ) + echom "Can't send message: Job was not initialised correctly" + redraw + return 0 + endif + + if !s:JobIsRunning( s:job ) + echom "Can't send message: Job is not running" + redraw + return 0 + endif + + call chansend( s:job, a:msg ) + return 1 +endfunction + +function! vimspector#internal#neojob#StopDebugSession() abort + if !exists( 's:job' ) + echom "Not stopping session: Job doesn't exist" + redraw + return + endif + + if s:JobIsRunning( s:job ) + echom 'Terminating job' + redraw + call jobstop( s:job ) + endif +endfunction + +function! vimspector#internal#neojob#Reset() abort + call vimspector#internal#neojob#StopDebugSession() +endfunction + +function! s:_OnCommandEvent( category, id, data, event ) abort + if a:data == [''] + return + endif + if a:event ==# 'stdout' + let buffer = s:commands[ a:category ][ a:id ].stdout + elseif a:event ==# 'stderr' + let buffer = s:commands[ a:category ][ a:id ].stderr + endif + + let last_line_list = getbufline( buffer, '$' ) + if len( last_line_list ) == 0 + let last_line = '' + else + let last_line = last_line_list[ 0 ] + endif + + call setbufline( buffer, '$', last_line . a:data[ 0 ] ) + call appendbufline( buffer, '$', a:data[ 1: ] ) +endfunction + +let s:commands = {} + +function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort + if ! has_key( s:commands, a:category ) + let s:commands[ a:category ] = {} + endif + + let stdout_buf = bufnr( '_vimspector_log_' . a:category . '_out', 1 ) + let stderr_buf = bufnr( '_vimspector_log_' . a:category . '_err', 1 ) + + let id = jobstart(a:cmd, + \ { + \ 'on_stdout': funcref( 's:_OnCommandEvent', + \ [ a:category ] ), + \ 'on_stderr': funcref( 's:_OnCommandEvent', + \ [ a:category ] ) + \ } ) + + let s:commands[ a:category ][ id ] = { + \ 'stdout': stdout_buf, + \ 'stderr': stderr_buf + \ } + + return [ stdout_buf, stderr_buf ] +endfunction + +function! vimspector#internal#neojob#CleanUpCommand( category ) abort + if ! has_key( s:commands, a:category ) + return + endif + + for id in keys( s:commands[ a:category ] ) + call jobstop( id ) + call jobwait( id ) + endfor + unlet! s:commands[ a:category ] +endfunction + +" Boilerplate {{{ +let &cpoptions=s:save_cpo +unlet s:save_cpo +" }}} diff --git a/autoload/vimspector/internal/neoterm.vim b/autoload/vimspector/internal/neoterm.vim new file mode 100644 index 0000000..ab71b2f --- /dev/null +++ b/autoload/vimspector/internal/neoterm.vim @@ -0,0 +1,67 @@ +" vimspector - A multi-language debugging system for Vim +" Copyright 2018 Ben Jackson +" +" Licensed under the Apache License, Version 2.0 (the "License"); +" you may not use this file except in compliance with the License. +" You may obtain a copy of the License at +" +" http://www.apache.org/licenses/LICENSE-2.0 +" +" Unless required by applicable law or agreed to in writing, software +" distributed under the License is distributed on an "AS IS" BASIS, +" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +" See the License for the specific language governing permissions and +" limitations under the License. + + +" Boilerplate {{{ +let s:save_cpo = &cpoptions +set cpoptions&vim +" }}} + +" Ids are unique throughtout the life of neovim, but obviously buffer numbers +" aren't +" +" FIXME: Tidy this map when buffers are closed ? +let s:buffer_to_id = {} + +function! vimspector#internal#neoterm#Start( cmd, opts ) abort + if ! get( a:opts, 'curwin', 0 ) + if get( a:opts, 'vertical', 0 ) + vsplit + else + split + endif + endif + + " FIXME: 'env' doesn't work + let id = termopen( a:cmd, { 'cwd': a:opts[ 'cwd' ] } ) + let bufnr = bufnr() + let s:buffer_to_id[ bufnr ] = id + return bufnr +endfunction + +function! s:JobIsRunning( job ) abort + return jobwait( [ a:job ], 0 )[ 0 ] == -1 +endfunction + +function! vimspector#internal#neoterm#IsFinished( bufno ) abort + if !has_key( s:buffer_to_id, a:bufno ) + return v:true + endif + + return !s:JobIsRunning( s:buffer_to_id[ a:bufno ] ) +endfunction + +function! vimspector#internal#neoterm#GetPID( bufno ) abort + if !has_key( s:buffer_to_id, a:bufno ) + return -1 + endif + + return jobpid( s:buffer_to_id[ a:bufno ] ) +endfunction + +" Boilerplate {{{ +let &cpoptions=s:save_cpo +unlet s:save_cpo +" }}} diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index f268164..11ed003 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -19,8 +19,13 @@ let s:save_cpo = &cpoptions set cpoptions&vim " }}} +let s:is_neovim = has( 'nvim' ) + function! vimspector#internal#state#Reset() abort let prefix = '' + if s:is_neovim + let prefix='neo' + endif py3 << EOF from vimspector import debug_session _vimspector_session = debug_session.DebugSession( vim.eval( 'prefix' ) ) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 46c2583..dd0c293 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -101,7 +101,7 @@ class OutputView( object ): vim.command( 'bdelete! {0}'.format( tab_buffer.buf.number ) ) except vim.error as e: # FIXME: For now just ignore the "no buffers were deleted" error - if 'E516' not in e: + if 'E516' not in str( e ): raise self._buffers = {} diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 809f393..a9f55df 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -104,7 +104,7 @@ def SetUpHiddenBuffer( buf, name ): def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ): # This feature is _super_ new, so only enable when available if not int( vim.eval( "exists( '*prompt_setprompt' )" ) ): - return SetUpScratchBuffer( buf, name ) + return SetUpHiddenBuffer( buf, name ) buf.options[ 'buftype' ] = 'prompt' buf.options[ 'swapfile' ] = False From 04bb03da0ab1ee0486cd1ac60df900f33ef2c131 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jan 2020 21:06:57 +0000 Subject: [PATCH 042/609] Add channel support for neovim --- autoload/vimspector/internal/channel.vim | 5 -- autoload/vimspector/internal/neochannel.vim | 85 +++++++++++++++++++++ autoload/vimspector/internal/neojob.vim | 2 +- 3 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 autoload/vimspector/internal/neochannel.vim diff --git a/autoload/vimspector/internal/channel.vim b/autoload/vimspector/internal/channel.vim index 9825c5d..4c3d1d9 100644 --- a/autoload/vimspector/internal/channel.vim +++ b/autoload/vimspector/internal/channel.vim @@ -25,11 +25,6 @@ _vimspector_session.OnChannelData( vim.eval( 'a:data' ) ) EOF endfunction -function! s:_OnServerError( channel, data ) abort - echom 'Channel received error: ' . a:data - redraw -endfunction - function! s:_OnClose( channel ) abort echom 'Channel closed' redraw diff --git a/autoload/vimspector/internal/neochannel.vim b/autoload/vimspector/internal/neochannel.vim new file mode 100644 index 0000000..22bc5d2 --- /dev/null +++ b/autoload/vimspector/internal/neochannel.vim @@ -0,0 +1,85 @@ +" vimspector - A multi-language debugging system for Vim +" Copyright 2020 Ben Jackson +" +" Licensed under the Apache License, Version 2.0 (the "License"); +" you may not use this file except in compliance with the License. +" You may obtain a copy of the License at +" +" http://www.apache.org/licenses/LICENSE-2.0 +" +" Unless required by applicable law or agreed to in writing, software +" distributed under the License is distributed on an "AS IS" BASIS, +" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +" See the License for the specific language governing permissions and +" limitations under the License. + + +" Boilerplate {{{ +let s:save_cpo = &cpoptions +set cpoptions&vim +" }}} + + + +function! s:_OnEvent( chan_id, data, event ) abort + if a:data == [''] + echom 'Channel closed' + redraw + unlet s:ch + py3 _vimspector_session.OnServerExit( 0 ) + else + py3 _vimspector_session.OnChannelData( '\n'.join( vim.eval( 'a:data' ) ) ) + endif +endfunction + +function! vimspector#internal#neochannel#StartDebugSession( config ) abort + if exists( 's:ch' ) + echom 'Not starging: Channel is already running' + redraw + return v:false + endif + + let addr = 'localhost:' . a:config[ 'port' ] + + let s:ch = sockconnect( 'tcp', addr, { 'on_data': funcref( 's:_OnEvent' ) } ) + if s:ch <= 0 + unlet s:ch + return v:false + endif + + return v:true +endfunction + +function! vimspector#internal#neochannel#Send( msg ) abort + if ! exists( 's:ch' ) + echom "Can't send message: Channel was not initialised correctly" + redraw + return 0 + endif + + call chansend( s:ch, a:msg ) + return 1 +endfunction + +function! vimspector#internal#neochannel#StopDebugSession() abort + if !exists( 's:ch' ) + echom "Not stopping session: Channel doesn't exist" + redraw + return + endif + + call chanclose( s:ch ) + " It doesn't look like we get a callback after chanclos. Who knows if we will + " subsequently receive data callbacks. + call s:_OnEvent( s:ch, [ '' ], 'data' ) +endfunction + +function! vimspector#internal#neochannel#Reset() abort + call vimspector#internal#neochannel#StopDebugSession() +endfunction + +" Boilerplate {{{ +let &cpoptions=s:save_cpo +unlet s:save_cpo +" }}} + diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index 64ef934..c88a319 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -1,5 +1,5 @@ " vimspector - A multi-language debugging system for Vim -" Copyright 2018 Ben Jackson +" 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. From 7456c26c2a833abe0cf29dd4ede071cc7222055b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jan 2020 20:53:59 +0000 Subject: [PATCH 043/609] Add some :commands for basic usages --- autoload/vimspector.vim | 13 +++++ autoload/vimspector/internal/neojob.vim | 4 +- plugin/vimspector.vim | 65 +++++++++++++++++-------- python3/vimspector/debug_session.py | 3 ++ python3/vimspector/output.py | 3 ++ 5 files changed, 67 insertions(+), 21 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 0250b90..7e71695 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -96,6 +96,11 @@ function! vimspector#AddWatchPrompt( expr ) abort call vimspector#AddWatch( a:expr ) endfunction +function! vimspector#Evaluate( expr ) abort + py3 _vimspector_session.ShowOutput( 'Console' ) + py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) ) +endfunction + function! vimspector#EvaluateConsole( expr ) abort stopinsert setlocal nomodified @@ -110,6 +115,14 @@ function! vimspector#ListBreakpoints() abort py3 _vimspector_session.ListBreakpoints() endfunction +function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort + return py3eval( '_vimspector_session.GetOutputBuffers()' ) +endfunction + +function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort + return [] +endfunction + " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index c88a319..3c5e3b1 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -126,8 +126,8 @@ function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort let s:commands[ a:category ] = {} endif - let stdout_buf = bufnr( '_vimspector_log_' . a:category . '_out', 1 ) - let stderr_buf = bufnr( '_vimspector_log_' . a:category . '_err', 1 ) + let stdout_buf = bufnr( '_vimspector_log_' . a:category . '_out', v:true ) + let stderr_buf = bufnr( '_vimspector_log_' . a:category . '_err', v:true ) let id = jobstart(a:cmd, \ { diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 13ffa3b..10eb374 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -26,6 +26,7 @@ if exists( 'g:loaded_vimpector' ) call s:restore_cpo() finish endif +"}}} " TODO: " - Check Vim version (for jobs) @@ -36,28 +37,54 @@ let g:loaded_vimpector = 1 let s:mappings = get( g:, 'vimspector_enable_mappings', '' ) +nnoremap VimspectorContinue :call vimspector#Continue() +nnoremap VimspectorStop :call vimspector#Stop() +nnoremap VimspectorRestart :call vimspector#Restart() +nnoremap VimspectorPause :call vimspector#Pause() +nnoremap VimspectorToggleBreakpoint + \ :call vimspector#ToggleBreakpoint() +nnoremap VimspectorAddFunctionBreakpoint + \ :call vimspector#AddFunctionBreakpoint( expand( '' ) ) +nnoremap VimspectorStopOver :call vimspector#StepOver() +nnoremap VimspectorStepInto :call vimspector#StepInto() +nnoremap VimspectorStepOut :call vimspector#StepOut() + if s:mappings ==# 'VISUAL_STUDIO' - nnoremap :call vimspector#Continue() - nnoremap :call vimspector#Stop() - nnoremap :call vimspector#Restart() - nnoremap :call vimspector#Pause() - nnoremap :call vimspector#ToggleBreakpoint() - nnoremap :call vimspector#AddFunctionBreakpoint( expand( '' ) ) - nnoremap :call vimspector#StepOver() - nnoremap :call vimspector#StepInto() - nnoremap :call vimspector#StepOut() + nmap VimspectorContinue + nmap VimspectorStop + nmap VimspectorRestart + nmap VimspectorPause + nmap VimspectorToggleBreakpoint + nmap VimspectorAddFunctionBreakpoint + nmap VimspectorStepOver + nmap VimspectorStepInto + nmap VimspectorStepOut elseif s:mappings ==# 'HUMAN' - nnoremap :call vimspector#Continue() - nnoremap :call vimspector#Stop() - nnoremap :call vimspector#Restart() - nnoremap :call vimspector#Pause() - nnoremap :call vimspector#ToggleBreakpoint() - nnoremap :call vimspector#AddFunctionBreakpoint( expand( '' ) ) - nnoremap :call vimspector#StepOver() - nnoremap :call vimspector#StepInto() - nnoremap :call vimspector#StepOut() + nmap VimspectorContinue + nmap VimspectorStop + nmap VimspectorRestart + nmap VimspectorPause + nmap VimspectorToggleBreakpoint + nmap VimspectorAddFunctionBreakpoint + nmap VimspectorStepOver + nmap VimspectorStepInto + nmap VimspectorStepOut endif -"}}} +command! -bar -nargs=1 -complete=customlist,vimspector#CompleteExpr + \ VimspectorWatch + \ call vimspector#AddWatch( ) +command! -bar -nargs=1 -complete=customlist,vimspector#CompleteOutput + \ VimspectorShowOutput + \ call vimspector#ShowOutput( ) +command! -bar -nargs=1 -complete=customlist,vimspector#CompleteExpr + \ VimspectorEval + \ call vimspector#Evaluate( ) +command! -bar + \ VimspectorReset + \ call vimspector#Reset() +" boilerplate {{{ call s:restore_cpo() +" }}} + diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 11e602a..a4d801b 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -403,6 +403,9 @@ class DebugSession( object ): def ShowOutput( self, category ): self._outputView.ShowOutput( category ) + def GetOutputBuffers( self ): + return self._outputView.GetCategories() + def _SetUpUI( self ): vim.command( 'tabnew' ) self._uiTab = vim.current.tabpage diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index dd0c293..fb90929 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -206,3 +206,6 @@ class OutputView( object ): tab_buffer.index, utils.Escape( category ), '*' if tab_buffer.flag else '' ) ) + + def GetCategories( self ): + return [ category for category in self._buffers.keys() ] From a00e6a19d6bf4bf80aaa81f21c783a8337dba7d3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jan 2020 21:59:25 +0000 Subject: [PATCH 044/609] Fix output buffers. bufnr creates an _unloaded_ buffer, so we have to load it --- autoload/vimspector/internal/neojob.vim | 39 +++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index 3c5e3b1..f8a687a 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -102,23 +102,52 @@ function! s:_OnCommandEvent( category, id, data, event ) abort if a:data == [''] return endif + if a:event ==# 'stdout' let buffer = s:commands[ a:category ][ a:id ].stdout elseif a:event ==# 'stderr' let buffer = s:commands[ a:category ][ a:id ].stderr endif + call bufload( buffer ) + let last_line_list = getbufline( buffer, '$' ) + if len( last_line_list ) == 0 let last_line = '' else let last_line = last_line_list[ 0 ] endif - call setbufline( buffer, '$', last_line . a:data[ 0 ] ) - call appendbufline( buffer, '$', a:data[ 1: ] ) + + call s:MakeBufferWritable( buffer ) + try + call setbufline( buffer, '$', last_line . a:data[ 0 ] ) + call appendbufline( buffer, '$', a:data[ 1: ] ) + finally + call s:MakeBufferReadOnly( buffer ) + call setbufvar( buffer, '&modified', 0 ) + endtry + endfunction +function! s:SetUpHiddenBuffer( buffer ) abort + call setbufvar( a:buffer, '&hidden', 1 ) + call setbufvar( a:buffer, '&bufhidden', 'hide' ) + call s:MakeBufferReadOnly( a:buffer ) +endfunction + +function! s:MakeBufferReadOnly( buffer ) abort + call setbufvar( a:buffer, '&modifiable', 0 ) + call setbufvar( a:buffer, '&readonly', 1 ) +endfunction + +function! s:MakeBufferWritable( buffer ) abort + call setbufvar( a:buffer, '&readonly', 0 ) + call setbufvar( a:buffer, '&modifiable', 1 ) +endfunction + + let s:commands = {} function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort @@ -129,6 +158,12 @@ function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort let stdout_buf = bufnr( '_vimspector_log_' . a:category . '_out', v:true ) let stderr_buf = bufnr( '_vimspector_log_' . a:category . '_err', v:true ) + " FIXME: This largely duplicates the same stuff in the python layer, but we + " don't want to potentially mess up Vim behaviour where the job output is + " attached to a buffer set up by Vim. So we sort o mimic that here. + call s:SetUpHiddenBuffer( stdout_buf ) + call s:SetUpHiddenBuffer( stderr_buf ) + let id = jobstart(a:cmd, \ { \ 'on_stdout': funcref( 's:_OnCommandEvent', From c898eb47b2c620eacd04406d94e22df81894dbc8 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jan 2020 22:36:30 +0000 Subject: [PATCH 045/609] Fix filtering VimspectorShowOutput --- autoload/vimspector.vim | 3 ++- plugin/vimspector.vim | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 7e71695..c3ead44 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -116,7 +116,8 @@ function! vimspector#ListBreakpoints() abort endfunction function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort - return py3eval( '_vimspector_session.GetOutputBuffers()' ) + let buffers = py3eval( '_vimspector_session.GetOutputBuffers()' ) + return join( buffers, "\n" ) endfunction function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 10eb374..6034d53 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -74,7 +74,7 @@ endif command! -bar -nargs=1 -complete=customlist,vimspector#CompleteExpr \ VimspectorWatch \ call vimspector#AddWatch( ) -command! -bar -nargs=1 -complete=customlist,vimspector#CompleteOutput +command! -bar -nargs=1 -complete=custom,vimspector#CompleteOutput \ VimspectorShowOutput \ call vimspector#ShowOutput( ) command! -bar -nargs=1 -complete=customlist,vimspector#CompleteExpr From 149d48b68857ddb931e2486f9b782eabf8e3fb7e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jan 2020 22:36:47 +0000 Subject: [PATCH 046/609] Scroll the output buffer when visible --- autoload/vimspector/internal/neojob.vim | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index f8a687a..77d4d2c 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -119,7 +119,6 @@ function! s:_OnCommandEvent( category, id, data, event ) abort let last_line = last_line_list[ 0 ] endif - call s:MakeBufferWritable( buffer ) try call setbufline( buffer, '$', last_line . a:data[ 0 ] ) @@ -129,6 +128,17 @@ function! s:_OnCommandEvent( category, id, data, event ) abort call setbufvar( buffer, '&modified', 0 ) endtry + " if the buffer is visible, scroll it + let w = bufwinnr( buffer ) + if w > 0 + let cw = winnr() + try + execute w . 'wincmd w' + normal G + finally + execute cw . 'wincmd w' + endtry + endif endfunction function! s:SetUpHiddenBuffer( buffer ) abort From 6ee8f7875d71090097a864140b88b457c3140d01 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 15 Jan 2020 21:00:15 +0000 Subject: [PATCH 047/609] Tests: Import test framework changs from YCM --- run_tests | 49 ++++++++++++++++++++++++++++++++++++------ tests/.gitignore | 1 + tests/lib/run_test.vim | 1 - 3 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 tests/.gitignore diff --git a/run_tests b/run_tests index f216c58..5200bf1 100755 --- a/run_tests +++ b/run_tests @@ -1,8 +1,23 @@ #!/usr/bin/env bash +if [ "$1" == "--help" ]; then + echo "$(basename $0) " + echo "e.g.: " + echo " - run all tests: $0" + echo " - run specific tests script: $0 signature_help.test.vim" + echo " - run specific tests fun: $0 signature_help.test.vim:Test_signatures_TopLine\(\)" + exit 0 +fi + RUN_VIM="vim --clean --not-a-term" RUN_TEST="${RUN_VIM} -S lib/run_test.vim" +if [ $(node --version | awk 'BEGIN { FS="." } { print $1 }') != "v10" ]; then + echo "ERROR: Tests must be run against node v10 because python." >&2 + echo "You have $(node --version)" >&2 + exit 1 +fi + if [ -z "$VIMSPECTOR_MIMODE" ]; then if which lldb >/dev/null 2>&1; then export VIMSPECTOR_MIMODE=lldb @@ -24,8 +39,8 @@ set -e set +e echo "%DONE - built test programs" -pushd tests > /dev/null - +# Start +pushd $(dirname $0)/tests > /dev/null echo "Running Vimspector Vim tests" RESULT=0 @@ -39,23 +54,45 @@ fi for t in ${TESTS}; do echo "" echo "%RUN: $t" - rm -f messages debuglog # split on : into fileName and testName IFS=: read -s t T <<< "$t" - if ${RUN_TEST} --cmd 'au SwapExists * let v:swapchoice = "e"' $t $T; then + TESTLOGDIR=$(pwd)/logs/$t + + if ${RUN_TEST} --cmd 'au SwapExists * let v:swapchoice = "e"' $t $T \ + && [ -f $t.res ]; then echo "%PASS: $t PASSED" else - cat messages - echo "%FAIL: $t FAILED" + echo "%FAIL: $t FAILED - see $TESTLOGDIR" RESULT=1 fi + + rm -rf $TESTLOGDIR + mkdir -p $TESTLOGDIR + ${RUN_VIM} --version > ${TESTLOGDIR}/vimversion + for l in messages debuglog *.log; do + # In CI we can't view the output files, so we just have to cat them + if [ -f $l ]; then + if [ "$VIMSPECTOR_TEST_STDOUT" ]; then + echo "" + echo "" + echo "*** START: $l ***" + cat $l + echo "*** END: $l ***" + fi + mv $l $TESTLOGDIR + fi + done + + rm -f $t.res done +echo "Done running tests" popd > /dev/null echo "" echo "All done." + exit $RESULT diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..333c1e9 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +logs/ diff --git a/tests/lib/run_test.vim b/tests/lib/run_test.vim index f8cef56..226aa55 100644 --- a/tests/lib/run_test.vim +++ b/tests/lib/run_test.vim @@ -211,7 +211,6 @@ func AfterTheTest() let logfile = s:testid_filesafe . '.vimspector.log' call writefile( log, logfile, 's' ) call add( s:messages, 'Wrote log for failed test: ' . logfile ) - call extend( s:messages, log ) endif endfunc From d711d655aadb1d2fac3ee7549c910f0d9d66e2a9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 15 Jan 2020 21:15:30 +0000 Subject: [PATCH 048/609] FixUp: Brokern StopOver mapping --- plugin/vimspector.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 6034d53..61c956a 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -45,7 +45,7 @@ nnoremap VimspectorToggleBreakpoint \ :call vimspector#ToggleBreakpoint() nnoremap VimspectorAddFunctionBreakpoint \ :call vimspector#AddFunctionBreakpoint( expand( '' ) ) -nnoremap VimspectorStopOver :call vimspector#StepOver() +nnoremap VimspectorStepOver :call vimspector#StepOver() nnoremap VimspectorStepInto :call vimspector#StepInto() nnoremap VimspectorStepOut :call vimspector#StepOut() From f6eccf1314cc53e3327db4283757fbd012ba936f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 15 Jan 2020 21:16:00 +0000 Subject: [PATCH 049/609] More: Import latest YCM test framework changes --- tests/lib/run_test.vim | 131 +++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 57 deletions(-) diff --git a/tests/lib/run_test.vim b/tests/lib/run_test.vim index 226aa55..d8821ed 100644 --- a/tests/lib/run_test.vim +++ b/tests/lib/run_test.vim @@ -4,7 +4,7 @@ " " To execute only specific test functions, add a second argument. It will be " matched against the names of the Test_ funtion. E.g.: -" ../vim -u NONE -S runtest.vim test_channel.vim open_delay +" ../vim -Nu NONE vimrc -S lib/run_test.vim test_channel.vim open_delay " The output can be found in the "messages" file. " " The test script may contain anything, only functions that start with @@ -26,8 +26,16 @@ " It will be called after each Test_ function. " " When debugging a test it can be useful to add messages to v:errors: -" call add(v:errors, "this happened") +" call add(v:errors, "this happened") +" +" But for real debug logging: +" call ch_log( ",,,message..." ) +" Then view it in 'debuglog' +" Let a test take up to 1 minute +let s:single_test_timeout = 60000 + +" Restrict the runtimepath to the exact minimum needed for testing set rtp=$PWD/lib,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after if has('packages') let &packpath = &rtp @@ -37,7 +45,7 @@ call ch_logfile( 'debuglog', 'w' ) " For consistency run all tests with 'nocompatible' set. " This also enables use of line continuation. -set nocp viminfo+=nviminfo +set nocp " Use utf-8 by default, instead of whatever the system default happens to be. " Individual tests can overrule this at the top of the file. @@ -47,11 +55,23 @@ set encoding=utf-8 set nomore " Output all messages in English. -lang mess C +lang messages C " Always use forward slashes. set shellslash +func s:TestFailed() + let log = readfile( expand( '~/.vimspector.log' ) ) + let logfile = s:testid_filesafe . '_vimspector.log.testlog' + call writefile( log, logfile, 's' ) + call add( s:messages, 'Wrote log for failed test: ' . logfile ) +endfunc + +func! Abort( timer_id ) + call assert_report( 'Test timed out!!!' ) + qa! +endfunc + func RunTheTest(test) echo 'Executing ' . a:test @@ -107,40 +127,38 @@ func RunTheTest(test) call add(s:messages, 'Executing ' . a:test) let s:done += 1 + let timer = timer_start( s:single_test_timeout, funcref( 'Abort' ) ) - if a:test =~ 'Test_nocatch_' - " Function handles errors itself. This avoids skipping commands after the - " error. + try + let s:test = a:test + let s:testid = g:testpath . ':' . a:test + + let test_filesafe = substitute( a:test, '[)(,:]', '_', 'g' ) + let s:testid_filesafe = g:testpath . '_' . test_filesafe + + au VimLeavePre * call EarlyExit(s:test) exe 'call ' . a:test - else - try - let s:test = a:test - let s:testid = g:testpath . ':' . a:test - let test_filesafe = substitute( a:test, ')', '_', 'g' ) - let test_filesafe = substitute( test_filesafe, '(', '_', 'g' ) - let test_filesafe = substitute( test_filesafe, ',', '_', 'g' ) - let test_filesafe = substitute( test_filesafe, ':', '_', 'g' ) - let s:testid_filesafe = g:testpath . '_' . test_filesafe - au VimLeavePre * call EarlyExit(s:test) - exe 'call ' . a:test - au! VimLeavePre - catch /^\cskipped/ - call add(s:messages, ' Skipped') - call add(s:skipped, - \ 'SKIPPED ' . a:test - \ . ': ' - \ . substitute(v:exception, '^\S*\s\+', '', '')) - catch - call add(v:errors, - \ 'Caught exception in ' . a:test - \ . ': ' - \ . v:exception - \ . ' @ ' - \ . g:testpath - \ . ':' - \ . v:throwpoint) - endtry - endif + au! VimLeavePre + catch /^\cskipped/ + call add(s:messages, ' Skipped') + call add(s:skipped, + \ 'SKIPPED ' . a:test + \ . ': ' + \ . substitute(v:exception, '^\S*\s\+', '', '')) + catch + call add(v:errors, + \ 'Caught exception in ' . a:test + \ . ': ' + \ . v:exception + \ . ' @ ' + \ . g:testpath + \ . ':' + \ . v:throwpoint) + + call s:TestFailed() + endtry + + call timer_stop( timer ) " In case 'insertmode' was set and something went wrong, make sure it is " reset to avoid trouble with anything else. @@ -203,23 +221,16 @@ endfunc func AfterTheTest() if len(v:errors) > 0 let s:fail += 1 + call s:TestFailed() call add(s:errors, 'Found errors in ' . s:testid . ':') call extend(s:errors, v:errors) let v:errors = [] - - let log = readfile( expand( '~/.vimspector.log' ) ) - let logfile = s:testid_filesafe . '.vimspector.log' - call writefile( log, logfile, 's' ) - call add( s:messages, 'Wrote log for failed test: ' . logfile ) endif endfunc func EarlyExit(test) " It's OK for the test we use to test the quit detection. - if a:test != 'Test_zz_quit_detected()' - call add(v:errors, 'Test caused Vim to exit: ' . a:test) - endif - + call add(v:errors, 'Test caused Vim to exit: ' . a:test) call FinishTesting() endfunc @@ -232,17 +243,19 @@ func FinishTesting() if s:fail == 0 " Success, create the .res file so that make knows it's done. - exe 'split ' . fnamemodify(g:testname, ':r') . '.res' - write + call writefile( [], g:testname . '.res', 's' ) endif if len(s:errors) > 0 " Append errors to test.log - split test.log - call append(line('$'), '') - call append(line('$'), 'From ' . g:testpath . ':') - call append(line('$'), s:errors) - write + let l = [] + if filereadable( 'test.log' ) + let l = readfile( 'test.log' ) + endif + call writefile( l->extend( [ '', 'From ' . g:testpath . ':' ] ) + \ ->extend( s:errors ), + \ 'test.log', + \ 's' ) endif if s:done == 0 @@ -263,11 +276,14 @@ func FinishTesting() call extend(s:messages, s:skipped) " Append messages to the file "messages" - split messages - call append(line('$'), '') - call append(line('$'), 'From ' . g:testpath . ':') - call append(line('$'), s:messages) - write + let l = [] + if filereadable( 'messages' ) + let l = readfile( 'messages' ) + endif + call writefile( l->extend( [ '', 'From ' . g:testpath . ':' ] ) + \ ->extend( s:messages ), + \ 'messages', + \ 's' ) if s:fail > 0 cquit! @@ -316,6 +332,7 @@ endif for s:test in sort(s:tests) " Silence, please! set belloff=all + call RunTheTest(s:test) call AfterTheTest() endfor From 3b975002894c88d54af33a1dec7eb7168a86c68f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 15 Jan 2020 21:44:15 +0000 Subject: [PATCH 050/609] Make sure the buffer is modifiable when updating the console --- python3/vimspector/output.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index fb90929..38ce6d3 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -117,17 +117,19 @@ class OutputView( object ): def Evaluate( self, frame, expression ): console = self._buffers[ 'Console' ].buf - utils.AppendToBuffer( console, 'Evaluating: ' + expression ) + with utils.ModifiableScratchBuffer( console ): + utils.AppendToBuffer( console, 'Evaluating: ' + expression ) def print_result( message ): - utils.AppendToBuffer( console, - 'Evaluated: ' + expression ) + with utils.ModifiableScratchBuffer( console ): + utils.AppendToBuffer( console, + 'Evaluated: ' + expression ) - result = message[ 'body' ][ 'result' ] - if result is None: - result = 'null' + result = message[ 'body' ][ 'result' ] + if result is None: + result = 'null' - utils.AppendToBuffer( console, ' Result: ' + result ) + utils.AppendToBuffer( console, ' Result: ' + result ) request = { 'command': 'evaluate', From 2252a2bf02f9dce2fa96c83effa94ebff8ea4995 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 15 Jan 2020 21:44:41 +0000 Subject: [PATCH 051/609] Delete the watches and vars buffers when resetting --- python3/vimspector/variables.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index f0ff99e..759abdf 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -105,6 +105,9 @@ class VariablesView( object ): for k, v in self._oldoptions.items(): vim.options[ k ] = v + vim.command( 'bdelete! ' + str( self._watch.win.buffer.number ) ) + vim.command( 'bdelete! ' + str( self._vars.win.buffer.number ) ) + def LoadScopes( self, frame ): def scopes_consumer( message ): old_scopes = self._scopes From c2b0cc8f18203a930d0c409844ae50a2dd9f7e75 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 15 Jan 2020 21:45:13 +0000 Subject: [PATCH 052/609] Attempt to make the output windows a little less rubbish on neovim --- autoload/vimspector/internal/neojob.vim | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index 77d4d2c..339b5f2 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -111,17 +111,17 @@ function! s:_OnCommandEvent( category, id, data, event ) abort call bufload( buffer ) - let last_line_list = getbufline( buffer, '$' ) - - if len( last_line_list ) == 0 - let last_line = '' - else - let last_line = last_line_list[ 0 ] - endif + let numlines = py3eval( "len( vim.buffers[ int( vim.eval( 'buffer' ) ) ] )" ) + let last_line = getbufline( buffer, '$' )[ 0 ] call s:MakeBufferWritable( buffer ) try - call setbufline( buffer, '$', last_line . a:data[ 0 ] ) + if numlines == 1 && last_line ==# '' + call setbufline( buffer, 1, a:data[ 0 ] ) + else + call setbufline( buffer, '$', last_line . a:data[ 0 ] ) + endif + call appendbufline( buffer, '$', a:data[ 1: ] ) finally call s:MakeBufferReadOnly( buffer ) @@ -134,7 +134,7 @@ function! s:_OnCommandEvent( category, id, data, event ) abort let cw = winnr() try execute w . 'wincmd w' - normal G + normal Gz. finally execute cw . 'wincmd w' endtry @@ -144,6 +144,7 @@ endfunction function! s:SetUpHiddenBuffer( buffer ) abort call setbufvar( a:buffer, '&hidden', 1 ) call setbufvar( a:buffer, '&bufhidden', 'hide' ) + call setbufvar( a:buffer, '&wrap', 0 ) call s:MakeBufferReadOnly( a:buffer ) endfunction From 099ffc2af92722ac704f32f1b690bffb94e3eb39 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 15 Jan 2020 23:14:40 +0000 Subject: [PATCH 053/609] Disable swap files in the neovim buffers too --- autoload/vimspector/internal/neojob.vim | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index 339b5f2..bfe987b 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -48,11 +48,11 @@ function! vimspector#internal#neojob#StartDebugSession( config ) abort \ 'on_stderr': funcref( 's:_OnEvent' ), \ 'on_exit': funcref( 's:_OnEvent' ), \ 'cwd': a:config[ 'cwd' ], + \ 'env': a:config[ 'env' ], \ } \ ) " FIXME: Missing in neovim 0.4. But in master: - " \ 'env': a:config[ 'env' ], " " FIXME: error handling ? @@ -82,14 +82,12 @@ endfunction function! vimspector#internal#neojob#StopDebugSession() abort if !exists( 's:job' ) - echom "Not stopping session: Job doesn't exist" - redraw return endif if s:JobIsRunning( s:job ) - echom 'Terminating job' - redraw + echom 'Terminating job' + redraw call jobstop( s:job ) endif endfunction @@ -145,6 +143,7 @@ function! s:SetUpHiddenBuffer( buffer ) abort call setbufvar( a:buffer, '&hidden', 1 ) call setbufvar( a:buffer, '&bufhidden', 'hide' ) call setbufvar( a:buffer, '&wrap', 0 ) + call setbufvar( a:buffer, '&swapfile', 0 ) call s:MakeBufferReadOnly( a:buffer ) endfunction From 1e8bd384b91f24d976dc6a4773212fadea62e69e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 15 Jan 2020 23:23:22 +0000 Subject: [PATCH 054/609] FixUp: importing ycm test lib changs' --- run_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_tests b/run_tests index 5200bf1..812aad5 100755 --- a/run_tests +++ b/run_tests @@ -71,7 +71,7 @@ for t in ${TESTS}; do rm -rf $TESTLOGDIR mkdir -p $TESTLOGDIR ${RUN_VIM} --version > ${TESTLOGDIR}/vimversion - for l in messages debuglog *.log; do + 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 From 355f0f0e0c09569f0dba29a12be55075da4d16dc Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 17 Jan 2020 00:02:24 +0000 Subject: [PATCH 055/609] Implement command line completion for watch/eval --- autoload/vimspector.vim | 5 +++- plugin/vimspector.vim | 4 +-- .../vimspector/debug_adapter_connection.py | 27 +++++++++++++++++++ python3/vimspector/debug_session.py | 20 ++++++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index c3ead44..f2b39d5 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -121,7 +121,10 @@ function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort endfunction function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort - return [] + return join( py3eval( '_vimspector_session.GetCompletionsSync( ' + \.' vim.eval( "a:CmdLine" ),' + \.' int( vim.eval( "a:CursorPos" ) ) )' ), + \ "\n" ) endfunction " Boilerplate {{{ diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 61c956a..0c71090 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -71,13 +71,13 @@ elseif s:mappings ==# 'HUMAN' nmap VimspectorStepOut endif -command! -bar -nargs=1 -complete=customlist,vimspector#CompleteExpr +command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr \ VimspectorWatch \ call vimspector#AddWatch( ) command! -bar -nargs=1 -complete=custom,vimspector#CompleteOutput \ VimspectorShowOutput \ call vimspector#ShowOutput( ) -command! -bar -nargs=1 -complete=customlist,vimspector#CompleteExpr +command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr \ VimspectorEval \ call vimspector#Evaluate( ) command! -bar diff --git a/python3/vimspector/debug_adapter_connection.py b/python3/vimspector/debug_adapter_connection.py index 73a4549..295b83d 100644 --- a/python3/vimspector/debug_adapter_connection.py +++ b/python3/vimspector/debug_adapter_connection.py @@ -65,6 +65,33 @@ class DebugAdapterConnection( object ): if not self._SendMessage( msg ): self._AbortRequest( request, 'Unable to send message' ) + + def DoRequestSync( self, msg, timeout = 5000 ): + result = {} + + def handler( msg ): + result[ 'response' ] = msg + + def failure_handler( reason, msg ): + result[ 'response' ] = msg + result[ 'exception' ] = RuntimeError( reason ) + + self.DoRequest( handler, msg, failure_handler, timeout ) + + bug_catcher = 1000 + while not result and bug_catcher >= 0: + vim.command( 'sleep 10m' ) + bug_catcher -= 10 + + if result.get( 'exception' ) is not None: + raise result[ 'exception' ] + + if result.get( 'response' ) is None: + raise RuntimeError( "No response" ) + + return result[ 'response' ] + + def OnRequestTimeout( self, timer_id ): request_id = None for seq, request in self._outstanding_requests.items(): diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index a4d801b..474e3d0 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -406,6 +406,26 @@ class DebugSession( object ): def GetOutputBuffers( self ): return self._outputView.GetCategories() + def GetCompletionsSync( self, text_line, column_in_bytes ): + if not self._server_capabilities.get( 'supportsCompletionsRequest' ): + return [] + + response = self._connection.DoRequestSync( { + 'command': 'completions', + 'arguments': { + 'frameId': self._stackTraceView.GetCurrentFrame()[ 'id' ], + # TODO: encoding ? bytes/codepoints + 'text': text_line, + 'column': column_in_bytes + } + } ) + # TODO: + # - start / length + # - sortText + return [ i.get( 'text' ) or i[ 'label' ] + for i in response[ 'body' ][ 'targets' ] ] + + def _SetUpUI( self ): vim.command( 'tabnew' ) self._uiTab = vim.current.tabpage From df759ec9dc6e8eebc00a5c87f514daf4dddb1e29 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 17 Jan 2020 15:26:52 +0000 Subject: [PATCH 056/609] Upgrade bashdb and add more useful defaults --- install_gadget.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 8cd8023..cb8fe3e 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -207,9 +207,37 @@ GADGETS = { 'download/${version}/${file_name}', }, 'all': { - 'file_name': 'bash-debug-0.3.5.vsix', - 'version': 'v0.3.5', + 'file_name': 'bash-debug-0.3.6.vsix', + 'version': 'v0.3.6', 'checksum': '', + }, + 'do': lambda name, root: InstallBashDebug( name, root ), + 'adapters': { + "vscode-bash": { + "name": "bashdb", + "command": [ + "node", + "${gadgetDir}/vscode-bash-debug/out/bashDebug.js" + ], + "variables": { + "BASHDB_HOME": "${gadgetDir}/vscode-bash-debug/bashdb_dir" + }, + "configuration": { + "request": "launch", + "type": "bashdb", + "program": "${file}", + "args": [], + "env": {}, + "pathBash": "bash", + "pathBashdb": "${BASHDB_HOME}/bashdb", + "pathBashdbLib": "${BASHDB_HOME}", + "pathCat": "cat", + "pathMkfifo": "mkfifo", + "pathPkill": "pkill", + "cwd": "${workspaceRoot}", + "terminalKind": "integrated", + } + } } }, 'vscode-go': { @@ -318,6 +346,11 @@ def InstallCppTools( name, root ): MakeExtensionSymlink( name, root ) +def InstallBashDebug( name, root ): + MakeExecutable( os.path.join( root, 'extension', 'bashdb_dir', 'bashdb' ) ) + MakeExtensionSymlink( name, root ) + + def InstallTclProDebug( name, root ): configure = [ './configure' ] From 7ca51f8f080d41e9ab3af0debade6a978513f8f6 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 19 Jan 2020 10:22:43 +0000 Subject: [PATCH 057/609] FixUp: Node <10 not node=10 --- run_tests | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/run_tests b/run_tests index 812aad5..1c59f76 100755 --- a/run_tests +++ b/run_tests @@ -12,8 +12,10 @@ fi RUN_VIM="vim --clean --not-a-term" RUN_TEST="${RUN_VIM} -S lib/run_test.vim" -if [ $(node --version | awk 'BEGIN { FS="." } { print $1 }') != "v10" ]; then - echo "ERROR: Tests must be run against node v10 because python." >&2 +NODE_VERSION_MAJ=$(node --version \ + | awk 'BEGIN { FS="." } { print substr( $1, 2 ) }') +if [ ${NODE_VERSION_MAJ} -gt "10" ]; then + echo "ERROR: Tests must be run against node 10 or earlier because python." >&2 echo "You have $(node --version)" >&2 exit 1 fi From ef4a6fc10cfcb8c2ae21d552599b9b834ab6df18 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 19 Jan 2020 10:28:34 +0000 Subject: [PATCH 058/609] FixUp: use normal! to avoid user mappings, thanks vint! --- autoload/vimspector/internal/neojob.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index bfe987b..5650029 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -132,7 +132,7 @@ function! s:_OnCommandEvent( category, id, data, event ) abort let cw = winnr() try execute w . 'wincmd w' - normal Gz. + normal! Gz. finally execute cw . 'wincmd w' endtry From 6cf4f9f9ab175527f11c66dd490ba4d5c52728b2 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 19 Jan 2020 20:07:18 +0000 Subject: [PATCH 059/609] FixUp: Some comments --- autoload/vimspector/internal/neojob.vim | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index 5650029..5b95fea 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -31,6 +31,8 @@ function! s:_OnEvent( chan_id, data, event ) abort echom 'Channel exit with status ' . a:data redraw unlet s:job + " This causes terminal spam in neovim due to + " https://github.com/neovim/neovim/issues/11725 py3 _vimspector_session.OnServerExit( vim.eval( 'a:data' ) ) endif endfunction @@ -52,10 +54,7 @@ function! vimspector#internal#neojob#StartDebugSession( config ) abort \ } \ ) - " FIXME: Missing in neovim 0.4. But in master: - " - - " FIXME: error handling ? + " FIXME: env might not work: Missing in neovim 0.4. But in master: return v:true endfunction From 5ee8ffc3f69a261ee8e1c675739b9effa836e546 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 19 Jan 2020 20:16:52 +0000 Subject: [PATCH 060/609] Catch E325 in neovim --- autoload/vimspector/internal/neojob.vim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index 5b95fea..f885337 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -106,7 +106,12 @@ function! s:_OnCommandEvent( category, id, data, event ) abort let buffer = s:commands[ a:category ][ a:id ].stderr endif - call bufload( buffer ) + try + call bufload( buffer ) + catch /E325/ + " Ignore E325/ATTENTION + endtry + let numlines = py3eval( "len( vim.buffers[ int( vim.eval( 'buffer' ) ) ] )" ) let last_line = getbufline( buffer, '$' )[ 0 ] From 9e3d7237a5961f2bd9c356dff6dfead60e8c6ad0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 19 Jan 2020 20:32:07 +0000 Subject: [PATCH 061/609] Update README --- README.md | 55 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 80c59be..37c9a6e 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ Alternatively, you can clone the repo and select which gadgets are installed: Vimspector requires: -* Vim (*not neovim*) version 8.1 with at least patch 1264 +* Vim version 8.1 with at least patch 1264, or Neovim 0.4.3 * One of the following operating systems: * Linux * macOS Mojave or pater @@ -149,15 +149,10 @@ author. PRs are welcome. Which Linux versions? I only test on Ubuntu 18.04 and later and RHEL 7. -Why not neovim? A few fiddly reasons (not philosophy): +### Neovim differences -* neovim doesn't implement some features Vimspector relies on -* neovim's Python API is not compatible with Vim's -* I don't personally use or test with neovim, and doing so doubles the -effort required. - -If enough people +1 [this issue](https://github.com/puremourning/vimspector/issues/29), -I will consider it. +neovim doesn't implement some features Vimspector relies on (WinBar and prompt +buffers), so you have to use `:Vimspector*` commands (see [Usage][#usage]). ## Language dependencies @@ -382,12 +377,26 @@ minimise this, or at least announce on Gitter. By default, vimspector does not change any of your mappings. Mappings are very personal and so you should work out what you like and use vim's powerful mapping -features to set your own mappings. For example, if you want `` to -start/continue debugging, add this to some appropriate place, such as your -`vimrc` (hint: run `:e $MYVIMRC`). +features to set your own mappings. To that end, Vimspector defines the following +`` mappings: + +* `VimspectorContinue` +* `VimspectorStop` +* `VimspectorRestart` +* `VimspectorPause` +* `VimspectorToggleBreakpoint` +* `VimspectorAddFunctionBreakpoint` +* `VimspectorStepOver` +* `VimspectorStepInto` +* `VimspectorStepOut` + +These map 1-1 with the API functions below. + +For example, if you want `` to start/continue debugging, add this to some +appropriate place, such as your `vimrc` (hint: run `:e $MYVIMRC`). ```viml -nnoremap :call vimspector#Continue() +nmap VimspectorContinue ``` That said, many people are familiar with particular debuggers, so the following @@ -470,11 +479,13 @@ breakpoint. ## Watches -The watches window is a prompt buffer. Enter insert mode to add a -new watch expression. +The watches window is a prompt buffer, where that's available. Enter insert mode +to add a new watch expression. * Add watches to the variables window by entering insert mode and typing the expression. Commit with ``. +* Alternatively, use `:VimspectorWatch `. Tab-completion for + expression is available in some debug adapters. * Expand result with ``. * Delete with ``. @@ -486,15 +497,20 @@ new watch expression. ## Program Output * In the outputs window use the WinBar to select the output channel. +* Alternatively, use `:VimspectorShowOutput `. Use command-line + completion to see the categories. * The debugee prints to the stdout channel. * Other channels may be useful for debugging. ### Console -The console window is a prompt buffer and can be used as an interactive -CLI for the debug adapter. Support for this varies amongt adapters. +The console window is a prompt buffer, where that's available, and can be used +as an interactive CLI for the debug adapter. Support for this varies amongt +adapters. -* Enter insert mode to enter a command to evaluate +* Enter insert mode to enter a command to evaluate. +* Alternatively, `:VimspectorEval `. Completion is available with + some debug adapters. * Commit the request with `` * The request and subsequent result are printed. @@ -504,7 +520,8 @@ NOTE: See also [Watches](#watches) above. To close the debugger, use: -* `Reset` button when mouse support is enabled in vim (`set mouse=a`) +* `Reset` WinBar button (`set mouse=a`) +* `:VimspectorReset` when the WinBar is not available. * `call vimspector#Reset()` # Debug adapter configuration From 287286cadb31f8dd098feb02fd83fd36a1c1fc7a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 21 Jan 2020 22:24:59 +0000 Subject: [PATCH 062/609] Add way to install a different java debugger, though it doens't work well --- install_gadget.py | 29 +++++++++++++++++++ .../test/java/test_project/.vimspector.json | 10 +++++++ 2 files changed, 39 insertions(+) diff --git a/install_gadget.py b/install_gadget.py index cb8fe3e..b13286c 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -127,6 +127,35 @@ GADGETS = { } }, }, + 'java-language-server': { + 'language': 'javac', + 'enabled': False, + 'download': { + 'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/' + 'publishers/georgewfraser/vsextensions/vscode-javac/${version}/' + 'vspackage', + 'target': 'georgewfraser.vscode-javac-0.2.31.vsix.gz', + 'format': 'zip.gz', + }, + 'all': { + 'version': '0.2.31', + 'file_name': 'georgewfraser.vscode-javac-0.2.31.vsix.gz', + 'checksum': + '', + }, + 'adapters': { + "vscode-javac": { + "name": "vscode-javac", + "type": "vscode-javac", + "command": [ + "${gadgetDir}/java-language-server/dist/debug_adapter_mac.sh" + ], + "attach": { + "pidSelect": "none" + } + } + }, + }, 'tclpro': { 'language': 'tcl', 'repo': { diff --git a/support/test/java/test_project/.vimspector.json b/support/test/java/test_project/.vimspector.json index 43b07aa..9ccc19f 100644 --- a/support/test/java/test_project/.vimspector.json +++ b/support/test/java/test_project/.vimspector.json @@ -17,6 +17,16 @@ "stopOnEntry": true, "console": "integratedTerminal" } + }, + "Attach with vscode-javac": { + "adapter": "vscode-javac", + "configuration": { + "request": "attach", + "port": "${debugPort}", + "sourceRoots": [ + "${workspaceRoot}/src/main/java" + ] + } } } } From 9b5960f81e8660aabf31f0edd62e5042334c2e91 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jan 2020 21:34:32 +0000 Subject: [PATCH 063/609] Fix go language server returning 0 column causing errors in neovim --- python3/vimspector/code.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index b1b7698..27d9586 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -75,8 +75,10 @@ class CodeView( object ): return False # SIC: column is 0-based, line is 1-based in vim. Why? Nobody knows. + # Note: max() with 0 because some debug adapters (go) return 0 for the + # column. try: - self._window.cursor = ( frame[ 'line' ], frame[ 'column' ] - 1 ) + self._window.cursor = ( frame[ 'line' ], max( frame[ 'column' ] - 1, 0 ) ) except vim.error: self._logger.exception( "Unable to jump to %s:%s in %s, maybe the file " "doesn't exist", From d69e63719bef700fadc87336ccf7d5bb91605c22 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 17:42:03 +0000 Subject: [PATCH 064/609] Elaborate neovim deficiencies --- README.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 37c9a6e..1e064ed 100644 --- a/README.md +++ b/README.md @@ -151,8 +151,23 @@ Which Linux versions? I only test on Ubuntu 18.04 and later and RHEL 7. ### Neovim differences -neovim doesn't implement some features Vimspector relies on (WinBar and prompt -buffers), so you have to use `:Vimspector*` commands (see [Usage][#usage]). +neovim doesn't implement some features Vimspector relies on: + +* WinBar - used for the buttons at the top of the code window and for changing + the output window's current output. +* Prompt Buffers - used to send commands in the Console and add Watches +* Balloons - used to display the values of variables when debugging. + +Workarounds are in place as follows: + +* WinBar - There are [mappings](#mappings), + [`:VimspectorShowOutput`](#program-output) and + [`:VimspectorReset`](#closing-debugger) +* Prompt Buffers - There are [`:VimspectorEval`](#console) + and [`:VimspectorWatch`](#watches) + +There is no workaroud for the lack of baloons; you'll just have to use +`:VimspectorEval` or `:VimspectorWatch`, or switch to Vim. ## Language dependencies From 94f19459b94be39ded1b1b41b8b1475087216363 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 17:49:20 +0000 Subject: [PATCH 065/609] Update TOC --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1e064ed..fda0b06 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,11 @@ For a tutorial and usage overview, take a look at the * [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) * [Language dependencies](#language-dependencies) * [Clone the plugin](#clone-the-plugin) * [Install some gadgets](#install-some-gadgets) @@ -38,11 +40,10 @@ For a tutorial and usage overview, take a look at the * [Debug adapter configuration](#debug-adapter-configuration) * [Supported Languages](#supported-languages-1) * [Partially supported](#partially-supported) - * [Unsupported](#unsupported) * [FAQ](#faq) * [License](#license) - + @@ -149,7 +150,7 @@ author. PRs are welcome. Which Linux versions? I only test on Ubuntu 18.04 and later and RHEL 7. -### Neovim differences +## Neovim differences neovim doesn't implement some features Vimspector relies on: @@ -166,7 +167,7 @@ Workarounds are in place as follows: * Prompt Buffers - There are [`:VimspectorEval`](#console) and [`:VimspectorWatch`](#watches) -There is no workaroud for the lack of baloons; you'll just have to use +There is no workaroud for the lack of balloons; you'll just have to use `:VimspectorEval` or `:VimspectorWatch`, or switch to Vim. ## Language dependencies From ffe5209ea628d162a443d89616222463c23597c5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 20:04:45 +0000 Subject: [PATCH 066/609] Correct template for neovim support --- .github/ISSUE_TEMPLATE/bug_report.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index c93aba8..e4579bb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -31,8 +31,12 @@ Please include: **Environemnt** -NOTE: NeoVim is not supported. -NOTE: Windows is not supported. +NOTE: NeoVim is supported only on a best-effort basis. Please check the README +for limitations of neovim. Don't be offended if I ask you to reproduce issues in +Vim. + +NOTE: Windows is not supported. There is a branch with windows support, which +you can contribute to, rather than opening an issue. * Output of `vim --version` From 9c3813f11b54ecfb1b6d99667486d83e7ec98e4c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 20:05:50 +0000 Subject: [PATCH 067/609] Add code of conduct --- CODE_OF_CONDUCT.md | 128 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0b7cfc3 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +`benjacksonbkg@gmail.com`. All complaints will be reviewed and investigated +promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. From 8a4a8b39da5b0ff034edeb0a93a095e25d9c326f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 21:04:17 +0000 Subject: [PATCH 068/609] Add contributing.md --- CONTRIBUTING.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a921ab2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing to Vimspector + +Contributions to Vimspector are always welcome. Contributions can take many +forms, such as: + +* Raising, responding to, or reacting to Issues or Pull Requests +* Testing new in-progress changes and providing feedback +* Discussing in the Gitter channel +* etc. + +At all times the [code of conduct](#code-of-conduct) applies. + +## Issues + +The GitHub issue tracker is for *bug reports* and *features requests* for the +Vimspector project, and on-topic comments and follow-ups to them. It is not for +general discussion, general support or for any other purpose. + +Please do not be offended if your Issue or comment is closed or hidden, for any +of the following reasons: + +* The issue template was not completed +* The issue or comment is off-topic +* The issue does not represent a Vimspector bug or feature request +* etc. + +But above all else, please *please* complete the issue template. I know it is a +little tedious to get all the various diagnostics, but you *must* provide them, +*even if you think they are irrelevant*. This is important, because the +maintainer(s) can quickly cross-check theories by inspecting the provided +diagnostics without having to spend time asking for them, and waiting for the +response. This means *you get a better answer, faster*. So it's worth it, +honestly. + +## Pull Requests + +When contributing pull requests, I ask that: + +* You provide a clear and complete summary of the change, the use case and how + the change was tested. +* You avoid using APIs that are not available in the versions listed in the + dependencies on README.md +* You add tests for your PR. +* You follow the style of the code as-is; the python code is YCM-stye, it is + *not* PEP8, nor should it be. + +### Running the tests locally + +There are 2 ways: + +1. In the docker container. The CI tests for linux run in a container, so as to + ensure a consistent test environment. The container is defined in + `./tests/ci/`. There is also a container in `./tests/manual` which can be + used to run the tests interractively. To do this install and start docker, + then run `./tests/manual/run`. This will drop you into a bash shell inside + the linux container with your local vimspector code mounted. You can then + follow the instructions for running tets directly. +1. Directly: Run `./install_gadget.py --all` and then `./run_tests`. Note that + this depends on your runtime environment and might not match CI. I recommend + running the tests in the docker container. + +When tests fail, they dump a load of logs to a directory for each failed tests. +Usually the most useful output is `messages`, which tells you what actually +failed. + +For more infomration on the test framework, see +[this article](https://vimways.org/2019/a-test-to-attest-to/), authored by the +Vimspector creator. + +# Code of conduct + +Please see [code of conduct][CODE_OF_CONDUCT.md]. From a524762f980d842536581a4c15ba1b6445f5d037 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 21:08:02 +0000 Subject: [PATCH 069/609] Fix link in contrinbuting guide --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a921ab2..133709d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -69,4 +69,4 @@ Vimspector creator. # Code of conduct -Please see [code of conduct][CODE_OF_CONDUCT.md]. +Please see [code of conduct](CODE_OF_CONDUCT.md). From bd820b2a8b593d50e3d4edf055e435446bbbc101 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 21:15:01 +0000 Subject: [PATCH 070/609] upgrade cpptools to 0.26.3 This sort-of fixes macOS issues when used with my lldb-mi. --- README.md | 32 ++++++++++++++++++++++++++++---- install_gadget.py | 6 +++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fda0b06..da5bd6d 100644 --- a/README.md +++ b/README.md @@ -551,10 +551,32 @@ Current tested with the following debug adapters. * C++: [vscode-cpptools](https://github.com/Microsoft/vscode-cpptools) +--- + ***NOTE FOR macOS USERS***: Currently VSCode cpptools does *not* work on macOS -(see this issue: https://github.com/microsoft/vscode-cpptools/issues/3829). -Therefore it is highly recommended to use `lldb-vscode`, which comes with llvm. -Here's how: +out of the box. +See [this issue]( https://github.com/microsoft/vscode-cpptools/issues/3829) +for the full details. + +To resolve it: + +* Install [my build](https://github.com/puremourning/lldb-mi/releases) of + `lldb-mi`. Instructions + [here](https://github.com/microsoft/vscode-cpptools/issues/3829#issuecomment-578454043). +* Add the following to the `configuration` section of your `.vimspector.json` + (below): + +``` +{ + "configurations": { + ": Launch": { + "adapter": "vscode-cpptools", + "configuration": { + "miDebuggerPath": "/path/to/my/build/of/lldb-mi", + ... +``` + +An alternative is to to use `lldb-vscode`, which comes with llvm. Here's how: * Install llvm with HomeBrew: `brew install llvm` * Create a file named @@ -586,8 +608,10 @@ Here's how: } ``` +--- + Example `.vimspector.json` (works with both `vscode-cpptools` and `lldb-vscode`. -For `lldb-vscode` replace the name of the adapter with `lldb-vscode`): +For `lldb-vscode` replace the name of the adapter with `lldb-vscode`: ``` { diff --git a/install_gadget.py b/install_gadget.py index b13286c..9b6f56f 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -56,17 +56,17 @@ GADGETS = { }, 'do': lambda name, root: InstallCppTools( name, root ), 'all': { - 'version': '0.26.2', + 'version': '0.26.3', }, 'linux': { 'file_name': 'cpptools-linux.vsix', 'checksum': - '767aed01f0c0b5eb9e9eff96aba47b576d153d2b2d9fc28e306722ea45a02ff5' + 'd660367fbedd6acaffcc233c1d41ef280ff79aeba81846b86bf9bd97b4887379' }, 'macos': { 'file_name': 'cpptools-osx.vsix', 'checksum': - '6fd52562e1e53287c0e9b94813709c6fab487c16ff3054eda6233d6c0241eb0e', + 'ca0793dd1dfd70757491da80cf04b2b15631048b572de2ebe3864f9eed96dff9', }, 'windows': { 'file_name': 'cpptools-win32.vsix', From 9b2710dd476fddf0d0436f44fefe9d511d31b8c2 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 21:41:58 +0000 Subject: [PATCH 071/609] Update README.me to have languages in TOC --- README.md | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index da5bd6d..cfe8c18 100644 --- a/README.md +++ b/README.md @@ -38,12 +38,17 @@ For a tutorial and usage overview, take a look at the * [Console](#console) * [Closing debugger](#closing-debugger) * [Debug adapter configuration](#debug-adapter-configuration) - * [Supported Languages](#supported-languages-1) - * [Partially supported](#partially-supported) + * [C, C , Rust, etc.](#c-c-rust-etc) + * [Python](#python) + * [TCL](#tcl) + * [C♯](#c) + * [Go](#go) + * [JavaScript, TypeScript, etc.](#javascript-typescript-etc) + * [Java - partially supported](#java---partially-supported) * [FAQ](#faq) * [License](#license) - + @@ -542,13 +547,13 @@ To close the debugger, use: # Debug adapter configuration -## Supported Languages - For more information on the configuration of `.vimspector.json`, take a look at the Getting Started section of the [Vimspector website][website]. Current tested with the following debug adapters. +## C, C++, Rust, etc. + * C++: [vscode-cpptools](https://github.com/Microsoft/vscode-cpptools) --- @@ -645,6 +650,8 @@ For `lldb-vscode` replace the name of the adapter with `lldb-vscode`: } ``` +## Python + * Python: [vscode-python](https://github.com/Microsoft/vscode-python) ``` @@ -668,10 +675,14 @@ For `lldb-vscode` replace the name of the adapter with `lldb-vscode`: } ``` +## TCL + * TCL (TclProDebug) See [my fork of TclProDebug](https://github.com/puremourning/TclProDebug) for instructions. +## C♯ + * C# - dotnet core Requires `install_gadget.py --force-enable-c-sharp` @@ -719,6 +730,8 @@ Requires `install_gadget.py --force-enable-c-sharp`. } ``` +## Go + * Go Requires: @@ -743,6 +756,8 @@ Requires: } ``` +## JavaScript, TypeScript, etc. + * Node.js Requires: @@ -798,9 +813,7 @@ It allows you to debug scripts running inside chrome from within Vim. } ``` -Also the mock debugger, but that isn't actually useful. - -## Partially supported +## Java - partially supported * Java Debug Server. The [java debug server][java-debug-server] runs as a jdt.ls plugin, rather than a standalone debug adapter. This makes a lot @@ -811,6 +824,11 @@ Also the mock debugger, but that isn't actually useful. on which it is listening. See [this issue](https://github.com/puremourning/vimspector/issues/3) for more background. +* Java - vscode-javac. This works, but is not as functional as Java Debug + Server. Take a look at [this + comment](https://github.com/puremourning/vimspector/issues/3#issuecomment-576916076) + for instructions. + # FAQ 1. Q: Does it work? A: Yeah, sort of. It's _incredibly_ buggy and unpolished. @@ -818,6 +836,8 @@ Also the mock debugger, but that isn't actually useful. necessarily be easy to work out what to put in the `.vimspector.json`. As you can see above, some of the servers aren't really editor agnostic, and require very-specific unique handling. +3. How do i stop it starting a new Terminal.app on macOS? See [this + comment](https://github.com/puremourning/vimspector/issues/90#issuecomment-577857322) # License From b8d2b548d867729e98cfab5b876187376d91e81d Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 22:57:56 +0000 Subject: [PATCH 072/609] Add customisation of signs --- README.md | 22 +++++++++++++++++++ python3/vimspector/breakpoints.py | 8 ++++--- python3/vimspector/code.py | 3 ++- python3/vimspector/utils.py | 36 ++++++++++++++++++++++++++++++- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index cfe8c18..40c2b7f 100644 --- a/README.md +++ b/README.md @@ -829,6 +829,28 @@ It allows you to debug scripts running inside chrome from within Vim. comment](https://github.com/puremourning/vimspector/issues/3#issuecomment-576916076) for instructions. +# Customisation + +There is very limited support for customistaion of the UI. + +## Changing the default signs + +Vimsector uses the following signs internally. If they are defined before +Vimsector uses them, they will not be replaced. So to customise the signs, +define them in your `vimrc`. + +* `vimspectorBP`: A breakpoint. +* `vimspectorBPDisabled`: A disabled breakpoint +* `vimspectorPC` The program counter, i.e. current line. + +For example, to use some unicode symbols, you could put this in your `vimrc`: + +```viml +sign define vimspectorBP text=🔴 texthl=Normal +sign define vimspectorBPDisabled text=🔵 texthl=Normal +sign define vimspectorPC text=🔶 texthl=SpellBad +``` + # FAQ 1. Q: Does it work? A: Yeah, sort of. It's _incredibly_ buggy and unpolished. diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 7e767d0..6e0b76f 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -52,9 +52,11 @@ class ProjectBreakpoints( object ): self._next_sign_id = 1 - # TODO: Change to sign_define ? - vim.command( 'sign define vimspectorBP text==> texthl=Error' ) - vim.command( 'sign define vimspectorBPDisabled text=!> texthl=Warning' ) + if not utils.SignDefined( 'vimspectorBP' ): + vim.command( 'sign define vimspectorBP text==> texthl=Error' ) + + if not utils.SignDefined( 'vimspectorBPDisabled' ): + vim.command( 'sign define vimspectorBPDisabled text=!> texthl=Warning' ) def ConnectionUp( self, connection ): diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 27d9586..4b21192 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -50,7 +50,8 @@ class CodeView( object ): vim.command( 'nnoremenu WinBar.Restart :call vimspector#Restart()' ) vim.command( 'nnoremenu WinBar.Reset :call vimspector#Reset()' ) - vim.command( 'sign define vimspectorPC text=-> texthl=Search' ) + if not utils.SignDefined( 'vimspectorPC' ): + vim.command( 'sign define vimspectorPC text=-> texthl=Search' ) def SetCurrentFrame( self, frame ): diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index a9f55df..b04f28f 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -20,6 +20,8 @@ import contextlib import vim import json import string +import functools + _log_handler = logging.FileHandler( os.path.expanduser( '~/.vimspector.log' ), mode = 'w' ) @@ -103,7 +105,7 @@ def SetUpHiddenBuffer( buf, name ): def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ): # This feature is _super_ new, so only enable when available - if not int( vim.eval( "exists( '*prompt_setprompt' )" ) ): + if not Exists( '*prompt_setprompt' ): return SetUpHiddenBuffer( buf, name ) buf.options[ 'buftype' ] = 'prompt' @@ -467,3 +469,35 @@ def Call( vimscript_function, *args ): call += ')' _logger.debug( 'Calling: {}'.format( call ) ) return vim.eval( call ) + + +def SignDefined( name ): + if Exists( "*sign_getdefined" ): + return int( vim.eval( f"len( sign_getdefined( '{ Escape( name ) }' ) )" ) ) + + return False + + +MEMO = {} + + +def memoize( func ): + global MEMO + + @functools.wraps( func ) + def wrapper( *args, **kwargs ): + dct = MEMO.setdefault( func, {} ) + key = ( args, frozenset( kwargs.items() ) ) + try: + return dct[ key ] + except KeyError: + result = func( *args, **kwargs ) + dct[ key ] = result + return result + + return wrapper + + +@memoize +def Exists( expr ): + return int( vim.eval( f'exists( "{ expr }" )' ) ) From 7903e0e0fe7cdcbd6977b4d408103c0e2a91a3ee Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 23:23:12 +0000 Subject: [PATCH 073/609] Add screenshot to README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 40c2b7f..9ea76c1 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,10 @@ language that Visual Studio Code supports (but see caveats). The [Vimspector website][website] has an overview of the UI, along with basic instructions for configuration and setup. +But for now, here's a (rather old) screenshot of Vimsepctor debugging Vim: + +![vimspector-vim-screenshot](https://puremourning.github.io/vimspector-web/img/vimspector-overview.png) + ## Supported debugging features - breakpoints (function, line and exception breakpoints) From c6e192d5f54ebeb7c411e1e865fea6cb22780475 Mon Sep 17 00:00:00 2001 From: panders23 Date: Wed, 22 Jan 2020 15:09:23 +0100 Subject: [PATCH 074/609] add felixfbecker/vscode-php-debug --- install_gadget.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/install_gadget.py b/install_gadget.py index 9b6f56f..ef4b46a 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -291,6 +291,29 @@ GADGETS = { }, }, }, + 'vscode-php-debug': { + 'language': 'php', + 'enabled': False, + 'download': { + 'url': 'https://github.com/felixfbecker/vscode-php-debug/releases/download/' + '${version}/${file_name}', + }, + 'all': { + 'version': 'v1.13.0', + 'file_name': 'php-debug.vsix', + 'checksum': + '8a51e593458fd14623c1c89ebab87347b087d67087717f18bcf77bb788052718', + }, + 'adapters': { + 'vscode-php-debug': { + 'name': "php-debug", + 'command': [ + 'node', + "${gadgetDir}/vscode-php-debug/out/phpDebug.js", + ] + } + } + }, 'vscode-node-debug2': { 'language': 'node', 'enabled': False, From eda6b890de1de084fb1eba850c434da0af0a005b Mon Sep 17 00:00:00 2001 From: panders23 Date: Mon, 27 Jan 2020 20:22:55 +0100 Subject: [PATCH 075/609] add readme.md entry for php --- README.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/README.md b/README.md index 9ea76c1..62053c8 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ For a tutorial and usage overview, take a look at the * [TCL](#tcl) * [C♯](#c) * [Go](#go) + * [PHP](#php) * [JavaScript, TypeScript, etc.](#javascript-typescript-etc) * [Java - partially supported](#java---partially-supported) * [FAQ](#faq) @@ -760,6 +761,77 @@ Requires: } ``` +## PHP + +This uses the php-debug, see +https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug + +Requires: + +* `install_gadget.py --force-enable-php` +* configured php xdebug extension +```ini +zend_extension=xdebug.so +xdebug.remote_enable=on +xdebug.remote_handler=dbgp +xdebug.remote_host=localhost +xdebug.remote_port=9000 +``` +replace `localhost` with the ip of your workstation. + +lazy alternative +```ini +zend_extension=xdebug.so +xdebug.remote_enable=on +xdebug.remote_handler=dbgp +xdebug.remote_connect_back=true +xdebug.remote_port=9000 +``` + +* .vimspectory.json +```json +{ + "configurations": { + "Listen for XDebug": { + "adapter": "vscode-php-debug", + "configuration": { + "name": "Listen for XDebug", + "type": "php", + "request": "launch", + "port": 9000, + "stopOnEntry": false, + "pathMappings": { + "/var/www/html": "${workspaceRoot}" + } + } + }, + "Launch currently open script": { + "adapter": "vscode-php-debug", + "configuration": { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 9000 + } + } + } +} +``` + +### Debug web application +append `XDEBUG_SESSION_START=xdebug` to your query string +``` +curl "http://localhost?XDEBUG_SESSION_START=xdebug" +``` + +### Debug cli application +``` +export XDEBUG_CONFIG="idekey=xdebug" +php +``` + ## JavaScript, TypeScript, etc. * Node.js From da0fc6533395e30fc5929d1eea0871b45c03966f Mon Sep 17 00:00:00 2001 From: panders23 Date: Mon, 27 Jan 2020 20:35:26 +0100 Subject: [PATCH 076/609] Mention xdebug helper extension for php --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 62053c8..f2b1a76 100644 --- a/README.md +++ b/README.md @@ -768,6 +768,7 @@ https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug Requires: +* (optional) Xdebug helper for chrome https://chrome.google.com/webstore/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc * `install_gadget.py --force-enable-php` * configured php xdebug extension ```ini @@ -825,6 +826,7 @@ append `XDEBUG_SESSION_START=xdebug` to your query string ``` curl "http://localhost?XDEBUG_SESSION_START=xdebug" ``` +or use the previously mentioned Xdebug Helper extension (which sets a `XDEBUG_SESSION` cookie) ### Debug cli application ``` From 1da905a955bb06bae8d7aaea004641ae0b6e5de8 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 21:58:55 +0000 Subject: [PATCH 077/609] Upgrade to macOS 10.14 In Mojave, the TCL headers were removed, so we install brew tcl. Oh and the all of the nonesense around authorizing applications to connect a debugger mean the tests don't work, so we force LLDB to use the system debugserver with LLDB_DEBUGSERVER_PATH. --- azure-pipelines.yml | 9 +++++++-- tests/testdata/cpp/simple/.vimspector.json | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 053df1f..3768745 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -61,6 +61,8 @@ stages: displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: gdb + VIMSPECTOR_TEST_STDOUT: true + VIMSPECTOR_MI_DEBUGGER: "" - bash: ./make_package linux $(Build.SourceVersion) displayName: 'Package' @@ -72,11 +74,11 @@ stages: - job: 'macos' pool: - vmImage: 'macOS-10.13' + vmImage: 'macOS-10.14' steps: - bash: | brew unlink node@6 - brew install macvim node@10 + brew install macvim node@10 tcl-tk brew link --force --overwrite node@10 displayName: 'Install vim and node' @@ -99,6 +101,9 @@ stages: displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: lldb + VIMSPECTOR_TEST_STDOUT: true + VIMSPECTOR_MI_DEBUGGER: "" + LLDB_DEBUGSERVER_PATH: "/Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Resources/debugserver" - bash: ./make_package macos $(Build.SourceVersion) displayName: 'Package' diff --git a/tests/testdata/cpp/simple/.vimspector.json b/tests/testdata/cpp/simple/.vimspector.json index d7a4377..e68ab85 100644 --- a/tests/testdata/cpp/simple/.vimspector.json +++ b/tests/testdata/cpp/simple/.vimspector.json @@ -9,10 +9,14 @@ "args": [], "cwd": "${workspaceRoot}", "environment": [], - "externalConsole": true, + "externalConsole": false, "stopAtEntry": true, "stopOnEntry": true, - "MImode": "${VIMSPECTOR_MIMODE}" + "MImode": "${VIMSPECTOR_MIMODE}", + "miDebuggerPath": "${VIMSPECTOR_MI_DEBUGGER}", + "logging": { + "engineLogging": true + } }, "breakpoints": { "exception": { From ea1a56ed0df915119bfb6185eb4e68368fb3f1a2 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Jan 2020 21:59:19 +0000 Subject: [PATCH 078/609] Update checksums in install_gadget.py --- install_gadget.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index ef4b46a..bf6ccfa 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -118,7 +118,7 @@ GADGETS = { 'version': '0.23.0', 'file_name': 'vscode-java-debug-0.23.0.vsix', 'checksum': - '', + 'f98a35c8dd4f3079ecd9657b04f1edbfea04c364c0fbf7eee1285f942782710b', }, 'adapters': { "vscode-java": { @@ -141,7 +141,7 @@ GADGETS = { 'version': '0.2.31', 'file_name': 'georgewfraser.vscode-javac-0.2.31.vsix.gz', 'checksum': - '', + '5b0248ec1198d3ece9a9c6b9433b30c22e308f0ae6e4c7bd09cd943c454e3e1d', }, 'adapters': { "vscode-javac": { @@ -238,7 +238,8 @@ GADGETS = { 'all': { 'file_name': 'bash-debug-0.3.6.vsix', 'version': 'v0.3.6', - 'checksum': '', + 'checksum': + '502ee5732851fc4f309294fc296a291b1a114008a1fbcb232f3763be2b8d9c1f', }, 'do': lambda name, root: InstallBashDebug( name, root ), 'adapters': { From 40bec48e97048b6f4b289bbe748661c8663f1530 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 27 Jan 2020 21:50:53 +0000 Subject: [PATCH 079/609] Use my lldb-mi build --- azure-pipelines.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3768745..09d5d8e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -78,7 +78,7 @@ stages: steps: - bash: | brew unlink node@6 - brew install macvim node@10 tcl-tk + brew install macvim node@10 tcl-tk llvm brew link --force --overwrite node@10 displayName: 'Install vim and node' @@ -97,12 +97,22 @@ stages: - bash: vim --version displayName: 'Print vim version information' + - bash: | + set -ex + mkdir -p lldb-mi + pushd lldb-mi + curl -LO https://github.com/puremourning/lldb-mi/releases/download/1041/lldb-mi-db65c20a3d8e09d9ff2f3748c01cf43ec035a4d8.tar.gz + tar zxvf lldb-mi-db65c20a3d8e09d9ff2f3748c01cf43ec035a4d8.tar.gz + cp usr/local/bin/lldb-mi /usr/local/bin + popd + displayName: 'Install lldb-mi' + - bash: ./run_tests displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: lldb VIMSPECTOR_TEST_STDOUT: true - VIMSPECTOR_MI_DEBUGGER: "" + VIMSPECTOR_MI_DEBUGGER: "/usr/local/bin/lldb-mi" LLDB_DEBUGSERVER_PATH: "/Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Resources/debugserver" - bash: ./make_package macos $(Build.SourceVersion) From 8253c1687b71c634c382bbc259de8019d0f5f53a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 28 Jan 2020 08:31:51 +0000 Subject: [PATCH 080/609] Document LaunchWithSettings This is useful enough to be made public. Closes #97. --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f2b1a76..b56803b 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ For a tutorial and usage overview, take a look at the * [Human Mode](#human-mode) * [Usage](#usage) * [Launch and attach by PID:](#launch-and-attach-by-pid) + * [Launch with options](#launch-with-options) * [Breakpoints](#breakpoints) * [Stepping](#stepping) * [Variables and scopes](#variables-and-scopes) @@ -44,12 +45,16 @@ For a tutorial and usage overview, take a look at the * [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 - partially supported](#java---partially-supported) + * [Customisation](#customisation) + * [Changing the default signs](#changing-the-default-signs) * [FAQ](#faq) * [License](#license) - + @@ -485,6 +490,36 @@ let g:vimspector_enable_mappings = 'HUMAN' * Create `vimspector.json`. See [below](#supported-languages). * `:call vimspector#Launch()` and select a configuration. +### Launch with options + +To launch a specific debug configuration, or specify [replacement +variables][vimspector-ref-var] for the launch, you can use: + +* `:call vimspector#LaunchWithSettings( dict )` + +The argument is a `dict` with the following keys: + +* `configuration`: (optional) Name of the debug configuration to launch +* ``: (optional) Name of a variable to set + +This allows for some intergration and automation. For example, if you have a +configuration named `Run Test` that contains a [replacement +variable][vimspector-ref-var] named `${Test}` you could write a mapping which +ultimately executes: + +```viml +vimspector#LaunchWithSettings( #{ configuration: 'Run Test' + \ Test: 'Name of the test' } ) +``` + +This would start the `Run Test` configuration with `${Test}` set to `'Name of +the test'` and Vimspector would _not_ prompt the user to enter or confirm these +things. + +See [this issue](https://github.com/puremourning/vimspector/issues/97) for +another example where it can be used to specify the port to connect the [java +debugger](#java---partially-supported) + ## Breakpoints * Use `vimspector#ToggleBreakpoint()` to set/disable/delete a line breakpoint. @@ -552,9 +587,12 @@ To close the debugger, use: # Debug adapter configuration -For more information on the configuration of `.vimspector.json`, take a look at +For an introduction to the configuration of `.vimspector.json`, take a look at the Getting Started section of the [Vimspector website][website]. +For full explanation, including how to use variables, substitutions and how to +specify exception breakpoints, see [the docs](vimspector-ref). + Current tested with the following debug adapters. ## C, C++, Rust, etc. @@ -951,3 +989,5 @@ Copyright © 2018 Ben Jackson [website]: https://puremourning.github.io/vimspector-web/ [delve]: https://github.com/go-delve/delve [delve-install]: https://github.com/go-delve/delve/tree/master/Documentation/installation +[vimspector-ref]: https://puremourning.github.io/vimspector/configuration.html +[vimspector-ref-var]: https://puremourning.github.io/vimspector/configuration.html#replacements-and-variables From 23b3bb719babaa8a007ca9be7bd1546ed5713844 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 28 Jan 2020 08:45:14 +0000 Subject: [PATCH 081/609] Document exception breakpints --- README.md | 5 ++++- docs/configuration.md | 46 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b56803b..60c446c 100644 --- a/README.md +++ b/README.md @@ -974,8 +974,10 @@ sign define vimspectorPC text=🔶 texthl=SpellBad necessarily be easy to work out what to put in the `.vimspector.json`. As you can see above, some of the servers aren't really editor agnostic, and require very-specific unique handling. -3. How do i stop it starting a new Terminal.app on macOS? See [this +3. How do I stop it starting a new Terminal.app on macOS? See [this comment](https://github.com/puremourning/vimspector/issues/90#issuecomment-577857322) +4. Can I specify answers to the annoying questions about exception breakpoints + in my `vimspector.json` ? Yes, see [here][vimspector-ref-exception]. # License @@ -991,3 +993,4 @@ Copyright © 2018 Ben Jackson [delve-install]: https://github.com/go-delve/delve/tree/master/Documentation/installation [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 diff --git a/docs/configuration.md b/docs/configuration.md index 97418e1..3c4a827 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -6,6 +6,23 @@ title: Configuration This document defines the supported format for project and adapter configuration for Vimspector. + + * [Concepts](#concepts) + * [Debug adapter configuration](#debug-adapter-configuration) + * [Debug profile configuration](#debug-profile-configuration) + * [Replacements and variables](#replacements-and-variables) + * [Configuration Format](#configuration-format) + * [Files and locations](#files-and-locations) + * [Adapter configurations](#adapter-configurations) + * [Debug configurations](#debug-configurations) + * [Exception breakpionts](#exception-breakpionts) + * [Predefined Variables](#predefined-variables) + * [Appendix: Editor configuration](#appendix-editor-configuration) + + + + + ## Concepts As Vimspector supports debugging arbitrary projects, you need to tell it a few @@ -235,6 +252,35 @@ typical example looks like this: } ``` +### Exception breakpionts + +Debug adapters have arbitrary configuration for exception breakpoints. Normally +this is presented as a series of question to the user on startnig the debug +session. The question includes the name of the exception breakpoint option, +the default and the list of valid responses (usually `Y` or `N`). + +You can pre-configure the answers to these questions in the `breakpoints` +section of the debug configuration. For each question, take the name provided +and configure the response `exception` mapping in the `breakpoints` mapping. If +the configured response is empty string, the debug adapter default will be used. + +Referring to the above example, the following tells the debug adapter to use the +default value for `caught` exceptoins and to break on `uncaught` exception: + +``` +{ + "configurations": { + "example-debug-configuration": { + "adapter": "example-adapter-name", + "breakpoints": { + "exception": { + "caught": "", + "uncaught": "Y" + } + }, + ... +``` + ## Predefined Variables The following variables are provided: From b915ada0c85eae732012d119537222907886cba4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 28 Jan 2020 20:51:06 +0000 Subject: [PATCH 082/609] Update issue template a bit more for neovim --- .github/ISSUE_TEMPLATE/bug_report.md | 19 +++++++++++++++++-- CONTRIBUTING.md | 5 +++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e4579bb..f049d29 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -38,13 +38,16 @@ Vim. NOTE: Windows is not supported. There is a branch with windows support, which you can contribute to, rather than opening an issue. -* Output of `vim --version` +* Version of Vimspector: (e.g. output of `git rev-parse HEAD` if cloned or the + name of the tarball used to install otherwise) + +* Output of `vim --version` or `nvim --version` ``` paste here ``` -* Output of `which vim`: +* Output of `which vim` or `which nvim`: ``` paste here @@ -56,6 +59,18 @@ paste here paste here ``` +* Output of `:py3 import vim`: + +``` +paste here +``` + +* For neovim: output of `:checkhealth` + +``` +paste here +``` + * Operating system: and version **Additional context** diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 133709d..2ae7717 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,11 @@ of the following reasons: * The issue does not represent a Vimspector bug or feature request * etc. +Issue titles are important. It's not usually helpful to write a title like +`Issue with Vimspector` or `Issue configuring` or even pasting an error message. +Spend a minute to come up with a consise summary of the problem. This helps with +management of issues, with triage, and above all with searching. + But above all else, please *please* complete the issue template. I know it is a little tedious to get all the various diagnostics, but you *must* provide them, *even if you think they are irrelevant*. This is important, because the From ab56d2cef498c869419805770f058af68ab8fd50 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 28 Jan 2020 20:51:29 +0000 Subject: [PATCH 083/609] Import the vim module In Vim, the vim module is always imported by magic in the global scope, but the docs suggest that you're supposed to import it anyway. In NeoVim it's never imported so we were relying on some other plugins having already imported it. --- autoload/vimspector/internal/state.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index 11ed003..4e048c4 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -27,6 +27,7 @@ function! vimspector#internal#state#Reset() abort let prefix='neo' endif py3 << EOF +import vim from vimspector import debug_session _vimspector_session = debug_session.DebugSession( vim.eval( 'prefix' ) ) EOF From bd794526b71807092ff9357914d235a370bfa912 Mon Sep 17 00:00:00 2001 From: Alexander Tuzhikov Date: Tue, 28 Jan 2020 16:11:21 -0800 Subject: [PATCH 084/609] Add a --no-ssl flag that switches off certificate verification --- .gitignore | 1 + install_gadget.py | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 681e4fd..df249b4 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ README.md.toc.* .DS_Store *.vimspector.log support/test/csharp/*.exe* +.neomake.log diff --git a/install_gadget.py b/install_gadget.py index bf6ccfa..3108ae2 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -35,6 +35,7 @@ import sys import json import functools import time +import ssl try: from io import BytesIO # for Python 3 @@ -484,7 +485,7 @@ def UrlOpen( *args, **kwargs ): return urllib2.urlopen( *args, **kwargs ) -def DownloadFileTo( url, destination, file_name = None, checksum = None ): +def DownloadFileTo( url, destination, file_name = None, checksum = None, sslcheck = True): if not file_name: file_name = url.split( '/' )[ -1 ] @@ -509,7 +510,12 @@ def DownloadFileTo( url, destination, file_name = None, checksum = None ): print( "Downloading {} to {}/{}".format( url, destination, file_name ) ) - with contextlib.closing( UrlOpen( r ) ) as u: + if not sslcheck: + kwargs = {"context": ssl._create_unverified_context()} + else: + kwargs = {} + + with contextlib.closing( UrlOpen( r, **kwargs ) ) as u: with open( file_path, 'wb' ) as f: f.write( u.read() ) @@ -662,6 +668,13 @@ for name, gadget in GADGETS.items(): help = "Don't install the {} debug adapter for {} support " '(when supplying --all)'.format( name, lang ) ) +parser.add_argument( + "--no-ssl", + action = "store_true", + help = "Switch the gadget download ssl certificate verification" + "off (use only in you have a self-signed certificate in your chain)" +) + args = parser.parse_args() if args.force_all and not args.all: @@ -693,12 +706,14 @@ for name, gadget in GADGETS.items(): destination = os.path.join( gadget_dir, 'download', name, v[ 'version' ] ) url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v ) + verify_cert_off = getattr(args, "no_ssl") file_path = DownloadFileTo( url, destination, file_name = gadget[ 'download' ].get( 'target' ), - checksum = v.get( 'checksum' ) ) + checksum = v.get( 'checksum' ), + sslcheck = not verify_cert_off) root = os.path.join( destination, 'root' ) ExtractZipTo( file_path, root, From 96a7083f292afd182f786d7f045624b38509f2fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adelar=20da=20Silva=20Queir=C3=B3z?= Date: Wed, 29 Jan 2020 00:16:13 -0300 Subject: [PATCH 085/609] Spell correction --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 60c446c..36cdc75 100644 --- a/README.md +++ b/README.md @@ -728,7 +728,7 @@ See [my fork of TclProDebug](https://github.com/puremourning/TclProDebug) for in * C# - dotnet core -Requires `install_gadget.py --force-enable-c-sharp` +Requires `install_gadget.py --force-enable-csharp` ```json { @@ -748,7 +748,7 @@ Requires `install_gadget.py --force-enable-c-sharp` * C# - mono -Requires `install_gadget.py --force-enable-c-sharp`. +Requires `install_gadget.py --force-enable-csharp`. ***Known not to work.**** From 309c29241347d67f39e73b208fed92e42b2665d9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 17 Jan 2020 17:44:44 +0000 Subject: [PATCH 086/609] Store variables supplied _before_ parsing adapter/config varibales --- python3/vimspector/debug_session.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 474e3d0..44afd72 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -171,6 +171,12 @@ class DebugSession( object ): # working directory. 'cwd': os.getcwd(), } + + # Pretend that vars passed to the launch command were typed in by the user + # (they may have been in theory) + USER_CHOICES.update( launch_variables ) + self._variables.update( launch_variables ) + self._variables.update( utils.ParseVariables( adapter.get( 'variables', {} ), self._variables, @@ -180,12 +186,6 @@ class DebugSession( object ): self._variables, USER_CHOICES ) ) - # Pretend that vars passed to the launch command were typed in by the user - # (they may have been in theory) - # TODO: Is it right that we do this _after_ ParseVariables, rather than - # before ? - USER_CHOICES.update( launch_variables ) - self._variables.update( launch_variables ) utils.ExpandReferencesInDict( configuration, self._variables, From 8d4ec3f53a218a473177ed43347baccb94e57a0f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 17 Jan 2020 17:45:02 +0000 Subject: [PATCH 087/609] Supress errors in more appropriate places --- python3/vimspector/debug_session.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 44afd72..cba77f5 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -268,7 +268,6 @@ class DebugSession( object ): return fct(self, *args, **kwargs) return wrapper - @IfConnected def OnChannelData( self, data ): self._connection.OnData( data ) @@ -279,7 +278,6 @@ class DebugSession( object ): self._outputView.Print( 'server', data ) - @IfConnected def OnRequestTimeout( self, timer_id ): self._connection.OnRequestTimeout( timer_id ) @@ -365,20 +363,25 @@ class DebugSession( object ): def Pause( self ): self._stackTraceView.Pause() + @IfConnected def ExpandVariable( self ): self._variablesView.ExpandVariable() + @IfConnected def AddWatch( self, expression ): self._variablesView.AddWatch( self._stackTraceView.GetCurrentFrame(), expression ) + @IfConnected def EvaluateConsole( self, expression ): self._outputView.Evaluate( self._stackTraceView.GetCurrentFrame(), expression ) + @IfConnected def DeleteWatch( self ): self._variablesView.DeleteWatch() + @IfConnected def ShowBalloon( self, winnr, expression ): """Proxy: ballonexpr -> variables.ShowBallon""" frame = self._stackTraceView.GetCurrentFrame() @@ -397,6 +400,7 @@ class DebugSession( object ): # Return variable aware function return self._variablesView.ShowBalloon( frame, expression ) + @IfConnected def ExpandFrameOrThread( self ): self._stackTraceView.ExpandFrameOrThread() From 74eb94fe530871093431aac0449bf38c50fbd5f4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 17 Jan 2020 17:45:18 +0000 Subject: [PATCH 088/609] Allow PID to be set by variables, e.g. shell --- python3/vimspector/debug_session.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index cba77f5..6f65a0b 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -605,8 +605,10 @@ class DebugSession( object ): self._outputView.RunJobWithOutput( 'Remote', cmd ) else: if atttach_config[ 'pidSelect' ] == 'ask': - pid = utils.AskForInput( 'Enter PID to attach to: ' ) - launch_config[ atttach_config[ 'pidProperty' ] ] = pid + prop = atttach_config[ 'pidProperty' ] + if prop not in launch_config: + pid = utils.AskForInput( 'Enter PID to attach to: ' ) + launch_config[ prop ] = pid return elif atttach_config[ 'pidSelect' ] == 'none': return From 018ae05de81c6414402cd460356c4af07f95ca6e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 17 Jan 2020 17:50:54 +0000 Subject: [PATCH 089/609] Add 'example' allowing simple command-line attachment --- support/test/example/attach.vim | 13 +++++++++++++ support/test/example/cpp.json | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 support/test/example/attach.vim create mode 100644 support/test/example/cpp.json diff --git a/support/test/example/attach.vim b/support/test/example/attach.vim new file mode 100644 index 0000000..dda00a9 --- /dev/null +++ b/support/test/example/attach.vim @@ -0,0 +1,13 @@ +if argc() < 2 + echom 'Usage:' v:argv[ 0 ] 'processName binary' + cquit! +endif + +setfiletype cpp +call vimspector#LaunchWithSettings( #{ + \ configuration: "C++ - Attach Local Process", + \ processName: argv( 0 ), + \ binary: argv( 1 ), + \ } ) + +1,2argd diff --git a/support/test/example/cpp.json b/support/test/example/cpp.json new file mode 100644 index 0000000..ba52fcd --- /dev/null +++ b/support/test/example/cpp.json @@ -0,0 +1,24 @@ +{ + "configurations": { + "C++ - Attach Local Process": { + "adapter": "vscode-cpptools", + "variables": { + "PID": { + "shell": [ "GetPIDForProcess", "${processName}" ] + } + }, + "configuration": { + "name": "test", + "request": "attach", + "program": "${binary}", + "processId": "${PID}", + + "type": "cppdbg", + "stopAtEntry": true, + "setupCommands": [ + { "text": "source ${initFile}", "ignoreFailures": true } + ] + } + } + } +} From 7f3d7a46e467c789e925795fc773a8b9d30ae340 Mon Sep 17 00:00:00 2001 From: Alexander Tuzhikov Date: Fri, 31 Jan 2020 08:50:06 -0800 Subject: [PATCH 090/609] Address cl requests in pr-103; --- install_gadget.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 3108ae2..239936c 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -511,7 +511,10 @@ def DownloadFileTo( url, destination, file_name = None, checksum = None, sslchec print( "Downloading {} to {}/{}".format( url, destination, file_name ) ) if not sslcheck: - kwargs = {"context": ssl._create_unverified_context()} + context = ssl.create_default_context() + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + kwargs = { "context": context } else: kwargs = {} @@ -669,10 +672,9 @@ for name, gadget in GADGETS.items(): '(when supplying --all)'.format( name, lang ) ) parser.add_argument( - "--no-ssl", + "--no-check-certificate", action = "store_true", - help = "Switch the gadget download ssl certificate verification" - "off (use only in you have a self-signed certificate in your chain)" + help = "Do not verify SSL certificates for file downloads." ) args = parser.parse_args() @@ -706,7 +708,7 @@ for name, gadget in GADGETS.items(): destination = os.path.join( gadget_dir, 'download', name, v[ 'version' ] ) url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v ) - verify_cert_off = getattr(args, "no_ssl") + verify_cert_off = args.no_check_certificate file_path = DownloadFileTo( url, From 6bcc58a39b342ed3706ce5dbb4595e5ec97022c2 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 31 Jan 2020 22:55:25 +0000 Subject: [PATCH 091/609] Document how to use debugpy directly instead of vscode-python --- README.md | 46 ++++++++++++++++++- .../python/simple_python/.vimspector.json | 30 ++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 36cdc75..099741d 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ The debug adapters themselves have certain runtime dependencies: | Language | Status | Switch | Adapter | Dependencies | |------------------|--------------|------------------------------|-------------------|------------------------| | C, C++, etc. | Supported | `--all` or ` --enable-c` | vscode-cpptools | mono-core | -| Python | Supported | `--all` or `--enable-python` | vscode-python | Python 2.7 or Python 3 | +| Python | Supported | `--all` or `--enable-python` | vscode-python | Node 10, Python 2.7 or Python 3 | | TCL | Experimental | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | | Bourne Shell | Experimental | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | | C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | @@ -695,7 +695,14 @@ For `lldb-vscode` replace the name of the adapter with `lldb-vscode`: ## Python +NOTE: Please see the alternative approach below, as this will become the +standard approach in future. + * Python: [vscode-python](https://github.com/Microsoft/vscode-python) +* NOTE: You must be running `node` 10. See [this issue](https://github.com/puremourning/vimspector/issues/105) + +I recommend installing `nvm` and then `nvm use 10` prior to starting your Vim +session. ``` { @@ -718,6 +725,43 @@ For `lldb-vscode` replace the name of the adapter with `lldb-vscode`: } ``` +### Alternative: Use debugpy directly + +If you can't get a node 10 environment set up for whatver reason, then you can +avoid that issue by using `debugpy` (formerly `ptvsd`) directly. + +Here's how: + +1. Instal `debugpy`: `pip install debugpy` +2. Create `/path/to/vimspector/gadgets//.gadgets.d/debugpy.json`: + +```json +{ + "adapters": { + "debugpy": { + "command": [ + "python", + "-m", + "debugpy.adapter" + ], + "name": "debugpy", + "configuration": { + "python": "python" + } + } + } +} +``` + +Then in theory you should just have to change `"adapter": "vscode-python"` to +`"adapter": "debugpy"`. + +See `support/test/python/simple_python/.vimspector.json` as an example. + +NOTE: This will likely become the default in future, and vscode-python will be +phased out. + + ## TCL * TCL (TclProDebug) diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 3de14d8..7b7761e 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -1,4 +1,17 @@ { + "adapters": { + "debugpy": { + "command": [ + "python", + "-m", + "debugpy.adapter" + ], + "name": "debugpy", + "configuration": { + "python": "python" + } + } + }, "configurations": { "run": { "adapter": "vscode-python", @@ -31,6 +44,23 @@ "uncaught": "" } } + }, + "run - debugpy": { + "adapter": "debugpy", + "configuration": { + "request": "launch", + "type": "python", + "cwd": "${workspaceRoot}", + "program": "${file}", + "stopOnEntry": true, + "console": "integratedTerminal" + }, + "breakpoints": { + "exception": { + "raised": "N", + "uncaught": "" + } + } } } } From 44efc0a1b34f360a05166bc98dc3ff65366039f1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 1 Feb 2020 00:06:49 +0000 Subject: [PATCH 092/609] Neovim 0.4 doesn't work for debugpy because it is missing features --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 099741d..bcb2ed7 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,7 @@ neovim doesn't implement some features Vimspector relies on: the output window's current output. * Prompt Buffers - used to send commands in the Console and add Watches * Balloons - used to display the values of variables when debugging. +* Environment variables when laucnhing debugee in embedded terminal. Workarounds are in place as follows: @@ -185,6 +186,9 @@ Workarounds are in place as follows: There is no workaroud for the lack of balloons; you'll just have to use `:VimspectorEval` or `:VimspectorWatch`, or switch to Vim. +The only workaround for the missing environment variables feature is to use +neovim master (it doesn't work in neovim 0.4). + ## Language dependencies The debug adapters themselves have certain runtime dependencies: @@ -727,6 +731,10 @@ session. ### Alternative: Use debugpy directly +*** NOTE: This solution does not work in NeoVim 0.4 due to missing environment +variables support when launching a terminal. Do not raise issues about this if +you are using NeoVim 0.4. *** + If you can't get a node 10 environment set up for whatver reason, then you can avoid that issue by using `debugpy` (formerly `ptvsd`) directly. From 3f3ede48ba7e0452391e534125850972861b7b75 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 1 Feb 2020 00:08:43 +0000 Subject: [PATCH 093/609] Update TOC --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bcb2ed7..76df4a7 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ For a tutorial and usage overview, take a look at the * [Debug adapter configuration](#debug-adapter-configuration) * [C, C , Rust, etc.](#c-c-rust-etc) * [Python](#python) + * [Alternative: Use debugpy directly](#alternative-use-debugpy-directly) * [TCL](#tcl) * [C♯](#c) * [Go](#go) @@ -54,7 +55,7 @@ For a tutorial and usage overview, take a look at the * [FAQ](#faq) * [License](#license) - + From 896b20f49004831fa2378c5e344f1804cdf35fa6 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 2 Feb 2020 17:31:05 +0000 Subject: [PATCH 094/609] Hackaround for missing 'env' in termopen and jobstart in neovim --- autoload/vimspector/internal/neojob.vim | 30 ++++++++++++------- autoload/vimspector/internal/neoterm.vim | 38 ++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index f885337..2a87c4c 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -44,17 +44,27 @@ function! vimspector#internal#neojob#StartDebugSession( config ) abort return v:false endif - let s:job = jobstart( a:config[ 'command' ], - \ { - \ 'on_stdout': funcref( 's:_OnEvent' ), - \ 'on_stderr': funcref( 's:_OnEvent' ), - \ 'on_exit': funcref( 's:_OnEvent' ), - \ 'cwd': a:config[ 'cwd' ], - \ 'env': a:config[ 'env' ], - \ } - \ ) - " FIXME: env might not work: Missing in neovim 0.4. But in master: + " HACK: Workaround for 'env' not being supported. + + let old_env={} + try + let old_env = vimspector#internal#neoterm#PrepareEnvironment( + \ a:config[ 'env' ] ) + let s:job = jobstart( a:config[ 'command' ], + \ { + \ 'on_stdout': funcref( 's:_OnEvent' ), + \ 'on_stderr': funcref( 's:_OnEvent' ), + \ 'on_exit': funcref( 's:_OnEvent' ), + \ 'cwd': a:config[ 'cwd' ], + \ 'env': a:config[ 'env' ], + \ } + \ ) + finally + call vimspector#internal#neoterm#ResetEnvironment( a:config[ 'env' ], + \ old_env ) + endtry + return v:true endfunction diff --git a/autoload/vimspector/internal/neoterm.vim b/autoload/vimspector/internal/neoterm.vim index ab71b2f..627f570 100644 --- a/autoload/vimspector/internal/neoterm.vim +++ b/autoload/vimspector/internal/neoterm.vim @@ -25,6 +25,27 @@ set cpoptions&vim " FIXME: Tidy this map when buffers are closed ? let s:buffer_to_id = {} +function! vimspector#internal#neoterm#PrepareEnvironment( env ) abort + let old_env = {} + + let new_env = copy( environ() ) + for key in keys( a:env ) + if has_key( new_env, key ) + let old_env[ key ] = new_env[ key ] + endif + call setenv( key, a:env[ key ] ) + endfor + + return old_env +endfunction + +function! vimspector#internal#neoterm#ResetEnvironment( env, old_env ) abort + for key in keys( a:env ) + let value = get( a:old_env, key, v:null ) + call setenv( key, value ) + endfor +endfunction + function! vimspector#internal#neoterm#Start( cmd, opts ) abort if ! get( a:opts, 'curwin', 0 ) if get( a:opts, 'vertical', 0 ) @@ -34,8 +55,21 @@ function! vimspector#internal#neoterm#Start( cmd, opts ) abort endif endif - " FIXME: 'env' doesn't work - let id = termopen( a:cmd, { 'cwd': a:opts[ 'cwd' ] } ) + " HACK: Neovim's termopen doesn't support env + + let old_env={} + try + let old_env = vimspector#internal#neoterm#PrepareEnvironment( + \ a:opts[ 'env' ] ) + let id = termopen( a:cmd, { + \ 'cwd': a:opts[ 'cwd' ], + \ 'env': a:opts[ 'env' ], + \ } ) + finally + call vimspector#internal#neoterm#ResetEnvironment( a:opts[ 'env' ], + \ old_env ) + endtry + let bufnr = bufnr() let s:buffer_to_id[ bufnr ] = id return bufnr From 6878c80cfbfe993fe3a2213c98d5762f588a373e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 2 Feb 2020 17:33:33 +0000 Subject: [PATCH 095/609] Update readme for environment hackaround --- README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.md b/README.md index 76df4a7..1811a04 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,6 @@ neovim doesn't implement some features Vimspector relies on: the output window's current output. * Prompt Buffers - used to send commands in the Console and add Watches * Balloons - used to display the values of variables when debugging. -* Environment variables when laucnhing debugee in embedded terminal. Workarounds are in place as follows: @@ -187,9 +186,6 @@ Workarounds are in place as follows: There is no workaroud for the lack of balloons; you'll just have to use `:VimspectorEval` or `:VimspectorWatch`, or switch to Vim. -The only workaround for the missing environment variables feature is to use -neovim master (it doesn't work in neovim 0.4). - ## Language dependencies The debug adapters themselves have certain runtime dependencies: @@ -732,10 +728,6 @@ session. ### Alternative: Use debugpy directly -*** NOTE: This solution does not work in NeoVim 0.4 due to missing environment -variables support when launching a terminal. Do not raise issues about this if -you are using NeoVim 0.4. *** - If you can't get a node 10 environment set up for whatver reason, then you can avoid that issue by using `debugpy` (formerly `ptvsd`) directly. From db95fe0c1dbd6ab71f1b98f7739704542b38d357 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 2 Feb 2020 18:27:01 +0000 Subject: [PATCH 096/609] Update github pages to fix website, maybe --- docs/Gemfile.lock | 145 +++++++++++++++++++++++----------------------- docs/README.local | 13 +++++ 2 files changed, 85 insertions(+), 73 deletions(-) create mode 100644 docs/README.local diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 6e0170b..7163701 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,11 +1,12 @@ GEM remote: https://rubygems.org/ specs: - activesupport (4.2.11.1) - i18n (~> 0.7) + activesupport (6.0.2.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) + zeitwerk (~> 2.2) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) coffee-script (2.4.1) @@ -25,33 +26,32 @@ GEM ffi (>= 1.3.0) eventmachine (1.2.7) execjs (2.7.0) - faraday (0.17.0) + faraday (1.0.0) multipart-post (>= 1.2, < 3) - ffi (1.11.1) + ffi (1.12.2) forwardable-extended (2.6.0) gemoji (3.0.1) - github-pages (202) - activesupport (= 4.2.11.1) + github-pages (204) github-pages-health-check (= 1.16.1) jekyll (= 3.8.5) - jekyll-avatar (= 0.6.0) + jekyll-avatar (= 0.7.0) jekyll-coffeescript (= 1.1.1) jekyll-commonmark-ghpages (= 0.1.6) jekyll-default-layout (= 0.1.4) - jekyll-feed (= 0.11.0) + jekyll-feed (= 0.13.0) jekyll-gist (= 1.5.0) - jekyll-github-metadata (= 2.12.1) - jekyll-mentions (= 1.4.1) - jekyll-optional-front-matter (= 0.3.0) + jekyll-github-metadata (= 2.13.0) + jekyll-mentions (= 1.5.1) + jekyll-optional-front-matter (= 0.3.2) jekyll-paginate (= 1.1.0) - jekyll-readme-index (= 0.2.0) - jekyll-redirect-from (= 0.14.0) - jekyll-relative-links (= 0.6.0) - jekyll-remote-theme (= 0.4.0) + jekyll-readme-index (= 0.3.0) + jekyll-redirect-from (= 0.15.0) + jekyll-relative-links (= 0.6.1) + jekyll-remote-theme (= 0.4.1) jekyll-sass-converter (= 1.5.2) - jekyll-seo-tag (= 2.5.0) - jekyll-sitemap (= 1.2.0) - jekyll-swiss (= 0.4.0) + jekyll-seo-tag (= 2.6.1) + jekyll-sitemap (= 1.4.0) + jekyll-swiss (= 1.0.0) jekyll-theme-architect (= 0.1.1) jekyll-theme-cayman (= 0.1.1) jekyll-theme-dinky (= 0.1.1) @@ -61,19 +61,18 @@ GEM jekyll-theme-midnight (= 0.1.1) jekyll-theme-minimal (= 0.1.1) jekyll-theme-modernist (= 0.1.1) - jekyll-theme-primer (= 0.5.3) + jekyll-theme-primer (= 0.5.4) jekyll-theme-slate (= 0.1.1) jekyll-theme-tactile (= 0.1.1) jekyll-theme-time-machine (= 0.1.1) - jekyll-titles-from-headings (= 0.5.1) - jemoji (= 0.10.2) + jekyll-titles-from-headings (= 0.5.3) + jemoji (= 0.11.1) kramdown (= 1.17.0) - liquid (= 4.0.0) - listen (= 3.1.5) + liquid (= 4.0.3) mercenary (~> 0.3) - minima (= 2.5.0) + minima (= 2.5.1) nokogiri (>= 1.10.4, < 2.0) - rouge (= 3.11.0) + rouge (= 3.13.0) terminal-table (~> 1.4) github-pages-health-check (1.16.1) addressable (~> 2.3) @@ -81,7 +80,7 @@ GEM octokit (~> 4.0) public_suffix (~> 3.0) typhoeus (~> 1.3) - html-pipeline (2.12.0) + html-pipeline (2.12.3) activesupport (>= 2) nokogiri (>= 1.4) http_parser.rb (0.6.0) @@ -100,8 +99,8 @@ GEM pathutil (~> 0.9) rouge (>= 1.7, < 4) safe_yaml (~> 1.0) - jekyll-avatar (0.6.0) - jekyll (~> 3.0) + jekyll-avatar (0.7.0) + jekyll (>= 3.0, < 5.0) jekyll-coffeescript (1.1.1) coffee-script (~> 2.2) coffee-script-source (~> 1.11.1) @@ -114,36 +113,36 @@ GEM rouge (>= 2.0, < 4.0) jekyll-default-layout (0.1.4) jekyll (~> 3.0) - jekyll-feed (0.11.0) - jekyll (~> 3.3) + jekyll-feed (0.13.0) + jekyll (>= 3.7, < 5.0) jekyll-gist (1.5.0) octokit (~> 4.2) - jekyll-github-metadata (2.12.1) - jekyll (~> 3.4) + jekyll-github-metadata (2.13.0) + jekyll (>= 3.4, < 5.0) octokit (~> 4.0, != 4.4.0) - jekyll-mentions (1.4.1) + jekyll-mentions (1.5.1) html-pipeline (~> 2.3) - jekyll (~> 3.0) - jekyll-optional-front-matter (0.3.0) - jekyll (~> 3.0) + jekyll (>= 3.7, < 5.0) + jekyll-optional-front-matter (0.3.2) + jekyll (>= 3.0, < 5.0) jekyll-paginate (1.1.0) - jekyll-readme-index (0.2.0) - jekyll (~> 3.0) - jekyll-redirect-from (0.14.0) - jekyll (~> 3.3) - jekyll-relative-links (0.6.0) - jekyll (~> 3.3) - jekyll-remote-theme (0.4.0) + jekyll-readme-index (0.3.0) + jekyll (>= 3.0, < 5.0) + jekyll-redirect-from (0.15.0) + jekyll (>= 3.3, < 5.0) + jekyll-relative-links (0.6.1) + jekyll (>= 3.3, < 5.0) + jekyll-remote-theme (0.4.1) addressable (~> 2.0) - jekyll (~> 3.5) - rubyzip (>= 1.2.1, < 3.0) + jekyll (>= 3.5, < 5.0) + rubyzip (>= 1.3.0) jekyll-sass-converter (1.5.2) sass (~> 3.4) - jekyll-seo-tag (2.5.0) - jekyll (~> 3.3) - jekyll-sitemap (1.2.0) - jekyll (~> 3.3) - jekyll-swiss (0.4.0) + jekyll-seo-tag (2.6.1) + jekyll (>= 3.3, < 5.0) + jekyll-sitemap (1.4.0) + jekyll (>= 3.7, < 5.0) + jekyll-swiss (1.0.0) jekyll-theme-architect (0.1.1) jekyll (~> 3.5) jekyll-seo-tag (~> 2.0) @@ -171,8 +170,8 @@ GEM jekyll-theme-modernist (0.1.1) jekyll (~> 3.5) jekyll-seo-tag (~> 2.0) - jekyll-theme-primer (0.5.3) - jekyll (~> 3.5) + jekyll-theme-primer (0.5.4) + jekyll (> 3.5, < 5.0) jekyll-github-metadata (~> 2.9) jekyll-seo-tag (~> 2.0) jekyll-theme-slate (0.1.1) @@ -184,43 +183,42 @@ GEM jekyll-theme-time-machine (0.1.1) jekyll (~> 3.5) jekyll-seo-tag (~> 2.0) - jekyll-titles-from-headings (0.5.1) - jekyll (~> 3.3) + jekyll-titles-from-headings (0.5.3) + jekyll (>= 3.3, < 5.0) jekyll-watch (2.2.1) listen (~> 3.0) - jemoji (0.10.2) + jemoji (0.11.1) gemoji (~> 3.0) html-pipeline (~> 2.2) - jekyll (~> 3.0) + jekyll (>= 3.0, < 5.0) kramdown (1.17.0) - liquid (4.0.0) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) + liquid (4.0.3) + listen (3.2.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) mini_portile2 (2.4.0) - minima (2.5.0) - jekyll (~> 3.5) + minima (2.5.1) + jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.13.0) + minitest (5.14.0) multipart-post (2.1.1) - nokogiri (1.10.5) + nokogiri (1.10.7) mini_portile2 (~> 2.4.0) - octokit (4.14.0) + octokit (4.15.0) + faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (3.1.1) rb-fsevent (0.10.3) - rb-inotify (0.10.0) + rb-inotify (0.10.1) ffi (~> 1.0) - rouge (3.11.0) + rouge (3.13.0) ruby-enum (0.7.2) i18n - ruby_dep (1.5.0) - rubyzip (2.0.0) + rubyzip (2.2.0) safe_yaml (1.0.5) sass (3.7.4) sass-listen (~> 4.0.0) @@ -235,9 +233,10 @@ GEM thread_safe (0.3.6) typhoeus (1.3.1) ethon (>= 0.9.0) - tzinfo (1.2.5) + tzinfo (1.2.6) thread_safe (~> 0.1) - unicode-display_width (1.6.0) + unicode-display_width (1.6.1) + zeitwerk (2.2.2) PLATFORMS ruby diff --git a/docs/README.local b/docs/README.local new file mode 100644 index 0000000..7848fba --- /dev/null +++ b/docs/README.local @@ -0,0 +1,13 @@ +To update/install: + +gem install bundler +bundle install + +To run a local server/test the build + +bundle exec jekyll serve + +To update deps + +bundle update +or bundle update github-pages From 19cc58f09e528fdd53f6d6d8f438112f0a1f6f22 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 4 Feb 2020 13:04:09 +0000 Subject: [PATCH 097/609] Set syntax in stack trace too --- python3/vimspector/debug_session.py | 1 + python3/vimspector/stack_trace.py | 54 ++++++++++++++++++----------- python3/vimspector/utils.py | 14 ++++++++ python3/vimspector/variables.py | 17 +++------ 4 files changed, 52 insertions(+), 34 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 6f65a0b..c85d752 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -483,6 +483,7 @@ class DebugSession( object ): if frame: self._variablesView.SetSyntax( self._codeView.current_syntax ) + self._stackTraceView.SetSyntax( self._codeView.current_syntax ) self._variablesView.LoadScopes( frame ) self._variablesView.EvaluateWatches() else: diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index cf3be50..96edb4d 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -29,14 +29,20 @@ class StackTraceView( object ): self._session = session self._connection = connection - self._currentThread = None - self._currentFrame = None + self._current_thread = None + self._current_frame = None + self._current_syntax = "" self._threads = [] self._sources = {} utils.SetUpScratchBuffer( self._buf, 'vimspector.StackTrace' ) + vim.current.buffer = self._buf + # FIXME: Remove all usage of "Windown" and just use buffers to prevent all + # the bugs around the window being closed. + self._win = vim.current.window + vim.command( 'nnoremap :call vimspector#GoToFrame()' ) self._line_to_frame = {} @@ -53,14 +59,15 @@ class StackTraceView( object ): def GetCurrentThreadId( self ): - return self._currentThread + return self._current_thread def GetCurrentFrame( self ): - return self._currentFrame + return self._current_frame def Clear( self ): - self._currentFrame = None - self._currentThread = None + self._current_frame = None + self._current_thread = None + self._current_syntax = "" self._threads = [] self._sources = {} with utils.ModifiableScratchBuffer( self._buf ): @@ -102,10 +109,10 @@ class StackTraceView( object ): for thread in message[ 'body' ][ 'threads' ]: self._threads.append( thread ) - if infer_current_frame and thread[ 'id' ] == self._currentThread: + if infer_current_frame and thread[ 'id' ] == self._current_thread: self._LoadStackTrace( thread, True ) - elif infer_current_frame and self._currentThread is None: - self._currentThread = thread[ 'id' ] + elif infer_current_frame and self._current_thread is None: + self._current_thread = thread[ 'id' ] self._LoadStackTrace( thread, True ) self._DrawThreads() @@ -170,8 +177,8 @@ class StackTraceView( object ): def _JumpToFrame( self, frame ): def do_jump(): if 'line' in frame and frame[ 'line' ] > 0: - self._currentFrame = frame - return self._session.SetCurrentFrame( self._currentFrame ) + self._current_frame = frame + return self._session.SetCurrentFrame( self._current_frame ) return False source = frame.get( 'source' ) or {} @@ -188,32 +195,32 @@ class StackTraceView( object ): def OnStopped( self, event ): if 'threadId' in event: - self._currentThread = event[ 'threadId' ] + self._current_thread = event[ 'threadId' ] elif event.get( 'allThreadsStopped', False ) and self._threads: - self._currentThread = self._threads[ 0 ][ 'id' ] + self._current_thread = self._threads[ 0 ][ 'id' ] - if self._currentThread is not None: + if self._current_thread is not None: for thread in self._threads: - if thread[ 'id' ] == self._currentThread: + if thread[ 'id' ] == self._current_thread: self._LoadStackTrace( thread, True ) return self.LoadThreads( True ) def OnThreadEvent( self, event ): - if event[ 'reason' ] == 'started' and self._currentThread is None: - self._currentThread = event[ 'threadId' ] + if event[ 'reason' ] == 'started' and self._current_thread is None: + self._current_thread = event[ 'threadId' ] self.LoadThreads( True ) def Continue( self ): - if self._currentThread is None: + if self._current_thread is None: utils.UserMessage( 'No current thread', persist = True ) return self._session._connection.DoRequest( None, { 'command': 'continue', 'arguments': { - 'threadId': self._currentThread, + 'threadId': self._current_thread, }, } ) @@ -221,14 +228,14 @@ class StackTraceView( object ): self.LoadThreads( True ) def Pause( self ): - if self._currentThread is None: + if self._current_thread is None: utils.UserMessage( 'No current thread', persist = True ) return self._session._connection.DoRequest( None, { 'command': 'pause', 'arguments': { - 'threadId': self._currentThread, + 'threadId': self._current_thread, }, } ) @@ -294,3 +301,8 @@ class StackTraceView( object ): 'source': source } } ) + + def SetSyntax( self, syntax ): + self._current_syntax = utils.SetSyntax( self._current_syntax, + syntax, + self._win ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index b04f28f..c68ac45 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -501,3 +501,17 @@ def memoize( func ): @memoize def Exists( expr ): return int( vim.eval( f'exists( "{ expr }" )' ) ) + + +def SetSyntax( current_syntax, syntax, *args ): + if not syntax: + syntax = '' + + if current_syntax == syntax: + return + + for win in args: + with LetCurrentWindow( win ): + vim.command( 'set syntax={}'.format( Escape( syntax ) ) ) + + return syntax diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 759abdf..92bbedf 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -387,18 +387,9 @@ class VariablesView( object ): def SetSyntax( self, syntax ): - if not syntax: - syntax = '' - - if self._current_syntax == syntax: - return - - self._current_syntax = syntax - - with utils.LetCurrentWindow( self._vars.win ): - vim.command( 'set syntax={}'.format( utils.Escape( syntax ) ) ) - - with utils.LetCurrentWindow( self._watch.win ): - vim.command( 'set syntax={}'.format( utils.Escape( syntax ) ) ) + self._current_syntax = utils.SetSyntax( self._current_syntax, + syntax, + self._vars.win, + self._watch.win ) # vim: sw=2 From a90a529cedb093afc4906f6a5ce947a182624e5d Mon Sep 17 00:00:00 2001 From: Sergey Vlasov Date: Wed, 5 Feb 2020 16:55:20 +0200 Subject: [PATCH 098/609] README referenced link typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1811a04..eb58af9 100644 --- a/README.md +++ b/README.md @@ -592,7 +592,7 @@ For an introduction to the configuration of `.vimspector.json`, take a look at the Getting Started section of the [Vimspector website][website]. For full explanation, including how to use variables, substitutions and how to -specify exception breakpoints, see [the docs](vimspector-ref). +specify exception breakpoints, see [the docs][vimspector-ref]. Current tested with the following debug adapters. From 933f946801e90b6befea615fc3556fd76f8b7531 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 8 Feb 2020 20:53:18 +0000 Subject: [PATCH 099/609] Fix CI - use latest lldb-mi --- azure-pipelines.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 09d5d8e..dc94cab 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -78,7 +78,11 @@ stages: steps: - bash: | brew unlink node@6 - brew install macvim node@10 tcl-tk llvm + brew update + for p in macvim node@10 tcl-tk llvm; do + brew install $p + brew outdated $p || brew install $p + done brew link --force --overwrite node@10 displayName: 'Install vim and node' @@ -98,11 +102,13 @@ stages: displayName: 'Print vim version information' - bash: | + VERSION=66e8ed9af1812ca0bb6cfcc7023a35742e1bd323 + BUILD=1092 set -ex mkdir -p lldb-mi pushd lldb-mi - curl -LO https://github.com/puremourning/lldb-mi/releases/download/1041/lldb-mi-db65c20a3d8e09d9ff2f3748c01cf43ec035a4d8.tar.gz - tar zxvf lldb-mi-db65c20a3d8e09d9ff2f3748c01cf43ec035a4d8.tar.gz + curl -LO https://github.com/puremourning/lldb-mi/releases/download/${BUILD}/lldb-mi-${VERSION}.tar.gz + tar zxvf lldb-mi-${VERSION}.tar.gz cp usr/local/bin/lldb-mi /usr/local/bin popd displayName: 'Install lldb-mi' From a56bee7b0a5abe6c0eab60f69dbec59c235182db Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 8 Feb 2020 18:05:21 +0000 Subject: [PATCH 100/609] Switch to debugpy over vscode-python This is just better in every way, and the vscode-python typescript adapter is being phased out. --- .ycm_extra_conf.py | 7 ++ azure-pipelines.yml | 2 +- install_gadget.py | 76 +++++++++++++++---- python3/vimspector/breakpoints.py | 8 +- .../python/simple_python/.vimspector.json | 19 +---- 5 files changed, 76 insertions(+), 36 deletions(-) diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py index d988326..f2e32ae 100644 --- a/.ycm_extra_conf.py +++ b/.ycm_extra_conf.py @@ -38,6 +38,13 @@ def Settings( **kwargs ): } } + if kwargs[ 'language' ] == 'python': + return { + 'sys_path': [ + p.join( PATH_TO_THIS_DIR, 'python3' ) + ] + } + if IgnoreExtraConf: raise IgnoreExtraConf() diff --git a/azure-pipelines.yml b/azure-pipelines.yml index dc94cab..2721c7a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,7 +15,7 @@ stages: - bash: pip3 install -r dev_requirements.txt displayName: "Install requirements" - - bash: $HOME/.local/bin/flake8 python3/ + - bash: '$HOME/.local/bin/flake8 python3/ *.py' displayName: "Run flake8" - job: 'Vimscript' diff --git a/install_gadget.py b/install_gadget.py index 239936c..7050041 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -55,7 +55,7 @@ GADGETS = { 'url': 'https://github.com/Microsoft/vscode-cpptools/releases/download/' '${version}/${file_name}', }, - 'do': lambda name, root: InstallCppTools( name, root ), + 'do': lambda name, root, gadget: InstallCppTools( name, root, gadget ), 'all': { 'version': '0.26.3', }, @@ -87,7 +87,8 @@ GADGETS = { }, }, 'vscode-python': { - 'language': 'python', + 'language': 'python.legacy', + 'enabled': False, 'download': { 'url': 'https://github.com/Microsoft/vscode-python/releases/download/' '${version}/${file_name}', @@ -108,6 +109,31 @@ GADGETS = { } }, }, + 'debugpy': { + 'language': 'python', + 'download': { + 'url': 'https://github.com/microsoft/debugpy/archive/${file_name}' + }, + 'all': { + 'version': '1.0.0b1', + 'file_name': 'v1.0.0b1.zip', + 'checksum': + 'eb26ac276213bcf26aaeadd7c3c284f7a20f43b63331822831a783dea558e60e' + }, + 'do': lambda name, root, gadget: InstallDebugpy( name, root, gadget ), + 'adapters': { + 'debugpy': { + "command": [ + "python3", + "${gadgetDir}/debugpy/build/lib/debugpy/adapter" + ], + "name": "debugpy", + "configuration": { + "python": sys.executable + } + } + }, + }, 'vscode-java-debug': { 'language': 'java', 'enabled': False, @@ -163,7 +189,7 @@ GADGETS = { 'url': 'https://github.com/puremourning/TclProDebug', 'ref': 'f5c56b7067661ce84e205765060224076569ae0e', # master 26/10/2019 }, - 'do': lambda name, root: InstallTclProDebug( name, root ) + 'do': lambda name, root, gadget: InstallTclProDebug( name, root, gadget ) }, 'netcoredbg': { 'language': 'csharp', @@ -184,9 +210,10 @@ GADGETS = { 'file_name': 'netcoredbg-linux-master.tar.gz', 'checksum': '', }, - 'do': lambda name, root: MakeSymlink( gadget_dir, - name, - os.path.join( root, 'netcoredbg' ) ), + 'do': lambda name, root, gadget: MakeSymlink( + gadget_dir, + name, + os.path.join( root, 'netcoredbg' ) ), 'adapters': { 'netcoredbg': { "name": "netcoredbg", @@ -242,7 +269,7 @@ GADGETS = { 'checksum': '502ee5732851fc4f309294fc296a291b1a114008a1fbcb232f3763be2b8d9c1f', }, - 'do': lambda name, root: InstallBashDebug( name, root ), + 'do': lambda name, root, gadget: InstallBashDebug( name, root, gadget ), 'adapters': { "vscode-bash": { "name": "bashdb", @@ -297,8 +324,9 @@ GADGETS = { 'language': 'php', 'enabled': False, 'download': { - 'url': 'https://github.com/felixfbecker/vscode-php-debug/releases/download/' - '${version}/${file_name}', + 'url': + 'https://github.com/felixfbecker/vscode-php-debug/releases/download/' + '${version}/${file_name}', }, 'all': { 'version': 'v1.13.0', @@ -323,7 +351,7 @@ GADGETS = { 'url': 'https://github.com/microsoft/vscode-node-debug2', 'ref': 'v1.39.1', }, - 'do': lambda name, root: InstallNodeDebug( name, root ), + 'do': lambda name, root, gadget: InstallNodeDebug( name, root, gadget ), 'adapters': { 'vscode-node': { 'name': 'node2', @@ -381,7 +409,7 @@ def MakeExecutable( file_path ): os.chmod( file_path, 0o755 ) -def InstallCppTools( name, root ): +def InstallCppTools( name, root, gadget ): extension = os.path.join( root, 'extension' ) # It's hilarious, but the execute bits aren't set in the vsix. So they @@ -400,12 +428,24 @@ def InstallCppTools( name, root ): MakeExtensionSymlink( name, root ) -def InstallBashDebug( name, root ): +def InstallBashDebug( name, root, gadget ): MakeExecutable( os.path.join( root, 'extension', 'bashdb_dir', 'bashdb' ) ) MakeExtensionSymlink( name, root ) -def InstallTclProDebug( name, root ): +def InstallDebugpy( name, root, gadget ): + wd = os.getcwd() + root = os.path.join( root, 'debugpy-{}'.format( gadget[ 'version' ] ) ) + os.chdir( root ) + try: + subprocess.check_call( [ sys.executable, 'setup.py', 'build' ] ) + finally: + os.chdir( wd ) + + MakeSymlink( gadget_dir, name, root ) + + +def InstallTclProDebug( name, root, gadget ): configure = [ './configure' ] if OS == 'macos': @@ -440,7 +480,7 @@ def InstallTclProDebug( name, root ): MakeSymlink( gadget_dir, name, root ) -def InstallNodeDebug( name, root ): +def InstallNodeDebug( name, root, gadget ): node_version = subprocess.check_output( [ 'node', '--version' ], universal_newlines=True ).strip() print( "Node.js version: {}".format( node_version ) ) @@ -485,7 +525,11 @@ def UrlOpen( *args, **kwargs ): return urllib2.urlopen( *args, **kwargs ) -def DownloadFileTo( url, destination, file_name = None, checksum = None, sslcheck = True): +def DownloadFileTo( url, + destination, + file_name = None, + checksum = None, + sslcheck = True): if not file_name: file_name = url.split( '/' )[ -1 ] @@ -729,7 +773,7 @@ for name, gadget in GADGETS.items(): root = destination if 'do' in gadget: - gadget[ 'do' ]( name, root ) + gadget[ 'do' ]( name, root, v ) else: MakeExtensionSymlink( name, root ) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 6e0b76f..7d55704 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -217,6 +217,11 @@ class ProjectBreakpoints( object ): response_received() + # NOTE: Must do this _first_ otherwise we might send requests and get + # replies before we finished sending all the requests. + if self._exception_breakpoints is None: + self._SetUpExceptionBreakpoints( self._configured_breakpoints ) + # TODO: add the _configured_breakpoints to line_breakpoints # TODO: the line numbers might have changed since pressing the F9 key! @@ -272,9 +277,6 @@ class ProjectBreakpoints( object ): failure_handler = lambda *_: response_received() ) - if self._exception_breakpoints is None: - self._SetUpExceptionBreakpoints( self._configured_breakpoints ) - if self._exception_breakpoints: awaiting = awaiting + 1 self._connection.DoRequest( diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 7b7761e..c2af1b4 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -1,19 +1,6 @@ { - "adapters": { - "debugpy": { - "command": [ - "python", - "-m", - "debugpy.adapter" - ], - "name": "debugpy", - "configuration": { - "python": "python" - } - } - }, "configurations": { - "run": { + "run legacy vscode-python": { "adapter": "vscode-python", "configuration": { "request": "launch", @@ -45,14 +32,14 @@ } } }, - "run - debugpy": { + "run": { "adapter": "debugpy", "configuration": { "request": "launch", "type": "python", "cwd": "${workspaceRoot}", "program": "${file}", - "stopOnEntry": true, + "stopOnEntry": false, "console": "integratedTerminal" }, "breakpoints": { From 8052484cc75f27ac2e4315573788e10415e85e38 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 8 Feb 2020 18:09:13 +0000 Subject: [PATCH 101/609] Use YCM python style --- .vintrc.yml | 3 +++ CONTRIBUTING.md | 23 +++++++++++++++++ azure-pipelines.yml | 2 +- dev_requirements.txt | 2 ++ install_gadget.py | 4 +-- python3/vimspector/debug_session.py | 6 ++--- python3/vimspector/output.py | 2 +- python3/vimspector/utils.py | 10 ++++---- tests/breakpoints.test.vim | 2 +- tests/language_python.test.vim | 2 +- tests/lib/plugin/screendump.vim | 8 +++--- tests/lib/plugin/shared.vim | 26 +++++++++---------- tests/lib/run_test.vim | 39 ++++++++++++++++------------- tests/tabpage.test.vim | 2 +- 14 files changed, 82 insertions(+), 49 deletions(-) create mode 100644 .vintrc.yml diff --git a/.vintrc.yml b/.vintrc.yml new file mode 100644 index 0000000..7e87977 --- /dev/null +++ b/.vintrc.yml @@ -0,0 +1,3 @@ +cmdargs: + color: true + severity: style_problem diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ae7717..388dc21 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,6 +72,29 @@ For more infomration on the test framework, see [this article](https://vimways.org/2019/a-test-to-attest-to/), authored by the Vimspector creator. +### Code Style + +The code style of the Python code is "YCM" style, because that's how I like it. +[`flake8`][] is used to check for certain errors and code style. + +The code style of the Vimscript is largely the same, and it is linted by +[`vint`][]. + +To run them: + +* (optional) Create and activate a virtual env: + `python3 -m venv venv ; source venv/bin/activate` +* Install the development dependencies: `pip install -r dev_requirements.txt` +* Run `flake8`: `flake8 python3/ *.py` +* Run `vint`: `vint autoload/ plugin/ tests/` + +They're also run by CI, so please check for lint failures. The canonical +definition of the command to run is the command run in CI, i.e. in +`azure-pipelines.yml`. + # Code of conduct Please see [code of conduct](CODE_OF_CONDUCT.md). + +[vint]: https://github.com/Vimjas/vint +[flake8]: https://flake8.pycqa.org/en/latest/ diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2721c7a..37c0fec 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -27,7 +27,7 @@ stages: - bash: pip3 install -r dev_requirements.txt displayName: "Install requirements" - - bash: $HOME/.local/bin/vint autoload/ plugin/ + - bash: $HOME/.local/bin/vint autoload/ plugin/ tests/ displayName: "Run vint" - job: 'linux' diff --git a/dev_requirements.txt b/dev_requirements.txt index f6e9448..a5a04ea 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,2 +1,4 @@ flake8==3.7.7 vim-vint==0.3.21 +flake8-comprehensions +flake8-ycm>= 0.1.0 diff --git a/install_gadget.py b/install_gadget.py index 7050041..46c7596 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -529,7 +529,7 @@ def DownloadFileTo( url, destination, file_name = None, checksum = None, - sslcheck = True): + sslcheck = True ): if not file_name: file_name = url.split( '/' )[ -1 ] @@ -759,7 +759,7 @@ for name, gadget in GADGETS.items(): destination, file_name = gadget[ 'download' ].get( 'target' ), checksum = v.get( 'checksum' ), - sslcheck = not verify_cert_off) + sslcheck = not verify_cert_off ) root = os.path.join( destination, 'root' ) ExtractZipTo( file_path, root, diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index c85d752..08b29cd 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -112,7 +112,7 @@ class DebugSession( object ): else: configuration_name = utils.SelectFromList( 'Which launch configuration?', - sorted( list( configurations.keys() ) ) ) + sorted( configurations.keys() ) ) if not configuration_name or configuration_name not in configurations: return @@ -259,13 +259,13 @@ class DebugSession( object ): def IfConnected( fct ): """Decorator, call fct if self._connected else echo warning""" @functools.wraps( fct ) - def wrapper(self, *args, **kwargs): + 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 fct( self, *args, **kwargs ) return wrapper def OnChannelData( self, data ): diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 38ce6d3..43b1cc3 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -210,4 +210,4 @@ class OutputView( object ): '*' if tab_buffer.flag else '' ) ) def GetCategories( self ): - return [ category for category in self._buffers.keys() ] + return list( self._buffers.keys() ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index c68ac45..4ae7f94 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -232,7 +232,7 @@ def Escape( msg ): return msg.replace( "'", "''" ) -def UserMessage( msg, persist=False, error=False): +def UserMessage( msg, persist=False, error=False ): if persist: _logger.warning( 'User Msg: ' + msg ) else: @@ -242,11 +242,11 @@ def UserMessage( msg, persist=False, error=False): vim.command( 'redraw' ) try: if error: - vim.command("echohl WarningMsg") + vim.command( "echohl WarningMsg" ) for line in msg.split( '\n' ): vim.command( "{0} '{1}'".format( cmd, Escape( line ) ) ) finally: - vim.command('echohl None') if error else None + vim.command( 'echohl None' ) if error else None vim.command( 'redraw' ) @@ -324,7 +324,7 @@ def SetBufferContents( buf, lines, modified=False ): if not isinstance( lines, list ): lines = lines.splitlines() - buf[:] = lines + buf[ : ] = lines finally: buf.options[ 'modified' ] = modified @@ -347,7 +347,7 @@ def ExpandReferencesInObject( obj, mapping, user_choices ): return obj -def ExpandReferencesInString( orig_s, mapping, user_choices): +def ExpandReferencesInString( orig_s, mapping, user_choices ): s = os.path.expanduser( orig_s ) s = os.path.expandvars( s ) diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index 1c36c08..dd00840 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -275,7 +275,7 @@ function! Test_Insert_Code_Above_Breakpoint() " CHeck that we break at the right point call setpos( '.', [ 0, 1, 1 ] ) - call vimspector#LaunchWithSettings( { "configuration": "run" } ) + call vimspector#LaunchWithSettings( { 'configuration': 'run' } ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) call vimspector#Reset() call vimspector#test#setup#WaitForReset() diff --git a/tests/language_python.test.vim b/tests/language_python.test.vim index 7bf95a8..b970b3a 100644 --- a/tests/language_python.test.vim +++ b/tests/language_python.test.vim @@ -28,7 +28,7 @@ function! Test_Python_Simple() call setpos( '.', [ 0, 1, 1 ] ) " Here we go. Start Debugging - call vimspector#LaunchWithSettings( { "configuration": "run" } ) + call vimspector#LaunchWithSettings( { 'configuration': 'run' } ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 6, 1 ) " Step diff --git a/tests/lib/plugin/screendump.vim b/tests/lib/plugin/screendump.vim index 1c710d7..9b823c3 100644 --- a/tests/lib/plugin/screendump.vim +++ b/tests/lib/plugin/screendump.vim @@ -28,7 +28,7 @@ endif func RunVimInTerminal(arguments, options) " If Vim doesn't exit a swap file remains, causing other tests to fail. " Remove it here. - call delete(".swp") + call delete('.swp') if exists('$COLORFGBG') " Clear $COLORFGBG to avoid 'background' being set to "dark", which will @@ -61,7 +61,7 @@ func RunVimInTerminal(arguments, options) \ 'term_rows': rows, \ 'term_cols': cols, \ }) - if &termwinsize == '' + if &termwinsize ==# '' " in the GUI we may end up with a different size, try to set it. if term_getsize(buf) != [rows, cols] call term_setsize(buf, rows, cols) @@ -80,7 +80,7 @@ func RunVimInTerminal(arguments, options) call WaitFor({-> len(term_getline(buf, rows)) >= cols - 1 || len(term_getline(buf, rows - statusoff)) >= cols - 1}) catch /timed out after/ let lines = map(range(1, rows), {key, val -> term_getline(buf, val)}) - call assert_report('RunVimInTerminal() failed, screen contents: ' . join(lines, "")) + call assert_report('RunVimInTerminal() failed, screen contents: ' . join(lines, '')) endtry return buf @@ -88,7 +88,7 @@ endfunc " Stop a Vim running in terminal buffer "buf". func StopVimInTerminal(buf) - call assert_equal("running", term_getstatus(a:buf)) + call assert_equal('running', term_getstatus(a:buf)) " CTRL-O : works both in Normal mode and Insert mode to start a command line. " In Command-line it's inserted, the CTRL-U removes it again. diff --git a/tests/lib/plugin/shared.vim b/tests/lib/plugin/shared.vim index b6f0205..ee9a0af 100644 --- a/tests/lib/plugin/shared.vim +++ b/tests/lib/plugin/shared.vim @@ -37,8 +37,8 @@ endfunc func RunCommand(cmd) let job = 0 if has('job') - let job = job_start(a:cmd, {"stoponexit": "hup"}) - call job_setoptions(job, {"stoponexit": "kill"}) + let job = job_start(a:cmd, {'stoponexit': 'hup'}) + call job_setoptions(job, {'stoponexit': 'kill'}) elseif has('win32') exe 'silent !start cmd /c start "test_channel" ' . a:cmd else @@ -53,7 +53,7 @@ func GetPort() " with 200 it sometimes failed for i in range(400) try - let l = readfile("Xportnr") + let l = readfile('Xportnr') catch endtry if len(l) >= 1 @@ -61,7 +61,7 @@ func GetPort() endif sleep 10m endfor - call delete("Xportnr") + call delete('Xportnr') if len(l) == 0 " Can't make the connection, give up. @@ -74,14 +74,14 @@ endfunc " Always kills the server before returning. func RunServer(cmd, testfunc, args) " The Python program writes the port number in Xportnr. - call delete("Xportnr") + call delete('Xportnr') if len(a:args) == 1 let arg = ' ' . a:args[0] else let arg = '' endif - let pycmd = s:python . " " . a:cmd . arg + let pycmd = s:python . ' ' . a:cmd . arg try let g:currentJob = RunCommand(pycmd) @@ -108,10 +108,10 @@ func s:kill_server(cmd) unlet g:currentJob endif elseif has('win32') - let cmd = substitute(a:cmd, ".py", '', '') + let cmd = substitute(a:cmd, '.py', '', '') call system('taskkill /IM ' . s:python . ' /T /F /FI "WINDOWTITLE eq ' . cmd . '"') else - call system("pkill -f " . a:cmd) + call system('pkill -f ' . a:cmd) endif endfunc @@ -253,7 +253,7 @@ func GetVimCommand(...) " "vimcmd" file, including environment options. " Other Makefiles just write the executable in the first line, so fall back " to that if there is no second line or it is empty. - if len(lines) > 1 && lines[1] != '' + if len(lines) > 1 && lines[1] !=# '' let cmd = lines[1] else let cmd = lines[0] @@ -267,7 +267,7 @@ func GetVimCommand(...) let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '') " If using valgrind, make sure every run uses a different log file. - if cmd =~ 'valgrind.*--log-file=' + if cmd =~# 'valgrind.*--log-file=' let cmd = substitute(cmd, '--log-file=\(^\s*\)', '--log-file=\1.' . g:valgrind_cnt, '') let g:valgrind_cnt += 1 endif @@ -308,7 +308,7 @@ func RunVimPiped(before, after, arguments, pipecmd) let args .= ' -S Xafter.vim' endif - exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments + exe 'silent !' . a:pipecmd . cmd . args . ' ' . a:arguments if len(a:before) > 0 call delete('Xbefore.vim') @@ -320,7 +320,7 @@ func RunVimPiped(before, after, arguments, pipecmd) endfunc func CanRunGui() - return has('gui') && ($DISPLAY != "" || has('gui_running')) + return has('gui') && ($DISPLAY !=# '' || has('gui_running')) endfunc func WorkingClipboard() @@ -328,7 +328,7 @@ func WorkingClipboard() return 0 endif if has('x11') - return $DISPLAY != "" + return $DISPLAY !=# '' endif return 1 endfunc diff --git a/tests/lib/run_test.vim b/tests/lib/run_test.vim index d8821ed..a5c2f39 100644 --- a/tests/lib/run_test.vim +++ b/tests/lib/run_test.vim @@ -36,16 +36,18 @@ let s:single_test_timeout = 60000 " Restrict the runtimepath to the exact minimum needed for testing -set rtp=$PWD/lib,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after +set runtimepath=$PWD/lib,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after if has('packages') - let &packpath = &rtp + let &packpath = &runtimepath endif call ch_logfile( 'debuglog', 'w' ) " For consistency run all tests with 'nocompatible' set. " This also enables use of line continuation. -set nocp +" vint: -ProhibitSetNoCompatible +set nocompatible +" vint: +ProhibitSetNoCompatible " Use utf-8 by default, instead of whatever the system default happens to be. " Individual tests can overrule this at the top of the file. @@ -93,7 +95,7 @@ func RunTheTest(test) " directory after executing the test. let save_cwd = getcwd() - if exists("*SetUp_" . a:test) + if exists('*SetUp_' . a:test) try exe 'call SetUp_' . a:test catch @@ -109,7 +111,7 @@ func RunTheTest(test) endtry endif - if exists("*SetUp") + if exists('*SetUp') try call SetUp() catch @@ -136,9 +138,14 @@ func RunTheTest(test) let test_filesafe = substitute( a:test, '[)(,:]', '_', 'g' ) let s:testid_filesafe = g:testpath . '_' . test_filesafe - au VimLeavePre * call EarlyExit(s:test) + augroup EarlyExit + au! + au VimLeavePre * call EarlyExit(s:test) + augroup END + exe 'call ' . a:test - au! VimLeavePre + + au! EarlyExit catch /^\cskipped/ call add(s:messages, ' Skipped') call add(s:skipped, @@ -164,7 +171,7 @@ func RunTheTest(test) " reset to avoid trouble with anything else. set noinsertmode - if exists("*TearDown") + if exists('*TearDown') try call TearDown() catch @@ -179,7 +186,7 @@ func RunTheTest(test) endtry endif - if exists("*TearDown_" . a:test) + if exists('*TearDown_' . a:test) try exe 'call TearDown_' . a:test catch @@ -252,10 +259,9 @@ func FinishTesting() if filereadable( 'test.log' ) let l = readfile( 'test.log' ) endif - call writefile( l->extend( [ '', 'From ' . g:testpath . ':' ] ) - \ ->extend( s:errors ), - \ 'test.log', - \ 's' ) + call extend( l, [ '', 'From ' . g:testpath . ':' ] ) + call extend( l, s:errors ) + call writefile( l, 'test.log', 's' ) endif if s:done == 0 @@ -280,10 +286,9 @@ func FinishTesting() if filereadable( 'messages' ) let l = readfile( 'messages' ) endif - call writefile( l->extend( [ '', 'From ' . g:testpath . ':' ] ) - \ ->extend( s:messages ), - \ 'messages', - \ 's' ) + call extend( l, [ '', 'From ' . g:testpath . ':' ] ) + call extend( l, s:messages ) + call writefile( l, 'messages', 's' ) if s:fail > 0 cquit! diff --git a/tests/tabpage.test.vim b/tests/tabpage.test.vim index 0aefe7f..2dfd4d9 100644 --- a/tests/tabpage.test.vim +++ b/tests/tabpage.test.vim @@ -28,7 +28,7 @@ function! Test_Step_With_Different_Tabpage() call assert_equal( 1, col( '.' ), 'Current column' ) " Switch to the other tab - normal gt + normal! gt call assert_notequal( vimspector_tabnr, tabpagenr() ) From dcabc52937b4f5f61d8a63a58088d5e52aafbdb0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 8 Feb 2020 18:22:17 +0000 Subject: [PATCH 102/609] Update readme --- README.md | 114 +++++++++++++++++++++++++++--------------------------- 1 file changed, 56 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index eb58af9..0610277 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ For a tutorial and usage overview, take a look at the * [Debug adapter configuration](#debug-adapter-configuration) * [C, C , Rust, etc.](#c-c-rust-etc) * [Python](#python) - * [Alternative: Use debugpy directly](#alternative-use-debugpy-directly) + * [Legacy: vscode-python](#legacy-vscode-python) * [TCL](#tcl) * [C♯](#c) * [Go](#go) @@ -55,7 +55,7 @@ For a tutorial and usage overview, take a look at the * [FAQ](#faq) * [License](#license) - + @@ -188,19 +188,26 @@ There is no workaroud for the lack of balloons; you'll just have to use ## Language dependencies -The debug adapters themselves have certain runtime dependencies: +The debug adapters themselves have certain runtime dependencies. They are +categorised as follows: -| Language | Status | Switch | Adapter | Dependencies | -|------------------|--------------|------------------------------|-------------------|------------------------| -| C, C++, etc. | Supported | `--all` or ` --enable-c` | vscode-cpptools | mono-core | -| Python | Supported | `--all` or `--enable-python` | vscode-python | Node 10, Python 2.7 or Python 3 | -| TCL | Experimental | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | -| Bourne Shell | Experimental | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | -| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | -| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | -| Go | Experimental | `--enable-go` | vscode-go | Go, [Delve][] | -| Node.js | Experimental | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | -| Javascript | Experimental | `--force-enable-chrome` | debugger-for-chrome | Chrome | +* `Tested` : Fully supported, Vimspector regression tests cover them +* `Supported` : Fully supported, frequently used and manually tested +* `Experimental`: Working, but not frequently used and rarely tested +* `Legacy`: No longer supported, please migrate your config + +| Language | Status | Switch | Adapter | Dependencies | +|------------------|--------------|--------------------------------|---------------------|---------------------------------| +| C, C++, etc. | Tested | `--all` or `--enable-c` | vscode-cpptools | mono-core | +| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | +| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] | +| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | +| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | +| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | +| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | +| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | +| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | +| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | For other languages, you'll need some other way to install the gadget. @@ -696,16 +703,43 @@ For `lldb-vscode` replace the name of the adapter with `lldb-vscode`: ## Python -NOTE: Please see the alternative approach below, as this will become the -standard approach in future. +* Python: [debugpy][] +* Requires `install_gadget.py --enable-python`, this requires a working compiler +to build a C python extension for performance. -* Python: [vscode-python](https://github.com/Microsoft/vscode-python) +**Migrating from `vscode-python`**: change `"adapter": "vscode-python"` to +`"adapter": "debugpy"`. + +```json +{ + "configurations": { + ": Launch": { + "adapter": "debugpy", + "configuration": { + "name": ": Launch", + "type": "python", + "request": "launch", + "cwd": "", + "python": "/path/to/python/interpreter/to/use", + "stopOnEntry": true, + "console": "externalTerminal", + "debugOptions": [], + "program": "", + } + } + ... + } +} +``` + +### Legacy: vscode-python + +* No longer installed by default - please pass `--force-enable-python.legacy` if + you just want to continue using your working setup. +* [vscode-python](https://github.com/Microsoft/vscode-python) * NOTE: You must be running `node` 10. See [this issue](https://github.com/puremourning/vimspector/issues/105) -I recommend installing `nvm` and then `nvm use 10` prior to starting your Vim -session. - -``` +```json { "configurations": { ": Launch": { @@ -726,43 +760,6 @@ session. } ``` -### Alternative: Use debugpy directly - -If you can't get a node 10 environment set up for whatver reason, then you can -avoid that issue by using `debugpy` (formerly `ptvsd`) directly. - -Here's how: - -1. Instal `debugpy`: `pip install debugpy` -2. Create `/path/to/vimspector/gadgets//.gadgets.d/debugpy.json`: - -```json -{ - "adapters": { - "debugpy": { - "command": [ - "python", - "-m", - "debugpy.adapter" - ], - "name": "debugpy", - "configuration": { - "python": "python" - } - } - } -} -``` - -Then in theory you should just have to change `"adapter": "vscode-python"` to -`"adapter": "debugpy"`. - -See `support/test/python/simple_python/.vimspector.json` as an example. - -NOTE: This will likely become the default in future, and vscode-python will be -phased out. - - ## TCL * TCL (TclProDebug) @@ -1039,3 +1036,4 @@ 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 +[debugpy]: https://github.com/microsoft/debugpy From 6b735ce4d0ed9e9e2241ec4dba56b9dca7664df2 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 15 Feb 2020 11:30:42 +0000 Subject: [PATCH 103/609] Drop python 2 support in installer --- install_gadget.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 46c7596..433261e 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # vimspector - A multi-language debugging system for Vim # Copyright 2019 Ben Jackson @@ -15,11 +15,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -try: - import urllib.request as urllib2 -except ImportError: - import urllib2 +import sys +if sys.version_info.major < 3: + exit( "Sorry, you need to be running this script with python3 or later" ) + +from urllib import request import argparse import contextlib import os @@ -31,16 +32,11 @@ import subprocess import traceback import tarfile import hashlib -import sys import json import functools import time import ssl - -try: - from io import BytesIO # for Python 3 -except ImportError: - from BytesIO import BytesIO +import io # Include vimspector source, for utils sys.path.insert( 1, os.path.join( os.path.dirname( __file__ ), @@ -522,7 +518,7 @@ def WithRetry( f ): @WithRetry def UrlOpen( *args, **kwargs ): - return urllib2.urlopen( *args, **kwargs ) + return request.urlopen( *args, **kwargs ) def DownloadFileTo( url, @@ -550,7 +546,7 @@ def DownloadFileTo( url, print( "Removing existing {}".format( file_path ) ) os.remove( file_path ) - r = urllib2.Request( url, headers = { 'User-Agent': 'Vimspector' } ) + r = request.Request( url, headers = { 'User-Agent': 'Vimspector' } ) print( "Downloading {} to {}/{}".format( url, destination, file_name ) ) @@ -627,7 +623,7 @@ def ExtractZipTo( file_path, destination, format ): with gzip.open( file_path, 'rb' ) as f: file_contents = f.read() - with ModePreservingZipFile( BytesIO( file_contents ) ) as f: + with ModePreservingZipFile( io.BytesIO( file_contents ) ) as f: f.extractall( path = destination ) elif format == 'tar': From 583fb95ea0738a9358cb3463612f3a5d9d00aa27 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 17 Jan 2020 17:44:17 +0000 Subject: [PATCH 104/609] Read configurations from a per-filetype directory or a default location as well as local config file --- .gitignore | 1 + python3/vimspector/debug_session.py | 57 ++++++++++++++++++++--------- python3/vimspector/install.py | 10 +++++ python3/vimspector/utils.py | 5 +++ 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index df249b4..b09d196 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ README.md.toc.* *.vimspector.log support/test/csharp/*.exe* .neomake.log +configurations/ diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 08b29cd..a0d8418 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -79,31 +79,37 @@ class DebugSession( object ): self._adapter = None current_file = utils.GetBufferFilepath( vim.current.buffer ) - - launch_config_file = utils.PathToConfigFile( - '.vimspector.json', - os.path.dirname( current_file ) ) - - if not launch_config_file: - utils.UserMessage( 'Unable to find .vimspector.json. You need to tell ' - 'vimspector how to launch your application.' ) - return - - with open( launch_config_file, 'r' ) as f: - database = json.load( f ) - - configurations = database.get( 'configurations' ) + filetypes = utils.GetBufferFiletypes( vim.current.buffer ) + configurations = {} adapters = {} glob.glob( install.GetGadgetDir( VIMSPECTOR_HOME, install.GetOS() ) ) for gadget_config_file in PathsToAllGadgetConfigs( VIMSPECTOR_HOME, current_file ): self._logger.debug( f'Reading gadget config: {gadget_config_file}' ) - if gadget_config_file and os.path.exists( gadget_config_file ): - with open( gadget_config_file, 'r' ) as f: - adapters.update( json.load( f ).get( 'adapters' ) or {} ) + if not gadget_config_file or not os.path.exists( gadget_config_file ): + continue - adapters.update( database.get( 'adapters' ) or {} ) + with open( gadget_config_file, 'r' ) as f: + adapters.update( json.load( f ).get( 'adapters' ) or {} ) + + for launch_config_file in PathsToAllConfigFiles( VIMSPECTOR_HOME, + current_file, + filetypes ): + self._logger.debug( f'Reading configurations from: {launch_config_file}' ) + if not launch_config_file or not os.path.exists( launch_config_file ): + continue + + with open( launch_config_file, 'r' ) as f: + database = json.load( f ) + adapters.update( database.get( 'adapters' ) or {} ) + configurations.update( database.get( 'configurations' or {} ) ) + + if not configurations: + utils.UserMessage( 'Unable to find any debug configurations. ' + 'You need to tell vimspector how to launch your ' + 'application.' ) + return if 'configuration' in launch_variables: configuration_name = launch_variables.pop( 'configuration' ) @@ -936,3 +942,18 @@ def PathsToAllGadgetConfigs( vimspector_base, current_file ): yield utils.PathToConfigFile( '.gadgets.json', os.path.dirname( current_file ) ) + + +def PathsToAllConfigFiles( vimspector_base, current_file, filetypes ): + for ft in filetypes: + for p in sorted( glob.glob( + os.path.join( install.GetConfigDirForFiletype( vimspector_base, ft ), + '*.json' ) ) ): + yield p + + for ft in filetypes: + yield utils.PathToConfigFile( f'.vimspector.{ft}.json', + os.path.dirname( current_file ) ) + + yield utils.PathToConfigFile( '.vimspector.json', + os.path.dirname( current_file ) ) diff --git a/python3/vimspector/install.py b/python3/vimspector/install.py index 2adafcd..b91c90b 100644 --- a/python3/vimspector/install.py +++ b/python3/vimspector/install.py @@ -38,3 +38,13 @@ def GetGadgetConfigFile( vimspector_base ): def GetGadgetConfigDir( vimspector_base ): return os.path.join( GetGadgetDir( vimspector_base, GetOS() ), '.gadgets.d' ) + + +def GetConfigDirForFiletype( vimspector_base, filetype ): + if not filetype: + filetype = 'default' + + return os.path.join( os.path.abspath( vimspector_base ), + 'configurations', + GetOS(), + filetype ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 4ae7f94..3ac41bf 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -515,3 +515,8 @@ def SetSyntax( current_syntax, syntax, *args ): vim.command( 'set syntax={}'.format( Escape( syntax ) ) ) return syntax + + +def GetBufferFiletypes( buf ): + ft = ToUnicode( vim.eval( f"getbufvar( {buf.number}, '&ft' )" ) ) + return ft.split( '.' ) From be32a0a1a82ce6420c7bc7eedb5672dac0023479 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jan 2020 22:36:55 +0000 Subject: [PATCH 105/609] Add --enable-custom to load custom gadget meta --- install_gadget.py | 155 ++++++++++++++++++---------- python3/vimspector/debug_session.py | 2 +- 2 files changed, 102 insertions(+), 55 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 433261e..08738c1 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -18,7 +18,8 @@ import sys if sys.version_info.major < 3: - exit( "Sorry, you need to be running this script with python3 or later" ) + sys.exit( "You need to run this with python 3. Your version is " + + '.'.join( map( str, sys.version_info[ :3 ] ) ) ) from urllib import request import argparse @@ -37,6 +38,8 @@ import functools import time import ssl import io +import operator +import glob # Include vimspector source, for utils sys.path.insert( 1, os.path.join( os.path.dirname( __file__ ), @@ -200,7 +203,8 @@ GADGETS = { }, 'macos': { 'file_name': 'netcoredbg-osx-master.tar.gz', - 'checksum': '', + 'checksum': + 'c1dc6ed58c3f5b0473cfb4985a96552999360ceb9795e42d9c9be64af054f821', }, 'linux': { 'file_name': 'netcoredbg-linux-master.tar.gz', @@ -345,7 +349,7 @@ GADGETS = { 'enabled': False, 'repo': { 'url': 'https://github.com/microsoft/vscode-node-debug2', - 'ref': 'v1.39.1', + 'ref': 'v1.42.0', }, 'do': lambda name, root, gadget: InstallNodeDebug( name, root, gadget ), 'adapters': { @@ -525,7 +529,7 @@ def DownloadFileTo( url, destination, file_name = None, checksum = None, - sslcheck = True ): + check_certificate = True ): if not file_name: file_name = url.split( '/' )[ -1 ] @@ -550,7 +554,7 @@ def DownloadFileTo( url, print( "Downloading {} to {}/{}".format( url, destination, file_name ) ) - if not sslcheck: + if not check_certificate: context = ssl.create_default_context() context.check_hostname = False context.verify_mode = ssl.CERT_NONE @@ -663,13 +667,75 @@ def CloneRepoTo( url, ref, destination ): '--recursive' ] ) +def InstallGagdet( name, gadget, failed, all_adapters ): + try: + v = {} + v.update( gadget.get( 'all', {} ) ) + v.update( gadget.get( OS, {} ) ) + + if 'download' in gadget: + if 'file_name' not in v: + raise RuntimeError( "Unsupported OS {} for gadget {}".format( OS, + name ) ) + + destination = os.path.join( gadget_dir, 'download', name, v[ 'version' ] ) + + url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v ) + + file_path = DownloadFileTo( + url, + destination, + file_name = gadget[ 'download' ].get( 'target' ), + checksum = v.get( 'checksum' ), + check_certificate = not args.no_check_certificate ) + + root = os.path.join( destination, 'root' ) + ExtractZipTo( file_path, + root, + format = gadget[ 'download' ].get( 'format', 'zip' ) ) + elif 'repo' in gadget: + url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( v ) + ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( v ) + + destination = os.path.join( gadget_dir, 'download', name ) + CloneRepoTo( url, ref, destination ) + root = destination + + if 'do' in gadget: + gadget[ 'do' ]( name, root, v ) + else: + MakeExtensionSymlink( name, root ) + + all_adapters.update( gadget.get( 'adapters', {} ) ) + + print( "Done installing {}".format( name ) ) + except Exception as e: + traceback.print_exc() + failed.append( name ) + print( "FAILED installing {}: {}".format( name, e ) ) + + +vimspector_base = os.path.dirname( __file__ ) OS = install.GetOS() -gadget_dir = install.GetGadgetDir( os.path.dirname( __file__ ), OS ) +gadget_dir = install.GetGadgetDir( vimspector_base, OS ) print( 'OS = ' + OS ) print( 'gadget_dir = ' + gadget_dir ) -parser = argparse.ArgumentParser() +parser = argparse.ArgumentParser( + description = 'Install DAP Servers for use with Vimspector.', + epilog = + """ + If you're not sure, normally --all is enough to get started. + + Custom server definitions can be defined in JSON files, allowing + installation of arbitrary servers packaged in one of the ways that this + installer understands. + + The format of the file can be found on the Vimspector reference guide: + https://puremourning.github.io/vimspector + """ +) parser.add_argument( '--all', action = 'store_true', help = 'Enable all supported completers' ) @@ -682,6 +748,15 @@ parser.add_argument( '--no-gadget-config', action = 'store_true', help = "Don't write the .gagets.json, just install" ) +parser.add_argument( '--enable-custom', + dest='custom_gadget_file', + action='append', + nargs='*', + default = [], + help = 'Read custom gadget from supplied file. This ' + 'can be supplied multiple times and each time ' + 'multiple files can be passed.' ) + done_languages = set() for name, gadget in GADGETS.items(): lang = gadget[ 'language' ] @@ -722,6 +797,18 @@ args = parser.parse_args() if args.force_all and not args.all: args.all = True +CUSTOM_GADGETS = {} +custom_files = glob.glob( os.path.join( vimspector_base, + 'gadgets', + 'custom', + '*.json' ) ) +for custom_file_name in functools.reduce( operator.add, + args.custom_gadget_file, + custom_files ): + with open( custom_file_name, 'r' ) as custom_file: + CUSTOM_GADGETS.update( json.load( custom_file ) ) + + failed = [] all_adapters = {} for name, gadget in GADGETS.items(): @@ -735,53 +822,14 @@ for name, gadget in GADGETS.items(): if getattr( args, 'disable_' + gadget[ 'language' ] ): continue - try: - v = {} - v.update( gadget.get( 'all', {} ) ) - v.update( gadget.get( OS, {} ) ) - - if 'download' in gadget: - if 'file_name' not in v: - raise RuntimeError( "Unsupported OS {} for gadget {}".format( OS, - name ) ) - - destination = os.path.join( gadget_dir, 'download', name, v[ 'version' ] ) - - url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v ) - verify_cert_off = args.no_check_certificate - - file_path = DownloadFileTo( - url, - destination, - file_name = gadget[ 'download' ].get( 'target' ), - checksum = v.get( 'checksum' ), - sslcheck = not verify_cert_off ) - root = os.path.join( destination, 'root' ) - ExtractZipTo( file_path, - root, - format = gadget[ 'download' ].get( 'format', 'zip' ) ) - elif 'repo' in gadget: - url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( v ) - ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( v ) - - destination = os.path.join( gadget_dir, 'download', name ) - CloneRepoTo( url, ref, destination ) - root = destination - - if 'do' in gadget: - gadget[ 'do' ]( name, root, v ) - else: - MakeExtensionSymlink( name, root ) - - all_adapters.update( gadget.get( 'adapters', {} ) ) + InstallGagdet( name, + gadget, + failed, + all_adapters ) - print( "Done installing {}".format( name ) ) - except Exception as e: - traceback.print_exc() - failed.append( name ) - print( "FAILED installing {}: {}".format( name, e ) ) - +for name, gadget in CUSTOM_GADGETS.items(): + InstallGagdet( name, gadget, failed, all_adapters ) adapter_config = json.dumps ( { 'adapters': all_adapters }, indent=2, @@ -792,8 +840,7 @@ if args.no_gadget_config: print( "Would write the following gadgets: " ) print( adapter_config ) else: - with open( install.GetGadgetConfigFile( os.path.dirname( __file__ ) ), - 'w' ) as f: + with open( install.GetGadgetConfigFile( vimspector_base ), 'w' ) as f: f.write( adapter_config ) if failed: diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index a0d8418..87deaf6 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -98,7 +98,7 @@ class DebugSession( object ): filetypes ): self._logger.debug( f'Reading configurations from: {launch_config_file}' ) if not launch_config_file or not os.path.exists( launch_config_file ): - continue + continue with open( launch_config_file, 'r' ) as f: database = json.load( f ) From e37ef18c2812cbe2341a9c32dce613d485e0858e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 9 Feb 2020 16:08:55 +0000 Subject: [PATCH 106/609] Move installation routines into proper library --- install_gadget.py | 234 ++++---------------------------- python3/vimspector/installer.py | 218 +++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+), 210 deletions(-) create mode 100644 python3/vimspector/installer.py diff --git a/install_gadget.py b/install_gadget.py index 08738c1..561af21 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -21,23 +21,13 @@ if sys.version_info.major < 3: sys.exit( "You need to run this with python 3. Your version is " + '.'.join( map( str, sys.version_info[ :3 ] ) ) ) -from urllib import request import argparse -import contextlib import os import string -import zipfile -import gzip -import shutil import subprocess import traceback -import tarfile -import hashlib import json import functools -import time -import ssl -import io import operator import glob @@ -45,7 +35,7 @@ import glob sys.path.insert( 1, os.path.join( os.path.dirname( __file__ ), 'python3' ) ) -from vimspector import install +from vimspector import install, installer GADGETS = { 'vscode-cpptools': { @@ -210,7 +200,7 @@ GADGETS = { 'file_name': 'netcoredbg-linux-master.tar.gz', 'checksum': '', }, - 'do': lambda name, root, gadget: MakeSymlink( + 'do': lambda name, root, gadget: installer.MakeSymlink( gadget_dir, name, os.path.join( root, 'netcoredbg' ) ), @@ -393,29 +383,15 @@ GADGETS = { } -@contextlib.contextmanager -def CurrentWorkingDir( d ): - cur_d = os.getcwd() - try: - os.chdir( d ) - yield - finally: - os.chdir( cur_d ) - - -def MakeExecutable( file_path ): - # TODO: import stat and use them by _just_ adding the X bit. - print( 'Making executable: {}'.format( file_path ) ) - os.chmod( file_path, 0o755 ) - - def InstallCppTools( name, root, gadget ): extension = os.path.join( root, 'extension' ) # It's hilarious, but the execute bits aren't set in the vsix. So they # actually have javascript code which does this. It's just a horrible horrible # hack that really is not funny. - MakeExecutable( os.path.join( extension, 'debugAdapters', 'OpenDebugAD7' ) ) + installer.MakeExecutable( os.path.join( extension, + 'debugAdapters', + 'OpenDebugAD7' ) ) with open( os.path.join( extension, 'package.json' ) ) as f: package = json.load( f ) runtime_dependencies = package[ 'runtimeDependencies' ] @@ -423,14 +399,17 @@ def InstallCppTools( name, root, gadget ): for binary in dependency.get( 'binaries' ): file_path = os.path.abspath( os.path.join( extension, binary ) ) if os.path.exists( file_path ): - MakeExecutable( os.path.join( extension, binary ) ) + installer.MakeExecutable( os.path.join( extension, binary ) ) - MakeExtensionSymlink( name, root ) + installer.MakeExtensionSymlink( vimspector_base, name, root ) def InstallBashDebug( name, root, gadget ): - MakeExecutable( os.path.join( root, 'extension', 'bashdb_dir', 'bashdb' ) ) - MakeExtensionSymlink( name, root ) + installer.MakeExecutable( os.path.join( root, + 'extension', + 'bashdb_dir', + 'bashdb' ) ) + installer.MakeExtensionSymlink( vimspector_base, name, root ) def InstallDebugpy( name, root, gadget ): @@ -442,7 +421,7 @@ def InstallDebugpy( name, root, gadget ): finally: os.chdir( wd ) - MakeSymlink( gadget_dir, name, root ) + installer.MakeSymlink( gadget_dir, name, root ) def InstallTclProDebug( name, root, gadget ): @@ -473,11 +452,11 @@ def InstallTclProDebug( name, root, gadget ): break - with CurrentWorkingDir( os.path.join( root, 'lib', 'tclparser' ) ): + with installer.CurrentWorkingDir( os.path.join( root, 'lib', 'tclparser' ) ): subprocess.check_call( configure ) subprocess.check_call( [ 'make' ] ) - MakeSymlink( gadget_dir, name, root ) + installer.MakeSymlink( gadget_dir, name, root ) def InstallNodeDebug( name, root, gadget ): @@ -495,176 +474,10 @@ def InstallNodeDebug( name, root, gadget ): print( " $ ./install_gadget.py --enable-node ..." ) raise RuntimeError( 'Invalid node environent for node debugger' ) - with CurrentWorkingDir( root ): + with installer.CurrentWorkingDir( root ): subprocess.check_call( [ 'npm', 'install' ] ) subprocess.check_call( [ 'npm', 'run', 'build' ] ) - MakeSymlink( gadget_dir, name, root ) - - -def WithRetry( f ): - retries = 5 - timeout = 1 # seconds - - @functools.wraps( f ) - def wrapper( *args, **kwargs ): - thrown = None - for _ in range( retries ): - try: - return f( *args, **kwargs ) - except Exception as e: - thrown = e - print( "Failed - {}, will retry in {} seconds".format( e, timeout ) ) - time.sleep( timeout ) - raise thrown - - return wrapper - - -@WithRetry -def UrlOpen( *args, **kwargs ): - return request.urlopen( *args, **kwargs ) - - -def DownloadFileTo( url, - destination, - file_name = None, - checksum = None, - check_certificate = True ): - if not file_name: - file_name = url.split( '/' )[ -1 ] - - file_path = os.path.abspath( os.path.join( destination, file_name ) ) - - if not os.path.isdir( destination ): - os.makedirs( destination ) - - if os.path.exists( file_path ): - if checksum: - if ValidateCheckSumSHA256( file_path, checksum ): - print( "Checksum matches for {}, using it".format( file_path ) ) - return file_path - else: - print( "Checksum doesn't match for {}, removing it".format( - file_path ) ) - - print( "Removing existing {}".format( file_path ) ) - os.remove( file_path ) - - r = request.Request( url, headers = { 'User-Agent': 'Vimspector' } ) - - print( "Downloading {} to {}/{}".format( url, destination, file_name ) ) - - if not check_certificate: - context = ssl.create_default_context() - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE - kwargs = { "context": context } - else: - kwargs = {} - - with contextlib.closing( UrlOpen( r, **kwargs ) ) as u: - with open( file_path, 'wb' ) as f: - f.write( u.read() ) - - if checksum: - if not ValidateCheckSumSHA256( file_path, checksum ): - raise RuntimeError( - 'Checksum for {} ({}) does not match expected {}'.format( - file_path, - GetChecksumSHA254( file_path ), - checksum ) ) - else: - print( "Checksum for {}: {}".format( file_path, - GetChecksumSHA254( file_path ) ) ) - - return file_path - - -def GetChecksumSHA254( file_path ): - with open( file_path, 'rb' ) as existing_file: - return hashlib.sha256( existing_file.read() ).hexdigest() - - -def ValidateCheckSumSHA256( file_path, checksum ): - existing_sha256 = GetChecksumSHA254( file_path ) - return existing_sha256 == checksum - - -def RemoveIfExists( destination ): - if os.path.exists( destination ) or os.path.islink( destination ): - if os.path.islink( destination ): - print( "Removing file {}".format( destination ) ) - os.remove( destination ) - else: - print( "Removing dir {}".format( destination ) ) - shutil.rmtree( destination ) - - -# Python's ZipFile module strips execute bits from files, for no good reason -# other than crappy code. Let's do it's job for it. -class ModePreservingZipFile( zipfile.ZipFile ): - def extract( self, member, path = None, pwd = None ): - if not isinstance( member, zipfile.ZipInfo ): - member = self.getinfo( member ) - - if path is None: - path = os.getcwd() - - ret_val = self._extract_member( member, path, pwd ) - attr = member.external_attr >> 16 - os.chmod( ret_val, attr ) - return ret_val - - -def ExtractZipTo( file_path, destination, format ): - print( "Extracting {} to {}".format( file_path, destination ) ) - RemoveIfExists( destination ) - - if format == 'zip': - with ModePreservingZipFile( file_path ) as f: - f.extractall( path = destination ) - elif format == 'zip.gz': - with gzip.open( file_path, 'rb' ) as f: - file_contents = f.read() - - with ModePreservingZipFile( io.BytesIO( file_contents ) ) as f: - f.extractall( path = destination ) - - elif format == 'tar': - try: - with tarfile.open( file_path ) as f: - f.extractall( path = destination ) - except Exception: - # There seems to a bug in python's tarfile that means it can't read some - # windows-generated tar files - os.makedirs( destination ) - with CurrentWorkingDir( destination ): - subprocess.check_call( [ 'tar', 'zxvf', file_path ] ) - - -def MakeExtensionSymlink( name, root ): - MakeSymlink( gadget_dir, name, os.path.join( root, 'extension' ) ), - - -def MakeSymlink( in_folder, link, pointing_to ): - RemoveIfExists( os.path.join( in_folder, link ) ) - - in_folder = os.path.abspath( in_folder ) - pointing_to = os.path.relpath( os.path.abspath( pointing_to ), - in_folder ) - os.symlink( pointing_to, os.path.join( in_folder, link ) ) - - -def CloneRepoTo( url, ref, destination ): - RemoveIfExists( destination ) - git_in_repo = [ 'git', '-C', destination ] - subprocess.check_call( [ 'git', 'clone', url, destination ] ) - subprocess.check_call( git_in_repo + [ 'checkout', ref ] ) - subprocess.check_call( git_in_repo + [ 'submodule', 'sync', '--recursive' ] ) - subprocess.check_call( git_in_repo + [ 'submodule', - 'update', - '--init', - '--recursive' ] ) + installer.MakeSymlink( gadget_dir, name, root ) def InstallGagdet( name, gadget, failed, all_adapters ): @@ -682,7 +495,7 @@ def InstallGagdet( name, gadget, failed, all_adapters ): url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v ) - file_path = DownloadFileTo( + file_path = installer.DownloadFileTo( url, destination, file_name = gadget[ 'download' ].get( 'target' ), @@ -690,21 +503,22 @@ def InstallGagdet( name, gadget, failed, all_adapters ): check_certificate = not args.no_check_certificate ) root = os.path.join( destination, 'root' ) - ExtractZipTo( file_path, - root, - format = gadget[ 'download' ].get( 'format', 'zip' ) ) + installer.ExtractZipTo( + file_path, + root, + format = gadget[ 'download' ].get( 'format', 'zip' ) ) elif 'repo' in gadget: url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( v ) ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( v ) destination = os.path.join( gadget_dir, 'download', name ) - CloneRepoTo( url, ref, destination ) + installer.CloneRepoTo( url, ref, destination ) root = destination if 'do' in gadget: gadget[ 'do' ]( name, root, v ) else: - MakeExtensionSymlink( name, root ) + installer.MakeExtensionSymlink( vimspector_base, name, root ) all_adapters.update( gadget.get( 'adapters', {} ) ) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py new file mode 100644 index 0000000..53a0150 --- /dev/null +++ b/python3/vimspector/installer.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python3 + +# vimspector - A multi-language debugging system for Vim +# Copyright 2019 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. + +from urllib import request +import io +import contextlib +import zipfile +import gzip +import shutil +import tarfile +import hashlib +import time +import ssl +import subprocess +import functools +import os + +from vimspector import install + + +@contextlib.contextmanager +def CurrentWorkingDir( d ): + cur_d = os.getcwd() + try: + os.chdir( d ) + yield + finally: + os.chdir( cur_d ) + + +def MakeExecutable( file_path ): + # TODO: import stat and use them by _just_ adding the X bit. + print( 'Making executable: {}'.format( file_path ) ) + os.chmod( file_path, 0o755 ) + + + +def WithRetry( f ): + retries = 5 + timeout = 1 # seconds + + @functools.wraps( f ) + def wrapper( *args, **kwargs ): + thrown = None + for _ in range( retries ): + try: + return f( *args, **kwargs ) + except Exception as e: + thrown = e + print( "Failed - {}, will retry in {} seconds".format( e, timeout ) ) + time.sleep( timeout ) + raise thrown + + return wrapper + + +@WithRetry +def UrlOpen( *args, **kwargs ): + return request.urlopen( *args, **kwargs ) + + +def DownloadFileTo( url, + destination, + file_name = None, + checksum = None, + check_certificate = True ): + if not file_name: + file_name = url.split( '/' )[ -1 ] + + file_path = os.path.abspath( os.path.join( destination, file_name ) ) + + if not os.path.isdir( destination ): + os.makedirs( destination ) + + if os.path.exists( file_path ): + if checksum: + if ValidateCheckSumSHA256( file_path, checksum ): + print( "Checksum matches for {}, using it".format( file_path ) ) + return file_path + else: + print( "Checksum doesn't match for {}, removing it".format( + file_path ) ) + + print( "Removing existing {}".format( file_path ) ) + os.remove( file_path ) + + r = request.Request( url, headers = { 'User-Agent': 'Vimspector' } ) + + print( "Downloading {} to {}/{}".format( url, destination, file_name ) ) + + if not check_certificate: + context = ssl.create_default_context() + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + kwargs = { "context": context } + else: + kwargs = {} + + with contextlib.closing( UrlOpen( r, **kwargs ) ) as u: + with open( file_path, 'wb' ) as f: + f.write( u.read() ) + + if checksum: + if not ValidateCheckSumSHA256( file_path, checksum ): + raise RuntimeError( + 'Checksum for {} ({}) does not match expected {}'.format( + file_path, + GetChecksumSHA254( file_path ), + checksum ) ) + else: + print( "Checksum for {}: {}".format( file_path, + GetChecksumSHA254( file_path ) ) ) + + return file_path + + +def GetChecksumSHA254( file_path ): + with open( file_path, 'rb' ) as existing_file: + return hashlib.sha256( existing_file.read() ).hexdigest() + + +def ValidateCheckSumSHA256( file_path, checksum ): + existing_sha256 = GetChecksumSHA254( file_path ) + return existing_sha256 == checksum + + +def RemoveIfExists( destination ): + if os.path.exists( destination ) or os.path.islink( destination ): + if os.path.islink( destination ): + print( "Removing file {}".format( destination ) ) + os.remove( destination ) + else: + print( "Removing dir {}".format( destination ) ) + shutil.rmtree( destination ) + + +# Python's ZipFile module strips execute bits from files, for no good reason +# other than crappy code. Let's do it's job for it. +class ModePreservingZipFile( zipfile.ZipFile ): + def extract( self, member, path = None, pwd = None ): + if not isinstance( member, zipfile.ZipInfo ): + member = self.getinfo( member ) + + if path is None: + path = os.getcwd() + + ret_val = self._extract_member( member, path, pwd ) + attr = member.external_attr >> 16 + os.chmod( ret_val, attr ) + return ret_val + + +def ExtractZipTo( file_path, destination, format ): + print( "Extracting {} to {}".format( file_path, destination ) ) + RemoveIfExists( destination ) + + if format == 'zip': + with ModePreservingZipFile( file_path ) as f: + f.extractall( path = destination ) + elif format == 'zip.gz': + with gzip.open( file_path, 'rb' ) as f: + file_contents = f.read() + + with ModePreservingZipFile( io.BytesIO( file_contents ) ) as f: + f.extractall( path = destination ) + + elif format == 'tar': + try: + with tarfile.open( file_path ) as f: + f.extractall( path = destination ) + except Exception: + # There seems to a bug in python's tarfile that means it can't read some + # windows-generated tar files + os.makedirs( destination ) + with CurrentWorkingDir( destination ): + subprocess.check_call( [ 'tar', 'zxvf', file_path ] ) + + +def MakeExtensionSymlink( vimspector_base, name, root ): + MakeSymlink( install.GetGadgetDir( vimspector_base, + install.GetOS() ), + name, + os.path.join( root, 'extension' ) ), + + +def MakeSymlink( in_folder, link, pointing_to ): + RemoveIfExists( os.path.join( in_folder, link ) ) + + in_folder = os.path.abspath( in_folder ) + pointing_to = os.path.relpath( os.path.abspath( pointing_to ), + in_folder ) + os.symlink( pointing_to, os.path.join( in_folder, link ) ) + + +def CloneRepoTo( url, ref, destination ): + RemoveIfExists( destination ) + git_in_repo = [ 'git', '-C', destination ] + subprocess.check_call( [ 'git', 'clone', url, destination ] ) + subprocess.check_call( git_in_repo + [ 'checkout', ref ] ) + subprocess.check_call( git_in_repo + [ 'submodule', 'sync', '--recursive' ] ) + subprocess.check_call( git_in_repo + [ 'submodule', + 'update', + '--init', + '--recursive' ] ) From 267f202dad7e974b3e9c36509760efb7551f6ff1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 16 Feb 2020 17:50:28 +0000 Subject: [PATCH 107/609] Fix LGTM error: python default arguments are mutable\! --- python3/vimspector/debug_session.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 87deaf6..f684d6a 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -72,7 +72,12 @@ class DebugSession( object ): self._on_init_complete_handlers = [] self._server_capabilities = {} - def Start( self, launch_variables = {} ): + def Start( self, launch_variables = None ): + # We mutate launch_variables, so don't mutate the default argument. + # https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments + if launch_variables is None: + launch_variables = {} + self._logger.info( "User requested start debug session with %s", launch_variables ) self._configuration = None From 68a45b5771d8301d12a73e208ae113a7c16a74a0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 16 Feb 2020 19:59:31 +0000 Subject: [PATCH 108/609] Fix workspace root when no vimspector conf - use dir of current file --- .vimspector.json | 139 ---------------------------- python3/vimspector/debug_session.py | 5 +- support/test/python/no_conf/main.py | 29 ++++++ 3 files changed, 33 insertions(+), 140 deletions(-) delete mode 100644 .vimspector.json create mode 100755 support/test/python/no_conf/main.py diff --git a/.vimspector.json b/.vimspector.json deleted file mode 100644 index 5f7a93d..0000000 --- a/.vimspector.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "adapters": { - "lldb-mi": { - "name": "lldb-mi", - "command": [ - "node", - "$HOME/.vscode/extensions/webfreak.debug-0.22.0/out/src/lldb.js" - ] - }, - "cppdbg": { - "name": "cppdbg", - "command": [ "$HOME/.vscode/extensions/ms-vscode.cpptools-0.20.1/debugAdapters/OpenDebugAD7" ], - "attach": { - "pidProperty": "processId", - "pidSelect": "ask" - } - }, - "python": { - "name": "python", - "command": [ - "node", - "$HOME/.vscode/extensions/ms-python.python-2018.4.0/out/client/debugger/Main.js" - ] - }, - "bashdb": { - "name": "bashdb", - "command": [ - "node", - "$HOME/.vscode/extensions/rogalmic.bash-debug-0.2.0/out/bashDebug.js" - ] - }, - "lldb": { - "name": "lldb", - "command": [ - "lldb", - "-b", - "-O", - "command script import '$HOME/.vscode/extensions/vadimcn.vscode-lldb-0.8.7/adapter'", - "-O", - "script adapter.main.run_stdio_session()" - ] - } - }, - "configurations": { - "simple_c_program - lldb-mi Launch": { - "adapter": "lldb-mi", - "configuration": { - "request": "launch", - "target": "support/test/cpp/simple_c_program/test", - "args": [], - "cwd": ".", - "lldbmipath": "$HOME/.vscode/extensions/ms-vscode.cpptools-0.20.1/debugAdapters/lldb/bin/lldb-mi", - "trace": true, - "logFilePath": "$HOME/.vimspector.protocol.log" - } - }, - "simple_c_progra - ms Launch": { - "adapter": "cppdbg", - "configuration": { - "name": "ms Launch", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceRoot}/support/test/cpp/simple_c_program/test", - "args": [], - "cwd": "$HOME", - "environment": [], - "externalConsole": true, - "MIMode": "lldb" - } - }, - "simple_python - launch": { - "adapter": "python", - "configuration": { - "name": "Python: Current File", - "type": "python", - "request": "launch", - "cwd": "${workspaceRoot}/support/test/python/simple_python", - "stopOnEntry": true, - "console": "externalTerminal", - "debugOptions": [], - "program": "${workspaceRoot}/support/test/python/simple_python/main.py" - } - }, - "simple_c_program - MS Attach": { - "adapter": "cppdbg", - "configuration": { - "name": "(lldb) Attach", - "type": "cppdbg", - "request": "attach", - "program": "${workspaceRoot}/support/test/cpp/simple_c_program/test", - "MIMode": "lldb" - } - }, - "bashdb": { - "adapter": "bashdb", - "configuration": { - "type": "bashdb", - "request": "launch", - "name": "Bash-Debug (simplest configuration)", - "program": "$HOME/.vim/bundle/YouCompleteMe/install.sh", - "args": [], - "cwd": "$HOME/.vim/bundle/YouCompleteMe", - "pathBash": "bash", - "pathBashdb": "bashdb", - "pathCat": "cat", - "pathMkfifo": "mkfifo", - "pathPkill": "pkill", - "showDebugOutput": true, - "trace": true - } - }, - "lldb launch": { - "adapter": "lldb", - "configuration": { - "type": "lldb", - "request": "launch", - "name": "LLDB: Launch", - "program": "$HOME/Development/vim/src/vim", - "args": [], - "cwd": "$HOME/Development/vim" - } - }, - "racerd": { - "adapter": "lldb", - "configuration": { - "type": "lldb", - "request": "launch", - "name": "LLDB: Launch", - "program": "$HOME/.vim/bundle/YouCompleteMe/third_party/ycmd/third_party/racerd/target/debug/racerd", - "args": [ - "serve", - "--port=12345", - "--secret-file=secretfile" - ], - "cwd": "$HOME/.vim/bundle/YouCompleteMe/third_party/ycmd" - } - } - } -} diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index f684d6a..83c64f7 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -128,7 +128,10 @@ class DebugSession( object ): if not configuration_name or configuration_name not in configurations: return - self._workspace_root = os.path.dirname( launch_config_file ) + if launch_config_file: + self._workspace_root = os.path.dirname( launch_config_file ) + else: + self._workspace_root = os.path.dirname( current_file ) configuration = configurations[ configuration_name ] adapter = configuration.get( 'adapter' ) diff --git a/support/test/python/no_conf/main.py b/support/test/python/no_conf/main.py new file mode 100755 index 0000000..6515c3f --- /dev/null +++ b/support/test/python/no_conf/main.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + + +class TestClass( object ): + def __init__( self, value ): + self._var = value + try: + self.DoSomething() + except ValueError: + pass + + def DoSomething( self ): + for i in range( 0, 100 ): + if i < self._var: + print( '{0} is less than the value'.format( i ) ) + else: + print( '{0} might be more'.format( i ) ) + + raise ValueError( 'Done' ) + + +def Main(): + t = TestClass( 18 ) + + t._var = 99 + t.DoSomething() + + +Main() From 37fefafe35250e9925274858ad086429ba7af810 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 2 Jan 2020 23:55:46 +0000 Subject: [PATCH 109/609] Fix tracebacks when the debug adapter dies very quickly --- autoload/vimspector/internal/job.vim | 19 +++++++++++++------ python3/vimspector/debug_session.py | 6 +++++- python3/vimspector/output.py | 11 ++++++----- python3/vimspector/utils.py | 6 ++++-- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index 4ed9ea8..18c5b08 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -30,7 +30,9 @@ endfunction function! s:_OnExit( channel, status ) abort echom 'Channel exit with status ' . a:status redraw - unlet s:job + if exists( 's:job' ) + unlet s:job + endif py3 _vimspector_session.OnServerExit( vim.eval( 'a:status' ) ) endfunction @@ -61,12 +63,17 @@ function! vimspector#internal#job#StartDebugSession( config ) abort \ } \ ) - echom 'Started job, status is: ' . job_status( s:job ) + if !exists( 's:job' ) + " The job died immediately after starting and we cleaned up + return v:false + endif + + let status = job_status( s:job ) + + echom 'Started job, status is: ' . status redraw - if job_status( s:job ) !=# 'run' - echom 'Unable to start job, status is: ' . job_status( s:job ) - redraw + if status !=# 'run' return v:false endif @@ -140,7 +147,7 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort \ } ) ) if job_status( s:commands[ a:category ][ index ] ) !=# 'run' - echom 'Unable to start job for ' . a:cmd + echom 'Unable to start job for ' . string( a:cmd ) redraw return v:none endif diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 83c64f7..2f26e4f 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -879,7 +879,11 @@ class DebugSession( object ): status ) self.Clear() - self._connection.Reset() + if self._connection is not None: + # Can be None if the server dies _before_ StartDebugSession vim function + # returns + self._connection.Reset() + self._stackTraceView.ConnectionClosed() self._variablesView.ConnectionClosed() self._outputView.ConnectionClosed() diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 43b1cc3..bbb8588 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from vimspector import utils +from vimspector import utils,install import vim import json @@ -49,10 +49,7 @@ class OutputView( object ): for b in set( BUFFER_MAP.values() ): self._CreateBuffer( b ) - self._CreateBuffer( - 'Vimspector', - file_name = vim.eval( 'expand( "~/.vimspector.log" )' ) ) - + self._CreateBuffer( 'Vimspector', file_name = utils.LOG_FILE ) self._ShowOutput( 'Console' ) def Print( self, categroy, text ): @@ -161,6 +158,10 @@ class OutputView( object ): if file_name is not None: assert cmd is None + if install.GetOS() == "windows": + # FIXME: Can't display fiels in windows (yet?) + return + cmd = [ 'tail', '-F', '-n', '+1', '--', file_name ] if cmd is not None: diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 3ac41bf..6f746ac 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -23,8 +23,10 @@ import string import functools -_log_handler = logging.FileHandler( os.path.expanduser( '~/.vimspector.log' ), - mode = 'w' ) +LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log') ) + +_log_handler = logging.FileHandler( LOG_FILE, mode = 'w' ) + _log_handler.setFormatter( logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) ) From 4a7e3b9229fedb6b63472d4c8ff3ec2d5646b246 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 3 Jan 2020 00:19:47 +0000 Subject: [PATCH 110/609] Fix opening paths on Windows --- python3/vimspector/installer.py | 21 ++++++++++++++++++--- python3/vimspector/output.py | 2 +- python3/vimspector/utils.py | 6 +++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 53a0150..95ab19f 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -201,9 +201,24 @@ def MakeSymlink( in_folder, link, pointing_to ): RemoveIfExists( os.path.join( in_folder, link ) ) in_folder = os.path.abspath( in_folder ) - pointing_to = os.path.relpath( os.path.abspath( pointing_to ), - in_folder ) - os.symlink( pointing_to, os.path.join( in_folder, link ) ) + pointing_to_relative = os.path.relpath( os.path.abspath( pointing_to ), + in_folder ) + link_path = os.path.join( in_folder, link ) + + if install.GetOS() == 'windows': + # While symlinks do exist on Windows, they require elevated privileges, so + # let's use a directory junction which is all we need. + link_path = os.path.abspath( link_path ) + if os.path.isdir( link_path ): + os.rmdir( link_path ) + subprocess.check_call( [ 'cmd.exe', + '/c', + 'mklink', + '/J', + link_path, + pointing_to ] ) + else: + os.symlink( pointing_to_relative, link_path ) def CloneRepoTo( url, ref, destination ): diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index bbb8588..c02e84a 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from vimspector import utils,install +from vimspector import utils, install import vim import json diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 6f746ac..3e58330 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -23,7 +23,7 @@ import string import functools -LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log') ) +LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log' ) ) _log_handler = logging.FileHandler( LOG_FILE, mode = 'w' ) @@ -42,7 +42,7 @@ SetUpLogging( _logger ) def BufferNumberForFile( file_name ): - return int( vim.eval( 'bufnr( "{0}", 1 )'.format( file_name ) ) ) + return int( vim.eval( "bufnr( '{0}', 1 )".format( Escape( file_name ) ) ) ) def BufferForFile( file_name ): @@ -52,7 +52,7 @@ def BufferForFile( file_name ): def OpenFileInCurrentWindow( file_name ): buffer_number = BufferNumberForFile( file_name ) try: - vim.command( 'bu {0}'.format( buffer_number ) ) + vim.current.buffer = vim.buffers[ buffer_number ] except vim.error as e: if 'E325' not in str( e ): raise From 3366e1c7844ae1bce5844334f0015b20ce108cbf Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 16 Feb 2020 17:00:22 +0000 Subject: [PATCH 111/609] Use the python used to run the installer to run debugpy --- install_gadget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install_gadget.py b/install_gadget.py index 561af21..e7a5e7f 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -113,7 +113,7 @@ GADGETS = { 'adapters': { 'debugpy': { "command": [ - "python3", + sys.executable, "${gadgetDir}/debugpy/build/lib/debugpy/adapter" ], "name": "debugpy", From 0aba8e0179535249ac7dbe896400a1de989b4817 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 16 Feb 2020 19:43:54 +0000 Subject: [PATCH 112/609] Work around neovim bug where environ() crashes --- autoload/vimspector/internal/neojob.vim | 4 +++- autoload/vimspector/internal/neoterm.vim | 5 ++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index 2a87c4c..4c7abbb 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -30,7 +30,9 @@ function! s:_OnEvent( chan_id, data, event ) abort elseif a:event ==# 'exit' echom 'Channel exit with status ' . a:data redraw - unlet s:job + if exists( 's:job' ) + unlet s:job + endif " This causes terminal spam in neovim due to " https://github.com/neovim/neovim/issues/11725 py3 _vimspector_session.OnServerExit( vim.eval( 'a:data' ) ) diff --git a/autoload/vimspector/internal/neoterm.vim b/autoload/vimspector/internal/neoterm.vim index 627f570..f36c0db 100644 --- a/autoload/vimspector/internal/neoterm.vim +++ b/autoload/vimspector/internal/neoterm.vim @@ -28,10 +28,9 @@ let s:buffer_to_id = {} function! vimspector#internal#neoterm#PrepareEnvironment( env ) abort let old_env = {} - let new_env = copy( environ() ) for key in keys( a:env ) - if has_key( new_env, key ) - let old_env[ key ] = new_env[ key ] + if exists( '$' . key ) + let old_env[ key ] = getenv( key ) endif call setenv( key, a:env[ key ] ) endfor From a74783513c48301821e85e3e4f0110fed254f928 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 16 Feb 2020 20:21:17 +0000 Subject: [PATCH 113/609] Update README --- README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0610277..5170aec 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,8 @@ Vimspector requires: * Vim version 8.1 with at least patch 1264, or Neovim 0.4.3 * One of the following operating systems: * Linux - * macOS Mojave or pater + * macOS Mojave or later + * Windows (experimental) Why such a new vim ? Well 2 reasons: @@ -161,8 +162,9 @@ Why such a new vim ? Well 2 reasons: 2. Because there are Vim bugs that vimspector triggers that will frustrate you if you hit them. -Why no Windows support? Because it's effort and it's not a priority for the -author. PRs are welcome. +Why Windows support experimental? Because it's effort and it's not a priority +for the author. PRs are welcome to fix bugs. Windows will not be regularly +tested. Which Linux versions? I only test on Ubuntu 18.04 and later and RHEL 7. @@ -186,6 +188,12 @@ Workarounds are in place as follows: There is no workaroud for the lack of balloons; you'll just have to use `:VimspectorEval` or `:VimspectorWatch`, or switch to Vim. +## Windows differences + +The following features are not implemented for Windows: + +* Tailing the vimspector log in the Output Window. + ## Language dependencies The debug adapters themselves have certain runtime dependencies. They are From 926029e343cccae0db08533298a2e13aa87755dc Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 16 Feb 2020 22:30:55 +0000 Subject: [PATCH 114/609] Set some window options to their deafults --- python3/vimspector/stack_trace.py | 1 + python3/vimspector/utils.py | 7 +++++++ python3/vimspector/variables.py | 3 +++ 3 files changed, 11 insertions(+) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 96edb4d..9a98618 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -42,6 +42,7 @@ class StackTraceView( object ): # FIXME: Remove all usage of "Windown" and just use buffers to prevent all # the bugs around the window being closed. self._win = vim.current.window + utils.SetUpUIWindow( self._win ) vim.command( 'nnoremap :call vimspector#GoToFrame()' ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 3e58330..35d5d63 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -126,6 +126,13 @@ def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ): Escape( callback ) ) ) +def SetUpUIWindow( win ): + win.options[ 'wrap' ] = False + win.options[ 'number' ] = False + win.options[ 'signcolumn' ] = 'no' + win.options[ 'spell' ] = False + win.options[ 'list' ] = False + @contextlib.contextmanager def ModifiableScratchBuffer( buf ): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 92bbedf..e86b23f 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -65,6 +65,9 @@ class VariablesView( object ): 'Expression: ', 'vimspector#AddWatchPrompt' ) + utils.SetUpUIWindow( self._vars.win ) + utils.SetUpUIWindow( self._watch.win ) + has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) ) has_balloon_term = int( vim.eval( "has( 'balloon_eval_term' )" ) ) From c4bedcee8903084d4aa6c3ccaae667eefdca9f11 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 16 Feb 2020 22:49:15 +0000 Subject: [PATCH 115/609] Set the code window to the current buffer --- python3/vimspector/code.py | 4 +++- python3/vimspector/debug_session.py | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 4b21192..e62a9ff 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -22,7 +22,7 @@ from vimspector import utils class CodeView( object ): - def __init__( self, window, api_prefix ): + def __init__( self, window, original_window, api_prefix ): self._window = window self._api_prefix = api_prefix @@ -40,7 +40,9 @@ class CodeView( object ): 'breakpoints': [] } + with utils.LetCurrentWindow( self._window ): + vim.current.buffer = original_window.buffer vim.command( 'nnoremenu WinBar.Continue :call vimspector#Continue()' ) vim.command( 'nnoremenu WinBar.Next :call vimspector#StepOver()' ) vim.command( 'nnoremenu WinBar.Step :call vimspector#StepInto()' ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 2f26e4f..9c60ab8 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -445,11 +445,15 @@ class DebugSession( object ): def _SetUpUI( self ): + original_window = vim.current.window + vim.command( 'tabnew' ) self._uiTab = vim.current.tabpage # Code window - self._codeView = code.CodeView( vim.current.window, self._api_prefix ) + self._codeView = code.CodeView( vim.current.window, + original_window, + self._api_prefix ) # Call stack with utils.TemporaryVimOptions( { 'splitright': False, From 2c1060c49efff59d59769ee4e00f4f82b14ab2ca Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 31 Jan 2020 11:53:37 +0000 Subject: [PATCH 116/609] Support *\$var in lists to splat (expand in place) shell-split user input. Useful for commandline-> args list --- python3/vimspector/utils.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 35d5d63..cffc425 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -21,6 +21,9 @@ import vim import json import string import functools +import subprocess +import shlex + LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log' ) ) @@ -346,10 +349,27 @@ def ExpandReferencesInObject( obj, mapping, user_choices ): if isinstance( obj, dict ): ExpandReferencesInDict( obj, mapping, user_choices ) elif isinstance( obj, list ): - for i, _ in enumerate( obj ): - # FIXME: We are assuming that it is a list of string, but could be a - # list of list of a list of dict, etc. - obj[ i ] = ExpandReferencesInObject( obj[ i ], mapping, user_choices ) + j_offset = 0 + obj_copy = list( obj ) + + for i, _ in enumerate( obj_copy ): + j = i + j_offset + if ( isinstance( obj_copy[ i ], str ) and + len( obj_copy[ i ] ) > 2 and + obj_copy[ i ][ 0:2 ] == '*$' ): + # *${something} - expand list in place + value = ExpandReferencesInString( obj_copy[ i ][ 1: ], + mapping, + user_choices ) + obj.pop( j ) + j_offset -= 1 + for opt_index, opt in enumerate( shlex.split( value ) ): + obj.insert( j + opt_index, opt ) + j_offset += 1 + else: + obj[ j ] = ExpandReferencesInObject( obj_copy[ i ], + mapping, + user_choices ) elif isinstance( obj, str ): obj = ExpandReferencesInString( obj, mapping, user_choices ) @@ -409,9 +429,6 @@ def ParseVariables( variables_list, mapping, user_choices ): for n, v in variables.items(): if isinstance( v, dict ): if 'shell' in v: - import subprocess - import shlex - new_v = v.copy() # Bit of a hack. Allows environment variables to be used. ExpandReferencesInDict( new_v, new_mapping, user_choices ) From cd26e81fd9465111927a19474593c343e7eb01f0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 23 Feb 2020 16:12:35 +0000 Subject: [PATCH 117/609] When requesting source from debugger, use the path, if supplied --- python3/vimspector/stack_trace.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 9a98618..6746b61 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -283,7 +283,8 @@ class StackTraceView( object ): def consume_source( msg ): self._sources[ source_reference ] = source - buf_name = os.path.join( '_vimspector_tmp', source[ 'name' ] ) + buf_name = os.path.join( '_vimspector_tmp', + source.get( 'path', source[ 'name' ] ) ) self._logger.debug( "Received source %s: %s", buf_name, msg ) From 1b7402915cdf73a32de1effe936480b4d69ee3b6 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 8 Mar 2020 18:18:29 +0000 Subject: [PATCH 118/609] Catch adapter being None and print user-friendly message: --- python3/vimspector/debug_session.py | 12 +++++++++++- support/test/python/simple_python/.vimspector.json | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 9c60ab8..8212ab1 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -136,7 +136,17 @@ class DebugSession( object ): configuration = configurations[ configuration_name ] adapter = configuration.get( 'adapter' ) if isinstance( adapter, str ): - adapter = adapters.get( adapter ) + adapter_dict = adapters.get( adapter ) + + if adapter_dict is None: + utils.UserMessage( f"The specified adapter '{adapter}' is not " + "available. Did you forget to run " + "'install_gadget.py'?", + persist = True, + error = True ) + return + + adapter = adapter_dict # TODO: Do we want some form of persistence ? e.g. self._staticVariables, # set from an api call like SetLaunchParam( 'var', 'value' ), perhaps also a diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index c2af1b4..5e5902a 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -18,7 +18,7 @@ } }, "attach": { - "adapter": "vscode-python", + "adapter": "debugpy", "configuration": { "request": "attach", "type": "python", From c1283a292c9cd4d458fa71891e585e98663d1331 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 9 Mar 2020 20:52:34 +0000 Subject: [PATCH 119/609] When creating a terminal window in neovim, use a new buffer neovim's termopen() replaces the buffer object in the current window with a terminal, internally, this completely breaks the buffer list. Repro: * Create test.vim: ``` vsplit call termopen( '/bin/bash', { 'cwd': getcwd() } ) call bufload( expand( '' ) ) ``` Then: * `nvim -Nu NONE test.vim` * `:source %` Error is "Invald buffer name 'test.vim'" Anyway, the correct thing to do is to create a _new_ buffer before making it into a terminal (vnew, new) rather than a split of the current one. This was only working before because the CodeView window never had any buffer in it and was broken by the change to use the current buffer when staring debugging. Fixes #131 --- autoload/vimspector/internal/neoterm.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/neoterm.vim b/autoload/vimspector/internal/neoterm.vim index f36c0db..61f9e9c 100644 --- a/autoload/vimspector/internal/neoterm.vim +++ b/autoload/vimspector/internal/neoterm.vim @@ -48,9 +48,9 @@ endfunction function! vimspector#internal#neoterm#Start( cmd, opts ) abort if ! get( a:opts, 'curwin', 0 ) if get( a:opts, 'vertical', 0 ) - vsplit + vnew else - split + new endif endif From 6c8657c9a7ec55c2a843aa2fb583e1b7fe6ee2d2 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 9 Mar 2020 21:25:55 +0000 Subject: [PATCH 120/609] Guess VIMSPECTOR_MI_DEBUGGER path --- run_tests | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/run_tests b/run_tests index 1c59f76..f29176f 100755 --- a/run_tests +++ b/run_tests @@ -31,7 +31,18 @@ if [ -z "$VIMSPECTOR_MIMODE" ]; then fi fi -echo "Testing with VIMSPECTOR_MIMODE=$VIMSPECTOR_MIMODE" +if [ -z "${VIMSPECTOR_MI_DEBUGGER}" ]; then + if which lldb-mi >/dev/null 2>&1; then + export VIMSPECTOR_MI_DEBUGGER=lldb-mi + else + echo "Couldn't guess VIMSPECTOR_MI_DEBUGGER. Need lldb-mi in path" + exit 1 + fi +fi + +echo "Testing with:" +echo " * VIMSPECTOR_MIMODE=$VIMSPECTOR_MIMODE" +echo " * VIMSPECTOR_MI_DEBUGGER=${VIMSPECTOR_MI_DEBUGGER}" echo "%SETUP - Building test programs..." set -e From de04598bc6550de382f47fcb10f50a8deb510a76 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 9 Mar 2020 21:31:14 +0000 Subject: [PATCH 121/609] Clear exception breakpoints when calling ClearBreakpoints --- python3/vimspector/breakpoints.py | 1 + support/test/python/simple_python/.vimspector.json | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 7d55704..464f2a7 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -123,6 +123,7 @@ class ProjectBreakpoints( object ): self._line_breakpoints = defaultdict( list ) self._func_breakpoints = [] + self._exception_breakpoints = None self.UpdateUI() diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 5e5902a..5cb036c 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -48,6 +48,17 @@ "uncaught": "" } } + }, + "run - exception question": { + "adapter": "debugpy", + "configuration": { + "request": "launch", + "type": "python", + "cwd": "${workspaceRoot}", + "program": "${file}", + "stopOnEntry": false, + "console": "integratedTerminal" + } } } } From ec36099f97497ca82d01e60e0aca0443f3a47ea4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 14 Mar 2020 09:26:49 +0000 Subject: [PATCH 122/609] Document: splat, central config dirs --- docs/configuration.md | 86 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 3c4a827..67e7665 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -47,7 +47,7 @@ Along with optional additional configuration for things like: The adapter to use for a particular debug session can be specified inline within the _debug configuration_, but more usually the debug adapter is defined -separately and just referenced from the _debug configuration_. +separately and just referenced from the _debug configuration_. The adapter configuration includes things like: @@ -65,7 +65,7 @@ of the following, for a given source tree: * Locally Python test and break exception * Remotely attach to a c++ process * Locally launch a bash script -* Attach to a JVM listening on a port +* Attach to a JVM listening on a port Each of these represents a different use case and a different _debug configuration_. As mentioned above, a _debug configuration_ is essentially: @@ -153,6 +153,47 @@ the following variable substitutions: the shell command. Note these variables can be supplied by both the debug and adapter configurations and can be either static strings or shell commands. +### The splat operator + +Often we want to create a single `.vimspector.json` entry which encompasses many +use cases, as it is tedious to write every use case/start up option in JSON. +This is why we have the replacement variables after all. + +Frequently debug adapters request command arguments as a JSON array, for +example: + +```json + "args": [ "one", "two three", "four" ], +``` + +To help with this sort of case, vimspector supports a 'splat' operator for +replacement variables apperaing within lists. The syntax is: `"*${var}`, which +means roughly "splice the contents of `${var}` into the list at this position". +`${var}` is parsed like a shell command (using python's `shlex` parser) and each +word is added as a list item. + +For example: + +```json + "args": [ "*${CommandLineArgs}" ] +``` + +This would: + +* Ask the user to provide the variable `CommandLineArgs`. Let's say they entered + `one "two three" four` +* Split `CommandLineArgs` like shell arguments: `one`, `two three` and `four` +* Set `args` in the settings dict to: `[ "one", "two three", "four" ]` + +You can also combine with static values: + +```json + "args": [ "First", "*${CommandLineArgs}", "Last" ] +``` + +This would yield the intuitive result: +`[ "First", "one", "two three", "four", "Last" ]` + ## Configuration Format All Vimspector configuration is defined in a JSON object. The complete @@ -172,7 +213,7 @@ embedded within ``, though this is not recommended usage. ## Files and locations The above configuration object is constructed from a number of configuration -files, by merging objects i specified order. +files, by merging objects in a specified order. In a minimal sense, the only file required is a `.vimspector.json` file in the root of your project which defines the [full configuration object][schema], but @@ -185,6 +226,8 @@ abbreviations: * `` means the path to the Vimspector installation (such as `$HOME/.vim/pack/vimspector/start/vimspector`) * `` is either `macos` or `linux` depending on the host operating system. +* `` is the Vim fileytype. Where multiple filetypes are in effect, + typically all filetypes are checked. ## Adapter configurations @@ -199,7 +242,7 @@ definition. These files are user-supplied and override the above. * The first such `.gadgets.json` file found in all parent directories of the file open in Vim. -* The `.vimspector.json` (see below) +* The `.vimspector.json` and any filetype-specific configurations (see below) In all cases, the required format is: @@ -225,14 +268,33 @@ The specification for the gadget object is defined in the [gadget schema][]. ## Debug configurations -The debug configurations are read from `.vimspector.json`. The file is found -(like `.gadgets.json` above) by recursively searching up the directory hierarchy -from the directory of the file open in Vim. The first file found is read and no -further searching is done. +There are two locations for debug configurations for a project: -Only a single `.vimspector.json` is read. +* `/configurations///*.json` +* `.vimspector.json` in the project source -Debug configurations are re-read at the start of each debug session. +Typically, the debug configurations are read from `.vimspector.json`. The file +is found (like `.gadgets.json` above) by recursively searching up the directory +hierarchy from the directory of the file open in Vim. The first file found is +read and no further searching is done. + +Only a single `.vimspector.json` is read. If one is found, the location of this +file is used for `${workspaceRoot}` and other workspace-relative paths. + +In addition, users can create filetype-specific configurations in the vimspector +installation directory. This can be useful where the parameters for the debug +session for a particular filetype are always known in advance, or can always be +entered by the user. This allows for debugging to "just work" without any +modification to the project source (no need to add a `.vimspector.json`). In +this case, the `${workspaceRoot}` and workspace-relative paths are interpreted +relative to the file open in Vim. This isn't ideal, but there is no other +obvious way to default this variable. + +As with gadgets, any debug configurations appearing within `.vimspector.json` +override any that appear in the common configuration dir. + +Debug configurations are re-read at the start of each debug session, so +modifications are picked up without any restarts of Vim. The specification for the gadget object is defined in the [schema][], but a typical example looks like this: @@ -278,7 +340,7 @@ default value for `caught` exceptoins and to break on `uncaught` exception: "uncaught": "Y" } }, - ... + ... ``` ## Predefined Variables @@ -317,7 +379,7 @@ Internet: { "json": { "schemas": [ - { + { "fileMatch": [ ".vimspector.json" ], "url": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json" }, From a5765b153c7a3314feaaba6bf6c1ce49536ce963 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 14 Mar 2020 16:46:53 +0000 Subject: [PATCH 123/609] Azure: Set explicit triggers (https://status.dev.azure.com/_event/179641421) --- azure-pipelines.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 37c0fec..85b7ba0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,7 +1,8 @@ -# Starter pipeline -# Start with a minimal pipeline that you can customize to build and deploy your code. -# Add steps that build, run tests, deploy, and more: -# https://aka.ms/yaml +trigger: +- master + +pr: +- master stages: - stage: Build From 9393c1d80eacd5ccf453b0bb9d8ed042338e3a86 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 21 Mar 2020 14:04:48 +0000 Subject: [PATCH 124/609] Don't run under sudo --- install_gadget.py | 14 ++++++++++++++ python3/vimspector/installer.py | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/install_gadget.py b/install_gadget.py index e7a5e7f..4ecc4b6 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -537,6 +537,7 @@ print( 'OS = ' + OS ) print( 'gadget_dir = ' + gadget_dir ) parser = argparse.ArgumentParser( + formatter_class = argparse.RawDescriptionHelpFormatter, description = 'Install DAP Servers for use with Vimspector.', epilog = """ @@ -548,6 +549,12 @@ parser = argparse.ArgumentParser( The format of the file can be found on the Vimspector reference guide: https://puremourning.github.io/vimspector + + NOTE: This script should usually _not_ be run under `sudo` or as root. It + downloads and extracts things only to directories under its own path. No + system files or folders are chnaged by this script. If you really want to + run under sudo, pass --sudo, but this is _almost certainly_ the wrong thing + to do. """ ) parser.add_argument( '--all', @@ -571,6 +578,11 @@ parser.add_argument( '--enable-custom', 'can be supplied multiple times and each time ' 'multiple files can be passed.' ) +parser.add_argument( '--sudo', + action='store_true', + help = "If you're really really really sure you want to " + "run this as root via sudo, pass this flag." ) + done_languages = set() for name, gadget in GADGETS.items(): lang = gadget[ 'language' ] @@ -608,6 +620,8 @@ parser.add_argument( args = parser.parse_args() +installer.AbortIfSUperUser( args.sudo ) + if args.force_all and not args.all: args.all = True diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 95ab19f..1553098 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -28,6 +28,7 @@ import ssl import subprocess import functools import os +import sys from vimspector import install @@ -220,7 +221,6 @@ def MakeSymlink( in_folder, link, pointing_to ): else: os.symlink( pointing_to_relative, link_path ) - def CloneRepoTo( url, ref, destination ): RemoveIfExists( destination ) git_in_repo = [ 'git', '-C', destination ] @@ -231,3 +231,17 @@ def CloneRepoTo( url, ref, destination ): 'update', '--init', '--recursive' ] ) + + +def AbortIfSUperUser( force_sudo ): + # TODO: We should probably check the effective uid too + is_su = False + if 'SUDO_COMMAND' in os.environ: + is_su = True + + if is_su: + if force_sudo: + print( "*** RUNNING AS SUPER USER DUE TO force_sudo! " + " All bets are off. ***" ) + else: + sys.exit( "This script should *not* be run as super user. Aborting." ) From d42fd51f357e74907dacc9197d658ce17e41b098 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 21 Mar 2020 15:10:07 +0000 Subject: [PATCH 125/609] Support c-style comments in JSON Debate rages about whether JSON should have comments. The specification says it shouldn't but the author of JSON suggested that if you want to use JSON for configuration, then pipe it through jsmin before parsing. So that's what we do, using a tiny JSON minifier from https://github.com/getify/JSON.minify/tree/python Closes #135 --- README.md | 5 + docs/configuration.md | 24 ++++- install_gadget.py | 3 +- python3/vimspector/debug_session.py | 6 +- python3/vimspector/installer.py | 1 + python3/vimspector/vendor/json_minify.py | 97 +++++++++++++++++++ .../python/simple_python/.vimspector.json | 3 +- tox.ini | 1 + 8 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 python3/vimspector/vendor/json_minify.py diff --git a/README.md b/README.md index 5170aec..c77b22b 100644 --- a/README.md +++ b/README.md @@ -609,6 +609,11 @@ the Getting Started section of the [Vimspector website][website]. For full explanation, including how to use variables, substitutions and how to specify exception breakpoints, see [the docs][vimspector-ref]. +The JSON configuration file allows C-style comments: + +* `// comment to end of line ...` +* `/* inline comment ... */` + Current tested with the following debug adapters. ## C, C++, Rust, etc. diff --git a/docs/configuration.md b/docs/configuration.md index 67e7665..4f67739 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -110,14 +110,15 @@ But for now, consider the following example snippet: { "configurations": { "example-debug-configuration": { + // This is a single-line comment explaining the purpose "adapter": "example-adapter-name", "variables": { - "SecretToken": { + "SecretToken": { // Variables should start with upper-case letters "shell" : [ "cat", "${HOME}/.secret_token" ] } }, "configuration": { - "request": "launch", + "request": "launch" /* or it could be "attach" */, "program": [ "${fileBasenameNoExtension}", "-c", "configuration_file.cfg", @@ -364,6 +365,25 @@ The following variables are provided: * `${fileExtname}` - the current opened file's extension * `${cwd}` - the current working directory of the active window on launch +## Appendix: Configuration file format + +The configuration files are text files which must be UTF-8 encoded. They +contain a single JSON object, along with optional comments. + +Comments are "c-style", i.e.: + +* `// single line comment ...` +* `/* inline comment */` + +There is much debate about whether JSON files should contain comments. I have +added them because they are useful in the context of configuration files. +Unforutnately this may mean your editor doesn't like them (they are strictly +invalid JSON) so it's up to you if you use them. + +Technically, Vimspector uses [JSON +minify](https://github.com/getify/JSON.minify) to strip comments before parsing +the JSON. + ## Appendix: Editor configuration If you would like some assistance with writing the JSON files, and your editor diff --git a/install_gadget.py b/install_gadget.py index 4ecc4b6..5d474c4 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -36,6 +36,7 @@ sys.path.insert( 1, os.path.join( os.path.dirname( __file__ ), 'python3' ) ) from vimspector import install, installer +from vimspector.vendor.json_minify import minify GADGETS = { 'vscode-cpptools': { @@ -634,7 +635,7 @@ for custom_file_name in functools.reduce( operator.add, args.custom_gadget_file, custom_files ): with open( custom_file_name, 'r' ) as custom_file: - CUSTOM_GADGETS.update( json.load( custom_file ) ) + CUSTOM_GADGETS.update( json.loads( minify( custom_file.read() ) ) ) failed = [] diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 8212ab1..fb03f41 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -31,6 +31,7 @@ from vimspector import ( breakpoints, stack_trace, utils, variables ) +from vimspector.vendor.json_minify import minify VIMSPECTOR_HOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ), '..', @@ -96,7 +97,8 @@ class DebugSession( object ): continue with open( gadget_config_file, 'r' ) as f: - adapters.update( json.load( f ).get( 'adapters' ) or {} ) + a = json.loads( minify( f.read() ) ).get( 'adapters' ) or {} + adapters.update( a ) for launch_config_file in PathsToAllConfigFiles( VIMSPECTOR_HOME, current_file, @@ -106,7 +108,7 @@ class DebugSession( object ): continue with open( launch_config_file, 'r' ) as f: - database = json.load( f ) + database = json.loads( minify( f.read() ) ) adapters.update( database.get( 'adapters' ) or {} ) configurations.update( database.get( 'configurations' or {} ) ) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 1553098..bca5084 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -221,6 +221,7 @@ def MakeSymlink( in_folder, link, pointing_to ): else: os.symlink( pointing_to_relative, link_path ) + def CloneRepoTo( url, ref, destination ): RemoveIfExists( destination ) git_in_repo = [ 'git', '-C', destination ] diff --git a/python3/vimspector/vendor/json_minify.py b/python3/vimspector/vendor/json_minify.py new file mode 100644 index 0000000..6b78901 --- /dev/null +++ b/python3/vimspector/vendor/json_minify.py @@ -0,0 +1,97 @@ +# Copyright © 2020 Kyle Simpson +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +"""A port of the `JSON-minify` utility to the Python language. + +Based on JSON.minify.js: https://github.com/getify/JSON.minify + +Contributers: + - Gerald Storer + - Contributed original version + - Felipe Machado + - Performance optimization + - Pradyun S. Gedam + - Conditions and variable names changed + - Reformatted tests and moved to separate file + - Made into a PyPI Package +""" + +import re + + +# Vimspector modification: strip_space defaults to False; we don't actually want +# to minify - we just want to strip comments. +def minify(string, strip_space=False): + tokenizer = re.compile('"|(/\*)|(\*/)|(//)|\n|\r') + end_slashes_re = re.compile(r'(\\)*$') + + in_string = False + in_multi = False + in_single = False + + new_str = [] + index = 0 + + for match in re.finditer(tokenizer, string): + + if not (in_multi or in_single): + tmp = string[index:match.start()] + if not in_string and strip_space: + # replace white space as defined in standard + tmp = re.sub('[ \t\n\r]+', '', tmp) + new_str.append(tmp) + elif not strip_space: + # Replace comments with white space so that the JSON parser reports + # the correct column numbers on parsing errors. + new_str.append(' ' * (match.start() - index)) + + index = match.end() + val = match.group() + + if val == '"' and not (in_multi or in_single): + escaped = end_slashes_re.search(string, 0, match.start()) + + # start of string or unescaped quote character to end string + if not in_string or (escaped is None or len(escaped.group()) % 2 == 0): # noqa + in_string = not in_string + index -= 1 # include " character in next catch + elif not (in_string or in_multi or in_single): + if val == '/*': + in_multi = True + elif val == '//': + in_single = True + elif val == '*/' and in_multi and not (in_string or in_single): + in_multi = False + if not strip_space: + new_str.append(' ' * len(val)) + elif val in '\r\n' and not (in_multi or in_string) and in_single: + in_single = False + elif not ((in_multi or in_single) or (val in ' \r\n\t' and strip_space)): # noqa + new_str.append(val) + + if not strip_space: + if val in '\r\n': + new_str.append(val) + elif in_multi or in_single: + new_str.append(' ' * len(val)) + + new_str.append(string[index:]) + return ''.join(new_str) diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 5cb036c..f585ca6 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -1,7 +1,8 @@ { "configurations": { + // This is a comment. "run legacy vscode-python": { - "adapter": "vscode-python", + "adapter": "vscode-python" /* coment goes here too */, "configuration": { "request": "launch", "type": "python", diff --git a/tox.ini b/tox.ini index dbd9626..7a6c06e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,3 +1,4 @@ [flake8] ignore = E111,E114,E121,E125,E126,E127,E128,E129,E131,E133,E201,E202,E203,E211,E221,E222,E241,E251,E261,E303,E402,W503,W504 max-line-length = 80 +exclude = python3/vimspector/vendor From 5d404708cfbaff38a551eb351a08290e0a3a1c6c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 21 Mar 2020 17:01:15 +0000 Subject: [PATCH 126/609] Fix CI on linux Don't use lldb-mi as the MIDebugger when we're in gdb mode (it's on the PATH because llvm is installed) --- azure-pipelines.yml | 2 +- run_tests | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 85b7ba0..6842856 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -63,7 +63,7 @@ stages: env: VIMSPECTOR_MIMODE: gdb VIMSPECTOR_TEST_STDOUT: true - VIMSPECTOR_MI_DEBUGGER: "" + VIMSPECTOR_MI_DEBUGGER: gdb - bash: ./make_package linux $(Build.SourceVersion) displayName: 'Package' diff --git a/run_tests b/run_tests index f29176f..aef0f36 100755 --- a/run_tests +++ b/run_tests @@ -23,23 +23,24 @@ fi if [ -z "$VIMSPECTOR_MIMODE" ]; then if which lldb >/dev/null 2>&1; then export VIMSPECTOR_MIMODE=lldb + + if [ -z "${VIMSPECTOR_MI_DEBUGGER}" ]; then + if which lldb-mi >/dev/null 2>&1; then + export VIMSPECTOR_MI_DEBUGGER=lldb-mi + else + echo "Couldn't guess VIMSPECTOR_MI_DEBUGGER. Need lldb-mi in path" + exit 1 + fi + fi elif which gdb >/dev/null 2>&1; then export VIMSPECTOR_MIMODE=gdb + export VIMSPECTOR_MI_DEBUGGER=gdb else echo "Couldn't guess VIMSPECTOR_MIMODE. Need lldb or gdb in path" exit 1 fi fi -if [ -z "${VIMSPECTOR_MI_DEBUGGER}" ]; then - if which lldb-mi >/dev/null 2>&1; then - export VIMSPECTOR_MI_DEBUGGER=lldb-mi - else - echo "Couldn't guess VIMSPECTOR_MI_DEBUGGER. Need lldb-mi in path" - exit 1 - fi -fi - echo "Testing with:" echo " * VIMSPECTOR_MIMODE=$VIMSPECTOR_MIMODE" echo " * VIMSPECTOR_MI_DEBUGGER=${VIMSPECTOR_MI_DEBUGGER}" From 8664c0ad78e44375920d9be60e97bf3dcba86f87 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 26 Mar 2020 21:13:04 +0000 Subject: [PATCH 127/609] Don't create a new buffer when opening the vimspector tab --- python3/vimspector/code.py | 3 +-- python3/vimspector/debug_session.py | 9 +-------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index e62a9ff..364a36a 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -22,7 +22,7 @@ from vimspector import utils class CodeView( object ): - def __init__( self, window, original_window, api_prefix ): + def __init__( self, window, api_prefix ): self._window = window self._api_prefix = api_prefix @@ -42,7 +42,6 @@ class CodeView( object ): with utils.LetCurrentWindow( self._window ): - vim.current.buffer = original_window.buffer vim.command( 'nnoremenu WinBar.Continue :call vimspector#Continue()' ) vim.command( 'nnoremenu WinBar.Next :call vimspector#StepOver()' ) vim.command( 'nnoremenu WinBar.Step :call vimspector#StepInto()' ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index fb03f41..f7ad9b0 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -150,10 +150,6 @@ class DebugSession( object ): adapter = adapter_dict - # TODO: Do we want some form of persistence ? e.g. self._staticVariables, - # set from an api call like SetLaunchParam( 'var', 'value' ), perhaps also a - # way to load .vimspector.local.json which just sets variables - # # Additional vars as defined by VSCode: # # ${workspaceFolder} - the path of the folder opened in VS Code @@ -457,14 +453,11 @@ class DebugSession( object ): def _SetUpUI( self ): - original_window = vim.current.window - - vim.command( 'tabnew' ) + vim.command( 'tabedit %' ) self._uiTab = vim.current.tabpage # Code window self._codeView = code.CodeView( vim.current.window, - original_window, self._api_prefix ) # Call stack From 1003cdc0b21f8819761cd37c5bc5e7b428dc6e79 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 26 Mar 2020 22:48:06 +0000 Subject: [PATCH 128/609] Add a splash popup while starting up Sometimes it can take quite a while to start up and initialise the debug adapter. So we use popup/float to display the status as we start up and shut down. This increases minimum Vim version to 8.2, but that's been out for ages now and I intend to agressively require latest/later vim/neovim versions. --- README.md | 8 ++- autoload/vimspector/internal/neopopup.vim | 70 +++++++++++++++++++++++ autoload/vimspector/internal/popup.vim | 38 ++++++++++++ python3/vimspector/debug_session.py | 41 ++++++++++++- python3/vimspector/utils.py | 17 ++++++ tests/ci/image/Dockerfile | 2 +- tests/tabpage.test.vim | 3 +- 7 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 autoload/vimspector/internal/neopopup.vim create mode 100644 autoload/vimspector/internal/popup.vim diff --git a/README.md b/README.md index c77b22b..ac589ca 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ Alternatively, you can clone the repo and select which gadgets are installed: Vimspector requires: -* Vim version 8.1 with at least patch 1264, or Neovim 0.4.3 +* At least Vim version 8.2, or Neovim 0.4.3 (experimental) * One of the following operating systems: * Linux * macOS Mojave or later @@ -162,6 +162,12 @@ Why such a new vim ? Well 2 reasons: 2. Because there are Vim bugs that vimspector triggers that will frustrate you if you hit them. +Why is neovim experimental? Because the author doesn't use neovim regularly, and +there are no regression tests for vimspector in neovim, so it's likely to break +frequently. Issue reports are handled on best-efforts basis, and PRs are +welcome to fix bugs. See also the next section descibing differences for neovim +vs vim. + Why Windows support experimental? Because it's effort and it's not a priority for the author. PRs are welcome to fix bugs. Windows will not be regularly tested. diff --git a/autoload/vimspector/internal/neopopup.vim b/autoload/vimspector/internal/neopopup.vim new file mode 100644 index 0000000..148107c --- /dev/null +++ b/autoload/vimspector/internal/neopopup.vim @@ -0,0 +1,70 @@ +" vimspector - A multi-language debugging system for Vim +" Copyright 2018 Ben Jackson +" +" Licensed under the Apache License, Version 2.0 (the "License"); +" you may not use this file except in compliance with the License. +" You may obtain a copy of the License at +" +" http://www.apache.org/licenses/LICENSE-2.0 +" +" Unless required by applicable law or agreed to in writing, software +" distributed under the License is distributed on an "AS IS" BASIS, +" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +" See the License for the specific language governing permissions and +" limitations under the License. + +" Boilerplate {{{ +let s:save_cpo = &cpoptions +set cpoptions&vim +" }}} + +" Neovim's float window API, like its job/channel API is painful to use +" compared to Vim's so we have to employ more hacks + +" We can't seem to pass a Window handle back to the python, so we have to +" maintain yet another cached here +let s:db = {} +let s:next_id = 0 + +function! vimspector#internal#neopopup#DisplaySplash( message ) abort + let buf = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(buf, 0, -1, v:true, [ a:message ] ) + + let l = len( a:message ) + + let opts = { + \ 'relative': 'editor', + \ 'width': l, + \ 'height': 1, + \ 'col': ( &columns / 2 ) - ( l / 2 ), + \ 'row': &lines / 2, + \ 'anchor': 'NW', + \ 'style': 'minimal', + \ 'focusable': v:false, + \ } + let win = nvim_open_win(buf, 0, opts) + call nvim_win_set_option(win, 'wrap', v:false) + + let id = s:next_id + let s:next_id += 1 + let s:db[ id ] = { 'win': win, 'buf': buf } + return id +endfunction + +function! vimspector#internal#neopopup#UpdateSplash( id, message ) abort + let splash = s:db[ a:id ] + call nvim_buf_set_lines(splash.buf, 0, -1, v:true, [ a:message ] ) + return a:id +endfunction + +function! vimspector#internal#neopopup#HideSplash( id ) abort + let splash = s:db[ a:id ] + call nvim_win_close( splash.win, v:true ) + unlet s:db[ a:id ] +endfunction + +" Boilerplate {{{ +let &cpoptions=s:save_cpo +unlet s:save_cpo +" }}} + diff --git a/autoload/vimspector/internal/popup.vim b/autoload/vimspector/internal/popup.vim new file mode 100644 index 0000000..fc8820b --- /dev/null +++ b/autoload/vimspector/internal/popup.vim @@ -0,0 +1,38 @@ +" vimspector - A multi-language debugging system for Vim +" Copyright 2018 Ben Jackson +" +" Licensed under the Apache License, Version 2.0 (the "License"); +" you may not use this file except in compliance with the License. +" You may obtain a copy of the License at +" +" http://www.apache.org/licenses/LICENSE-2.0 +" +" Unless required by applicable law or agreed to in writing, software +" distributed under the License is distributed on an "AS IS" BASIS, +" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +" See the License for the specific language governing permissions and +" limitations under the License. + + +" Boilerplate {{{ +let s:save_cpo = &cpoptions +set cpoptions&vim +" }}} + +function! vimspector#internal#popup#DisplaySplash( message ) abort + return popup_dialog( a:message, {} ) +endfunction + +function! vimspector#internal#popup#UpdateSplash( id, message ) abort + call popup_settext( a:id, a:message ) + return a:id +endfunction + +function! vimspector#internal#popup#HideSplash( id ) abort + call popup_hide( a:id ) +endfunction + +" Boilerplate {{{ +let &cpoptions=s:save_cpo +unlet s:save_cpo +" }}} diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index fb03f41..9ad34ef 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -19,7 +19,6 @@ import logging import os import shlex import subprocess -import traceback import functools import vim @@ -60,6 +59,7 @@ class DebugSession( object ): self._variablesView = None self._outputView = None self._breakpoints = breakpoints.ProjectBreakpoints() + self._splash_screen = None self._run_on_server_exit = None @@ -326,9 +326,12 @@ class DebugSession( object ): def _Reset( self ): self._logger.info( "Debugging complete." ) if self._uiTab: - self._logger.debug( "Clearing down UI with stack_trace: %s", - traceback.format_stack() ) + self._logger.debug( "Clearing down UI" ) vim.current.tabpage = self._uiTab + + self._splash_screen = utils.HideSplash( self._api_prefix, + self._splash_screen ) + self._stackTraceView.Reset() self._variablesView.Reset() self._outputView.Reset() @@ -436,6 +439,7 @@ class DebugSession( object ): def GetOutputBuffers( self ): return self._outputView.GetCategories() + @IfConnected def GetCompletionsSync( self, text_line, column_in_bytes ): if not self._server_capabilities.get( 'supportsCompletionsRequest' ): return [] @@ -523,6 +527,11 @@ class DebugSession( object ): return True def _StartDebugAdapter( self ): + self._splash_screen = utils.DisplaySplash( + self._api_prefix, + self._splash_screen, + "Starting debug adapter..." ) + if self._connection: utils.UserMessage( 'The connection is already created. Please try again', persist = True ) @@ -560,6 +569,9 @@ class DebugSession( object ): " g:_vimspector_adapter_spec " ")".format( self._connection_type ) ): self._logger.error( "Unable to start debug server" ) + self._splash_screen = utils.DisplaySplash( self._api_prefix, + self._splash_screen, + "Unable to start adapter" ) else: self._connection = debug_adapter_connection.DebugAdapterConnection( self, @@ -570,6 +582,11 @@ class DebugSession( object ): self._logger.info( 'Debug Adapter Started' ) def _StopDebugAdapter( self, callback = None ): + self._splash_screen = utils.DisplaySplash( + self._api_prefix, + self._splash_screen, + "Shutting down debug adapter..." ) + def handler( *args ): if callback: self._logger.debug( "Setting server exit handler before disconnect" ) @@ -706,6 +723,11 @@ class DebugSession( object ): return [ command ] def _Initialise( self ): + self._splash_screen = utils.DisplaySplash( + self._api_prefix, + self._splash_screen, + "Initializing debug adapter..." ) + # For a good explaination as to why this sequence is the way it is, see # https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522 # @@ -760,8 +782,18 @@ class DebugSession( object ): launch_config.get( 'request', 'launch' ) ) if request == "attach": + self._splash_screen = utils.DisplaySplash( + self._api_prefix, + self._splash_screen, + "Attaching to debugee..." ) + self._PrepareAttach( adapter_config, launch_config ) elif request == "launch": + self._splash_screen = utils.DisplaySplash( + self._api_prefix, + self._splash_screen, + "Launching debugee..." ) + # FIXME: This cmdLine hack is not fun. self._PrepareLaunch( self._configuration.get( 'remote-cmdLine', [] ), adapter_config, @@ -807,6 +839,9 @@ class DebugSession( object ): # leader rather than the process. The workaround is to manually SIGTRAP the # PID. # + self._splash_screen = utils.HideSplash( self._api_prefix, + self._splash_screen ) + if self._launch_complete and self._init_complete: for h in self._on_init_complete_handlers: h() diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index cffc425..13c43f8 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -546,3 +546,20 @@ def SetSyntax( current_syntax, syntax, *args ): def GetBufferFiletypes( buf ): ft = ToUnicode( vim.eval( f"getbufvar( {buf.number}, '&ft' )" ) ) return ft.split( '.' ) + + +def DisplaySplash( api_prefix, splash, text ): + if splash: + return Call( f'vimspector#internal#{api_prefix}popup#UpdateSplash', + splash, + text ) + else: + return Call( f'vimspector#internal#{api_prefix}popup#DisplaySplash', + text ) + + +def HideSplash( api_prefix, splash ): + if splash: + Call( f'vimspector#internal#{api_prefix}popup#HideSplash', splash ) + + return None diff --git a/tests/ci/image/Dockerfile b/tests/ci/image/Dockerfile index 71329b6..77e7131 100644 --- a/tests/ci/image/Dockerfile +++ b/tests/ci/image/Dockerfile @@ -27,7 +27,7 @@ RUN ln -fs /usr/share/zoneinfo/Europe/London /etc/localtime && \ ## cleanup of files from setup RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -ARG VIM_VERSION=v8.1.1270 +ARG VIM_VERSION=v8.2.0460 ENV CONF_ARGS "--with-features=huge \ --enable-python3interp \ diff --git a/tests/tabpage.test.vim b/tests/tabpage.test.vim index 2dfd4d9..356ee19 100644 --- a/tests/tabpage.test.vim +++ b/tests/tabpage.test.vim @@ -10,7 +10,7 @@ function! Test_Step_With_Different_Tabpage() lcd testdata/cpp/simple edit simple.cpp - " Add the breakpoing + " Add the breakpoint " TODO refactor FeedKeys 15 call assert_equal( 15, line( '.' ) ) @@ -47,6 +47,7 @@ function! Test_Step_With_Different_Tabpage() call vimspector#Reset() call vimspector#ClearBreakpoints() + call WaitForAssert( {-> assert_notequal( vimspector_tabnr, tabpagenr() ) } ) lcd - %bwipeout! endfunction From 5cb9c62e7f66d082839f63889e7f591848b19e91 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 28 Mar 2020 13:07:00 +0000 Subject: [PATCH 129/609] Fix tab creation when current buffer contains no file --- python3/vimspector/debug_session.py | 2 +- .../test/python/simple_python/.vimspector.json | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index ee3fc97..064b410 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -457,7 +457,7 @@ class DebugSession( object ): def _SetUpUI( self ): - vim.command( 'tabedit %' ) + vim.command( 'tab split' ) self._uiTab = vim.current.tabpage # Code window diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index f585ca6..8610cf7 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -50,6 +50,23 @@ } } }, + "run - main.py": { + "adapter": "debugpy", + "configuration": { + "request": "launch", + "type": "python", + "cwd": "${workspaceRoot}", + "program": "${workspaceRoot}/main.py", + "stopOnEntry": false, + "console": "integratedTerminal" + }, + "breakpoints": { + "exception": { + "raised": "N", + "uncaught": "" + } + } + }, "run - exception question": { "adapter": "debugpy", "configuration": { From ae7e6d0b0c1f124284865445855f2aeaa3d3a27b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 28 Mar 2020 20:28:29 +0000 Subject: [PATCH 130/609] Clarify that Python 3.6 is required closes #143 --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ac589ca..ba23a31 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,9 @@ Alternatively, you can clone the repo and select which gadgets are installed: Vimspector requires: -* At least Vim version 8.2, or Neovim 0.4.3 (experimental) +* One of: + * Vim 8.2 compiled with Python 3.6 or later + * Neovim 0.4.3 with Python 3.6 or later (experimental) * One of the following operating systems: * Linux * macOS Mojave or later From d0652187a32e924ffe9f25fc04485a4b43759ac5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 31 Mar 2020 13:33:58 +0100 Subject: [PATCH 131/609] Note what diagnostics are required, --- CONTRIBUTING.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 388dc21..165c771 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,13 @@ forms, such as: At all times the [code of conduct](#code-of-conduct) applies. +## Diagnostics + +Whenever reporting any type of fault, or difficulty in making the plugin +work, please always include _all_ of the diagnostics requested in the +[issue template][issue-template]. Please do not be offended if your request +is ignored if it does not include the requested diagnostics. + ## Issues The GitHub issue tracker is for *bug reports* and *features requests* for the @@ -19,7 +26,7 @@ general discussion, general support or for any other purpose. Please do not be offended if your Issue or comment is closed or hidden, for any of the following reasons: -* The issue template was not completed +* The [issue template][issue-template] was not completed * The issue or comment is off-topic * The issue does not represent a Vimspector bug or feature request * etc. @@ -98,3 +105,4 @@ Please see [code of conduct](CODE_OF_CONDUCT.md). [vint]: https://github.com/Vimjas/vint [flake8]: https://flake8.pycqa.org/en/latest/ +[issue-template]: https://github.com/puremourning/vimspector/blob/master/.github/ISSUE_TEMPLATE/bug_report.md From 0e4cad302deb92d512f434d05bdbf0a7a325001e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 4 Feb 2020 13:36:12 +0000 Subject: [PATCH 132/609] Don't terminal debuggee --- python3/vimspector/debug_session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 064b410..a62575a 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -591,7 +591,8 @@ class DebugSession( object ): arguments = {} if self._server_capabilities.get( 'supportTerminateDebuggee' ): - arguments[ 'terminateDebugee' ] = True + # If we attached, we should _not_ terminate the debuggee + arguments[ 'terminateDebuggee' ] = False self._connection.DoRequest( handler, { 'command': 'disconnect', From 1b7d0caf26b09fdad480d5c9a6a26383e871f9e3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 18 Feb 2020 17:29:54 +0000 Subject: [PATCH 133/609] Ignore loadedSource event --- python3/vimspector/debug_session.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index a62575a..d3d0c47 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -844,6 +844,10 @@ class DebugSession( object ): self._stackTraceView.LoadThreads( True ) + def OnEvent_loadedSource( self, msg ): + pass + + def OnEvent_capabilities( self, msg ): self._server_capabilities.update( ( msg.get( 'body' ) or {} ).get( 'capabilities' ) or {} ) From 93edbe468b5724e6d9a58849b188c421aa174c84 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 10 Mar 2020 14:03:35 +0000 Subject: [PATCH 134/609] Add autoselect to configs - set to false to prevent being used when no other configs are defined --- python3/vimspector/debug_session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index d3d0c47..1abd99a 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -120,7 +120,8 @@ class DebugSession( object ): if 'configuration' in launch_variables: configuration_name = launch_variables.pop( 'configuration' ) - elif len( configurations ) == 1: + elif ( len( configurations ) == 1 and + next( iter( configurations.values() ) ).get( "autoselect", True ) ): configuration_name = next( iter( configurations.keys() ) ) else: configuration_name = utils.SelectFromList( From db6d9efb7503740281663ca96590a3a321bf2b98 Mon Sep 17 00:00:00 2001 From: Neil Johari Date: Thu, 2 Apr 2020 14:28:32 -0400 Subject: [PATCH 135/609] Bump version of vscode-cpptools from 0.26.3 to 0.27.0 --- install_gadget.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 5d474c4..a4d2c83 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -47,17 +47,17 @@ GADGETS = { }, 'do': lambda name, root, gadget: InstallCppTools( name, root, gadget ), 'all': { - 'version': '0.26.3', + 'version': '0.27.0', }, 'linux': { 'file_name': 'cpptools-linux.vsix', 'checksum': - 'd660367fbedd6acaffcc233c1d41ef280ff79aeba81846b86bf9bd97b4887379' + '3695202e1e75a03de18049323b66d868165123f26151f8c974a480eaf0205435' }, 'macos': { 'file_name': 'cpptools-osx.vsix', 'checksum': - 'ca0793dd1dfd70757491da80cf04b2b15631048b572de2ebe3864f9eed96dff9', + 'cb061e3acd7559a539e5586f8d3f535101c4ec4e8a48195856d1d39380b5cf3c', }, 'windows': { 'file_name': 'cpptools-win32.vsix', From be260983157f038f8787a746b866deacb05bc90e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 3 Apr 2020 21:05:23 +0100 Subject: [PATCH 136/609] Remove workaround for broken cpptools --- README.md | 90 ++++++++-------------- azure-pipelines.yml | 15 ---- run_tests | 11 --- tests/testdata/cpp/simple/.vimspector.json | 1 - 4 files changed, 32 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index ba23a31..40cf4d8 100644 --- a/README.md +++ b/README.md @@ -628,64 +628,6 @@ Current tested with the following debug adapters. * C++: [vscode-cpptools](https://github.com/Microsoft/vscode-cpptools) ---- - -***NOTE FOR macOS USERS***: Currently VSCode cpptools does *not* work on macOS -out of the box. -See [this issue]( https://github.com/microsoft/vscode-cpptools/issues/3829) -for the full details. - -To resolve it: - -* Install [my build](https://github.com/puremourning/lldb-mi/releases) of - `lldb-mi`. Instructions - [here](https://github.com/microsoft/vscode-cpptools/issues/3829#issuecomment-578454043). -* Add the following to the `configuration` section of your `.vimspector.json` - (below): - -``` -{ - "configurations": { - ": Launch": { - "adapter": "vscode-cpptools", - "configuration": { - "miDebuggerPath": "/path/to/my/build/of/lldb-mi", - ... -``` - -An alternative is to to use `lldb-vscode`, which comes with llvm. Here's how: - -* Install llvm with HomeBrew: `brew install llvm` -* Create a file named - `/path/to/vimspector/gadgets/macos/.gadgets.d/lldb-vscode.json`: - -```json - -{ - "adapters": { - "lldb-vscode": { - "variables": { - "LLVM": { - "shell": "brew --prefix llvm" - } - }, - "attach": { - "pidProperty": "pid", - "pidSelect": "ask" - }, - "command": [ - "${LLVM}/bin/lldb-vscode" - ], - "env": { - "LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY": "YES" - }, - "name": "lldb" - } - } -} -``` - ---- Example `.vimspector.json` (works with both `vscode-cpptools` and `lldb-vscode`. For `lldb-vscode` replace the name of the adapter with `lldb-vscode`: @@ -722,6 +664,38 @@ For `lldb-vscode` replace the name of the adapter with `lldb-vscode`: } ``` +An alternative is to to use `lldb-vscode`, which comes with llvm. Here's how: + +* Install llvm with HomeBrew: `brew install llvm` +* Create a file named + `/path/to/vimspector/gadgets/macos/.gadgets.d/lldb-vscode.json`: + +```json + +{ + "adapters": { + "lldb-vscode": { + "variables": { + "LLVM": { + "shell": "brew --prefix llvm" + } + }, + "attach": { + "pidProperty": "pid", + "pidSelect": "ask" + }, + "command": [ + "${LLVM}/bin/lldb-vscode" + ], + "env": { + "LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY": "YES" + }, + "name": "lldb" + } + } +} +``` + ## Python * Python: [debugpy][] diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6842856..59059da 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -63,7 +63,6 @@ stages: env: VIMSPECTOR_MIMODE: gdb VIMSPECTOR_TEST_STDOUT: true - VIMSPECTOR_MI_DEBUGGER: gdb - bash: ./make_package linux $(Build.SourceVersion) displayName: 'Package' @@ -102,25 +101,11 @@ stages: - bash: vim --version displayName: 'Print vim version information' - - bash: | - VERSION=66e8ed9af1812ca0bb6cfcc7023a35742e1bd323 - BUILD=1092 - set -ex - mkdir -p lldb-mi - pushd lldb-mi - curl -LO https://github.com/puremourning/lldb-mi/releases/download/${BUILD}/lldb-mi-${VERSION}.tar.gz - tar zxvf lldb-mi-${VERSION}.tar.gz - cp usr/local/bin/lldb-mi /usr/local/bin - popd - displayName: 'Install lldb-mi' - - bash: ./run_tests displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: lldb VIMSPECTOR_TEST_STDOUT: true - VIMSPECTOR_MI_DEBUGGER: "/usr/local/bin/lldb-mi" - LLDB_DEBUGSERVER_PATH: "/Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Resources/debugserver" - bash: ./make_package macos $(Build.SourceVersion) displayName: 'Package' diff --git a/run_tests b/run_tests index aef0f36..6058e55 100755 --- a/run_tests +++ b/run_tests @@ -23,18 +23,8 @@ fi if [ -z "$VIMSPECTOR_MIMODE" ]; then if which lldb >/dev/null 2>&1; then export VIMSPECTOR_MIMODE=lldb - - if [ -z "${VIMSPECTOR_MI_DEBUGGER}" ]; then - if which lldb-mi >/dev/null 2>&1; then - export VIMSPECTOR_MI_DEBUGGER=lldb-mi - else - echo "Couldn't guess VIMSPECTOR_MI_DEBUGGER. Need lldb-mi in path" - exit 1 - fi - fi elif which gdb >/dev/null 2>&1; then export VIMSPECTOR_MIMODE=gdb - export VIMSPECTOR_MI_DEBUGGER=gdb else echo "Couldn't guess VIMSPECTOR_MIMODE. Need lldb or gdb in path" exit 1 @@ -43,7 +33,6 @@ fi echo "Testing with:" echo " * VIMSPECTOR_MIMODE=$VIMSPECTOR_MIMODE" -echo " * VIMSPECTOR_MI_DEBUGGER=${VIMSPECTOR_MI_DEBUGGER}" echo "%SETUP - Building test programs..." set -e diff --git a/tests/testdata/cpp/simple/.vimspector.json b/tests/testdata/cpp/simple/.vimspector.json index e68ab85..4d50e7a 100644 --- a/tests/testdata/cpp/simple/.vimspector.json +++ b/tests/testdata/cpp/simple/.vimspector.json @@ -13,7 +13,6 @@ "stopAtEntry": true, "stopOnEntry": true, "MImode": "${VIMSPECTOR_MIMODE}", - "miDebuggerPath": "${VIMSPECTOR_MI_DEBUGGER}", "logging": { "engineLogging": true } From a5a2966ba7d9ac4d2d827b5714eafee488e47691 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 5 Apr 2020 15:03:35 +0100 Subject: [PATCH 137/609] Update debugpy --- azure-pipelines.yml | 4 ++++ install_gadget.py | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 59059da..f30e84d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -67,6 +67,8 @@ stages: - bash: ./make_package linux $(Build.SourceVersion) displayName: 'Package' + # TODO: test the tarball + - task: PublishPipelineArtifact@0 inputs: artifactName: 'package-linux' @@ -110,6 +112,8 @@ stages: - bash: ./make_package macos $(Build.SourceVersion) displayName: 'Package' + # TODO: test the tarball + - task: PublishPipelineArtifact@0 inputs: artifactName: 'package-macos' diff --git a/install_gadget.py b/install_gadget.py index a4d2c83..a3c6aff 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -105,10 +105,10 @@ GADGETS = { 'url': 'https://github.com/microsoft/debugpy/archive/${file_name}' }, 'all': { - 'version': '1.0.0b1', - 'file_name': 'v1.0.0b1.zip', + 'version': '1.0.0b5', + 'file_name': 'v1.0.0b5.zip', 'checksum': - 'eb26ac276213bcf26aaeadd7c3c284f7a20f43b63331822831a783dea558e60e' + '410bcf14c4e8b0f40c326c61d7d5c4326e285e22c9ec5e6b6b501957911a8a10' }, 'do': lambda name, root, gadget: InstallDebugpy( name, root, gadget ), 'adapters': { From 980b80ebbb7de483933fd06f850ea89237fa52a0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 5 Apr 2020 14:41:57 +0100 Subject: [PATCH 138/609] Add troubleshooting guide Also make sure that the gadget installer and other docs are included in the tarball installation. --- CONTRIBUTING.md | 16 ++++++++++++++++ README.md | 30 ++++++++++++++++++++++++------ make_package | 9 ++++++++- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 165c771..b14ecb5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,22 @@ forms, such as: At all times the [code of conduct](#code-of-conduct) applies. +## Troubleshooting + +It's not completely trivial to configure Vimspector and there is a fairly large +amount of documentation. I know full well that documentation isn't everything, +so the first step in troubleshooting is to try a sample project that's known to +work, to check if the problem is your project configuration rather than an +actual bug. + +Therefore before raising an issue for a supported language, please check with +the sample projects in `support/test/` and `tests/testdata/` to see if +the problem is with your project settings, rather than with vimspector. + +Information on these is in [the README](README.md#trying-it-out). + +If in doubt, ask on Gitter. + ## Diagnostics Whenever reporting any type of fault, or difficulty in making the plugin diff --git a/README.md b/README.md index 40cf4d8..87b0e8f 100644 --- a/README.md +++ b/README.md @@ -255,12 +255,6 @@ packadd! vimspector See support/doc/example_vimrc.vim. -Also, if you want to try out vimspector without changing your vim config, run: - -``` -vim -Nu /path/to/vimspector/tests/vimrc --cmd "let g:vimspector_enable_mappings='HUMAN'" -``` - ## Install some gadgets There are a couple of ways of doing this, but ***using `install_gadget.py` is @@ -380,6 +374,30 @@ Vimspector will also load any fies matching: format as `.gadgets.json` but are not overwritten when running `install_gadget.py`. +## Trying it out + +If you just want to try out vimspector without changing your vim config, there +are example projects for a number of languages in `support/test`, including: + +* Python (`support/test/python/simple_python`) +* Go (`support/test/go/hello_world`) +* Nodejs (`support/test/node/simple`) +* Chrome (`support/test/chrome/`) +* etc. + +To test one of these out, cd to the directory and run: + +``` +vim -Nu /path/to/vimspector/tests/vimrc --cmd "let g:vimspector_enable_mappings='HUMAN'" +``` + +Then press ``. + +There's also a C++ project in `tests/testdata/cpp/simple/` with a `Makefile` +which can be used to check everything is working. This is used by the regression +tests in CI so should always work, and is a good way to check if the problem is +your configuration rather than a bug. + # About ## Background diff --git a/make_package b/make_package index 2401fce..1b34a05 100755 --- a/make_package +++ b/make_package @@ -16,13 +16,20 @@ mkdir -p ${PACK} pushd ${PACK} mkdir -p vimspector/opt/vimspector pushd vimspector/opt/vimspector - for d in autoload plugin python3 vendor doc; do + for d in autoload plugin python3 vendor doc support; do if [[ -d ${ROOT}/$d ]]; then cp -r ${ROOT}/$d . fi done mkdir -p gadgets cp -r ${ROOT}/gadgets/${OS} gadgets/ + for f in install_gadget.py \ + CODE_OF_CONDUCT.md \ + CONTRIBUTING.md \ + LICENCE \ + README.md; do + cp ${ROOT}/${f} . + done popd popd From f0ee397f5d2d789fa285936b9404aa6f802caa47 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 15 Apr 2020 13:15:33 +0100 Subject: [PATCH 139/609] Update bashdb --- install_gadget.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index a3c6aff..50aed23 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -255,10 +255,10 @@ GADGETS = { 'download/${version}/${file_name}', }, 'all': { - 'file_name': 'bash-debug-0.3.6.vsix', - 'version': 'v0.3.6', + 'file_name': 'bash-debug-0.3.7.vsix', + 'version': 'v0.3.7', 'checksum': - '502ee5732851fc4f309294fc296a291b1a114008a1fbcb232f3763be2b8d9c1f', + 'b73e5b4604375df8658fb5a72c645c355785a289aa785a986e508342c014bb4', }, 'do': lambda name, root, gadget: InstallBashDebug( name, root, gadget ), 'adapters': { From aed4144fe554f4125291a50e6c8d6c6cc82d28fe Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 15 Apr 2020 16:13:19 +0100 Subject: [PATCH 140/609] Update bashdb checkusm --- install_gadget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install_gadget.py b/install_gadget.py index 50aed23..be00e1e 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -258,7 +258,7 @@ GADGETS = { 'file_name': 'bash-debug-0.3.7.vsix', 'version': 'v0.3.7', 'checksum': - 'b73e5b4604375df8658fb5a72c645c355785a289aa785a986e508342c014bb4', + '7b73e5b4604375df8658fb5a72c645c355785a289aa785a986e508342c014bb4', }, 'do': lambda name, root, gadget: InstallBashDebug( name, root, gadget ), 'adapters': { From 7a70519b037503385b1bd0385391ebacad99ae91 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 25 Apr 2020 19:30:16 +0100 Subject: [PATCH 141/609] Add basic support for conditional breakpoints This is the minimal required for a user to use conditional breakpoint - we add an options dict to each breakpoint (line and function) and allow the condition to be supplied. We add a plug mapping and a default shortcut () to add one where we ask the user to enter the condition and hit expression. This isn't great but it works. We don't check the capabilities, so they would just be ignored if used on a server that doesn't support them. We also ask for a hit expression which most users won't understand so this isn't ideal either. No tests yet. --- README.md | 31 +++++++++-------- autoload/vimspector.vim | 9 ++--- plugin/vimspector.vim | 6 ++++ python3/vimspector/breakpoints.py | 52 ++++++++++++++++++++--------- python3/vimspector/debug_session.py | 8 ++--- 5 files changed, 69 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 87b0e8f..0e227de 100644 --- a/README.md +++ b/README.md @@ -458,6 +458,7 @@ features to set your own mappings. To that end, Vimspector defines the following * `VimspectorRestart` * `VimspectorPause` * `VimspectorToggleBreakpoint` +* `VimspectorToggleConditionalBreakpoint` * `VimspectorAddFunctionBreakpoint` * `VimspectorStepOver` * `VimspectorStepInto` @@ -513,17 +514,18 @@ loading vimspector**: let g:vimspector_enable_mappings = 'HUMAN' ``` -| Key | Function | API | -| --- | --- | --- | -| `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` | -| `F3` | Stop debugging. | `vimspector#Stop()` | -| `F4` | Restart debugging with the same configuration. | `vimspector#Restart()` | -| `F6` | Pause debugee. | `vimspector#Pause()` | -| `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` | -| `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '' )` | -| `F10` | Step Over | `vimspector#StepOver()` | -| `F11` | Step Into | `vimspector#StepInto()` | -| `F12` | Step out of current function scope | `vimspector#StepOut()` | +| Key | Function | API | +| --- | --- | --- | +| `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` | +| `F3` | Stop debugging. | `vimspector#Stop()` | +| `F4` | Restart debugging with the same configuration. | `vimspector#Restart()` | +| `F6` | Pause debugee. | `vimspector#Pause()` | +| `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` | +| `F9` | Toggle conditional line breakpoint on the current line. | `vimspector#ToggleBreakpoint( {condition, hit condition } )` | +| `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '' )` | +| `F10` | Step Over | `vimspector#StepOver()` | +| `F11` | Step Into | `vimspector#StepInto()` | +| `F12` | Step out of current function scope | `vimspector#StepOut()` | # Usage @@ -564,9 +566,10 @@ debugger](#java---partially-supported) ## Breakpoints -* Use `vimspector#ToggleBreakpoint()` to set/disable/delete a line breakpoint. -* Use `vimspector#AddFunctionBreakpoint( '' )` to add a function -breakpoint. +* Use `vimspector#ToggleBreakpoint([ { 'condition': '' } ])` + to set/disable/delete a line breakpoint, with optional condition. +* Use `vimspector#AddFunctionBreakpoint( '' [, { 'condition': '' } ] )` + to add a function breakpoint with optional condition. ## Stepping diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index f2b39d5..d0dac14 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -42,12 +42,13 @@ function! vimspector#ClearBreakpoints() abort py3 _vimspector_session.ClearBreakpoints() endfunction -function! vimspector#ToggleBreakpoint() abort - py3 _vimspector_session.ToggleBreakpoint() +function! vimspector#ToggleBreakpoint( options = {} ) abort + py3 _vimspector_session.ToggleBreakpoint( vim.eval( 'a:options' ) ) endfunction -function! vimspector#AddFunctionBreakpoint( function ) abort - py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ) ) +function! vimspector#AddFunctionBreakpoint( function, options = {} ) abort + py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ), + \ vim.eval( 'a:options' ) ) endfunction function! vimspector#StepOver() abort diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 0c71090..6909dab 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -43,6 +43,11 @@ nnoremap VimspectorRestart :call vimspector#Restart() nnoremap VimspectorPause :call vimspector#Pause() nnoremap VimspectorToggleBreakpoint \ :call vimspector#ToggleBreakpoint() +nnoremap VimspectorToggleConditionalBreakpoint + \ :call vimspector#ToggleBreakpoint( + \ { 'condition': input( 'Enter condition: ' ), + \ 'hitCondition': input( 'Enter hit condition: ' ) } + \ ) nnoremap VimspectorAddFunctionBreakpoint \ :call vimspector#AddFunctionBreakpoint( expand( '' ) ) nnoremap VimspectorStepOver :call vimspector#StepOver() @@ -65,6 +70,7 @@ elseif s:mappings ==# 'HUMAN' nmap VimspectorRestart nmap VimspectorPause nmap VimspectorToggleBreakpoint + nmap VimspectorToggleConditionalBreakpoint nmap VimspectorAddFunctionBreakpoint nmap VimspectorStepOver nmap VimspectorStepInto diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 464f2a7..ea50fd6 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -55,6 +55,9 @@ class ProjectBreakpoints( object ): if not utils.SignDefined( 'vimspectorBP' ): vim.command( 'sign define vimspectorBP text==> texthl=Error' ) + if not utils.SignDefined( 'vimspectorBPCond' ): + vim.command( 'sign define vimspectorBPCond text=?> texthl=Error' ) + if not utils.SignDefined( 'vimspectorBPDisabled' ): vim.command( 'sign define vimspectorBPDisabled text=!> texthl=Warning' ) @@ -96,8 +99,9 @@ class ProjectBreakpoints( object ): 'col': 1, 'type': 'L', 'valid': 1 if bp[ 'state' ] == 'ENABLED' else 0, - 'text': "Line breakpoint - {}".format( - bp[ 'state' ] ) + 'text': "Line breakpoint - {}: {}".format( + bp[ 'state' ], + json.dumps( bp[ 'options' ] ) ) } ) # I think this shows that the qf list is not right for this. for bp in self._func_breakpoints: @@ -107,7 +111,8 @@ class ProjectBreakpoints( object ): 'col': 1, 'type': 'F', 'valid': 1, - 'text': "Function breakpoint: {}".format( bp[ 'function' ] ), + 'text': "Function breakpoint: {}: {}".format( bp[ 'function' ], + bp[ 'options' ] ), } ) vim.eval( 'setqflist( {} )'.format( json.dumps( qf ) ) ) @@ -127,7 +132,7 @@ class ProjectBreakpoints( object ): self.UpdateUI() - def ToggleBreakpoint( self ): + def ToggleBreakpoint( self, options ): line, column = vim.current.window.cursor file_name = vim.current.buffer.name @@ -161,9 +166,10 @@ class ProjectBreakpoints( object ): self._line_breakpoints[ file_name ].append( { 'state': 'ENABLED', 'line': line, + 'options': options, # 'sign_id': , # - # Used by other breakpoint types: + # Used by other breakpoint types (specified in options): # 'condition': ..., # 'hitCondition': ..., # 'logMessage': ... @@ -171,10 +177,14 @@ class ProjectBreakpoints( object ): self.UpdateUI() - def AddFunctionBreakpoint( self, function ): + def AddFunctionBreakpoint( self, function, options ): self._func_breakpoints.append( { - 'state': 'ENABLED', - 'function': function, + 'state': 'ENABLED', + 'function': function, + 'options': options, + # Specified in options: + # 'condition': ..., + # 'hitCondition': ..., } ) # TODO: We don't really have aanything to update here, but if we're going to @@ -239,7 +249,10 @@ class ProjectBreakpoints( object ): if bp[ 'state' ] != 'ENABLED': continue - breakpoints.append( { 'line': bp[ 'line' ] } ) + dap_bp = {} + dap_bp.update( bp[ 'options' ] ) + dap_bp.update( { 'line': bp[ 'line' ] } ) + breakpoints.append( dap_bp ) source = { 'name': os.path.basename( file_name ), @@ -264,15 +277,21 @@ class ProjectBreakpoints( object ): if self._server_capabilities.get( 'supportsFunctionBreakpoints' ): awaiting = awaiting + 1 + breakpoints = [] + for bp in self._func_breakpoints: + if bp[ 'state' ] != 'ENABLED': + continue + dap_bp = {} + dap_bp.update( bp[ 'options' ] ) + dap_bp.update( { 'name': bp[ 'function' ] } ) + breakpoints.append( dap_bp ) + self._connection.DoRequest( lambda msg: response_handler( None, msg ), { 'command': 'setFunctionBreakpoints', 'arguments': { - 'breakpoints': [ - { 'name': bp[ 'function' ] } - for bp in self._func_breakpoints if bp[ 'state' ] == 'ENABLED' - ], + 'breakpoints': breakpoints, } }, failure_handler = lambda *_: response_received() @@ -354,12 +373,15 @@ class ProjectBreakpoints( object ): bp[ 'sign_id' ] = self._next_sign_id self._next_sign_id += 1 + sign = ( 'vimspectorBPDisabled' if bp[ 'state' ] != 'ENABLED' + else 'vimspectorBPCond' if 'condition' in bp[ 'options' ] + else 'vimspectorBP' ) + vim.command( 'sign place {0} group=VimspectorBP line={1} name={2} file={3}'.format( bp[ 'sign_id' ] , bp[ 'line' ], - 'vimspectorBP' if bp[ 'state' ] == 'ENABLED' - else 'vimspectorBPDisabled', + sign, file_name ) ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 1abd99a..f65f13c 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -982,8 +982,8 @@ class DebugSession( object ): def ListBreakpoints( self ): return self._breakpoints.ListBreakpoints() - def ToggleBreakpoint( self ): - return self._breakpoints.ToggleBreakpoint() + def ToggleBreakpoint( self, options ): + return self._breakpoints.ToggleBreakpoint( options ) def ClearBreakpoints( self ): if self._connection: @@ -991,8 +991,8 @@ class DebugSession( object ): return self._breakpoints.ClearBreakpoints() - def AddFunctionBreakpoint( self, function ): - return self._breakpoints.AddFunctionBreakpoint( function ) + def AddFunctionBreakpoint( self, function, options ): + return self._breakpoints.AddFunctionBreakpoint( function, options ) def PathsToAllGadgetConfigs( vimspector_base, current_file ): From f6d12eff722c7bbd02a3c115e811f72099a2754e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 25 Apr 2020 19:54:39 +0100 Subject: [PATCH 142/609] Few more minor doc updates --- README.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0e227de..e6b63e4 100644 --- a/README.md +++ b/README.md @@ -74,17 +74,20 @@ But for now, here's a (rather old) screenshot of Vimsepctor debugging Vim: ## Supported debugging features +- flexible configuration syntax that can be checked in to source control - breakpoints (function, line and exception breakpoints) +- conditional breakpoints (function, line) - step in/out/over/up, stop, restart - launch and attach - remote launch, remote attach - locals and globals display - watch expressions -- call stack and navigation +- call stack display and navigation - variable value display hover - interactive debug console - launch debugee within Vim's embedded terminal - logging/stdout display +- simple stable API for custom tooling (e.g. integrate with language server) ## Supported languages: @@ -427,14 +430,6 @@ than welcome. The backlog can be [viewed on Trello](https://trello.com/b/yvAKK0rD/vimspector). -In order to use it you have to currently: - -- Write a mostly undocumented configuration file that contains essentially - undocumented parameters. -- Accept that it isn't complete yet -- Work around some frustrating bugs in Vim -- Ignore probably many bugs in vimspector! - ### Experimental The plugin is currently _experimental_. That means that any part of it @@ -443,8 +438,9 @@ can (and probably will) change, including things like: - breaking changes to the configuration - keys, layout, functionatlity of the UI -If a large number of people start using it then I will do my best to -minimise this, or at least announce on Gitter. +However, I commit to only doing this in the most extreme cases and to annouce +such changes on Gitter well in advance. There's nothing more annoying than stuff +just breaking on you. I get that. # Mappings @@ -464,7 +460,7 @@ features to set your own mappings. To that end, Vimspector defines the following * `VimspectorStepInto` * `VimspectorStepOut` -These map 1-1 with the API functions below. +These map roughly 1-1 with the API functions below. For example, if you want `` to start/continue debugging, add this to some appropriate place, such as your `vimrc` (hint: run `:e $MYVIMRC`). From b20f61bcff404a57c504e989dfbd1aba0ab16f7a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 25 Apr 2020 20:07:03 +0100 Subject: [PATCH 143/609] Sigh, neovim doesn't support default method arguments --- autoload/vimspector.vim | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index d0dac14..3852753 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -42,13 +42,23 @@ function! vimspector#ClearBreakpoints() abort py3 _vimspector_session.ClearBreakpoints() endfunction -function! vimspector#ToggleBreakpoint( options = {} ) abort - py3 _vimspector_session.ToggleBreakpoint( vim.eval( 'a:options' ) ) +function! vimspector#ToggleBreakpoint( ... ) abort + if a:0 == 0 + let options = {} + else + let options = a:1 + endif + py3 _vimspector_session.ToggleBreakpoint( vim.eval( 'options' ) ) endfunction -function! vimspector#AddFunctionBreakpoint( function, options = {} ) abort +function! vimspector#AddFunctionBreakpoint( function, ... ) abort + if a:0 == 0 + let options = {} + else + let options = a:1 + endif py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ), - \ vim.eval( 'a:options' ) ) + \ vim.eval( 'options' ) ) endfunction function! vimspector#StepOver() abort From c25e9256aab37cd4468c1570929d4d9007b9a029 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Apr 2020 10:19:05 +0100 Subject: [PATCH 144/609] Fix restart command in neovim when terminal in use Neovim doesn't allow you to replace a terminal buffer if the buffer has received some output, so we tell it that the buffer is not modified as a hackaround. Fixes #154 --- autoload/vimspector/internal/neoterm.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/autoload/vimspector/internal/neoterm.vim b/autoload/vimspector/internal/neoterm.vim index 61f9e9c..381a6b0 100644 --- a/autoload/vimspector/internal/neoterm.vim +++ b/autoload/vimspector/internal/neoterm.vim @@ -60,6 +60,7 @@ function! vimspector#internal#neoterm#Start( cmd, opts ) abort try let old_env = vimspector#internal#neoterm#PrepareEnvironment( \ a:opts[ 'env' ] ) + setlocal nomodified let id = termopen( a:cmd, { \ 'cwd': a:opts[ 'cwd' ], \ 'env': a:opts[ 'env' ], From e153b9536e10ef0086579b2a92e554fbfb5f8ae2 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Apr 2020 11:17:14 +0100 Subject: [PATCH 145/609] No longer need node 10 for the tests since debugpy --- run_tests | 8 -------- 1 file changed, 8 deletions(-) diff --git a/run_tests b/run_tests index 6058e55..97c170d 100755 --- a/run_tests +++ b/run_tests @@ -12,14 +12,6 @@ fi RUN_VIM="vim --clean --not-a-term" RUN_TEST="${RUN_VIM} -S lib/run_test.vim" -NODE_VERSION_MAJ=$(node --version \ - | awk 'BEGIN { FS="." } { print substr( $1, 2 ) }') -if [ ${NODE_VERSION_MAJ} -gt "10" ]; then - echo "ERROR: Tests must be run against node 10 or earlier because python." >&2 - echo "You have $(node --version)" >&2 - exit 1 -fi - if [ -z "$VIMSPECTOR_MIMODE" ]; then if which lldb >/dev/null 2>&1; then export VIMSPECTOR_MIMODE=lldb From 77dc40007716900b0bdcd7c60378a32e382cf007 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Apr 2020 11:14:44 +0100 Subject: [PATCH 146/609] test and doc updates for conditional breakpoints use a better description of hit condition and describe the options dict. --- README.md | 37 +++++++- plugin/vimspector.vim | 4 +- python3/vimspector/variables.py | 2 +- tests/breakpoints.test.vim | 155 ++++++++++++++++++++++++++++++++ tests/lib/run_test.vim | 29 +++++- 5 files changed, 218 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e6b63e4..4f29955 100644 --- a/README.md +++ b/README.md @@ -517,7 +517,7 @@ let g:vimspector_enable_mappings = 'HUMAN' | `F4` | Restart debugging with the same configuration. | `vimspector#Restart()` | | `F6` | Pause debugee. | `vimspector#Pause()` | | `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` | -| `F9` | Toggle conditional line breakpoint on the current line. | `vimspector#ToggleBreakpoint( {condition, hit condition } )` | +| `F9` | Toggle conditional line breakpoint on the current line. | `vimspector#ToggleBreakpoint( { trigger expr, hit count expr } )` | | `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '' )` | | `F10` | Step Over | `vimspector#StepOver()` | | `F11` | Step Into | `vimspector#StepInto()` | @@ -530,6 +530,8 @@ let g:vimspector_enable_mappings = 'HUMAN' * Create `vimspector.json`. See [below](#supported-languages). * `:call vimspector#Launch()` and select a configuration. +![debug session](https://puremourning.github.io/vimspector-web/img/vimspector-overview.png) + ### Launch with options To launch a specific debug configuration, or specify [replacement @@ -562,16 +564,35 @@ debugger](#java---partially-supported) ## Breakpoints -* Use `vimspector#ToggleBreakpoint([ { 'condition': '' } ])` +* Use `vimspector#ToggleBreakpoint([ { 'condition': '' } ])` to set/disable/delete a line breakpoint, with optional condition. -* Use `vimspector#AddFunctionBreakpoint( '' [, { 'condition': '' } ] )` +* Use `vimspector#AddFunctionBreakpoint( '' [, { 'condition': '' } ] )` to add a function breakpoint with optional condition. +Both of these functions take a single optional argument which is a dictionary of +options. The dictionary can have the following keys: + +* `condition`: An optional expression evaluated to deterimie if the breakpoint + should fire. Not supported by all debug adapters. For example, to break when + `abc` is `10`, enter something like `abc == 10`, depending on the language. +* `hitCondition`: An optional expression evaluated to determine a number of + times the breakpoint should be ignored. Should (probablty?) not be used in + combination with `condition`. Not supported by all debug adapters. For + example, to break on the 3rd time hitting this line, enter `3`. + +In both cases, the expression is evaluated by the debugger, so should be in +whatever dialect the debugger understands when evaluating expressions. + +When using the `` mapping, the user is prompted to enter these +expressions in a command line (with history). + ## Stepping -* Step in/out, finish, continue, pause etc. using the WinBar. +* Step in/out, finish, continue, pause etc. using the WinBar, or mappings. * If you really want to, the API is `vimspector#StepInto()` etc. +![code window](https://puremourning.github.io/vimspector-web/img/vimspector-code-window.png) + ## Variables and scopes * Current scope shows values of locals. @@ -579,6 +600,8 @@ debugger](#java---partially-supported) * When changing the stack frame the locals window updates. * While paused, hover to see values +![locals window](https://puremourning.github.io/vimspector-web/img/vimspector-locals-window.png) + ## Watches The watches window is a prompt buffer, where that's available. Enter insert mode @@ -591,11 +614,15 @@ to add a new watch expression. * Expand result with ``. * Delete with ``. +![watch window](https://puremourning.github.io/vimspector-web/img/vimspector-watch-window.png) + ## Stack Traces * In the threads window, use `` to expand/collapse. * Use `` on a stack frame to jump to it. +![stack trace](https://puremourning.github.io/vimspector-web/img/vimspector-callstack-window.png) + ## Program Output * In the outputs window use the WinBar to select the output channel. @@ -604,6 +631,8 @@ to add a new watch expression. * The debugee prints to the stdout channel. * Other channels may be useful for debugging. +![output window](https://puremourning.github.io/vimspector-web/img/vimspector-output-window.png) + ### Console The console window is a prompt buffer, where that's available, and can be used diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 6909dab..ed61609 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -45,8 +45,8 @@ nnoremap VimspectorToggleBreakpoint \ :call vimspector#ToggleBreakpoint() nnoremap VimspectorToggleConditionalBreakpoint \ :call vimspector#ToggleBreakpoint( - \ { 'condition': input( 'Enter condition: ' ), - \ 'hitCondition': input( 'Enter hit condition: ' ) } + \ { 'condition': input( 'Enter condition expression: ' ), + \ 'hitCondition': input( 'Enter hit count expression: ' ) } \ ) nnoremap VimspectorAddFunctionBreakpoint \ :call vimspector#AddFunctionBreakpoint( expand( '' ) ) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index e86b23f..e1a21ce 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -313,7 +313,7 @@ class VariablesView( object ): if result_str is None: result_str = 'null' - line = '{0}{1} Result: {2} '.format( ' ' * indent, icon, result_str ) + line = '{0}{1} Result: {2}'.format( ' ' * indent, icon, result_str ) line = utils.AppendToBuffer( self._watch.win.buffer, line.split( '\n' ) ) self._watch.lines[ line ] = result diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index dd00840..07c008e 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -295,3 +295,158 @@ function! Test_Insert_Code_Above_Breakpoint() 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' ) + + " Disable the breakpoint + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 16, + \ 'vimspectorBPDisabled' ) + + " 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' ) + call setpos( '.', [ 0, 9, 1 ] ) + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 9, + \ 'vimspectorBP' ) + + call setpos( '.', [ 0, 17, 1 ] ) + call vimspector#ToggleBreakpoint( { 'condition': 'argc == 1' } ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorBP', + \ 17, + \ 'vimspectorBPCond' ) + + 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() + 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' ) + + 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 diff --git a/tests/lib/run_test.vim b/tests/lib/run_test.vim index a5c2f39..75d9a91 100644 --- a/tests/lib/run_test.vim +++ b/tests/lib/run_test.vim @@ -32,11 +32,12 @@ " call ch_log( ",,,message..." ) " Then view it in 'debuglog' -" Let a test take up to 1 minute +" Let a test take up to 1 minute, unless debugging let s:single_test_timeout = 60000 " Restrict the runtimepath to the exact minimum needed for testing -set runtimepath=$PWD/lib,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after +let &rtp = getcwd() . '/lib' +set runtimepath+=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after if has('packages') let &packpath = &runtimepath endif @@ -70,6 +71,10 @@ func s:TestFailed() endfunc func! Abort( timer_id ) + if exists( '&debugfunc' ) && &debugfunc != '' + return + endif + call assert_report( 'Test timed out!!!' ) qa! endfunc @@ -152,6 +157,26 @@ func RunTheTest(test) \ 'SKIPPED ' . a:test \ . ': ' \ . substitute(v:exception, '^\S*\s\+', '', '')) + catch /^\cxfail/ + if len( v:errors ) == 0 + call add(v:errors, + \ 'Expected failure but no error in ' . a:test + \ . ': ' + \ . v:exception + \ . ' @ ' + \ . g:testpath + \ . ':' + \ . v:throwpoint) + + call s:TestFailed() + else + let v:errors = [] + call add(s:messages, ' XFAIL' ) + call add(s:skipped, + \ 'XFAIL ' . a:test + \ . ': ' + \ . substitute(v:exception, '^\S*\s\+', '', '')) + endif catch call add(v:errors, \ 'Caught exception in ' . a:test From 77522cbd44ba71e6d84c96864fa7ba1e31d811e7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Apr 2020 13:05:24 +0100 Subject: [PATCH 147/609] Add way to run tests using my in-development vim debugger --- tests/.vimspector.json | 21 +++++++++++++++++++++ tests/lib/run_test.vim | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/.vimspector.json diff --git a/tests/.vimspector.json b/tests/.vimspector.json new file mode 100644 index 0000000..2a96d66 --- /dev/null +++ b/tests/.vimspector.json @@ -0,0 +1,21 @@ +{ + "configurations": { + "Run test": { + "adapter": "vim-debug-adapter", + "configuration": { + "request": "launch", + "cwd": "${workspaceRoot}", + "env": { + "VIMSPECTOR_MIMODE": "lldb" + }, + "args": [ + "--clean", + "--not-a-term", + "-S", "lib/run_test.vim", + "${file}", + "${TestFunction}" + ] + } + } + } +} diff --git a/tests/lib/run_test.vim b/tests/lib/run_test.vim index 75d9a91..e03a1d4 100644 --- a/tests/lib/run_test.vim +++ b/tests/lib/run_test.vim @@ -36,7 +36,7 @@ let s:single_test_timeout = 60000 " Restrict the runtimepath to the exact minimum needed for testing -let &rtp = getcwd() . '/lib' +let &runtimepath = getcwd() . '/lib' set runtimepath+=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after if has('packages') let &packpath = &runtimepath @@ -71,7 +71,7 @@ func s:TestFailed() endfunc func! Abort( timer_id ) - if exists( '&debugfunc' ) && &debugfunc != '' + if exists( '&debugfunc' ) && &debugfunc !=# '' return endif From 80ba64473284a0bcb75acc41a8608afc63478a1c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Apr 2020 20:03:22 +0100 Subject: [PATCH 148/609] Use silent for mappings --- plugin/vimspector.vim | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index ed61609..cb921eb 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -37,22 +37,29 @@ let g:loaded_vimpector = 1 let s:mappings = get( g:, 'vimspector_enable_mappings', '' ) -nnoremap VimspectorContinue :call vimspector#Continue() -nnoremap VimspectorStop :call vimspector#Stop() -nnoremap VimspectorRestart :call vimspector#Restart() -nnoremap VimspectorPause :call vimspector#Pause() -nnoremap VimspectorToggleBreakpoint +nnoremap VimspectorContinue + \ :call vimspector#Continue() +nnoremap VimspectorStop + \ :call vimspector#Stop() +nnoremap VimspectorRestart + \ :call vimspector#Restart() +nnoremap VimspectorPause + \ :call vimspector#Pause() +nnoremap VimspectorToggleBreakpoint \ :call vimspector#ToggleBreakpoint() -nnoremap VimspectorToggleConditionalBreakpoint +nnoremap VimspectorToggleConditionalBreakpoint \ :call vimspector#ToggleBreakpoint( \ { 'condition': input( 'Enter condition expression: ' ), \ 'hitCondition': input( 'Enter hit count expression: ' ) } \ ) -nnoremap VimspectorAddFunctionBreakpoint +nnoremap VimspectorAddFunctionBreakpoint \ :call vimspector#AddFunctionBreakpoint( expand( '' ) ) -nnoremap VimspectorStepOver :call vimspector#StepOver() -nnoremap VimspectorStepInto :call vimspector#StepInto() -nnoremap VimspectorStepOut :call vimspector#StepOut() +nnoremap VimspectorStepOver + \ :call vimspector#StepOver() +nnoremap VimspectorStepInto + \ :call vimspector#StepInto() +nnoremap VimspectorStepOut + \ :call vimspector#StepOut() if s:mappings ==# 'VISUAL_STUDIO' nmap VimspectorContinue From 4586aa36ff63b3556199f3564e122940add73e5a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 26 Apr 2020 21:31:22 +0100 Subject: [PATCH 149/609] Improve UI for watch window Add a WinBar as the whole prompt buffer thing is not that well undersood. Also allow you to delete a watch from any part of the expression result too. --- autoload/vimspector.vim | 14 ++++++++++++-- python3/vimspector/variables.py | 23 ++++++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 3852753..f37f27e 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -97,8 +97,18 @@ function! vimspector#GoToFrame() abort py3 _vimspector_session.ExpandFrameOrThread() endfunction -function! vimspector#AddWatch( expr ) abort - py3 _vimspector_session.AddWatch( vim.eval( 'a:expr' ) ) +function! vimspector#AddWatch( ... ) abort + if a:0 == 0 + let expr = input( 'Enter watch expression: ' ) + else + let expr = a:1 + endif + + if expr ==# '' + return + endif + + py3 _vimspector_session.AddWatch( vim.eval( 'expr' ) ) endfunction function! vimspector#AddWatchPrompt( expr ) abort diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index e1a21ce..89ff9d1 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -59,6 +59,13 @@ class VariablesView( object ): vim.command( 'nnoremap :call vimspector#DeleteWatch()' ) + vim.command( 'nnoremenu 1.1 WinBar.New ' + ':call vimspector#AddWatch()' ) + vim.command( 'nnoremenu 1.2 WinBar.Expand/Collapse ' + ':call vimspector#ExpandVariable()' ) + vim.command( 'nnoremenu 1.3 WinBar.Delete ' + ':call vimspector#DeleteWatch()' ) + utils.SetUpScratchBuffer( self._vars.win.buffer, 'vimspector.Variables' ) utils.SetUpPromptBuffer( self._watch.win.buffer, 'vimspector.Watches', @@ -165,12 +172,18 @@ class VariablesView( object ): current_line = vim.current.window.cursor[ 0 ] + best_index = -1 for index, watch in enumerate( self._watches ): - if '_line' in watch and watch[ '_line' ] == current_line: - del self._watches[ index ] - utils.UserMessage( 'Deleted' ) - self._DrawWatches() - return + if ( '_line' in watch + and watch[ '_line' ] <= current_line + and watch[ '_line' ] > best_index ): + best_index = index + + if best_index >= 0: + del self._watches[ best_index ] + utils.UserMessage( 'Deleted' ) + self._DrawWatches() + return utils.UserMessage( 'No watch found' ) From a87c9e8715f8aab5ac72853a3068295e76ef09b1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 27 Apr 2020 20:44:41 +0100 Subject: [PATCH 150/609] Add motivation - people buy why not what --- README.md | 74 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4f29955..b5a7b80 100644 --- a/README.md +++ b/README.md @@ -59,10 +59,54 @@ For a tutorial and usage overview, take a look at the +# Motivation + +A message from the author about the motivation for this plugin: + +> Many development environments have a built-in debugger. I spend an inordinate +> amount of my time in Vim. I do all my development in Vim and I have even +> customised my workflows for building code, running tests etc. +> +> For many years I have observed myself, friends and colleagues have been +> writing `printf`, `puts`, `print`, etc. debugging statements in all sorts of +> files simply because there is no _easy_ way to run a debugger for _whatever_ +> language we happen to be developing in. +> +> I truly believe that interactive, graphical debugging environments are the +> best way to understand and reason about both unfamiliar and familiar code, and +> that the lack of ready, simple access to a debugger is a huge hidden +> productivity hole for many. +> +> Don't get me wrong, I know there are literally millions of developers out +> there that are more than competent at developing without a graphical debugger, +> but I maintain that if they had the ability to _just press a key_ and jump +> into the debugger, it would be faster and more enjoyable that just cerebral +> code comprehension. +> +> I created Vimsepctor because I find changing tools frustrating. `gdb` for c++, +> `pdb` for python, etc. Each has its own syntax. Each its own lexicon. Each its +> own foibles. +> +> I designed the configuration system in such a way that the configuration can +> be committed to source control so that it _just works_ for any of your +> colleagues, friends, collaborators or complete strangers. +> +> I made remote debugging a first-class feature because that's a primary use +> case for me in my job. +> +> With Vimspector I can _just hit ``_ in all of the languages I develop in +> and debug locally or remotely using the exact same workflow, mappings and UI. +> I have integrated this with my Vim in such a way that I can hit a button and +> _run the test under the cursor in Vimspector_. This kind of integration has +> massively improved my workflow and productivity. It's even made the process +> of learning a new codebase... fun. +> +> \- Ben Jackson, Creator. + # Features and Usage The plugin is a capable Vim graphical debugger for multiple languages. -It's mostly tested for c++ and python, but in theory supports any +It's mostly tested for c++, python and TCL, but in theory supports any language that Visual Studio Code supports (but see caveats). The [Vimspector website][website] has an overview of the UI, along with basic @@ -137,7 +181,7 @@ pre-packaged. To use a release tarball: ``` $ mkdir -p $HOME/.vim/pack -$ curl -L | tar -C $HOME/.vim/pack zxvf - +$ curl -L | tar -C $HOME/.vim/pack zxvf - ``` 3. Configure your project's debug profiles (create `.vimspector.json`) @@ -163,7 +207,7 @@ Vimspector requires: Why such a new vim ? Well 2 reasons: -1. Because vimspector uses a lot of new Vim features +1. Because vimspector uses a lot of new Vim features 2. Because there are Vim bugs that vimspector triggers that will frustrate you if you hit them. @@ -305,7 +349,7 @@ out how to start it, and configure that in an `adapters` entry in either your The simplest way in practice is to install or start Visusal Studio Code and use its extension manager to install the relevant extension. You can then configure the adapter manually in the `adapters` section of your `.vimspector.json` or in -a `gadgets.json`. +a `gadgets.json`. PRs are always welcome to add configuration to do this to `install_gadget.py`. @@ -351,19 +395,19 @@ Example: }, "vscode-cpptools": { "attach": { - "pidProperty": "processId", + "pidProperty": "processId", "pidSelect": "ask" - }, + }, "command": [ "${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7" - ], + ], "name": "cppdbg" - }, + }, "vscode-python": { "command": [ - "node", + "node", "${gadgetDir}/vscode-python/out/client/debugger/debugAdapter/main.js" - ], + ], "name": "vscode-python" } } @@ -639,7 +683,7 @@ The console window is a prompt buffer, where that's available, and can be used as an interactive CLI for the debug adapter. Support for this varies amongt adapters. -* Enter insert mode to enter a command to evaluate. +* Enter insert mode to enter a command to evaluate. * Alternatively, `:VimspectorEval `. Completion is available with some debug adapters. * Commit the request with `` @@ -858,7 +902,7 @@ Requires `install_gadget.py --force-enable-csharp`. ## Go -* Go +* Go Requires: @@ -891,7 +935,7 @@ Requires: * (optional) Xdebug helper for chrome https://chrome.google.com/webstore/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc * `install_gadget.py --force-enable-php` -* configured php xdebug extension +* configured php xdebug extension ```ini zend_extension=xdebug.so xdebug.remote_enable=on @@ -1016,7 +1060,7 @@ It allows you to debug scripts running inside chrome from within Vim. * Java Debug Server. The [java debug server][java-debug-server] runs as a jdt.ls plugin, rather than a standalone debug adapter. This makes a lot - of sense if you already happen to be running the language server. + of sense if you already happen to be running the language server. Vimspector is not in the business of running language servers. So, rather than doing so, vimspector simply allows you to start the java debug server manually (however you might do so) and you can tell vimspector the port @@ -1030,7 +1074,7 @@ It allows you to debug scripts running inside chrome from within Vim. # Customisation -There is very limited support for customistaion of the UI. +There is very limited support for customistaion of the UI. ## Changing the default signs From 56c0c721db3c75d64c06d4b8a3ac23b059f8e16c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 28 Apr 2020 22:55:42 +0100 Subject: [PATCH 151/609] Update debugpy to v1.0.0b8 --- install_gadget.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index be00e1e..38098a3 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -105,10 +105,10 @@ GADGETS = { 'url': 'https://github.com/microsoft/debugpy/archive/${file_name}' }, 'all': { - 'version': '1.0.0b5', - 'file_name': 'v1.0.0b5.zip', + 'version': '1.0.0b8', + 'file_name': 'v1.0.0b8.zip', 'checksum': - '410bcf14c4e8b0f40c326c61d7d5c4326e285e22c9ec5e6b6b501957911a8a10' + '07c208bcd2a18088757f3bcb6f3bfc68d42c16a504c716e35d34fbe6b010a7b3' }, 'do': lambda name, root, gadget: InstallDebugpy( name, root, gadget ), 'adapters': { From ccf427a7c8a291b7df4d7b0bb79e84e69914e411 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 7 May 2020 22:28:31 +0100 Subject: [PATCH 152/609] Update java instructions now YCM has nice API for it --- README.md | 115 ++++++++++++++---- install_gadget.py | 2 +- python3/vimspector/variables.py | 3 +- .../test/java/test_project/.vimspector.json | 18 +-- 4 files changed, 107 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index b5a7b80..f005ef6 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ with little effort, and are supported as first-class languages. - Python 2 and Python 3 - TCL - Bash scripts +- Java ## Languages known to work @@ -149,7 +150,6 @@ The following languages are used frequently by the author, but require some sort of hackery that makes it challenging to support generally. These languages are on a best-efforts basis: -- Java (see caveats) - C# (c-sharp) using dotnet core - Go (requires separate installation of [Delve][]) - Node.js (requires node <12 for installation) @@ -259,18 +259,19 @@ categorised as follows: * `Experimental`: Working, but not frequently used and rarely tested * `Legacy`: No longer supported, please migrate your config -| Language | Status | Switch | Adapter | Dependencies | -|------------------|--------------|--------------------------------|---------------------|---------------------------------| -| C, C++, etc. | Tested | `--all` or `--enable-c` | vscode-cpptools | mono-core | -| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | -| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] | -| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | -| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | -| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | -| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | -| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | -| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | -| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | +| Language | Status | Switch | Adapter | Dependencies | +|------------------|--------------|--------------------------------|---------------------|--------------------------------------------| +| C, C++, etc. | Tested | `--all` or `--enable-c` | vscode-cpptools | mono-core | +| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | +| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] | +| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | +| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | +| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | +| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | +| Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | +| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | +| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | +| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | For other languages, you'll need some other way to install the gadget. @@ -1056,16 +1057,85 @@ It allows you to debug scripts running inside chrome from within Vim. } ``` -## Java - partially supported +## Java -* Java Debug Server. The [java debug server][java-debug-server] runs as a - jdt.ls plugin, rather than a standalone debug adapter. This makes a lot - of sense if you already happen to be running the language server. - Vimspector is not in the business of running language servers. So, rather - than doing so, vimspector simply allows you to start the java debug server - manually (however you might do so) and you can tell vimspector the port - on which it is listening. See [this issue](https://github.com/puremourning/vimspector/issues/3) - for more background. +Vimsepctor works well with the [java debug server][java-debug-server], which +runs as a jdt.ls (Java Language Server) plugin, rather than a standalone +debug adapter. + +Vimspector is not in the business of running language servers, only debug +adapters, so this means that you need a compatible Language Server Protocol +editor plugin to use Java. I recommend [YouCompleteMe][], which has full support +for jdt.ls, and most importantly a trivial way to load the debug adapter and to +use it with Vimspector. + +### Usage with YouCompleteMe + +* Set up [YCM for java][YcmJava]. +* Get Vimspector to download the java debug plugin: + `install_gadget.py --force-enable-java ` +* Configure Vimspector for your project using the `vscode-java` adapter, e.g.: + +```json +{ + "configurations": { + "Java Attach": { + "adapter": "vscode-java", + "configuration": { + "request": "attach", + "hostName": "${host}", + "port": "${port}, + "sourcePaths": [ + "${workspaceRoot}/src/main/java", + "${workspaceRoot}/src/test/java", + ] + } + } + } +} +``` + +* Tell YCM to load the debugger plugin and create a mapping, such as + `` to start the debug server and launch vimspector, e.g. in + `~/.vim/ftplugin/java.vim`: + +```viml +" Tell YCM where to find the plugin. Add to any existing values. +let g:ycm_java_jdtls_extension_path = [ + \ '' + \ ] + +let s:jdt_ls_debugger_port = 0 +function! s:StartDebugging() + if s:jdt_ls_debugger_port <= 0 + " Get the DAP port + let s:jdt_ls_debugger_port = youcompleteme#GetCommandResponse( + \ 'ExecuteCommand', + \ 'vscode.java.startDebugSession' ) + + if s:jdt_ls_debugger_port == '' + echom "Unable to get DAP port - is JDT.LS initialized?" + let s:jdt_ls_debugger_port = 0 + return + endif + endif + + " Start debugging with the DAP port + call vimspector#LaunchWithSettings( { 'DAPPort': s:jdt_ls_debugger_port } ) +endfunction + +nnoremap :call StartDebugging() + +``` + +You can then use `` to start debugging rather than just ``. + +### Other LSP clients + +See [this issue](https://github.com/puremourning/vimspector/issues/3) for more +background. + +## Other servers * Java - vscode-javac. This works, but is not as functional as Java Debug Server. Take a look at [this @@ -1122,3 +1192,4 @@ Copyright © 2018 Ben Jackson [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 [debugpy]: https://github.com/microsoft/debugpy +[YouCompleteMe]: https://github.com/ycm-core/YouCompleteMe#java-semantic-completion diff --git a/install_gadget.py b/install_gadget.py index 38098a3..96780f2 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -140,7 +140,7 @@ GADGETS = { 'adapters': { "vscode-java": { "name": "vscode-java", - "port": "ask", + "port": "${DAPPort}", } }, }, diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 89ff9d1..ff6ee60 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -373,7 +373,8 @@ class VariablesView( object ): def handler( message ): # TODO: this result count be expandable, but we have no way to allow the - # user to interact with the balloon to expand it. + # user to interact with the balloon to expand it, unless we use a popup + # instead, but even then we don't really want to trap the cursor. body = message[ 'body' ] result = body[ 'result' ] if result is None: diff --git a/support/test/java/test_project/.vimspector.json b/support/test/java/test_project/.vimspector.json index 9ccc19f..c19cfcb 100644 --- a/support/test/java/test_project/.vimspector.json +++ b/support/test/java/test_project/.vimspector.json @@ -1,13 +1,7 @@ { - "adapters": { - "java-debug-server": { - "name": "vscode-java", - "port": "ask" - } - }, "configurations": { "Java Launch": { - "adapter": "java-debug-server", + "adapter": "vscode-java", "configuration": { "request": "launch", "mainClass": "com.vimspector.test.TestApplication", @@ -18,6 +12,16 @@ "console": "integratedTerminal" } }, + "Java Attach": { + "adapter": "vscode-java", + "configuration": { + "request": "attach", + "sourcePaths": [ "${workspaceRoot}/src/main/java" ], + "stopOnEntry": true, + "hostName": "localhost", + "port": "${JVMDebugPort}" + } + }, "Attach with vscode-javac": { "adapter": "vscode-javac", "configuration": { From 3354db275b4ad2b9a2dbad8c6ecfcb726e247c00 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 7 May 2020 22:32:20 +0100 Subject: [PATCH 153/609] Update TOC --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f005ef6..bd19269 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ 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) + * [Motivation](#motivation) * [Features and Usage](#features-and-usage) * [Supported debugging features](#supported-debugging-features) * [Supported languages:](#supported-languages) @@ -15,11 +16,13 @@ For a tutorial and usage overview, take a look at the * [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) @@ -49,13 +52,16 @@ For a tutorial and usage overview, take a look at the * [Debug web application](#debug-web-application) * [Debug cli application](#debug-cli-application) * [JavaScript, TypeScript, etc.](#javascript-typescript-etc) - * [Java - partially supported](#java---partially-supported) + * [Java](#java) + * [Usage with YouCompleteMe](#usage-with-youcompleteme) + * [Other LSP clients](#other-lsp-clients) + * [Other servers](#other-servers) * [Customisation](#customisation) * [Changing the default signs](#changing-the-default-signs) * [FAQ](#faq) * [License](#license) - + @@ -1130,6 +1136,9 @@ nnoremap :call StartDebugging() You can then use `` to start debugging rather than just ``. +For the launch arguments, see the +[vscode document](https://code.visualstudio.com/docs/java/java-debugging). + ### Other LSP clients See [this issue](https://github.com/puremourning/vimspector/issues/3) for more From bdfa0f92abadca3700bab7bc6cec862a1be8eeb7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 7 May 2020 22:33:01 +0100 Subject: [PATCH 154/609] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd19269..2b5359d 100644 --- a/README.md +++ b/README.md @@ -1090,7 +1090,7 @@ use it with Vimspector. "configuration": { "request": "attach", "hostName": "${host}", - "port": "${port}, + "port": "${port}", "sourcePaths": [ "${workspaceRoot}/src/main/java", "${workspaceRoot}/src/test/java", From abd8ba7d3841dd55df32fbaae3c6cdb103960232 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 May 2020 11:50:38 +0100 Subject: [PATCH 155/609] Stabilise some tests --- autoload/vimspector/internal/job.vim | 18 +++++++++++++++++- tests/breakpoints.test.vim | 2 ++ tests/ci/image/Dockerfile | 2 +- tests/lib/autoload/vimspector/test/setup.vim | 9 +++++++-- tests/manual/image/Dockerfile | 1 + 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index 18c5b08..88c63cd 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -20,10 +20,26 @@ set cpoptions&vim " }}} function! s:_OnServerData( channel, data ) abort + if py3eval( '_vimspector_session is None' ) + call ch_log( 'Unexpected stdout data received on channel ' + \ . a:channel + \ . 'after reset: ' + \ . a:data ) + return + endif + py3 _vimspector_session.OnChannelData( vim.eval( 'a:data' ) ) endfunction function! s:_OnServerError( channel, data ) abort + if py3eval( '_vimspector_session is None' ) + call ch_log( 'Unexpected stderr data received on channel ' + \ . a:channel + \ . 'after reset: ' + \ . a:data ) + return + endif + py3 _vimspector_session.OnServerStderr( vim.eval( 'a:data' ) ) endfunction @@ -43,7 +59,7 @@ endfunction function! vimspector#internal#job#StartDebugSession( config ) abort if exists( 's:job' ) - echom 'Not starging: Job is already running' + echom 'Not starting: Job is already running' redraw return v:false endif diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index 07c008e..fe3b303 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -72,6 +72,7 @@ function! Test_Signs_Placed_Using_API_Are_Shown() call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorBP' ) call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorCode' ) + call vimspector#test#setup#Reset() %bwipeout! endfunction @@ -244,6 +245,7 @@ function Test_DisableBreakpointWhileDebugging() call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorCode' ) lcd - + call vimspector#test#setup#Reset() %bwipeout! endfunction diff --git a/tests/ci/image/Dockerfile b/tests/ci/image/Dockerfile index 77e7131..d400ed6 100644 --- a/tests/ci/image/Dockerfile +++ b/tests/ci/image/Dockerfile @@ -27,7 +27,7 @@ RUN ln -fs /usr/share/zoneinfo/Europe/London /etc/localtime && \ ## cleanup of files from setup RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -ARG VIM_VERSION=v8.2.0460 +ARG VIM_VERSION=v8.2.0716 ENV CONF_ARGS "--with-features=huge \ --enable-python3interp \ diff --git a/tests/lib/autoload/vimspector/test/setup.vim b/tests/lib/autoload/vimspector/test/setup.vim index 6610647..d69acef 100644 --- a/tests/lib/autoload/vimspector/test/setup.vim +++ b/tests/lib/autoload/vimspector/test/setup.vim @@ -17,11 +17,14 @@ function! vimspector#test#setup#ClearDown() abort endfunction function! vimspector#test#setup#WaitForReset() abort + call WaitForAssert( {-> assert_equal( 1, len( gettabinfo() ) ) } ) call WaitForAssert( {-> - \ assert_true( pyxeval( '_vimspector_session._connection is None' ) ) + \ assert_true( pyxeval( '_vimspector_session is None or ' . + \ '_vimspector_session._connection is None' ) ) \ } ) call WaitForAssert( {-> - \ assert_true( pyxeval( '_vimspector_session._uiTab is None' ) ) + \ assert_true( pyxeval( '_vimspector_session is None or ' . + \ '_vimspector_session._uiTab is None' ) ) \ }, 10000 ) call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorCode' ) @@ -37,5 +40,7 @@ function! vimspector#test#setup#Reset() abort if exists( '*vimspector#internal#state#Reset' ) call vimspector#internal#state#Reset() endif + + call popup_clear() endfunction diff --git a/tests/manual/image/Dockerfile b/tests/manual/image/Dockerfile index badfa36..1b6b546 100644 --- a/tests/manual/image/Dockerfile +++ b/tests/manual/image/Dockerfile @@ -16,5 +16,6 @@ WORKDIR /home/dev ENV HOME /home/dev ENV PYTHON_CONFIGURE_OPTS --enable-shared +ENV VIMSPECTOR_MIMODE gdb ADD --chown=dev:dev .vim/ /home/dev/.vim/ From 2451d56de4a16eecb62befade3289345aa6491c5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 May 2020 13:11:17 +0100 Subject: [PATCH 156/609] Make tests work in manual container: install delve. This has to be done as the user because go --- tests/manual/image/Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/manual/image/Dockerfile b/tests/manual/image/Dockerfile index 1b6b546..7658ada 100644 --- a/tests/manual/image/Dockerfile +++ b/tests/manual/image/Dockerfile @@ -17,5 +17,9 @@ WORKDIR /home/dev ENV HOME /home/dev ENV PYTHON_CONFIGURE_OPTS --enable-shared ENV VIMSPECTOR_MIMODE gdb +ENV GOPATH /home/dev/go + +RUN eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) && \ + go get -u github.com/go-delve/delve/cmd/dlv ADD --chown=dev:dev .vim/ /home/dev/.vim/ From ce6ad5d1cad80821d0df0646a70da9324f68e090 Mon Sep 17 00:00:00 2001 From: eyalk5 Date: Sun, 10 May 2020 02:42:02 +0300 Subject: [PATCH 157/609] Update FAQ Added question about $file --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b5359d..e5d9f42 100644 --- a/README.md +++ b/README.md @@ -1175,7 +1175,7 @@ sign define vimspectorPC text=🔶 texthl=SpellBad # FAQ -1. Q: Does it work? A: Yeah, sort of. It's _incredibly_ buggy and unpolished. +1. Q: Does it work? A: Yeah. It's a bit unpolished. 2. Q: Does it work with _this_ language? A: Probably, but it won't necessarily be easy to work out what to put in the `.vimspector.json`. As you can see above, some of the servers aren't really editor agnostic, and require @@ -1184,6 +1184,8 @@ sign define vimspectorPC text=🔶 texthl=SpellBad comment](https://github.com/puremourning/vimspector/issues/90#issuecomment-577857322) 4. Can I specify answers to the annoying questions about exception breakpoints in my `vimspector.json` ? Yes, see [here][vimspector-ref-exception]. +5. Do I have to specify the file to execute in `.vimspector.json`, or could it be the current vim file? + You don't need to. You can specify $file for the current active file. See [here][vimspector-ref-var] for complete list of replacements in the configuration file. # License From c103e2adf525dbbdf32b8df2b579bd79c81ceb56 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 12 May 2020 19:28:34 +0100 Subject: [PATCH 158/609] Make sure we can still request threads if one threads request fails --- python3/vimspector/stack_trace.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 6746b61..d2c2b70 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -118,10 +118,14 @@ class StackTraceView( object ): self._DrawThreads() + def failure_handler( reason, msg ): + # Make sure we request them again if the request fails + self._requesting_threads = False + self._requesting_threads = True self._connection.DoRequest( consume_threads, { 'command': 'threads', - } ) + }, failure_handler ) def _DrawThreads( self ): self._line_to_frame.clear() From 5837135fee265dfbcf6989fb671f453c5171ff98 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 9 May 2020 15:22:42 +0100 Subject: [PATCH 159/609] Allow moving the gadget/configuration dirs to arbitrary location --- CONTRIBUTING.md | 7 +++- install_gadget.py | 34 ++++++++++++++++--- python3/vimspector/debug_session.py | 7 ++-- python3/vimspector/install.py | 12 +++++++ python3/vimspector/utils.py | 9 +++++ run_tests | 51 ++++++++++++++++++++++++++--- 6 files changed, 106 insertions(+), 14 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b14ecb5..aec70ef 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,7 +85,12 @@ There are 2 ways: follow the instructions for running tets directly. 1. Directly: Run `./install_gadget.py --all` and then `./run_tests`. Note that this depends on your runtime environment and might not match CI. I recommend - running the tests in the docker container. + running the tests in the docker container. If you have your own custom + gadgets and/or custom configurations (in `vimspector/configurations` and/or + `vimspector/gadget`, then consider using `./run_tests --install --basedir + /tmp/vimspector_test` (then delete `/tmp/vimspector_test`). This will install + the gadgets to that dir and use it for the gadget dir/config dir so that your + custom configuration won't interfere with the tess. When tests fail, they dump a load of logs to a directory for each failed tests. Usually the most useful output is `messages`, which tells you what actually diff --git a/install_gadget.py b/install_gadget.py index 96780f2..b0d47dc 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -530,12 +530,10 @@ def InstallGagdet( name, gadget, failed, all_adapters ): print( "FAILED installing {}: {}".format( name, e ) ) -vimspector_base = os.path.dirname( __file__ ) -OS = install.GetOS() -gadget_dir = install.GetGadgetDir( vimspector_base, OS ) +# ------------------------------------------------------------------------------ +# Entry point +# ------------------------------------------------------------------------------ -print( 'OS = ' + OS ) -print( 'gadget_dir = ' + gadget_dir ) parser = argparse.ArgumentParser( formatter_class = argparse.RawDescriptionHelpFormatter, @@ -566,6 +564,14 @@ parser.add_argument( '--force-all', action = 'store_true', help = 'Enable all unsupported completers' ) +parser.add_argument( '--basedir', + action = 'store', + help = 'Advanced option. ' + 'Base directory under which to keep gadgets, ' + 'configurations, etc.. Default: vimspector ' + 'installation dir. Useful for developers or ' + 'multi-user installations' ) + parser.add_argument( '--no-gadget-config', action = 'store_true', help = "Don't write the .gagets.json, just install" ) @@ -623,6 +629,18 @@ args = parser.parse_args() installer.AbortIfSUperUser( args.sudo ) +vimspector_base = os.path.dirname( __file__ ) +if args.basedir: + vimspector_base = os.path.abspath( args.basedir ) + +install.MakeInstallDirs( vimspector_base ) + +OS = install.GetOS() +gadget_dir = install.GetGadgetDir( vimspector_base, OS ) + +print( 'OS = ' + OS ) +print( 'gadget_dir = ' + gadget_dir ) + if args.force_all and not args.all: args.all = True @@ -675,3 +693,9 @@ else: if failed: raise RuntimeError( 'Failed to install gadgets: {}'.format( ','.join( failed ) ) ) + +if args.basedir: + print( "" ) + print( "***NOTE***: You set --basedir to " + args.basedir + + ". Therefore you _must_ ensure this is in your vimrc:\n" + "let g:vimspector_base_dir='" + vimspector_base + "'" ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index f65f13c..5d07f46 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -32,9 +32,8 @@ from vimspector import ( breakpoints, variables ) from vimspector.vendor.json_minify import minify -VIMSPECTOR_HOME = os.path.abspath( os.path.join( os.path.dirname( __file__ ), - '..', - '..' ) ) +# We cache this once, and don't allow it to change (FIXME?) +VIMSPECTOR_HOME = utils.GetVimspectorBase() # cache of what the user entered for any option we ask them USER_CHOICES = {} @@ -1007,7 +1006,7 @@ def PathsToAllGadgetConfigs( vimspector_base, current_file ): def PathsToAllConfigFiles( vimspector_base, current_file, filetypes ): - for ft in filetypes: + for ft in filetypes + [ '_all' ]: for p in sorted( glob.glob( os.path.join( install.GetConfigDirForFiletype( vimspector_base, ft ), '*.json' ) ) ): diff --git a/python3/vimspector/install.py b/python3/vimspector/install.py index b91c90b..4726a5b 100644 --- a/python3/vimspector/install.py +++ b/python3/vimspector/install.py @@ -26,6 +26,18 @@ def GetOS(): return 'linux' +def mkdirs( p ): + try: + os.makedirs( p ) + except FileExistsError: + pass + + +def MakeInstallDirs( vimspector_base ): + mkdirs( GetGadgetConfigDir( vimspector_base ) ) + mkdirs( GetConfigDirForFiletype( vimspector_base, '_all' ) ) + + def GetGadgetDir( vimspector_base, OS ): return os.path.join( os.path.abspath( vimspector_base ), 'gadgets', OS ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 13c43f8..ac752ac 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -563,3 +563,12 @@ def HideSplash( api_prefix, splash ): Call( f'vimspector#internal#{api_prefix}popup#HideSplash', splash ) return None + + +def GetVimspectorBase(): + try: + return vim.vars[ 'vimspector_base_dir' ].decode( 'utf-8' ) + except KeyError: + return os.path.abspath( os.path.join( os.path.dirname( __file__ ), + '..', + '..' ) ) diff --git a/run_tests b/run_tests index 97c170d..7ff2ebd 100755 --- a/run_tests +++ b/run_tests @@ -1,16 +1,54 @@ #!/usr/bin/env bash if [ "$1" == "--help" ]; then - echo "$(basename $0) " + 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 "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 -RUN_VIM="vim --clean --not-a-term" + +BASEDIR=$(dirname $0) +INSTALL=0 +RUN_VIM="vim -N --clean --not-a-term" RUN_TEST="${RUN_VIM} -S lib/run_test.vim" +BASEDIR_CMD='py3 pass' + +while [ -n "$1" ]; do + case "$1" in + "--basedir") + BASEDIR=$2 + if [[ ! $BASEDIR = /* ]]; then + # Relative + BASEDIR=$(pwd)/${BASEDIR} + fi + BASEDIR_CMD="let g:vimspector_base_dir='${BASEDIR}'" + shift + shift + ;; + "--install") + INSTALL=1 + shift + ;; + "--") + shift + break + ;; + *) + break + ;; + esac +done + +if [ $INSTALL = 1 ]; then + python3 $(dirname $0)/install_gadget.py --basedir ${BASEDIR} --all +fi if [ -z "$VIMSPECTOR_MIMODE" ]; then if which lldb >/dev/null 2>&1; then @@ -25,6 +63,10 @@ fi echo "Testing with:" echo " * VIMSPECTOR_MIMODE=$VIMSPECTOR_MIMODE" +echo " * RUN_VIM=$RUN_VIM" +echo " * RUN_TEST=$RUN_TEST" +echo " * BASEDIR_CMD=$BASEDIR_CMD" + echo "%SETUP - Building test programs..." set -e @@ -53,9 +95,10 @@ for t in ${TESTS}; do # split on : into fileName and testName IFS=: read -s t T <<< "$t" - TESTLOGDIR=$(pwd)/logs/$t + TESTLOGDIR=${BASEDIR}/tests/logs/$t - if ${RUN_TEST} --cmd 'au SwapExists * let v:swapchoice = "e"' $t $T \ + if ${RUN_TEST} --cmd "${BASEDIR_CMD}" \ + --cmd 'au SwapExists * let v:swapchoice = "e"' $t $T \ && [ -f $t.res ]; then echo "%PASS: $t PASSED" else From fd6b3537824631a9c172f0c6fa0b2e363824048f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 12 May 2020 19:39:20 +0100 Subject: [PATCH 160/609] Reset the tab test more robustly --- tests/tabpage.test.vim | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/tabpage.test.vim b/tests/tabpage.test.vim index 356ee19..e08a8c0 100644 --- a/tests/tabpage.test.vim +++ b/tests/tabpage.test.vim @@ -44,10 +44,7 @@ function! Test_Step_With_Different_Tabpage() call assert_equal( 'simple.cpp', bufname( '%' ), 'Current buffer' ) call assert_equal( 1, col( '.' ), 'Current column' ) - call vimspector#Reset() - call vimspector#ClearBreakpoints() - - call WaitForAssert( {-> assert_notequal( vimspector_tabnr, tabpagenr() ) } ) + call vimspector#test#setup#Reset() lcd - %bwipeout! endfunction From 0452329203433497ecd9c3b9245e71c4b859c25c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 13 May 2020 17:18:57 +0100 Subject: [PATCH 161/609] Update java debugger plugin --- install_gadget.py | 6 +++--- .../com/vimspector/test/TestApplication.java | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index b0d47dc..025a977 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -132,10 +132,10 @@ GADGETS = { '${version}/${file_name}', }, 'all': { - 'version': '0.23.0', - 'file_name': 'vscode-java-debug-0.23.0.vsix', + 'version': '0.26.0', + 'file_name': 'vscjava.vscode-java-debug-0.26.0.vsix', 'checksum': - 'f98a35c8dd4f3079ecd9657b04f1edbfea04c364c0fbf7eee1285f942782710b', + 'de49116ff3a3c941dad0c36d9af59baa62cd931e808a2ab392056cbb235ad5ef', }, 'adapters': { "vscode-java": { diff --git a/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java b/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java index 17f9c96..264c0e6 100644 --- a/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java +++ b/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java @@ -13,6 +13,22 @@ public class TestApplication { return list; } + private static class Bass extends Base { + String bass = "Pump"; + @Override + public String DoSomething() { + if ( Math.random() % 3 == 0 ) { + return bass; + } + return super.DoSomething(); + } + } + + private static void DoGeneric( T b ) { + TestGeneric foo = new TestGeneric<>( b ); + foo.DoSomethingUseful(); + } + public static void main( String[] args ) { int numEntries = 0; for ( String s : args ) { @@ -24,6 +40,8 @@ public class TestApplication { ++numEntries; } System.out.println( "Number of entries: " + numEntries ); + + DoGeneric( new Bass() ); } } From 62c1fe79dad5226951708678d41e43fc5ebd47c2 Mon Sep 17 00:00:00 2001 From: Ethan Setnik Date: Fri, 15 May 2020 11:32:09 -0400 Subject: [PATCH 162/609] Fix incorrect naming of `.viminspector.json` I got tripped up getting started because the documentation incorrectly uses `vimspector.json` and I was getting an error about no debug configurations found. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e5d9f42..2a7ea62 100644 --- a/README.md +++ b/README.md @@ -578,7 +578,7 @@ let g:vimspector_enable_mappings = 'HUMAN' ## Launch and attach by PID: -* Create `vimspector.json`. See [below](#supported-languages). +* Create `.vimspector.json`. See [below](#supported-languages). * `:call vimspector#Launch()` and select a configuration. ![debug session](https://puremourning.github.io/vimspector-web/img/vimspector-overview.png) @@ -1183,7 +1183,7 @@ sign define vimspectorPC text=🔶 texthl=SpellBad 3. How do I stop it starting a new Terminal.app on macOS? See [this comment](https://github.com/puremourning/vimspector/issues/90#issuecomment-577857322) 4. Can I specify answers to the annoying questions about exception breakpoints - in my `vimspector.json` ? Yes, see [here][vimspector-ref-exception]. + in my `.vimspector.json` ? Yes, see [here][vimspector-ref-exception]. 5. Do I have to specify the file to execute in `.vimspector.json`, or could it be the current vim file? You don't need to. You can specify $file for the current active file. See [here][vimspector-ref-var] for complete list of replacements in the configuration file. From dd13c7d54327ba67b88959d83020de837c15d264 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 13 May 2020 17:19:34 +0100 Subject: [PATCH 163/609] Move directories if they cannot be deleted --- python3/vimspector/installer.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index bca5084..1afea27 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -140,13 +140,34 @@ def ValidateCheckSumSHA256( file_path, checksum ): def RemoveIfExists( destination ): - if os.path.exists( destination ) or os.path.islink( destination ): - if os.path.islink( destination ): - print( "Removing file {}".format( destination ) ) - os.remove( destination ) - else: - print( "Removing dir {}".format( destination ) ) + if os.path.islink( destination ): + print( "Removing file {}".format( destination ) ) + os.remove( destination ) + return + + N = 1 + + + def BackupDir(): + return "{}.{}".format( destination, N ) + + while os.path.isdir( BackupDir() ): + print( "Removing old dir {}".format( BackupDir() ) ) + try: + shutil.rmtree( BackupDir() ) + print ( "OK, removed it" ) + break + except OSError: + print ( "FAILED" ) + N = N + 1 + + if os.path.exists( destination ): + print( "Removing dir {}".format( destination ) ) + try: shutil.rmtree( destination ) + except OSError: + print( "FAILED, moving {} to dir {}".format( destination, BackupDir() ) ) + os.rename( destination, BackupDir() ) # Python's ZipFile module strips execute bits from files, for no good reason From dc3deadb2676144c1cbe35caac0683f9269b2724 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 13 May 2020 17:19:42 +0100 Subject: [PATCH 164/609] Use java 11 --- support/test/java/test_project/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/support/test/java/test_project/pom.xml b/support/test/java/test_project/pom.xml index 10f207c..e6dc4d3 100644 --- a/support/test/java/test_project/pom.xml +++ b/support/test/java/test_project/pom.xml @@ -4,7 +4,7 @@ TestApplication 1 - 1.8 - 1.8 + 11 + 11 From 36253e58ce500911145c1ab64fcb464ed78dc571 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 15 May 2020 15:14:36 +0100 Subject: [PATCH 165/609] Optionally Update gadget config rather than repolace --- install_gadget.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/install_gadget.py b/install_gadget.py index 025a977..ced4d06 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -576,6 +576,11 @@ parser.add_argument( '--no-gadget-config', action = 'store_true', help = "Don't write the .gagets.json, just install" ) +parser.add_argument( '--update-gadget-config', + action = 'store_true', + help = + "Update the gadget config rather than overwrite it" ) + parser.add_argument( '--enable-custom', dest='custom_gadget_file', action='append', @@ -657,7 +662,12 @@ for custom_file_name in functools.reduce( operator.add, failed = [] -all_adapters = {} +if args.update_gadget_config: + with open( install.GetGadgetConfigFile( vimspector_base ), 'r' ) as f: + all_adapters = json.load( f ).get( 'adapters', {} ) +else: + all_adapters = {} + for name, gadget in GADGETS.items(): if not gadget.get( 'enabled', True ): if ( not args.force_all From c6d6c7f87627336d5f3350a173bbef6f9f029359 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 15 May 2020 15:18:06 +0100 Subject: [PATCH 166/609] Support "multi-session" mode properly Document remote debugging and add a test --- README.md | 79 +++++- autoload/vimspector/internal/channel.vim | 2 +- autoload/vimspector/internal/neochannel.vim | 2 +- compiler/vimspector_test.vim | 6 +- docs/configuration.md | 245 ++++++++++++++++++ install_gadget.py | 8 + .../python/simple_python/.vimspector.json | 9 +- .../python/simple_python/requirements.txt | 2 +- .../python/simple_python/run_with_debugpy | 20 ++ .../test/python/simple_python/run_with_ptvsd | 19 -- tests/ci/image/Dockerfile | 1 + tests/language_python.test.vim | 60 +++++ 12 files changed, 417 insertions(+), 36 deletions(-) create mode 100755 support/test/python/simple_python/run_with_debugpy delete mode 100755 support/test/python/simple_python/run_with_ptvsd diff --git a/README.md b/README.md index 2a7ea62..6564459 100644 --- a/README.md +++ b/README.md @@ -793,11 +793,25 @@ An alternative is to to use `lldb-vscode`, which comes with llvm. Here's how: } ``` +### Remote debugging + +The cpptools documentation describes how to attach cpptools to gdbserver using +`miDebuggerAddress`. Note that when doing this you should use the +`"request": "attach"`. + +### Remote launch and attach + +If you're feeling fancy, checkout the [reference guide][remote-debugging] for +an example of getting Vimspector to remotely launch and attach. + ## Python * Python: [debugpy][] -* Requires `install_gadget.py --enable-python`, this requires a working compiler -to build a C python extension for performance. +* Requires `install_gadget.py --enable-python`, ideally requires a working + compiler and the python development headers/libs to build a C python extension + for performance. +* Full options: https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings + **Migrating from `vscode-python`**: change `"adapter": "vscode-python"` to `"adapter": "debugpy"`. @@ -816,7 +830,7 @@ to build a C python extension for performance. "stopOnEntry": true, "console": "externalTerminal", "debugOptions": [], - "program": "", + "program": "" } } ... @@ -824,6 +838,50 @@ to build a C python extension for performance. } ``` +### Remote Debugging + +In order to use remote debugging with debugpy, you have to connect Vimspector +directly to the application that is being debugged. This is easy, but it's a +little different from how we normally configure things. Specifically, you need +to: + + +* Start your application with debugpy, specifying the `--listen` argument. See + [the debugpy + documentation](https://github.com/microsoft/debugpy#debugpy-cli-usage) for + details. +* use the built-in "multi-session" adapter. This just asks for the host/port to + connect to. For example: + +```json +{ + "configurations": { + "Python Attach": { + "adapter": "multi-session", + "configuration": { + "request": "attach", + "pathMappings": [ + // mappings here (optional) + ] + } + } + } +} +``` + +See [deatils of the launch +configuration](https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings) +for explanation of things like `pathMappings`. + +Additional documenation, including how to do this when the remote machine can +only be contacted via SSH [are provided by +debugpy](https://github.com/microsoft/debugpy/wiki/Debugging-over-SSH). + +### Remote launch and attach + +If you're feeling fancy, checkout the [reference guide][remote-debugging] for +an example of getting Vimspector to remotely launch and attach. + ### Legacy: vscode-python * No longer installed by default - please pass `--force-enable-python.legacy` if @@ -884,7 +942,7 @@ Requires `install_gadget.py --force-enable-csharp` Requires `install_gadget.py --force-enable-csharp`. -***Known not to work.**** +***Known not to work.*** ```json { @@ -1093,7 +1151,7 @@ use it with Vimspector. "port": "${port}", "sourcePaths": [ "${workspaceRoot}/src/main/java", - "${workspaceRoot}/src/test/java", + "${workspaceRoot}/src/test/java" ] } } @@ -1151,6 +1209,7 @@ background. comment](https://github.com/puremourning/vimspector/issues/3#issuecomment-576916076) for instructions. + # Customisation There is very limited support for customistaion of the UI. @@ -1186,6 +1245,15 @@ sign define vimspectorPC text=🔶 texthl=SpellBad in my `.vimspector.json` ? Yes, see [here][vimspector-ref-exception]. 5. Do I have to specify the file to execute in `.vimspector.json`, or could it be the current vim file? You don't need to. You can specify $file for the current active file. See [here][vimspector-ref-var] for complete list of replacements in the configuration file. +6. You allow comments in `.vimspector.json`, but Vim highlights these as errors, + do you know how to make this not-an-error? Yes, put this in + `~/.vim/after/syntax/json.vim`: + +```viml +syn region jsonComment start="/\*" end="\*/" +hi link jsonCommentError Comment +hi link jsonComment Comment +``` # License @@ -1204,3 +1272,4 @@ Copyright © 2018 Ben Jackson [vimspector-ref-exception]: https://puremourning.github.io/vimspector/configuration.html#exception-breakpoints [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/autoload/vimspector/internal/channel.vim b/autoload/vimspector/internal/channel.vim index 4c3d1d9..05cf26c 100644 --- a/autoload/vimspector/internal/channel.vim +++ b/autoload/vimspector/internal/channel.vim @@ -39,7 +39,7 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort return v:false endif - let l:addr = 'localhost:' . a:config[ 'port' ] + let l:addr = get( a:config, 'host', 'localhost' ) . ':' . a:config[ 'port' ] echo 'Connecting to ' . l:addr . '... (waiting fo up to 10 seconds)' let s:ch = ch_open( l:addr, diff --git a/autoload/vimspector/internal/neochannel.vim b/autoload/vimspector/internal/neochannel.vim index 22bc5d2..e692c50 100644 --- a/autoload/vimspector/internal/neochannel.vim +++ b/autoload/vimspector/internal/neochannel.vim @@ -39,7 +39,7 @@ function! vimspector#internal#neochannel#StartDebugSession( config ) abort return v:false endif - let addr = 'localhost:' . a:config[ 'port' ] + let l:addr = get( a:config, 'host', 'localhost' ) . ':' . a:config[ 'port' ] let s:ch = sockconnect( 'tcp', addr, { 'on_data': funcref( 's:_OnEvent' ) } ) if s:ch <= 0 diff --git a/compiler/vimspector_test.vim b/compiler/vimspector_test.vim index 3396538..a347f3b 100644 --- a/compiler/vimspector_test.vim +++ b/compiler/vimspector_test.vim @@ -90,7 +90,7 @@ function! s:RunTestUnderCursor() let l:cwd = getcwd() execute 'lcd ' . s:root_dir try - execute s:make_cmd . ' ' . l:test_arg + execute s:make_cmd . ' ' . get( b:, 'test_args', '' ) . ' ' . l:test_arg finally execute 'lcd ' . l:cwd endtry @@ -101,7 +101,7 @@ function! s:RunTest() let l:cwd = getcwd() execute 'lcd ' . s:root_dir try - execute s:make_cmd . ' %:p:t' + execute s:make_cmd . ' ' . get( b:, 'test_args', '' ) . ' %:p:t' finally execute 'lcd ' . l:cwd endtry @@ -112,7 +112,7 @@ function! s:RunAllTests() let l:cwd = getcwd() execute 'lcd ' . s:root_dir try - execute s:make_cmd + execute s:make_cmd . ' ' . get( b:, 'test_args', '' ) finally execute 'lcd ' . l:cwd endtry diff --git a/docs/configuration.md b/docs/configuration.md index 4f67739..3ad67f3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -365,6 +365,251 @@ The following variables are provided: * `${fileExtname}` - the current opened file's extension * `${cwd}` - the current working directory of the active window on launch +## Remote Debugging Support + +Vimspector has in-built support for exectuting remote debuggers (such as +`gdbserver`, `debugpy`, `llvm-server` etc.). This is useful for environments +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/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 +research that. + +Vimspector's tools are intended to automate your existing process for setting +this up rather than to offer batteries-included approach. Ultimately, all +vimspector is going to do is run your commands over SSH and co-ordinate with the +adapter. + +### Python (debugpy) Example + +Here is some examples using the vimspector built-in +remote support (using SSH) to remotely launch and attach a python application +and connect to it using debugpy. + +The usage pattern is to hit ``, enter `host` (the host where your app runs), +`account` (the account it runs under), and `port` (a port that will be opened on +the remote host). Vimspector then orchestrates the various tools to set you up. + +```json + +{ + "adapters": { + "python-remote": { + "port": "${port}", + "host": "${host}", + "launch": { + "remote": { + "host": "${host}", // Remote host to ssh to (mandatory) + "account": "${account}", // User to connect as (optional) + + // Optional.... Manual additional arguments for ssh + // "ssh": { + // "args": [ "-o", "StrictHostKeyChecking=no" ] + // }, + + // Command to launch the debugee and attach the debugger; + // %CMD% replaced with the remote-cmdLine configured in the launch + // configuration. (mandatory) + "launchCommmand": [ + "python", "-m", "debugpy", "--listen 0.0.0.0:${port}", + "%CMD%" + ] + + // Optional alternative to launchCommmand (if you need to run multiple + // commands) + // "launchCommmands": [ + // [ /* first command */ ], + // [ /* second command */ ] + // ] + + } + }, + "attach": { + "remote": { + "host": "${host}", // Remote host to ssh to (mandatory) + "account": "${account}", // User to connect as (optional) + + // Optional.... Manual additional arguments for ssh + // "ssh": { + // "args": [ "-o", "StrictHostKeyChecking=no" ] + // }, + + // Command to get the PID of the process to attach (mandatory) + "pidCommand": [ + // + // Remember taht you can use ${var} to ask for input. I use this to + // call a custom command to returm the PID for a named service, so + // here's an examle: + // + "/path/to/secret/script/GetPIDForService", "${ServiceName}" + ], + + // Command to attach the debugger; %PID% replaced with output of + // pidCommand above (mandatory) + "attachCommand": [ + "python", "-m", "debugpy", "--listen 0.0.0.0:${port}", + "--pid", "%PID%" + ] + + // Optional alternative to attachCommand (if you need to run multiple + // commands) + // "attachCommands": [ + // [ /* first command */ ], + // [ /* second command */ ] + // ], + + // Optional.... useful with buggy gdbservers to kill -TRAP %PID% + // "initCompleteCommand": [ + // /* optional command to run after initialized */ + // ] + + // Optional.... Manual additional arguments for ssh + // "ssh": { + // "args": [ "-o", "StrictHostKeyChecking=no" ] + // }, + } + } + } + }, + "configurations": { + "remote-launch": { + "adapter": "python-remote", + + "remote-request": "launch", + "remote-cmdLine": [ + "${RemoteRoot}/${fileBasename}", "*${args}" + ], + + "configuration": { + "request": "attach", + "pathMappings": [ + { + "localRoot": "${workspaceRoot}", + "remoteRoot": "${RemoteRoot}" + } + ] + } + }, + "remote-attach": { + "variables": { + // Just an example of how to specify a variable manually rather than + // vimspector asking for input from the user + "ServiceName": "${fileBasenameNoExtention}" + }, + + "adapter": "python-remote", + "remote-request": "attach", + + "configuration": { + "request": "attach", + "pathMappings": [ + { + "localRoot": "${workspaceRoot}", + "remoteRoot": "${RemoteRoot}" + } + ] + } + } + } +} +``` + +### C-family (gdbserver) Example + +This example uses vimspector to remotely luanch or attach to a binary using +`gdbserver` and then instructs vscode-cpptools to attach to that `gdbserver`. + +The appraoch is very similar to the above for python, just that we use gdbserver +and have to tell cpptools a few more options. + +```json +{ + "adapters": { + "cpptools-remote": { + "command": [ + "${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7" + ], + "name": "cppdbg", + "configuration": { + "type": "cppdbg" + }, + "launch": { + "remote": { + "host": "${host}", + "account": "${account}", + "launchCommmand": [ + "gdbserver", + "--once", + "--no-startup-with-shell", + "--disable-randomisation", + "0.0.0.0:${port}", + "%CMD%" + } + }, + "attach": { + "remote": { + "host": "${host}", + "account": "${account}", + "pidCommand": [ + "/path/to/secret/script/GetPIDForService", "${ServiceName}" + ], + "attachCommand": [ + "gdbserver", + "--once", + "--attach", + "0.0.0.0:${port}", + "%PID%" + ], + // + // If your application is started by a wrapper script, then you might + // need the followin. GDB can't pause an application because it only + // sends the signal to the process group leader. Or something. + // Basically, if you find that everything just hangs and the + // application never attaches, try using the following to manually + // force the trap signal. + // + "initCompleteCommand": [ + "kill", + "-TRAP", + "%PID%" + ] + } + } + } + }, + "configurations": { + "remote launch": { + "adapter": "cpptools-remote", + "remote-cmdLine": [ "/path/to/the/remote/executable", "args..." ], + "remote-request": "launch", + "configuration": { + "request": "attach", // yes, attach! + + "program": "/path/to/the/local/executable", + "MIMode": "gdb", + "miDebuggerAddress": "${host}:${port}" + } + }, + "remote attach": { + "adapter": "cpptools-remote", + "remote-request": "attach", + "configuration": { + "request": "attach", + + "program": "/path/to/the/local/executable", + "MIMode": "gdb", + "miDebuggerAddress": "${host}:${port}" + } + } +} +``` + ## Appendix: Configuration file format The configuration files are text files which must be UTF-8 encoded. They diff --git a/install_gadget.py b/install_gadget.py index ced4d06..b4760f8 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -668,6 +668,14 @@ if args.update_gadget_config: else: all_adapters = {} +# Include "built-in" adapter for multi-session mode +all_adapters.update( { + 'multi-session': { + 'port': '${port}', + 'host': '${host}' + }, +} ) + for name, gadget in GADGETS.items(): if not gadget.get( 'enabled', True ): if ( not args.force_all diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 8610cf7..028ff72 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -2,7 +2,7 @@ "configurations": { // This is a comment. "run legacy vscode-python": { - "adapter": "vscode-python" /* coment goes here too */, + "adapter": "vscode-python", /* coment goes here too */ "configuration": { "request": "launch", "type": "python", @@ -19,12 +19,9 @@ } }, "attach": { - "adapter": "debugpy", + "adapter": "multi-session", "configuration": { - "request": "attach", - "type": "python", - "host": "localhost", - "port": "5678" + "request": "attach" }, "breakpoints": { "exception": { diff --git a/support/test/python/simple_python/requirements.txt b/support/test/python/simple_python/requirements.txt index dffcdab..2802a6b 100644 --- a/support/test/python/simple_python/requirements.txt +++ b/support/test/python/simple_python/requirements.txt @@ -1 +1 @@ -ptvsd==4.3.2 +debugpy diff --git a/support/test/python/simple_python/run_with_debugpy b/support/test/python/simple_python/run_with_debugpy new file mode 100755 index 0000000..03da11c --- /dev/null +++ b/support/test/python/simple_python/run_with_debugpy @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +PYTHON=python3 + +if [ -n "$1" ]; then + PYTHON=$1 +fi + +pushd $(dirname $0) + if [ -d env ]; then + rm -rf env + fi + + $PYTHON -m venv env + . env/bin/activate + python -m pip install -r requirements.txt + echo "*** Launching ***" + python -m debugpy --wait-for-client --listen 0.0.0.0:5678 main.py +popd + diff --git a/support/test/python/simple_python/run_with_ptvsd b/support/test/python/simple_python/run_with_ptvsd deleted file mode 100755 index 6b8c401..0000000 --- a/support/test/python/simple_python/run_with_ptvsd +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -PYTHON=python3 - -if [ -n "$1" ]; then - PYTHON=$1 -fi - -pushd $(dirname $0) - if [ ! -d env ]; then - virtualenv -p $PYTHON env - fi - - . env/bin/activate - pip install -r requirements.txt - - python -m ptvsd --wait --host localhost --port 5678 main.py -popd - diff --git a/tests/ci/image/Dockerfile b/tests/ci/image/Dockerfile index d400ed6..15d0ad0 100644 --- a/tests/ci/image/Dockerfile +++ b/tests/ci/image/Dockerfile @@ -7,6 +7,7 @@ RUN apt-get update && \ apt-get -y dist-upgrade && \ apt-get -y install python3-dev \ python3-pip \ + python3-venv \ ca-cacert \ locales \ language-pack-en \ diff --git a/tests/language_python.test.vim b/tests/language_python.test.vim index b970b3a..dccdc72 100644 --- a/tests/language_python.test.vim +++ b/tests/language_python.test.vim @@ -45,3 +45,63 @@ function! Test_Python_Simple() %bwipeout! endfunction +function! SetUp_Test_Python_Remote_Attach() + let g:vimspector_enable_mappings = 'HUMAN' +endfunction + +function! Test_Python_Remote_Attach() + lcd ../support/test/python/simple_python + let fn='main.py' + exe 'edit ' . fn + + let ready = v:false + function! ReceiveFromLauncher( ch, data ) closure + if a:data ==# '*** Launching ***' + let ready = v:true + endif + endfunction + + let jobid = job_start( [ './run_with_debugpy' ], { + \ 'out_mode': 'nl', + \ 'out_cb': funcref( 'ReceiveFromLauncher' ), + \ } ) + + " Wait up to 60s for the debugee to be launched (the script faffs with + " virtualenvs etc.) + call WaitFor( {-> ready == v:true }, 60000 ) + + call setpos( '.', [ 0, 6, 1 ] ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 6, 1 ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 6 ) + + " Add the breakpoint + call feedkeys( "\", 'xt' ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', + \ 6, + \ 'vimspectorBP' ) + + call setpos( '.', [ 0, 1, 1 ] ) + + " Here we go. Start Debugging (note will wait up to 10s for the script to do + " its virtualenv thing) + call vimspector#LaunchWithSettings( { + \ 'configuration': 'attach', + \ 'port': 5678, + \ 'host': 'localhost' + \ } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 6, 1 ) + + " Step + call feedkeys( "\", 'xt' ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 7, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 7 ) + \ } ) + + call vimspector#test#setup#Reset() + call job_stop( jobid ) + lcd - + %bwipeout! +endfunction From 90bbbe90f5f2021ca31ed62a41af3dfb074ffdc7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 16 May 2020 22:54:46 +0100 Subject: [PATCH 167/609] Default some cpptools args: --- install_gadget.py | 6 ++++++ tests/testdata/cpp/simple/.vimspector.json | 4 ---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index b4760f8..3e39ed1 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -73,6 +73,12 @@ GADGETS = { "pidProperty": "processId", "pidSelect": "ask" }, + "configuration": { + "type": "cppdbg", + "args": [], + "cwd": "${workspaceRoot}", + "environment": [], + } }, }, }, diff --git a/tests/testdata/cpp/simple/.vimspector.json b/tests/testdata/cpp/simple/.vimspector.json index 4d50e7a..5eb06e1 100644 --- a/tests/testdata/cpp/simple/.vimspector.json +++ b/tests/testdata/cpp/simple/.vimspector.json @@ -3,12 +3,8 @@ "cpptools-run": { "adapter": "vscode-cpptools", "configuration": { - "type": "cppdbg", "request": "launch", "program": "${workspaceRoot}/${fileBasenameNoExtension}", - "args": [], - "cwd": "${workspaceRoot}", - "environment": [], "externalConsole": false, "stopAtEntry": true, "stopOnEntry": true, From 6a1c9a6b52103e438dd228a2427fff92af55cee5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 17 May 2020 19:15:39 +0100 Subject: [PATCH 168/609] Enable embedded vim python debugging This allows us to use vimspector to debug vimspector. The idea is that in Vim you call 'py3 from vimspector.developer import SetUpDebugpy; SetUpDebugpy()' and then just attach to localhost:5678 using the new multi-session mode support. Oof. --- .vimspector.json | 14 +++++++++++ python3/vimspector/developer.py | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 .vimspector.json create mode 100644 python3/vimspector/developer.py diff --git a/.vimspector.json b/.vimspector.json new file mode 100644 index 0000000..4a9889e --- /dev/null +++ b/.vimspector.json @@ -0,0 +1,14 @@ +{ + "configurations": { + "Python: Attach To Vim": { + "variables": { + "port": "5678", + "host": "localhost" + }, + "adapter": "multi-session", + "configuration": { + "request": "attach" + } + } + } +} diff --git a/python3/vimspector/developer.py b/python3/vimspector/developer.py new file mode 100644 index 0000000..4945e6a --- /dev/null +++ b/python3/vimspector/developer.py @@ -0,0 +1,42 @@ +# 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 sys +import os + +from vimspector import install, utils + + +def SetUpDebugpy( wait=False, port=5678 ): + sys.path.insert( + 1, + os.path.join( install.GetGadgetDir( utils.GetVimspectorBase(), + install.GetOS() ), + 'debugpy', + 'build', + 'lib' ) ) + import debugpy + + exe = sys.executable + try: + # debugpy uses sys.executable (which is `vim`, so we hack it) + sys.executable = 'python3' + debugpy.listen( port ) + finally: + sys.executable = exe + + if wait: + debugpy.wait_for_client() From 8c4112cd1f4fbb310576efb14ba7eb82ee5da09e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 12 May 2020 19:29:17 +0100 Subject: [PATCH 169/609] breakpoints list: fix listing while debugging Also, open the quickfix list when listing breakpoints, and add a test --- python3/vimspector/breakpoints.py | 5 +- python3/vimspector/debug_session.py | 8 +++- tests/breakpoints.test.vim | 74 +++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index ea50fd6..b6a975d 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -82,7 +82,7 @@ class ProjectBreakpoints( object ): # FIXME: If the adapter type changes, we should probably forget this ? - def ListBreakpoints( self ): + def BreakpointsAsQuickFix( self ): # FIXME: Handling of breakpoints is a mess, split between _codeView and this # object. This makes no sense and should be centralised so that we don't # have this duplication and bug factory. @@ -115,7 +115,8 @@ class ProjectBreakpoints( object ): bp[ 'options' ] ), } ) - vim.eval( 'setqflist( {} )'.format( json.dumps( qf ) ) ) + return qf + def ClearBreakpoints( self ): # These are the user-entered breakpoints. diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 5d07f46..c434c5a 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -979,7 +979,13 @@ class DebugSession( object ): self._stackTraceView.OnStopped( event ) def ListBreakpoints( self ): - return self._breakpoints.ListBreakpoints() + if self._connection: + qf = self._codeView.BreakpointsAsQuickFix() + else: + qf = self._breakpoints.BreakpointsAsQuickFix() + + vim.eval( 'setqflist( {} )'.format( json.dumps( qf ) ) ) + vim.command( 'copen' ) def ToggleBreakpoint( self, options ): return self._breakpoints.ToggleBreakpoint( options ) diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index fe3b303..0d2fa6c 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -452,3 +452,77 @@ endfunction " %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 From 58a95402c4056ffdae2ae5d102dda60770b66aa0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 17 May 2020 19:25:12 +0100 Subject: [PATCH 170/609] Use gloabl for test args as i always use a basedir now --- compiler/vimspector_test.vim | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/vimspector_test.vim b/compiler/vimspector_test.vim index a347f3b..18ef9ae 100644 --- a/compiler/vimspector_test.vim +++ b/compiler/vimspector_test.vim @@ -90,7 +90,9 @@ function! s:RunTestUnderCursor() let l:cwd = getcwd() execute 'lcd ' . s:root_dir try - execute s:make_cmd . ' ' . get( b:, 'test_args', '' ) . ' ' . l:test_arg + execute s:make_cmd . ' ' + \ . get( g:, 'vimspector_test_args', '' ) . ' ' + \ . l:test_arg finally execute 'lcd ' . l:cwd endtry @@ -101,7 +103,9 @@ function! s:RunTest() let l:cwd = getcwd() execute 'lcd ' . s:root_dir try - execute s:make_cmd . ' ' . get( b:, 'test_args', '' ) . ' %:p:t' + execute s:make_cmd . ' ' + \ . get( g:, 'vimspector_test_args', '' ) + \ . ' %:p:t' finally execute 'lcd ' . l:cwd endtry @@ -112,7 +116,8 @@ function! s:RunAllTests() let l:cwd = getcwd() execute 'lcd ' . s:root_dir try - execute s:make_cmd . ' ' . get( b:, 'test_args', '' ) + execute s:make_cmd . ' ' + \ . get( g:, 'vimspector_test_args', '' ) finally execute 'lcd ' . l:cwd endtry From 5f11fe4e6e1b75a490e074854b568ac6ae75974f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 18 May 2020 19:24:34 +0100 Subject: [PATCH 171/609] Fix up vint errors --- azure-pipelines.yml | 2 +- compiler/vimspector_test.vim | 8 +++++--- tests/breakpoints.test.vim | 2 +- tests/lib/run_test.vim | 3 +++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f30e84d..5dc944a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -28,7 +28,7 @@ stages: - bash: pip3 install -r dev_requirements.txt displayName: "Install requirements" - - bash: $HOME/.local/bin/vint autoload/ plugin/ tests/ + - bash: $HOME/.local/bin/vint autoload/ compiler/ plugin/ tests/ displayName: "Run vint" - job: 'linux' diff --git a/compiler/vimspector_test.vim b/compiler/vimspector_test.vim index 18ef9ae..8ccd734 100644 --- a/compiler/vimspector_test.vim +++ b/compiler/vimspector_test.vim @@ -13,12 +13,14 @@ " See the License for the specific language governing permissions and " limitations under the License. +scriptencoding utf-8 + " Compiler plugin to help running vimspector tests -if exists("current_compiler") +if exists('current_compiler') finish endif -let current_compiler = "vimspector_test" +let current_compiler = 'vimspector_test' setlocal errorformat= \Found\ errors\ in\ %f:%.%#: @@ -80,7 +82,7 @@ function! s:RunTestUnderCursor() let l:test_func_name = s:GetCurrentFunction() if l:test_func_name ==# '' - echo "No test method found" + echo 'No test method found' return endif diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index 0d2fa6c..689ed4b 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -458,7 +458,7 @@ function! s:CheckQuickFixEntries( entries ) let i = 0 for entry in a:entries if i >= len( qf ) - call assert_report( "Expected more quickfix entries" ) + call assert_report( 'Expected more quickfix entries' ) endif for key in keys( entry ) call assert_equal( entry[ key ], diff --git a/tests/lib/run_test.vim b/tests/lib/run_test.vim index e03a1d4..fca26a6 100644 --- a/tests/lib/run_test.vim +++ b/tests/lib/run_test.vim @@ -46,13 +46,16 @@ call ch_logfile( 'debuglog', 'w' ) " For consistency run all tests with 'nocompatible' set. " This also enables use of line continuation. +" vint: Workaround for https://github.com/Vimjas/vint/issues/363 " vint: -ProhibitSetNoCompatible set nocompatible " vint: +ProhibitSetNoCompatible " Use utf-8 by default, instead of whatever the system default happens to be. " Individual tests can overrule this at the top of the file. +" vint: -ProhibitEncodingOptionAfterScriptEncoding set encoding=utf-8 +" vint: +ProhibitEncodingOptionAfterScriptEncoding " Avoid stopping at the "hit enter" prompt set nomore From 2437aace41f7a9af5de0033629fe5330817b85e1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 19 May 2020 21:53:02 +0100 Subject: [PATCH 172/609] Disable subprocess debugging by default in debugpy --- install_gadget.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/install_gadget.py b/install_gadget.py index 3e39ed1..a728694 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -125,7 +125,11 @@ GADGETS = { ], "name": "debugpy", "configuration": { - "python": sys.executable + "python": sys.executable, + # Don't debug into subprocesses, as this leads to problems (vimspector + # doesn't support the custom messages) + # https://github.com/puremourning/vimspector/issues/141 + "subProcess": False, } } }, From c9b1456759452052f42649f8a8e69e671cdebb60 Mon Sep 17 00:00:00 2001 From: Aaron Walker Date: Sun, 17 May 2020 16:53:35 -0400 Subject: [PATCH 173/609] add docker exec as an attach command --- docs/configuration.md | 16 ++++++--------- docs/schema/vimspector.schema.json | 21 +++++++++++++++++--- python3/vimspector/debug_session.py | 30 +++++++++++++++++++++-------- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 3ad67f3..c37f26b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -394,7 +394,9 @@ and connect to it using debugpy. The usage pattern is to hit ``, enter `host` (the host where your app runs), `account` (the account it runs under), and `port` (a port that will be opened on -the remote host). Vimspector then orchestrates the various tools to set you up. +the remote host). Vimspector also supports exec'ing into Docker run containers +with `container` (the container name or id your app is running in). +Vimspector then orchestrates the various tools to set you up. ```json @@ -403,9 +405,10 @@ the remote host). Vimspector then orchestrates the various tools to set you up. "python-remote": { "port": "${port}", "host": "${host}", + "container": "${container}", "launch": { "remote": { - "host": "${host}", // Remote host to ssh to (mandatory) + "host": "${host}", // Remote host to ssh to (mandatory if not using container) "account": "${account}", // User to connect as (optional) // Optional.... Manual additional arguments for ssh @@ -432,14 +435,7 @@ the remote host). Vimspector then orchestrates the various tools to set you up. }, "attach": { "remote": { - "host": "${host}", // Remote host to ssh to (mandatory) - "account": "${account}", // User to connect as (optional) - - // Optional.... Manual additional arguments for ssh - // "ssh": { - // "args": [ "-o", "StrictHostKeyChecking=no" ] - // }, - + "container": "${container}" // Command to get the PID of the process to attach (mandatory) "pidCommand": [ // diff --git a/docs/schema/vimspector.schema.json b/docs/schema/vimspector.schema.json index ace8589..c8af196 100644 --- a/docs/schema/vimspector.schema.json +++ b/docs/schema/vimspector.schema.json @@ -78,8 +78,19 @@ "properties": { "remote": { "type": "object", - "required": [ "host" ], - "description": "Configures how Vimspector will marshal remote debugging requests. When remote debugging, Vimspector will ssh to 'account'@'host' and run 'pidCommand', 'attachCommands', 'runCommands', etc. based on the 'remote-command' option in the debug configuration. If 'remote-command' is 'launch', it runs 'runCommand(s)', otherwise (it's 'attach') vimspector runs 'pidCommand', followed by 'attachCommand(s)'.Then it starts up the debug adapter with the debug configuration as normal. Usually this is configured with an 'attach' request (whether we remotely 'launched' or not). Once the initialization exchange is complete, Vimspector runs the optional 'initCompleteCommand' which can be used to force the application to break, e.g. by sending it SIGINT. This is required on some platforms which have buggy gdbservers (for example)", + "oneOf": [ + { + "required": [ + "host" + ] + }, + { + "required": [ + "container" + ] + } + ], + "description": "Configures how Vimspector will marshal remote debugging requests. When remote debugging, Vimspector will either ssh to 'account'@'host' or docker exec -it to 'container' and run 'pidCommand', 'attachCommands', 'runCommands', etc. based on the 'remote-command' option in the debug configuration. If 'remote-command' is 'launch', it runs 'runCommand(s)', otherwise (it's 'attach') vimspector runs 'pidCommand', followed by 'attachCommand(s)'.Then it starts up the debug adapter with the debug configuration as normal. Usually this is configured with an 'attach' request (whether we remotely 'launched' or not). Once the initialization exchange is complete, Vimspector runs the optional 'initCompleteCommand' which can be used to force the application to break, e.g. by sending it SIGINT. This is required on some platforms which have buggy gdbservers (for example)", "properties": { "account": { "type": "string", @@ -87,7 +98,11 @@ }, "host": { "type": "string", - "description": "Name of the remote host to connect to (via passwordless SSH). " + "description": "Name of the remote host to connect to (via passwordless SSH)." + }, + "container": { + "type": "string", + "description": "Name or container id of the docker run container to connect to (via docker exec -it)." }, "pidCommand": { "type": "array", diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index c434c5a..14dca64 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -613,13 +613,13 @@ class DebugSession( object ): # e.g. expand variables when we use them, not all at once. This would # remove the whole %PID% hack. remote = atttach_config[ 'remote' ] - ssh = self._GetSSHCommand( remote ) + attach_cmd = self._GetAttachCommand( remote ) # FIXME: Why does this not use self._GetCommands ? - cmd = ssh + remote[ 'pidCommand' ] + pid_cmd = attach_cmd + remote[ 'pidCommand' ] - self._logger.debug( 'Getting PID: %s', cmd ) - pid = subprocess.check_output( cmd ).decode( 'utf-8' ).strip() + self._logger.debug( 'Getting PID: %s', pid_cmd ) + pid = subprocess.check_output( pid_cmd ).decode( 'utf-8' ).strip() self._logger.debug( 'Got PID: %s', pid ) if not pid: @@ -628,7 +628,7 @@ class DebugSession( object ): return if 'initCompleteCommand' in remote: - initcmd = ssh + remote[ 'initCompleteCommand' ][ : ] + initcmd = attach_cmd + remote[ 'initCompleteCommand' ][ : ] for index, item in enumerate( initcmd ): initcmd[ index ] = item.replace( '%PID%', pid ) @@ -638,7 +638,7 @@ class DebugSession( object ): commands = self._GetCommands( remote, 'attach' ) for command in commands: - cmd = ssh + command[ : ] + cmd = attach_cmd + command[ : ] for index, item in enumerate( cmd ): cmd[ index ] = item.replace( '%PID%', pid ) @@ -665,11 +665,11 @@ class DebugSession( object ): if 'remote' in run_config: remote = run_config[ 'remote' ] - ssh = self._GetSSHCommand( remote ) + attach_cmd = self._GetAttachCommand( remote ) commands = self._GetCommands( remote, 'run' ) for index, command in enumerate( commands ): - cmd = ssh + command[ : ] + cmd = attach_cmd + command[ : ] full_cmd = [] for item in cmd: if isinstance( command_line, list ): @@ -694,6 +694,20 @@ class DebugSession( object ): return ssh + def _GetDockerCommand( self, remote ): + docker = [ 'docker exec -it' ] + remote.get( 'docker', {} ).get( 'args', [] ) + if 'container' not in remote: + raise ValueError( "Invalid container; must be string" ) + docker.append( remote[ 'container' ] ) + return docker + + def _GetAttachCommand( self, remote ): + if any( remote.get( 'ssh' ), remote.get( 'account' ), remote.get( 'host' ) ): + return self._GetSSHCommand( remote ) + elif any( remote.get( 'docker' ), remote.get( 'container' ) ): + return self._GetDockerCommand( remote ) + raise ValueError( 'Could not determine attach command' ) + def _GetCommands( self, remote, pfx ): commands = remote.get( pfx + 'Commands', None ) From 62b3070c50470a27146d9306989b5d0001b1fff8 Mon Sep 17 00:00:00 2001 From: Aaron Walker Date: Sun, 17 May 2020 17:39:04 -0400 Subject: [PATCH 174/609] lint --- python3/vimspector/debug_session.py | 32 +++++++++++++++++++---------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 14dca64..21d2492 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -613,10 +613,10 @@ class DebugSession( object ): # e.g. expand variables when we use them, not all at once. This would # remove the whole %PID% hack. remote = atttach_config[ 'remote' ] - attach_cmd = self._GetAttachCommand( remote ) + remote_exec_cmd = self._GetRemoteExecCommand( remote ) # FIXME: Why does this not use self._GetCommands ? - pid_cmd = attach_cmd + remote[ 'pidCommand' ] + pid_cmd = remote_exec_cmd + remote[ 'pidCommand' ] self._logger.debug( 'Getting PID: %s', pid_cmd ) pid = subprocess.check_output( pid_cmd ).decode( 'utf-8' ).strip() @@ -628,7 +628,7 @@ class DebugSession( object ): return if 'initCompleteCommand' in remote: - initcmd = attach_cmd + remote[ 'initCompleteCommand' ][ : ] + initcmd = remote_exec_cmd + remote[ 'initCompleteCommand' ][ : ] for index, item in enumerate( initcmd ): initcmd[ index ] = item.replace( '%PID%', pid ) @@ -638,7 +638,7 @@ class DebugSession( object ): commands = self._GetCommands( remote, 'attach' ) for command in commands: - cmd = attach_cmd + command[ : ] + cmd = remote_exec_cmd + command[ : ] for index, item in enumerate( cmd ): cmd[ index ] = item.replace( '%PID%', pid ) @@ -665,11 +665,11 @@ class DebugSession( object ): if 'remote' in run_config: remote = run_config[ 'remote' ] - attach_cmd = self._GetAttachCommand( remote ) + remote_exec_cmd = self._GetRemoteExecCommand( remote ) commands = self._GetCommands( remote, 'run' ) for index, command in enumerate( commands ): - cmd = attach_cmd + command[ : ] + cmd = remote_exec_cmd + command[ : ] full_cmd = [] for item in cmd: if isinstance( command_line, list ): @@ -695,16 +695,26 @@ class DebugSession( object ): return ssh def _GetDockerCommand( self, remote ): - docker = [ 'docker exec -it' ] + remote.get( 'docker', {} ).get( 'args', [] ) + args = remote.get( 'docker', {} ).get( 'args', [] ) + docker = [ 'docker exec -it' ] + args if 'container' not in remote: - raise ValueError( "Invalid container; must be string" ) + raise ValueError( 'Invalid container; must be string' ) docker.append( remote[ 'container' ] ) return docker - def _GetAttachCommand( self, remote ): - if any( remote.get( 'ssh' ), remote.get( 'account' ), remote.get( 'host' ) ): + def _GetRemoteExecCommand( self, remote ): + is_ssh_cmd = any( + remote.get( 'ssh' ), + remote.get( 'account' ), + remote.get( 'host' ), + ) + is_docker_cmd = any( + remote.get( 'docker' ), + remote.get( 'container' ), + ) + if is_ssh_cmd: return self._GetSSHCommand( remote ) - elif any( remote.get( 'docker' ), remote.get( 'container' ) ): + elif is_docker_cmd: return self._GetDockerCommand( remote ) raise ValueError( 'Could not determine attach command' ) From 0bf511debcc814b7bd8c735fcf8af4f85f432462 Mon Sep 17 00:00:00 2001 From: Aaron Walker Date: Sun, 17 May 2020 20:22:40 -0400 Subject: [PATCH 175/609] add docker docs --- docs/configuration.md | 121 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 5 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index c37f26b..7ac5991 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -383,8 +383,8 @@ research that. Vimspector's tools are intended to automate your existing process for setting this up rather than to offer batteries-included approach. Ultimately, all -vimspector is going to do is run your commands over SSH and co-ordinate with the -adapter. +vimspector is going to do is run your commands over SSH, or docker, and +co-ordinate with the adapter. ### Python (debugpy) Example @@ -405,7 +405,6 @@ Vimspector then orchestrates the various tools to set you up. "python-remote": { "port": "${port}", "host": "${host}", - "container": "${container}", "launch": { "remote": { "host": "${host}", // Remote host to ssh to (mandatory if not using container) @@ -435,7 +434,8 @@ Vimspector then orchestrates the various tools to set you up. }, "attach": { "remote": { - "container": "${container}" + "host": "${host}", // Remote host to ssh to (mandatory if not using container) + "account": "${account}", // User to connect as (optional) // Command to get the PID of the process to attach (mandatory) "pidCommand": [ // @@ -518,7 +518,7 @@ Vimspector then orchestrates the various tools to set you up. ### C-family (gdbserver) Example -This example uses vimspector to remotely luanch or attach to a binary using +This example uses vimspector to remotely launch or attach to a binary using `gdbserver` and then instructs vscode-cpptools to attach to that `gdbserver`. The appraoch is very similar to the above for python, just that we use gdbserver @@ -606,6 +606,117 @@ and have to tell cpptools a few more options. } ``` +### Docker Example + +This example uses vimspector to remotely launch or attach to a docker container +port. + +``` json +{ + "adapters": { + "python-remote": { + "port": "${port}", + "launch": { + "remote": { + "container": "${container}", // Docker container id or name to exec into to. + + // Command to launch the debugee and attach the debugger; + // %CMD% replaced with the remote-cmdLine configured in the launch + // configuration. (mandatory) + "launchCommmand": [ + "python", "-m", "debugpy", "--listen 0.0.0.0:${port}", + "%CMD%" + ] + + // Optional alternative to launchCommmand (if you need to run multiple + // commands) + // "launchCommmands": [ + // [ /* first command */ ], + // [ /* second command */ ] + // ] + + } + }, + "attach": { + "remote": { + "container": "${container}", // Docker container id or name to exec into. + // Command to get the PID of the process to attach (mandatory) + // This command gets appended to "docker exec ${container}" + "pidCommand": [ + // + // Remember taht you can use ${var} to ask for input. I use this to + // call a custom command to returm the PID for a named service, so + // here's an examle: + // + "sh", "-c", "pgrep", "-f ${filename}" + ], + + // Command to attach the debugger; %PID% replaced with output of + // pidCommand above (mandatory) + "attachCommand": [ + "sh", "-c", "python", "-m", "debugpy", "--listen 0.0.0.0:${port}", + "--pid", "%PID%" + ] + + // Optional alternative to attachCommand (if you need to run multiple + // commands) + // "attachCommands": [ + // [ /* first command */ ], + // [ /* second command */ ] + // ], + + // Optional.... useful with buggy gdbservers to kill -TRAP %PID% + // "initCompleteCommand": [ + // /* optional command to run after initialized */ + // ] + + } + } + } + }, + "configurations": { + "remote-launch": { + "adapter": "python-remote", + + "remote-request": "launch", + "remote-cmdLine": [ + "${RemoteRoot}/${fileBasename}", "*${args}" + ], + + "configuration": { + "request": "attach", + "pathMappings": [ + { + "localRoot": "${workspaceRoot}", + "remoteRoot": "${RemoteRoot}" + } + ] + } + }, + "remote-attach": { + "variables": { + // Just an example of how to specify a variable manually rather than + // vimspector asking for input from the user + "FileName": "${fileName}" + }, + + "adapter": "python-remote", + "remote-request": "attach", + + "configuration": { + "request": "attach", + "pathMappings": [ + { + "localRoot": "${workspaceRoot}", + "remoteRoot": "${RemoteRoot}" + } + ] + } + } + } +} +``` + ## Appendix: Configuration file format The configuration files are text files which must be UTF-8 encoded. They From 5e64b07e8dc31b382355459e8388e706443afe4e Mon Sep 17 00:00:00 2001 From: Aaron Walker Date: Sun, 17 May 2020 20:22:53 -0400 Subject: [PATCH 176/609] flake and update docker exec command --- python3/vimspector/debug_session.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 21d2492..456fd54 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -695,10 +695,10 @@ class DebugSession( object ): return ssh def _GetDockerCommand( self, remote ): - args = remote.get( 'docker', {} ).get( 'args', [] ) - docker = [ 'docker exec -it' ] + args if 'container' not in remote: raise ValueError( 'Invalid container; must be string' ) + + docker = [ 'docker exec' ] docker.append( remote[ 'container' ] ) return docker @@ -716,7 +716,7 @@ class DebugSession( object ): return self._GetSSHCommand( remote ) elif is_docker_cmd: return self._GetDockerCommand( remote ) - raise ValueError( 'Could not determine attach command' ) + raise ValueError( 'Could not determine remote exec command' ) def _GetCommands( self, remote, pfx ): From cd1b304d3052f0b982c1121d634540f6c558e2dc Mon Sep 17 00:00:00 2001 From: Aaron Walker Date: Mon, 18 May 2020 00:57:13 -0400 Subject: [PATCH 177/609] relint --- python3/vimspector/debug_session.py | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 456fd54..775a03c 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -696,27 +696,27 @@ class DebugSession( object ): def _GetDockerCommand( self, remote ): if 'container' not in remote: - raise ValueError( 'Invalid container; must be string' ) + raise ValueError( 'Invalid container; must be string' ) docker = [ 'docker exec' ] docker.append( remote[ 'container' ] ) return docker def _GetRemoteExecCommand( self, remote ): - is_ssh_cmd = any( - remote.get( 'ssh' ), - remote.get( 'account' ), - remote.get( 'host' ), - ) - is_docker_cmd = any( - remote.get( 'docker' ), - remote.get( 'container' ), - ) - if is_ssh_cmd: - return self._GetSSHCommand( remote ) - elif is_docker_cmd: - return self._GetDockerCommand( remote ) - raise ValueError( 'Could not determine remote exec command' ) + is_ssh_cmd = any( + remote.get( 'ssh' ), + remote.get( 'account' ), + remote.get( 'host' ), + ) + is_docker_cmd = any( + remote.get( 'docker' ), + remote.get( 'container' ), + ) + if is_ssh_cmd: + return self._GetSSHCommand( remote ) + elif is_docker_cmd: + return self._GetDockerCommand( remote ) + raise ValueError( 'Could not determine remote exec command' ) def _GetCommands( self, remote, pfx ): From 3d113eaec49cc9d1bc801a3e68d1929dd78b27ff Mon Sep 17 00:00:00 2001 From: Aaron Walker Date: Wed, 20 May 2020 00:44:32 -0400 Subject: [PATCH 178/609] comments --- docs/configuration.md | 10 +++++----- python3/vimspector/debug_session.py | 17 ++++------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 7ac5991..6c82584 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -419,7 +419,7 @@ Vimspector then orchestrates the various tools to set you up. // %CMD% replaced with the remote-cmdLine configured in the launch // configuration. (mandatory) "launchCommmand": [ - "python", "-m", "debugpy", "--listen 0.0.0.0:${port}", + "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}", "%CMD%" ] @@ -449,7 +449,7 @@ Vimspector then orchestrates the various tools to set you up. // Command to attach the debugger; %PID% replaced with output of // pidCommand above (mandatory) "attachCommand": [ - "python", "-m", "debugpy", "--listen 0.0.0.0:${port}", + "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}", "--pid", "%PID%" ] @@ -624,7 +624,7 @@ port. // %CMD% replaced with the remote-cmdLine configured in the launch // configuration. (mandatory) "launchCommmand": [ - "python", "-m", "debugpy", "--listen 0.0.0.0:${port}", + "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}", "%CMD%" ] @@ -648,13 +648,13 @@ port. // call a custom command to returm the PID for a named service, so // here's an examle: // - "sh", "-c", "pgrep", "-f ${filename}" + "sh", "-c", "pgrep", "-f", "${filename}" ], // Command to attach the debugger; %PID% replaced with output of // pidCommand above (mandatory) "attachCommand": [ - "sh", "-c", "python", "-m", "debugpy", "--listen 0.0.0.0:${port}", + "sh", "-c", "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}", "--pid", "%PID%" ] diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 775a03c..6e65f20 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -695,23 +695,14 @@ class DebugSession( object ): return ssh def _GetDockerCommand( self, remote ): - if 'container' not in remote: - raise ValueError( 'Invalid container; must be string' ) - - docker = [ 'docker exec' ] + docker = [ 'docker', 'exec' ] docker.append( remote[ 'container' ] ) return docker def _GetRemoteExecCommand( self, remote ): - is_ssh_cmd = any( - remote.get( 'ssh' ), - remote.get( 'account' ), - remote.get( 'host' ), - ) - is_docker_cmd = any( - remote.get( 'docker' ), - remote.get( 'container' ), - ) + is_ssh_cmd = any( key in remote for key in [ 'ssh','host', 'account' ] ) + is_docker_cmd = 'container' in remote + if is_ssh_cmd: return self._GetSSHCommand( remote ) elif is_docker_cmd: From ae2ba01c5b08eb0cd0f52d3ea21ee4727d96b8e7 Mon Sep 17 00:00:00 2001 From: Aaron Walker Date: Wed, 20 May 2020 00:53:02 -0400 Subject: [PATCH 179/609] lint --- python3/vimspector/debug_session.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 6e65f20..9bf5c15 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -700,7 +700,9 @@ class DebugSession( object ): return docker def _GetRemoteExecCommand( self, remote ): - is_ssh_cmd = any( key in remote for key in [ 'ssh','host', 'account' ] ) + is_ssh_cmd = any( key in remote for key in [ 'ssh', + 'host', + 'account', ] ) is_docker_cmd = 'container' in remote if is_ssh_cmd: From 7705d6020c0e97ae12a715909ee8f9f961caf420 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 24 May 2020 12:16:04 +0100 Subject: [PATCH 180/609] Fix traceback in neovim: vim.vars returns str --- python3/vimspector/utils.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index ac752ac..a12f3dc 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -566,9 +566,12 @@ def HideSplash( api_prefix, splash ): def GetVimspectorBase(): - try: - return vim.vars[ 'vimspector_base_dir' ].decode( 'utf-8' ) - except KeyError: + base = vim.vars.get( 'vimspector_base_dir' ) + if base is None: return os.path.abspath( os.path.join( os.path.dirname( __file__ ), '..', '..' ) ) + elif isinstance( base, bytes ): + return base.decode( 'utf-8' ) + else: + return base From f2994dfad7d4e704226bb941250de4276378b0b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2020 18:05:00 +0000 Subject: [PATCH 181/609] Bump activesupport from 6.0.2.1 to 6.0.3.1 in /docs Bumps [activesupport](https://github.com/rails/rails) from 6.0.2.1 to 6.0.3.1. - [Release notes](https://github.com/rails/rails/releases) - [Changelog](https://github.com/rails/rails/blob/v6.0.3.1/activesupport/CHANGELOG.md) - [Commits](https://github.com/rails/rails/compare/v6.0.2.1...v6.0.3.1) Signed-off-by: dependabot[bot] --- docs/Gemfile.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 7163701..416db83 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,12 +1,12 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.2.1) + activesupport (6.0.3.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - zeitwerk (~> 2.2) + zeitwerk (~> 2.2, >= 2.2.2) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) coffee-script (2.4.1) @@ -16,7 +16,7 @@ GEM colorator (1.1.0) commonmarker (0.17.13) ruby-enum (~> 0.5) - concurrent-ruby (1.1.5) + concurrent-ruby (1.1.6) dnsruby (1.61.3) addressable (~> 2.5) em-websocket (0.5.1) @@ -202,7 +202,7 @@ GEM jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.14.0) + minitest (5.14.1) multipart-post (2.1.1) nokogiri (1.10.7) mini_portile2 (~> 2.4.0) @@ -233,10 +233,10 @@ GEM thread_safe (0.3.6) typhoeus (1.3.1) ethon (>= 0.9.0) - tzinfo (1.2.6) + tzinfo (1.2.7) thread_safe (~> 0.1) unicode-display_width (1.6.1) - zeitwerk (2.2.2) + zeitwerk (2.3.0) PLATFORMS ruby From 46e8e4c56b204fd29ce0b37fc7fec45b9d25d1cd Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 28 May 2020 21:00:30 +0100 Subject: [PATCH 182/609] Note that Huge build of Vim is required --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6564459..be79575 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ Alternatively, you can clone the repo and select which gadgets are installed: Vimspector requires: * One of: - * Vim 8.2 compiled with Python 3.6 or later + * Vim 8.2 Huge build compiled with Python 3.6 or later * Neovim 0.4.3 with Python 3.6 or later (experimental) * One of the following operating systems: * Linux From d6a6184421a016f9728559995f2a872acd9ad7b1 Mon Sep 17 00:00:00 2001 From: Ray Xu Date: Thu, 4 Jun 2020 16:39:36 +1000 Subject: [PATCH 183/609] set GO111MODULE to off and the debug wont fail in go 1.11+ --- support/test/go/hello_world/.vimspector.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/support/test/go/hello_world/.vimspector.json b/support/test/go/hello_world/.vimspector.json index 7934b48..f320b1b 100644 --- a/support/test/go/hello_world/.vimspector.json +++ b/support/test/go/hello_world/.vimspector.json @@ -7,7 +7,8 @@ "program": "${workspaceRoot}/hello-world.go", "mode": "debug", "dlvToolPath": "$HOME/go/bin/dlv", - "trace": true + "trace": true, + "env": { "GO111MODULE": "off" } } } } From f217a1d9efb83e035b956f4d8272268540eb9eea Mon Sep 17 00:00:00 2001 From: jmckiern Date: Sat, 6 Jun 2020 15:39:25 +0100 Subject: [PATCH 184/609] Fix typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index be79575..5c3488f 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ For a tutorial and usage overview, take a look at the * [Console](#console) * [Closing debugger](#closing-debugger) * [Debug adapter configuration](#debug-adapter-configuration) - * [C, C , Rust, etc.](#c-c-rust-etc) + * [C, C++, Rust, etc.](#c-c-rust-etc) * [Python](#python) * [Legacy: vscode-python](#legacy-vscode-python) * [TCL](#tcl) From 7f625fee9f959160cd96c9601d5b6b7b65cec989 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 5 Jun 2020 21:17:01 +0100 Subject: [PATCH 185/609] Upgrade debugpy --- install_gadget.py | 6 +++--- support/test/python/simple_python/.vimspector.json | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index a728694..a8a293a 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -111,10 +111,10 @@ GADGETS = { 'url': 'https://github.com/microsoft/debugpy/archive/${file_name}' }, 'all': { - 'version': '1.0.0b8', - 'file_name': 'v1.0.0b8.zip', + 'version': '1.0.0b10', + 'file_name': 'v1.0.0b10.zip', 'checksum': - '07c208bcd2a18088757f3bcb6f3bfc68d42c16a504c716e35d34fbe6b010a7b3' + 'c2e9b29298ca4669b94afe0bb27bd9656155263d94757c5f26ec65fde4a85731' }, 'do': lambda name, root, gadget: InstallDebugpy( name, root, gadget ), 'adapters': { diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 028ff72..3d51d2c 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -14,7 +14,8 @@ "breakpoints": { "exception": { "raised": "N", - "uncaught": "" + "uncaught": "", + "userUnhandled": "" } } }, @@ -26,7 +27,8 @@ "breakpoints": { "exception": { "raised": "N", - "uncaught": "" + "uncaught": "", + "userUnhandled": "" } } }, @@ -43,7 +45,8 @@ "breakpoints": { "exception": { "raised": "N", - "uncaught": "" + "uncaught": "", + "userUnhandled": "" } } }, @@ -60,7 +63,8 @@ "breakpoints": { "exception": { "raised": "N", - "uncaught": "" + "uncaught": "", + "userUnhandled": "" } } }, From 6f3884253ca477b0c0dbdd1b95c9a3f649ed717d Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 27 Jun 2020 17:28:05 +0100 Subject: [PATCH 186/609] Windows support for cpptools --- README.md | 40 +++++++++---- install_gadget.py | 60 +++++++++++++------ .../cpp/simple_c_program/.vimspector.json | 28 +-------- 3 files changed, 73 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 5c3488f..92592bd 100644 --- a/README.md +++ b/README.md @@ -729,31 +729,29 @@ Current tested with the following debug adapters. Example `.vimspector.json` (works with both `vscode-cpptools` and `lldb-vscode`. For `lldb-vscode` replace the name of the adapter with `lldb-vscode`: +* vscode-cpptools Linux/MacOS: + ``` { "configurations": { - ": Launch": { + "Launch": { "adapter": "vscode-cpptools", "configuration": { - "name": "", - "type": "cppdbg", "request": "launch", "program": "", "args": [ ... ], "cwd": "", "environment": [ ... ], "externalConsole": true, - "MIMode": "lldb" + "MIMode": "" } }, - ": Attach": { + "Attach": { "adapter": "vscode-cpptools", "configuration": { - "name": ": Attach", - "type": "cppdbg", "request": "attach", "program": "", - "MIMode": "lldb" + "MIMode": "" } } ... @@ -761,14 +759,36 @@ For `lldb-vscode` replace the name of the adapter with `lldb-vscode`: } ``` +* vscode-cpptools Windows + +***NOTE FOR WINDOWS USERS:*** You need to install `gdb.exe`. I recommend using +`scoop install gdb`. Vimspector cannot use the visual studio debugger due to +licensing. + +``` +{ + "configurations": { + "Launch": { + "adapter": "vscode-cpptools", + "configuration": { + "request": "launch", + "program": "", + "stopAtEntry": true + } + } + } +} +``` + +* lldb-vscode (MacOS) + An alternative is to to use `lldb-vscode`, which comes with llvm. Here's how: -* Install llvm with HomeBrew: `brew install llvm` +* Install llvm (e.g. with HomeBrew: `brew install llvm`) * Create a file named `/path/to/vimspector/gadgets/macos/.gadgets.d/lldb-vscode.json`: ```json - { "adapters": { "lldb-vscode": { diff --git a/install_gadget.py b/install_gadget.py index a8a293a..74f02cd 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -48,11 +48,29 @@ GADGETS = { 'do': lambda name, root, gadget: InstallCppTools( name, root, gadget ), 'all': { 'version': '0.27.0', + "adapters": { + "vscode-cpptools": { + "name": "cppdbg", + "command": [ + "${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7" + ], + "attach": { + "pidProperty": "processId", + "pidSelect": "ask" + }, + "configuration": { + "type": "cppdbg", + "args": [], + "cwd": "${workspaceRoot}", + "environment": [], + } + }, + }, }, 'linux': { 'file_name': 'cpptools-linux.vsix', 'checksum': - '3695202e1e75a03de18049323b66d868165123f26151f8c974a480eaf0205435' + '3695202e1e75a03de18049323b66d868165123f26151f8c974a480eaf0205435', }, 'macos': { 'file_name': 'cpptools-osx.vsix', @@ -61,24 +79,27 @@ GADGETS = { }, 'windows': { 'file_name': 'cpptools-win32.vsix', - 'checksum': None, - }, - "adapters": { - "vscode-cpptools": { - "name": "cppdbg", - "command": [ - "${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7" - ], - "attach": { - "pidProperty": "processId", - "pidSelect": "ask" + 'checksum': + 'aa294368ed16d48c59e49c8000e146eae5a19ad07b654efed5db8ec93b24229e', + "adapters": { + "vscode-cpptools": { + "name": "cppdbg", + "command": [ + "${gadgetDir}/vscode-cpptools/debugAdapters/bin/OpenDebugAD7.exe" + ], + "attach": { + "pidProperty": "processId", + "pidSelect": "ask" + }, + "configuration": { + "type": "cppdbg", + "args": [], + "cwd": "${workspaceRoot}", + "environment": [], + "MIMode": "gdb", + "MIDebuggerPath": "gdb.exe" + } }, - "configuration": { - "type": "cppdbg", - "args": [], - "cwd": "${workspaceRoot}", - "environment": [], - } }, }, }, @@ -531,6 +552,9 @@ def InstallGagdet( name, gadget, failed, all_adapters ): else: installer.MakeExtensionSymlink( vimspector_base, name, root ) + # Allow per-OS adapter overrides. v already did that for us... + all_adapters.update( v.get( 'adapters', {} ) ) + # Add any other "all" adapters all_adapters.update( gadget.get( 'adapters', {} ) ) print( "Done installing {}".format( name ) ) diff --git a/support/test/cpp/simple_c_program/.vimspector.json b/support/test/cpp/simple_c_program/.vimspector.json index 5f1ce6c..6623908 100644 --- a/support/test/cpp/simple_c_program/.vimspector.json +++ b/support/test/cpp/simple_c_program/.vimspector.json @@ -1,38 +1,12 @@ { - "adapters": { - "cppdbg": { - "name": "cppdbg", - "command": [ "$HOME/.vscode/extensions/ms-vscode.cpptools-0.20.1/debugAdapters/OpenDebugAD7" ], - "attach": { - "pidProperty": "processId", - "pidSelect": "ask" - } - } - }, "configurations": { "simple_c_program - Launch": { - "adapter": "cppdbg", + "adapter": "vscode-cpptools", "configuration": { - "name": "ms Launch", - "type": "cppdbg", "request": "launch", "program": "${workspaceRoot}/test", - "args": [], - "cwd": "${workspaceRoot}", - "environment": [], - "MIMode": "lldb", "stopAtEntry": true } - }, - "simple_c_program - Attach": { - "adapter": "cppdbg", - "configuration": { - "name": "(lldb) Attach", - "type": "cppdbg", - "request": "attach", - "program": "${workspaceRoot}/test", - "MIMode": "lldb" - } } } } From 8e2313bf855018cf5e518c2784a5bd757a5ca105 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 28 Jun 2020 16:47:54 +0100 Subject: [PATCH 187/609] Correct status of windows support --- .github/ISSUE_TEMPLATE/bug_report.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index f049d29..1aea572 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -35,8 +35,9 @@ NOTE: NeoVim is supported only on a best-effort basis. Please check the README for limitations of neovim. Don't be offended if I ask you to reproduce issues in Vim. -NOTE: Windows is not supported. There is a branch with windows support, which -you can contribute to, rather than opening an issue. +NOTE: Windows support is experimental and best-efrort only. If you find an +issue related to Windows or windows-isms, consider sending a PR or +discussing on Gitter rather than raising an issue. * Version of Vimspector: (e.g. output of `git rev-parse HEAD` if cloned or the name of the tarball used to install otherwise) From ec009a1332909e639632f01d73c7644ccf3d2e8b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 4 Jul 2020 10:33:05 +0100 Subject: [PATCH 188/609] UPdate debugpy --- install_gadget.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 74f02cd..90ad7f4 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -132,10 +132,10 @@ GADGETS = { 'url': 'https://github.com/microsoft/debugpy/archive/${file_name}' }, 'all': { - 'version': '1.0.0b10', - 'file_name': 'v1.0.0b10.zip', + 'version': '1.0.0b12', + 'file_name': 'v1.0.0b12.zip', 'checksum': - 'c2e9b29298ca4669b94afe0bb27bd9656155263d94757c5f26ec65fde4a85731' + '210632bba2221fbb841c9785a615258819ceec401d1abdbeb5f2326f12cc72a1' }, 'do': lambda name, root, gadget: InstallDebugpy( name, root, gadget ), 'adapters': { From b322a2e89cd95c95b9610713f84a74449ea5bd9d Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 3 Jul 2020 13:18:17 +0100 Subject: [PATCH 189/609] Allow 'channel' based transports to also start a job --- autoload/vimspector/internal/channel.vim | 28 +++++++++++++++++++----- autoload/vimspector/internal/job.vim | 4 ++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/autoload/vimspector/internal/channel.vim b/autoload/vimspector/internal/channel.vim index 05cf26c..0100f08 100644 --- a/autoload/vimspector/internal/channel.vim +++ b/autoload/vimspector/internal/channel.vim @@ -39,6 +39,21 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort return v:false endif + " If we _also_ have a command line, then start the actual job. This allows for + " servers which start up and listen on some port + if has_key( a:config, 'command' ) + let s:job = job_start( a:config[ 'command' ], + \ { + \ 'in_mode': 'raw', + \ 'out_mode': 'raw', + \ 'err_mode': 'raw', + \ 'stoponexit': 'term', + \ 'env': a:config[ 'env' ], + \ 'cwd': a:config[ 'cwd' ], + \ } + \ ) + endif + let l:addr = get( a:config, 'host', 'localhost' ) . ':' . a:config[ 'port' ] echo 'Connecting to ' . l:addr . '... (waiting fo up to 10 seconds)' @@ -72,11 +87,7 @@ EOF endfunction function! vimspector#internal#channel#StopDebugSession() abort - if !exists( 's:ch' ) - return - endif - - if ch_status( s:ch ) ==# 'open' + if exists( 's:ch' ) && ch_status( s:ch ) ==# 'open' " channel is open, close it and trigger the callback. The callback is _not_ " triggered when manually calling ch_close. if we get here and the channel " is not open, then we there is a _OnClose callback waiting for us, so do @@ -84,10 +95,15 @@ function! vimspector#internal#channel#StopDebugSession() abort call ch_close( s:ch ) call s:_OnClose( s:ch ) endif + + if exists( 's:job' ) && job_status( s:job ) ==# 'run' + call job_stop( s:job, 'kill' ) + unlet s:job + endif endfunction function! vimspector#internal#channel#Reset() abort - if exists( 's:ch' ) + if exists( 's:ch' ) || exists( 's:job' ) call vimspector#internal#channel#StopDebugSession() endif endfunction diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index 88c63cd..a93a004 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -128,8 +128,8 @@ function! vimspector#internal#job#StopDebugSession() abort endif if job_status( s:job ) ==# 'run' - echom 'Terminating job' - redraw + echom 'Terminating job' + redraw call job_stop( s:job, 'kill' ) endif endfunction From 7848629a1c51c6b033b694c0b64f7b175a908d28 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 3 Jul 2020 13:18:50 +0100 Subject: [PATCH 190/609] Add simple rust program tested with CodeLLDB --- support/test/rust/vimspector_test/.gitignore | 1 + .../rust/vimspector_test/.vimspector.json | 35 +++++++++++++++++++ support/test/rust/vimspector_test/Cargo.lock | 5 +++ support/test/rust/vimspector_test/Cargo.toml | 9 +++++ support/test/rust/vimspector_test/src/main.rs | 4 +++ 5 files changed, 54 insertions(+) create mode 100644 support/test/rust/vimspector_test/.gitignore create mode 100644 support/test/rust/vimspector_test/.vimspector.json create mode 100644 support/test/rust/vimspector_test/Cargo.lock create mode 100644 support/test/rust/vimspector_test/Cargo.toml create mode 100644 support/test/rust/vimspector_test/src/main.rs diff --git a/support/test/rust/vimspector_test/.gitignore b/support/test/rust/vimspector_test/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/support/test/rust/vimspector_test/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/support/test/rust/vimspector_test/.vimspector.json b/support/test/rust/vimspector_test/.vimspector.json new file mode 100644 index 0000000..f921d7a --- /dev/null +++ b/support/test/rust/vimspector_test/.vimspector.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json", + "configurations": { + "Run - CodeLLDB": { + "adapter": { + "variables": { + "CodeLLDB": "$HOME/.vscode/extensions/vadimcn.vscode-lldb-1.5.3", + "build": { + "shell": [ "cargo", "build" ], + "cwd": "${workspaceRoot}" + } + }, + "command": [ + "${CodeLLDB}/adapter/codelldb", + "--port", "${port}" + ], + "port": "${port}", + "configuration": { + "type": "lldb", + "name": "lldb", + "cargo": {} + } + }, + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/target/debug/vimspector_test", + "args": [], + "cwd": "${workspaceRoot}", + "env": {}, + "terminal": "integrated", + "stopOnEntry": true + } + } + } +} diff --git a/support/test/rust/vimspector_test/Cargo.lock b/support/test/rust/vimspector_test/Cargo.lock new file mode 100644 index 0000000..504f9c0 --- /dev/null +++ b/support/test/rust/vimspector_test/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "vimspector_test" +version = "0.1.0" diff --git a/support/test/rust/vimspector_test/Cargo.toml b/support/test/rust/vimspector_test/Cargo.toml new file mode 100644 index 0000000..2440a01 --- /dev/null +++ b/support/test/rust/vimspector_test/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "vimspector_test" +version = "0.1.0" +authors = ["Ben Jackson "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/support/test/rust/vimspector_test/src/main.rs b/support/test/rust/vimspector_test/src/main.rs new file mode 100644 index 0000000..f6fc72d --- /dev/null +++ b/support/test/rust/vimspector_test/src/main.rs @@ -0,0 +1,4 @@ +fn main() { + let s = "World!"; + println!("Hello, {}!", s); +} From 99faf76dccb6c2aa43bc31ee2af9ebdb5e67b261 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 3 Jul 2020 19:51:21 +0100 Subject: [PATCH 191/609] Fix traceback when a request is rejected --- python3/vimspector/debug_adapter_connection.py | 2 +- python3/vimspector/debug_session.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/python3/vimspector/debug_adapter_connection.py b/python3/vimspector/debug_adapter_connection.py index 295b83d..63e0c89 100644 --- a/python3/vimspector/debug_adapter_connection.py +++ b/python3/vimspector/debug_adapter_connection.py @@ -267,7 +267,7 @@ class DebugAdapterConnection( object ): if request.failure_handler: request.failure_handler( reason, message ) elif 'OnFailure' in dir( self._handler ): - self._handler.OnFailure( reason, message ) + self._handler.OnFailure( reason, request.msg, message ) else: utils.UserMessage( 'Request failed: {0}'.format( reason ) ) elif message[ 'type' ] == 'event': diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 9bf5c15..e3e1be4 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -776,9 +776,10 @@ class DebugSession( object ): } ) - def OnFailure( self, reason, message ): - msg = "Request for '{}' failed: {}".format( message[ 'command' ], - reason ) + def OnFailure( self, reason, request, message ): + msg = "Request for '{}' failed: {}\nResponse: {}".format( request, + reason, + message ) self._outputView.Print( 'server', msg ) def _Launch( self ): From 32c7960b5fd8f9ce7fe73661977ad6bf7c1dace4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 6 Jul 2020 10:34:16 +0100 Subject: [PATCH 192/609] Support channel-based adapters with jobs in neovim Again, the neovim API is lacking - we have to hack around our own retry loop (where vim offers 'waittime'). Note neovim documentation says that it returns 0 on connection failure, but actually it throws an error, so we catch that. There are probably a ton of other problems with error handling, but i'll rely on user testing/feedback for that. --- autoload/vimspector/internal/channel.vim | 8 ++- autoload/vimspector/internal/neochannel.vim | 65 ++++++++++++++++----- autoload/vimspector/internal/neojob.vim | 8 +-- 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/autoload/vimspector/internal/channel.vim b/autoload/vimspector/internal/channel.vim index 0100f08..62e91ec 100644 --- a/autoload/vimspector/internal/channel.vim +++ b/autoload/vimspector/internal/channel.vim @@ -67,7 +67,7 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort \ ) if ch_status( s:ch ) !=# 'open' - echom 'Unable to connect to debug adapter' + echom 'Unable to connect to' l:addr redraw return v:false endif @@ -96,8 +96,10 @@ function! vimspector#internal#channel#StopDebugSession() abort call s:_OnClose( s:ch ) endif - if exists( 's:job' ) && job_status( s:job ) ==# 'run' - call job_stop( s:job, 'kill' ) + if exists( 's:job' ) + if job_status( s:job ) ==# 'run' + call job_stop( s:job, 'kill' ) + endif unlet s:job endif endfunction diff --git a/autoload/vimspector/internal/neochannel.vim b/autoload/vimspector/internal/neochannel.vim index e692c50..511d4e5 100644 --- a/autoload/vimspector/internal/neochannel.vim +++ b/autoload/vimspector/internal/neochannel.vim @@ -34,20 +34,50 @@ endfunction function! vimspector#internal#neochannel#StartDebugSession( config ) abort if exists( 's:ch' ) - echom 'Not starging: Channel is already running' + echom 'Not starting: Channel is already running' redraw return v:false endif - let l:addr = get( a:config, 'host', 'localhost' ) . ':' . a:config[ 'port' ] - - let s:ch = sockconnect( 'tcp', addr, { 'on_data': funcref( 's:_OnEvent' ) } ) - if s:ch <= 0 - unlet s:ch - return v:false + " If we _also_ have a command line, then start the actual job. This allows for + " servers which start up and listen on some port + if has_key( a:config, 'command' ) + let old_env={} + try + let old_env = vimspector#internal#neoterm#PrepareEnvironment( + \ a:config[ 'env' ] ) + let s:job = jobstart( a:config[ 'command' ], + \ { + \ 'cwd': a:config[ 'cwd' ], + \ 'env': a:config[ 'env' ], + \ } + \ ) + finally + call vimspector#internal#neoterm#ResetEnvironment( a:config[ 'env' ], + \ old_env ) + endtry endif - return v:true + let l:addr = get( a:config, 'host', 'localhost' ) . ':' . a:config[ 'port' ] + + let attempt = 1 + while attempt <= 10 + echo 'Connecting to ' . l:addr . '... (attempt' attempt 'of 10)' + try + let s:ch = sockconnect( 'tcp', + \ addr, + \ { 'on_data': funcref( 's:_OnEvent' ) } ) + redraw + return v:true + catch /connection refused/ + sleep 1 + endtry + let attempt += 1 + endwhile + + echom 'Unable to connect to' l:addr 'after 10 attempts' + redraw + return v:false endfunction function! vimspector#internal#neochannel#Send( msg ) abort @@ -62,16 +92,19 @@ function! vimspector#internal#neochannel#Send( msg ) abort endfunction function! vimspector#internal#neochannel#StopDebugSession() abort - if !exists( 's:ch' ) - echom "Not stopping session: Channel doesn't exist" - redraw - return + if exists( 's:ch' ) + call chanclose( s:ch ) + " It doesn't look like we get a callback after chanclos. Who knows if we + " will subsequently receive data callbacks. + call s:_OnEvent( s:ch, [ '' ], 'data' ) endif - call chanclose( s:ch ) - " It doesn't look like we get a callback after chanclos. Who knows if we will - " subsequently receive data callbacks. - call s:_OnEvent( s:ch, [ '' ], 'data' ) + if exists( 's:job' ) + if vimspector#internal#neojob#JobIsRunning( s:job ) + call jobstop( s:job ) + endif + unlet s:job + endif endfunction function! vimspector#internal#neochannel#Reset() abort diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index 4c7abbb..35c409a 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -70,8 +70,8 @@ function! vimspector#internal#neojob#StartDebugSession( config ) abort return v:true endfunction -function! s:JobIsRunning( job ) abort - return jobwait( [ s:job ], 0 )[ 0 ] == -1 +function! vimspector#internal#neojob#JobIsRunning( job ) abort + return jobwait( [ a:job ], 0 )[ 0 ] == -1 endfunction function! vimspector#internal#neojob#Send( msg ) abort @@ -81,7 +81,7 @@ function! vimspector#internal#neojob#Send( msg ) abort return 0 endif - if !s:JobIsRunning( s:job ) + if !vimspector#internal#neojob#JobIsRunning( s:job ) echom "Can't send message: Job is not running" redraw return 0 @@ -96,7 +96,7 @@ function! vimspector#internal#neojob#StopDebugSession() abort return endif - if s:JobIsRunning( s:job ) + if vimspector#internal#neojob#JobIsRunning( s:job ) echom 'Terminating job' redraw call jobstop( s:job ) From 0e56146726333ed82fd3882454250d7559ff50b4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 6 Jul 2020 11:24:54 +0100 Subject: [PATCH 193/609] Add installer support for CodeLLDB --- install_gadget.py | 69 ++++++++++++++++++- .../rust/vimspector_test/.vimspector.json | 27 +------- 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 90ad7f4..11144e8 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -412,9 +412,76 @@ GADGETS = { }, }, }, + 'CodeLLDB': { + 'language': 'rust', + 'enabled': False, + 'download': { + 'url': 'https://github.com/vadimcn/vscode-lldb/releases/download/' + '${version}/${file_name}', + }, + 'all': { + 'version': 'v1.5.3', + }, + 'macos': { + 'file_name': 'codelldb-x86_64-darwin.vsix', + 'checksum': + '7505bc1cdfcfd1cb981e2996aec62d63577440709bac31dcadb41a3b4b44631a', + 'make_executable': [ + 'adapter/codelldb', + 'lldb/bin/debugserver', + 'lldb/bin/lldb', + 'lldb/bin/lldb-argdumper', + ], + }, + 'linux': { + 'file_name': 'codelldb-x86_64-linux.vsix', + 'checksum': + 'ce7efc3e94d775368e5942a02bf5c326b6809a0b4c389f79ffa6a8f6f6b72139', + 'make_executable': [ + 'adapter/codelldb', + 'lldb/bin/lldb', + 'lldb/bin/lldb-server', + 'lldb/bin/lldb-argdumper', + ], + }, + 'windows': { + 'file_name': 'codelldb-x86_64-windows.vsix', + 'checksum': + '', + 'make_executable': [] + }, + 'adapters': { + 'CodeLLDB': { + 'name': 'CodeLLDB', + 'type': 'CodeLLDB', + "command": [ + "${gadgetDir}/CodeLLDB/adapter/codelldb", + "--port", "${port}" + ], + "port": "${port}", + "configuration": { + "type": "lldb", + "name": "lldb", + "cargo": {}, + "args": [], + "cwd": "${workspaceRoot}", + "env": {}, + "terminal": "integrated", + } + }, + }, + }, } +def InstallGeneric( name, root, gadget ): + extension = os.path.join( root, 'extension' ) + for f in gadget.get( 'make_executable', [] ): + installer.MakeExecutable( os.path.join( extension, f ) ) + + installer.MakeExtensionSymlink( vimspector_base, name, root ) + + def InstallCppTools( name, root, gadget ): extension = os.path.join( root, 'extension' ) @@ -550,7 +617,7 @@ def InstallGagdet( name, gadget, failed, all_adapters ): if 'do' in gadget: gadget[ 'do' ]( name, root, v ) else: - installer.MakeExtensionSymlink( vimspector_base, name, root ) + InstallGeneric( name, root, v ) # Allow per-OS adapter overrides. v already did that for us... all_adapters.update( v.get( 'adapters', {} ) ) diff --git a/support/test/rust/vimspector_test/.vimspector.json b/support/test/rust/vimspector_test/.vimspector.json index f921d7a..6918e47 100644 --- a/support/test/rust/vimspector_test/.vimspector.json +++ b/support/test/rust/vimspector_test/.vimspector.json @@ -2,33 +2,10 @@ "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json", "configurations": { "Run - CodeLLDB": { - "adapter": { - "variables": { - "CodeLLDB": "$HOME/.vscode/extensions/vadimcn.vscode-lldb-1.5.3", - "build": { - "shell": [ "cargo", "build" ], - "cwd": "${workspaceRoot}" - } - }, - "command": [ - "${CodeLLDB}/adapter/codelldb", - "--port", "${port}" - ], - "port": "${port}", - "configuration": { - "type": "lldb", - "name": "lldb", - "cargo": {} - } - }, + "adapter": "CodeLLDB", "configuration": { "request": "launch", - "program": "${workspaceRoot}/target/debug/vimspector_test", - "args": [], - "cwd": "${workspaceRoot}", - "env": {}, - "terminal": "integrated", - "stopOnEntry": true + "program": "${workspaceRoot}/target/debug/vimspector_test" } } } From a2ae1c288e81fbe098d979660fdda5cce17ef60f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 6 Jul 2020 11:28:24 +0100 Subject: [PATCH 194/609] Documentation for codelldb/rust --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 92592bd..724e16b 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,7 @@ categorised as follows: | Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | | C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | | C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | +| Rust (CodeLLDB) | Experimental | `--force-enable-rust` | CodeLLDB | Python 3 | | Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | For other languages, you'll need some other way to install the gadget. @@ -1222,6 +1223,32 @@ For the launch arguments, see the See [this issue](https://github.com/puremourning/vimspector/issues/3) for more background. +## Rust + +Rust is supported with any gdb/lldb-based debugger. So it works fine with +`vscode-cpptools` and `lldb-vscode` above. However, support for rust is best in +[`CodeLLDB`](https://github.com/vadimcn/vscode-lldb#features). + +* `./install_gadget.py --force-enable-rust` +* Example: `support/test/rust/vimspector_test` + +```json +{ + "configurations": { + "launch": { + "adapter": "CodeLLDB", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/target/debug/vimspector_test" + } + } + } +} +``` + +* Docs: https://github.com/vadimcn/vscode-lldb/blob/master/MANUAL.md + + ## Other servers * Java - vscode-javac. This works, but is not as functional as Java Debug From b4868392692475b7e5a1a293afd21113441418b1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 6 Jul 2020 11:30:45 +0100 Subject: [PATCH 195/609] Update README TOC and restructure --- README.md | 99 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 724e16b..2a7befe 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ 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) - * [Motivation](#motivation) * [Features and Usage](#features-and-usage) * [Supported debugging features](#supported-debugging-features) * [Supported languages:](#supported-languages) @@ -42,8 +41,12 @@ For a tutorial and usage overview, take a look at the * [Console](#console) * [Closing debugger](#closing-debugger) * [Debug adapter configuration](#debug-adapter-configuration) - * [C, C++, Rust, etc.](#c-c-rust-etc) + * [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) @@ -55,60 +58,18 @@ For a tutorial and usage overview, take a look at the * [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) - + -# Motivation - -A message from the author about the motivation for this plugin: - -> Many development environments have a built-in debugger. I spend an inordinate -> amount of my time in Vim. I do all my development in Vim and I have even -> customised my workflows for building code, running tests etc. -> -> For many years I have observed myself, friends and colleagues have been -> writing `printf`, `puts`, `print`, etc. debugging statements in all sorts of -> files simply because there is no _easy_ way to run a debugger for _whatever_ -> language we happen to be developing in. -> -> I truly believe that interactive, graphical debugging environments are the -> best way to understand and reason about both unfamiliar and familiar code, and -> that the lack of ready, simple access to a debugger is a huge hidden -> productivity hole for many. -> -> Don't get me wrong, I know there are literally millions of developers out -> there that are more than competent at developing without a graphical debugger, -> but I maintain that if they had the ability to _just press a key_ and jump -> into the debugger, it would be faster and more enjoyable that just cerebral -> code comprehension. -> -> I created Vimsepctor because I find changing tools frustrating. `gdb` for c++, -> `pdb` for python, etc. Each has its own syntax. Each its own lexicon. Each its -> own foibles. -> -> I designed the configuration system in such a way that the configuration can -> be committed to source control so that it _just works_ for any of your -> colleagues, friends, collaborators or complete strangers. -> -> I made remote debugging a first-class feature because that's a primary use -> case for me in my job. -> -> With Vimspector I can _just hit ``_ in all of the languages I develop in -> and debug locally or remotely using the exact same workflow, mappings and UI. -> I have integrated this with my Vim in such a way that I can hit a button and -> _run the test under the cursor in Vimspector_. This kind of integration has -> massively improved my workflow and productivity. It's even made the process -> of learning a new codebase... fun. -> -> \- Ben Jackson, Creator. - # Features and Usage The plugin is a capable Vim graphical debugger for multiple languages. @@ -1302,6 +1263,50 @@ hi link jsonCommentError Comment hi link jsonComment Comment ``` +# Motivation + +A message from the author about the motivation for this plugin: + +> Many development environments have a built-in debugger. I spend an inordinate +> amount of my time in Vim. I do all my development in Vim and I have even +> customised my workflows for building code, running tests etc. +> +> For many years I have observed myself, friends and colleagues have been +> writing `printf`, `puts`, `print`, etc. debugging statements in all sorts of +> files simply because there is no _easy_ way to run a debugger for _whatever_ +> language we happen to be developing in. +> +> I truly believe that interactive, graphical debugging environments are the +> best way to understand and reason about both unfamiliar and familiar code, and +> that the lack of ready, simple access to a debugger is a huge hidden +> productivity hole for many. +> +> Don't get me wrong, I know there are literally millions of developers out +> there that are more than competent at developing without a graphical debugger, +> but I maintain that if they had the ability to _just press a key_ and jump +> into the debugger, it would be faster and more enjoyable that just cerebral +> code comprehension. +> +> I created Vimsepctor because I find changing tools frustrating. `gdb` for c++, +> `pdb` for python, etc. Each has its own syntax. Each its own lexicon. Each its +> own foibles. +> +> I designed the configuration system in such a way that the configuration can +> be committed to source control so that it _just works_ for any of your +> colleagues, friends, collaborators or complete strangers. +> +> I made remote debugging a first-class feature because that's a primary use +> case for me in my job. +> +> With Vimspector I can _just hit ``_ in all of the languages I develop in +> and debug locally or remotely using the exact same workflow, mappings and UI. +> I have integrated this with my Vim in such a way that I can hit a button and +> _run the test under the cursor in Vimspector_. This kind of integration has +> massively improved my workflow and productivity. It's even made the process +> of learning a new codebase... fun. +> +> \- Ben Jackson, Creator. + # License [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) From 2117a0515d338a699a0c98a83f5aa835f71a66c1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 7 Jul 2020 20:22:01 +0100 Subject: [PATCH 196/609] Remove hacky use of dicts and magic keys for variables --- python3/vimspector/variables.py | 267 ++++++++++++++++++++------------ 1 file changed, 166 insertions(+), 101 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index ff6ee60..d337960 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import abc import vim import logging from collections import namedtuple @@ -23,6 +24,62 @@ from vimspector import utils View = namedtuple( 'View', [ 'win', 'lines', 'draw' ] ) + +class VariablesContainer: + def __init__( self ): + self.variables = None + self.expanded = None + + def IsCollapsedByUser( self ): + return self.expanded is False + + def IsExpandedByUser( self ): + return self.expanded is True + + def ShouldDrawDrillDown( self ): + return self.IsExpandedByUser() and self.variables is not None + + def IsExpandable( self ): + return self.VariablesReference() > 0 + + @abc.abstractmethod + def VariablesReference( self ): + assert False + + +class Scope( VariablesContainer ): + def __init__( self, scope ): + super().__init__() + self.scope = scope + + def VariablesReference( self ): + return self.scope.get( 'variablesReference', 0 ) + + +class WatchResult( VariablesContainer ): + def __init__( self, result ): + super().__init__() + self.result = result + + def VariablesReference( self ): + return self.result.get( 'variablesReference', 0 ) + + +class Variable( VariablesContainer ): + def __init__( self, variable ): + super().__init__() + self.variable = variable + + def VariablesReference( self ): + return self.variable.get( 'variablesReference', 0 ) + + +class Watch: + def __init__( self, expression ): + self.expression = expression + self.result = None + + class VariablesView( object ): def __init__( self, connection, variables_win, watches_win ): self._logger = logging.getLogger( __name__ ) @@ -38,18 +95,10 @@ class VariablesView( object ): vim.command( 'nnoremap :call vimspector#ExpandVariable()' ) - # This is actually the tree (scopes are alwyas the root) - # it's just a list of DAP scope dicts, with one magic key (_variables) - # _variables is a list of DAP variable with the same magic key - # - # If _variables is present, then we have requested and should display the - # children. Otherwise, we haven't or shouldn't. + # List of current scopes of type Scope self._scopes = [] - # This is similar to scopes, but the top level is an "expression" (request) - # containing a special '_result' key which is the response. The response - # structure con contain _variables and is handled identically to the scopes - # above. It also has a special _line key which is where we printed it (last) + # List of current Watches of type Watch self._watches = [] # Allows us to hit to expand/collapse variables @@ -120,31 +169,44 @@ class VariablesView( object ): def LoadScopes( self, frame ): def scopes_consumer( message ): - old_scopes = self._scopes - self._scopes = [] + names = set() + for scope_body in message[ 'body' ][ 'scopes' ]: + # Find it in the scopes list + found = False + for index, s in enumerate( self._scopes ): + if s.scope[ 'name' ] == scope_body[ 'name' ]: + found = True + s.scope = scope_body + scope = s + break - for i, scope in enumerate( message[ 'body' ][ 'scopes' ] ): - if ( i < len( old_scopes ) and - old_scopes[ i ][ 'name' ] == scope[ 'name' ] ): - scope[ '_expanded' ] = old_scopes[ i ].get( '_expanded', False ) - scope[ '_old_variables' ] = old_scopes[ i ].get( '_variables', [] ) - elif not scope.get( 'expensive' ): - # Expand any non-expensive scope unless manually collapsed - scope[ '_expanded' ] = True - else: - scope[ '_expanded' ] = False + if not found: + scope = Scope( scope_body ) + self._scopes.append( scope ) - self._scopes.append( scope ) - if scope[ '_expanded' ]: + names.add( scope.scope[ 'name' ] ) + + if not scope.scope[ 'expensive' ] and not scope.IsCollapsedByUser(): + # Expand any non-expensive scope which is not manually collapsed + scope.expanded = True + + if scope.IsExpandedByUser(): self._connection.DoRequest( partial( self._ConsumeVariables, self._DrawScopes, scope ), { 'command': 'variables', 'arguments': { - 'variablesReference': scope[ 'variablesReference' ] + 'variablesReference': scope.scope[ 'variablesReference' ] }, } ) + marked = [] + for index, s in enumerate( self._scopes ): + if s.scope[ 'name' ] not in names: + marked.append( index ) + for m in marked: + self._scopes.pop( m ) + self._DrawScopes() self._connection.DoRequest( scopes_consumer, { @@ -162,7 +224,7 @@ class VariablesView( object ): if frame: watch[ 'frameId' ] = frame[ 'id' ] - self._watches.append( watch ) + self._watches.append( Watch( watch ) ) self.EvaluateWatches() def DeleteWatch( self ): @@ -174,9 +236,9 @@ class VariablesView( object ): best_index = -1 for index, watch in enumerate( self._watches ): - if ( '_line' in watch - and watch[ '_line' ] <= current_line - and watch[ '_line' ] > best_index ): + if ( watch.line is not None + and watch.line <= current_line + and watch.line > best_index ): best_index = index if best_index >= 0: @@ -192,30 +254,23 @@ class VariablesView( object ): self._connection.DoRequest( partial( self._UpdateWatchExpression, watch ), { 'command': 'evaluate', - 'arguments': watch, + 'arguments': watch.expression, } ) def _UpdateWatchExpression( self, watch, message ): - old_result = None - if '_result' in watch: - old_result = watch[ '_result' ] + if watch.result is not None: + watch.result.result = message[ 'body' ] + else: + watch.result = WatchResult( message[ 'body' ] ) - result = message[ 'body' ] - watch[ '_result' ] = result - - if old_result: - if '_expanded' in old_result: - result[ '_expanded' ] = old_result[ '_expanded' ] - result[ '_old_variables' ] = old_result.get( '_variables', [] ) - - if ( result.get( 'variablesReference', 0 ) > 0 and - result.get( '_expanded', False ) ): + if ( watch.result.IsExpandable() and + watch.result.IsExpandedByUser() ): self._connection.DoRequest( partial( self._ConsumeVariables, self._watch.draw, - result ), { + watch.result.result ), { 'command': 'variables', 'arguments': { - 'variablesReference': result[ 'variablesReference' ] + 'variablesReference': watch.result.result[ 'variablesReference' ] }, } ) @@ -235,23 +290,22 @@ class VariablesView( object ): variable = view.lines[ current_line ] - if '_variables' in variable: + if variable.expanded: # Collapse - del variable[ '_variables' ] - variable[ '_expanded' ] = False + variable.expanded = False view.draw() return - if variable.get( 'variablesReference', 0 ) <= 0: + if not variable.IsExpandable(): return - variable[ '_expanded' ] = True + variable.expanded = True self._connection.DoRequest( partial( self._ConsumeVariables, view.draw, variable ), { 'command': 'variables', 'arguments': { - 'variablesReference': variable[ 'variablesReference' ] + 'variablesReference': variable.VariablesReference() }, } ) @@ -261,15 +315,16 @@ class VariablesView( object ): view.win.buffer, '{indent}{icon} {name} ({type_}): {value}'.format( indent = ' ' * indent, - icon = '+' if ( variable.get( 'variablesReference', 0 ) > 0 and - '_variables' not in variable ) else '-', - name = variable[ 'name' ], - type_ = variable.get( 'type', '' ), - value = variable.get( 'value', '' ) ).split( '\n' ) ) + icon = '+' if ( variable.IsExpandable() + and not variable.IsExpandedByUser() ) else '-', + name = variable.variable[ 'name' ], + type_ = variable.variable.get( 'type', '' ), + value = variable.variable.get( 'value', + '' ) ).split( '\n' ) ) view.lines[ line ] = variable - if '_variables' in variable: - self._DrawVariables( view, variable[ '_variables' ], indent + 2 ) + if variable.ShouldDrawDrillDown(): + self._DrawVariables( view, variable.variables, indent + 2 ) def _DrawScopes( self ): # FIXME: The drawing is dumb and draws from scratch every time. This is @@ -295,74 +350,84 @@ class VariablesView( object ): utils.AppendToBuffer( self._watch.win.buffer, 'Watches: ----' ) for watch in self._watches: line = utils.AppendToBuffer( self._watch.win.buffer, - 'Expression: ' + watch[ 'expression' ] ) - watch[ '_line' ] = line + 'Expression: ' + + watch.expression[ 'expression' ] ) + watch.line = line self._DrawWatchResult( 2, watch ) def _DrawScope( self, indent, scope ): - icon = '+' if ( scope.get( 'variablesReference', 0 ) > 0 and - '_variables' not in scope ) else '-' + icon = '+' if scope.IsExpandable() and not scope.IsExpandedByUser() else '-' line = utils.AppendToBuffer( self._vars.win.buffer, - '{0}{1} Scope: {2}'.format( ' ' * indent, - icon, - scope[ 'name' ] ) ) + '{0}{1} Scope: {2}'.format( + ' ' * indent, + icon, + scope.scope[ 'name' ] ) ) self._vars.lines[ line ] = scope - if '_variables' in scope: + if scope.ShouldDrawDrillDown(): indent += 2 - self._DrawVariables( self._vars, scope[ '_variables' ], indent ) + self._DrawVariables( self._vars, scope.variables, indent ) def _DrawWatchResult( self, indent, watch ): - if '_result' not in watch: + if not watch.result: return - result = watch[ '_result' ] + icon = '+' if ( watch.result.IsExpandable() and + not watch.result.IsExpandedByUser() ) else '-' - icon = '+' if ( result.get( 'variablesReference', 0 ) > 0 and - '_variables' not in result ) else '-' - - result_str = result[ 'result' ] + result_str = watch.result.result[ 'result' ] if result_str is None: - result_str = 'null' + result_str = '' line = '{0}{1} Result: {2}'.format( ' ' * indent, icon, result_str ) line = utils.AppendToBuffer( self._watch.win.buffer, line.split( '\n' ) ) - self._watch.lines[ line ] = result + self._watch.lines[ line ] = watch.result - if '_variables' in result: + if watch.result.ShouldDrawDrillDown(): indent = 4 - self._DrawVariables( self._watch, result[ '_variables' ], indent ) + self._DrawVariables( self._watch, watch.result.variables, indent ) def _ConsumeVariables( self, draw, parent, message ): - for variable in message[ 'body' ][ 'variables' ]: - if '_variables' not in parent: - parent[ '_variables' ] = [] + names = set() - parent[ '_variables' ].append( variable ) - - # If the variable was previously expanded, expand it again - for index, v in enumerate( parent.get( '_old_variables', [] ) ): - if v[ 'name' ] == variable[ 'name' ]: - if ( v.get( '_expanded', False ) and - variable.get( 'variablesReference', 0 ) > 0 ): - - variable[ '_expanded' ] = True - variable[ '_old_variables' ] = v.get( '_variables', [] ) - - self._connection.DoRequest( partial( self._ConsumeVariables, - draw, - variable ), { - 'command': 'variables', - 'arguments': { - 'variablesReference': variable[ 'variablesReference' ] - }, - } ) + for variable_body in message[ 'body' ][ 'variables' ]: + if parent.variables is None: + parent.variables = [] + # Find the variable in parent + found = False + for index, v in enumerate( parent.variables ): + if v.variable[ 'name' ] == variable_body[ 'name' ]: + v.variable = variable_body + variable = v + found = True break - if '_old_variables' in parent: - del parent[ '_old_variables' ] + if not found: + variable = Variable( variable_body ) + parent.variables.append( variable ) + + names.add( variable.variable[ 'name' ] ) + + if variable.IsExpandable() and variable.IsExpandedByUser(): + self._connection.DoRequest( partial( self._ConsumeVariables, + draw, + variable ), { + 'command': 'variables', + 'arguments': { + 'variablesReference': variable.VariablesReference() + }, + } ) + + # Delete any variables from parent not seen in this pass + if parent.variables: + marked = [] + for index, v in enumerate( parent.variables ): + if v.variable[ 'name' ] not in names: + marked.append( index ) + for m in marked: + parent.variables.pop( m ) draw() From 39212f4ffcbafd5fdcfaa074cff5bb4e54bbff58 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 7 Jul 2020 22:18:43 +0100 Subject: [PATCH 197/609] Add some type hints (requires python 3.6) and update flake8 --- dev_requirements.txt | 4 ++-- python3/vimspector/variables.py | 40 ++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index a5a04ea..e65675c 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,4 +1,4 @@ -flake8==3.7.7 +flake8==3.8.3 vim-vint==0.3.21 -flake8-comprehensions +flake8-comprehensions==3.2.3 flake8-ycm>= 0.1.0 diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index d337960..b0bb80f 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -18,6 +18,7 @@ import vim import logging from collections import namedtuple from functools import partial +import typing from vimspector import utils @@ -25,10 +26,17 @@ View = namedtuple( 'View', [ 'win', 'lines', 'draw' ] ) -class VariablesContainer: +class Expandable: + """Base for anything which might contain a hierarchy of values represented by + a 'variablesReference' to be resolved by the 'variables' request. Records the + current state expanded/collapsed. Implementations just implement + VariablesReference to get the variables.""" def __init__( self ): - self.variables = None - self.expanded = None + self.variables: typing.List[ 'Variable' ] = None + # None is Falsy and represents collapsed _by default_. WHen set to False, + # this means the user explicitly collapsed it. When True, the user expanded + # it. + self.expanded: bool = None def IsCollapsedByUser( self ): return self.expanded is False @@ -47,8 +55,9 @@ class VariablesContainer: assert False -class Scope( VariablesContainer ): - def __init__( self, scope ): +class Scope( Expandable ): + """Holds an expandable scope (a DAP scope dict), with expand/collapse state""" + def __init__( self, scope: dict ): super().__init__() self.scope = scope @@ -56,8 +65,9 @@ class Scope( VariablesContainer ): return self.scope.get( 'variablesReference', 0 ) -class WatchResult( VariablesContainer ): - def __init__( self, result ): +class WatchResult( Expandable ): + """Holds the result of a Watch expression with expand/collapse.""" + def __init__( self, result: dict ): super().__init__() self.result = result @@ -65,8 +75,9 @@ class WatchResult( VariablesContainer ): return self.result.get( 'variablesReference', 0 ) -class Variable( VariablesContainer ): - def __init__( self, variable ): +class Variable( Expandable ): + """Holds one level of an expanded value tree. Also itself expandable.""" + def __init__( self, variable: dict ): super().__init__() self.variable = variable @@ -75,7 +86,10 @@ class Variable( VariablesContainer ): class Watch: - def __init__( self, expression ): + """Holds a user watch expression (DAP request) and the result (WatchResult)""" + def __init__( self, expression: dict ): + self.result: WatchResult + self.expression = expression self.result = None @@ -96,10 +110,10 @@ class VariablesView( object ): 'nnoremap :call vimspector#ExpandVariable()' ) # List of current scopes of type Scope - self._scopes = [] + self._scopes: typing.List[ 'Scope' ] = [] # List of current Watches of type Watch - self._watches = [] + self._watches: typing.List[ 'Watch' ] = [] # Allows us to hit to expand/collapse variables with utils.LetCurrentWindow( self._watch.win ): @@ -257,7 +271,7 @@ class VariablesView( object ): 'arguments': watch.expression, } ) - def _UpdateWatchExpression( self, watch, message ): + def _UpdateWatchExpression( self, watch: Watch, message: dict ): if watch.result is not None: watch.result.result = message[ 'body' ] else: From 4b7fc83ce3b5267dd83f0041caa47d1f47c8be4e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 7 Jul 2020 22:44:47 +0100 Subject: [PATCH 198/609] Add a marker when a variable value changes --- python3/vimspector/variables.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index b0bb80f..a865844 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -64,26 +64,44 @@ class Scope( Expandable ): def VariablesReference( self ): return self.scope.get( 'variablesReference', 0 ) + def Update( self, scope ): + self.scope = scope + class WatchResult( Expandable ): """Holds the result of a Watch expression with expand/collapse.""" def __init__( self, result: dict ): super().__init__() self.result = result + self.changed = False def VariablesReference( self ): return self.result.get( 'variablesReference', 0 ) + def Update( self, result ): + self.changed = False + if self.result[ 'result' ] != result[ 'result' ]: + self.changed = True + self.result = result + class Variable( Expandable ): """Holds one level of an expanded value tree. Also itself expandable.""" def __init__( self, variable: dict ): super().__init__() self.variable = variable + self.changed = False def VariablesReference( self ): return self.variable.get( 'variablesReference', 0 ) + def Update( self, variable ): + self.changed = False + if self.variable[ 'value' ] != variable[ 'value' ]: + self.changed = True + self.variable = variable + + class Watch: """Holds a user watch expression (DAP request) and the result (WatchResult)""" @@ -273,7 +291,7 @@ class VariablesView( object ): def _UpdateWatchExpression( self, watch: Watch, message: dict ): if watch.result is not None: - watch.result.result = message[ 'body' ] + watch.result.Update( message[ 'body' ] ) else: watch.result = WatchResult( message[ 'body' ] ) @@ -327,8 +345,9 @@ class VariablesView( object ): for variable in variables: line = utils.AppendToBuffer( view.win.buffer, - '{indent}{icon} {name} ({type_}): {value}'.format( + '{indent}{marker}{icon} {name} ({type_}): {value}'.format( indent = ' ' * indent, + marker = '*' if variable.changed else ' ', icon = '+' if ( variable.IsExpandable() and not variable.IsExpandedByUser() ) else '-', name = variable.variable[ 'name' ], @@ -394,7 +413,12 @@ class VariablesView( object ): if result_str is None: result_str = '' - line = '{0}{1} Result: {2}'.format( ' ' * indent, icon, result_str ) + line = '{indent}{marker}{icon} Result: {result}'.format( + indent = ' ' * indent, + marker = '*' if watch.result.changed else ' ', + icon = icon, + result = result_str ) + line = utils.AppendToBuffer( self._watch.win.buffer, line.split( '\n' ) ) self._watch.lines[ line ] = watch.result @@ -413,7 +437,7 @@ class VariablesView( object ): found = False for index, v in enumerate( parent.variables ): if v.variable[ 'name' ] == variable_body[ 'name' ]: - v.variable = variable_body + v.Update( variable_body ) variable = v found = True break From c43f16e2882801df1c0676afda68dac26bb0eaf7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 7 Jul 2020 22:45:12 +0100 Subject: [PATCH 199/609] FixUp: remove elements in reverse order to avoid invalidating indices --- python3/vimspector/variables.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index a865844..baf4d5b 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -236,7 +236,7 @@ class VariablesView( object ): for index, s in enumerate( self._scopes ): if s.scope[ 'name' ] not in names: marked.append( index ) - for m in marked: + for m in reversed( marked ): self._scopes.pop( m ) self._DrawScopes() @@ -464,7 +464,7 @@ class VariablesView( object ): for index, v in enumerate( parent.variables ): if v.variable[ 'name' ] not in names: marked.append( index ) - for m in marked: + for m in reversed( marked ): parent.variables.pop( m ) draw() From 8143992345f4b13aa360343069bcac762255c6f3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jul 2020 11:37:24 +0100 Subject: [PATCH 200/609] Restore spacing (don't waste a whole column); re-use one of the indent columns for the marker --- python3/vimspector/variables.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index baf4d5b..a29251f 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -342,11 +342,13 @@ class VariablesView( object ): } ) def _DrawVariables( self, view, variables, indent ): + assert indent > 0 for variable in variables: line = utils.AppendToBuffer( view.win.buffer, '{indent}{marker}{icon} {name} ({type_}): {value}'.format( - indent = ' ' * indent, + # We borrow 1 space of indent to draw the change marker + indent = ' ' * ( indent - 1 ), marker = '*' if variable.changed else ' ', icon = '+' if ( variable.IsExpandable() and not variable.IsExpandedByUser() ) else '-', @@ -406,6 +408,7 @@ class VariablesView( object ): if not watch.result: return + assert indent > 0 icon = '+' if ( watch.result.IsExpandable() and not watch.result.IsExpandedByUser() ) else '-' @@ -414,7 +417,8 @@ class VariablesView( object ): result_str = '' line = '{indent}{marker}{icon} Result: {result}'.format( - indent = ' ' * indent, + # We borrow 1 space of indent to draw the change marker + indent = ' ' * ( indent - 1 ), marker = '*' if watch.result.changed else ' ', icon = icon, result = result_str ) From e24d0a465994e34b13bc6ff6413027d158851da8 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jul 2020 11:45:20 +0100 Subject: [PATCH 201/609] Simplify extracting the result from a WatchResult --- python3/vimspector/variables.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index a29251f..20ae0d0 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -412,16 +412,12 @@ class VariablesView( object ): icon = '+' if ( watch.result.IsExpandable() and not watch.result.IsExpandedByUser() ) else '-' - result_str = watch.result.result[ 'result' ] - if result_str is None: - result_str = '' - line = '{indent}{marker}{icon} Result: {result}'.format( # We borrow 1 space of indent to draw the change marker indent = ' ' * ( indent - 1 ), marker = '*' if watch.result.changed else ' ', icon = icon, - result = result_str ) + result = watch.result.result.get( 'result', '' ) ) line = utils.AppendToBuffer( self._watch.win.buffer, line.split( '\n' ) ) self._watch.lines[ line ] = watch.result From 402071065f6c8d561f771dc10a35cdef507ef275 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jul 2020 13:19:14 +0100 Subject: [PATCH 202/609] Update tclpro; add a test --- install_gadget.py | 26 ++++++++++++++++-- support/test/tcl/.tclpro/extensions/test.pdx | 5 ++++ support/test/tcl/.vimspector.json | 11 ++++++++ support/test/tcl/test | 28 ++++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 support/test/tcl/.tclpro/extensions/test.pdx create mode 100644 support/test/tcl/.vimspector.json create mode 100644 support/test/tcl/test diff --git a/install_gadget.py b/install_gadget.py index 11144e8..627152e 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -208,9 +208,31 @@ GADGETS = { 'language': 'tcl', 'repo': { 'url': 'https://github.com/puremourning/TclProDebug', - 'ref': 'f5c56b7067661ce84e205765060224076569ae0e', # master 26/10/2019 + 'ref': 'master' + }, + 'do': lambda name, root, gadget: InstallTclProDebug( name, root, gadget ), + 'adapters': { + "tclpro": { + "name": "tclpro", + "type": "tclpro", + "command": [ + "${gadgetDir}/tclpro/bin/debugadapter" + ], + "attach": { + "pidSelect": "none" + }, + "configuration": { + "target": "${file}", + "args": [ "*${args}" ], + "tclsh": "tclsh", + "cwd": "${workspaceRoot}", + "extensionDirs": [ + "${workspaceRoot}/.tclpro/extensions", + "${HOME}/.tclpro/extensions", + ] + } + } }, - 'do': lambda name, root, gadget: InstallTclProDebug( name, root, gadget ) }, 'netcoredbg': { 'language': 'csharp', diff --git a/support/test/tcl/.tclpro/extensions/test.pdx b/support/test/tcl/.tclpro/extensions/test.pdx new file mode 100644 index 0000000..5cc928c --- /dev/null +++ b/support/test/tcl/.tclpro/extensions/test.pdx @@ -0,0 +1,5 @@ +instrument::addExtension 2.0 {test} + +instrument::addCommand Wrap { parseBody } + +# vim: ft=tcl diff --git a/support/test/tcl/.vimspector.json b/support/test/tcl/.vimspector.json new file mode 100644 index 0000000..6959778 --- /dev/null +++ b/support/test/tcl/.vimspector.json @@ -0,0 +1,11 @@ +{ + "configurations": { + "Run Current Script": { + "adapter": "tclpro", + "configuration": { + "request": "launch", + "target": "${file}" + } + } + } +} diff --git a/support/test/tcl/test b/support/test/tcl/test new file mode 100644 index 0000000..e77a486 --- /dev/null +++ b/support/test/tcl/test @@ -0,0 +1,28 @@ +#!/usr/bin/env tclsh + +set SCALAR g +array set ARRAY {key1 value1 key2 value2} + +proc Wrap { body } { + uplevel 1 $body +} + +proc Main {} { + global SCALAR + set prefix "VAR: " + Wrap { + puts $SCALAR + global ARRAY + puts [array names ARRAY] + + set vars [list] + foreach n [array names ::env] { + set prefix "ENVVAR: $n = " + puts "$prefix $::env($n)" + lappend vars $n + } + } + puts $vars +} + +Main From 50b8b0103cb6516ab87bec322675b34bc9613fb6 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jul 2020 13:20:37 +0100 Subject: [PATCH 203/609] Fix sequence of scopes/vars to match the server --- python3/vimspector/variables.py | 48 ++++++++++++--------------------- tests/breakpoints.test.vim | 2 +- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 20ae0d0..bb5b252 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -35,7 +35,7 @@ class Expandable: self.variables: typing.List[ 'Variable' ] = None # None is Falsy and represents collapsed _by default_. WHen set to False, # this means the user explicitly collapsed it. When True, the user expanded - # it. + # it (or we expanded it by default). self.expanded: bool = None def IsCollapsedByUser( self ): @@ -73,7 +73,8 @@ class WatchResult( Expandable ): def __init__( self, result: dict ): super().__init__() self.result = result - self.changed = False + # A new watch result is marked as changed + self.changed = True def VariablesReference( self ): return self.result.get( 'variablesReference', 0 ) @@ -90,7 +91,8 @@ class Variable( Expandable ): def __init__( self, variable: dict ): super().__init__() self.variable = variable - self.changed = False + # A new variable appearing is marked as changed + self.changed = True def VariablesReference( self ): return self.variable.get( 'variablesReference', 0 ) @@ -201,22 +203,22 @@ class VariablesView( object ): def LoadScopes( self, frame ): def scopes_consumer( message ): - names = set() + new_scopes = [] for scope_body in message[ 'body' ][ 'scopes' ]: # Find it in the scopes list found = False for index, s in enumerate( self._scopes ): if s.scope[ 'name' ] == scope_body[ 'name' ]: found = True - s.scope = scope_body scope = s break if not found: scope = Scope( scope_body ) - self._scopes.append( scope ) + else: + scope.Update( scope_body ) - names.add( scope.scope[ 'name' ] ) + new_scopes.append( scope ) if not scope.scope[ 'expensive' ] and not scope.IsCollapsedByUser(): # Expand any non-expensive scope which is not manually collapsed @@ -232,13 +234,7 @@ class VariablesView( object ): }, } ) - marked = [] - for index, s in enumerate( self._scopes ): - if s.scope[ 'name' ] not in names: - marked.append( index ) - for m in reversed( marked ): - self._scopes.pop( m ) - + self._scopes = new_scopes self._DrawScopes() self._connection.DoRequest( scopes_consumer, { @@ -364,8 +360,7 @@ class VariablesView( object ): def _DrawScopes( self ): # FIXME: The drawing is dumb and draws from scratch every time. This is # simple and works and makes sure the line-map is always correct. - # However it is really inefficient, and makes it so that expanded results - # are collapsed on every step. + # However it is pretty inefficient. self._vars.lines.clear() with utils.RestoreCursorPosition(): with utils.ModifiableScratchBuffer( self._vars.win.buffer ): @@ -376,8 +371,7 @@ class VariablesView( object ): def _DrawWatches( self ): # FIXME: The drawing is dumb and draws from scratch every time. This is # simple and works and makes sure the line-map is always correct. - # However it is really inefficient, and makes it so that expanded results - # are collapsed on every step. + # However it is pretty inefficient. self._watch.lines.clear() with utils.RestoreCursorPosition(): with utils.ModifiableScratchBuffer( self._watch.win.buffer ): @@ -427,8 +421,7 @@ class VariablesView( object ): self._DrawVariables( self._watch, watch.result.variables, indent ) def _ConsumeVariables( self, draw, parent, message ): - names = set() - + new_variables = [] for variable_body in message[ 'body' ][ 'variables' ]: if parent.variables is None: parent.variables = [] @@ -437,16 +430,16 @@ class VariablesView( object ): found = False for index, v in enumerate( parent.variables ): if v.variable[ 'name' ] == variable_body[ 'name' ]: - v.Update( variable_body ) variable = v found = True break if not found: variable = Variable( variable_body ) - parent.variables.append( variable ) + else: + variable.Update( variable_body ) - names.add( variable.variable[ 'name' ] ) + new_variables.append( variable ) if variable.IsExpandable() and variable.IsExpandedByUser(): self._connection.DoRequest( partial( self._ConsumeVariables, @@ -458,14 +451,7 @@ class VariablesView( object ): }, } ) - # Delete any variables from parent not seen in this pass - if parent.variables: - marked = [] - for index, v in enumerate( parent.variables ): - if v.variable[ 'name' ] not in names: - marked.append( index ) - for m in reversed( marked ): - parent.variables.pop( m ) + parent.variables = new_variables draw() diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index 689ed4b..acc10e2 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -394,7 +394,7 @@ function! Test_Conditional_Line_Breakpoint_Hit() " window (also, tests the watch window!) call vimspector#AddWatch( 'i' ) call WaitForAssert( {-> - \ assert_equal( [ ' - Result: 2' ], + \ assert_equal( [ ' *- Result: 2' ], \ getbufline( 'vimspector.Watches', '$' ) ) \ } ) From 044804ca20b75fceddea01d193401eb57e02bd25 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Jul 2020 21:22:28 +0100 Subject: [PATCH 204/609] Calculate variables on-demand; add an unused-local-port variable-function --- python3/vimspector/debug_session.py | 40 +++++++++++------- python3/vimspector/utils.py | 63 +++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 32 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index e3e1be4..244a0c4 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -177,43 +177,53 @@ class DebugSession( object ): return [ '', '' ] return os.path.splitext( p ) - self._variables = { + variables = { 'dollar': '$', # HACK. Hote '$$' also works. 'workspaceRoot': self._workspace_root, 'workspaceFolder': self._workspace_root, - 'gadgetDir': install.GetGadgetDir( VIMSPECTOR_HOME, install.GetOS() ), 'file': current_file, - 'relativeFile': relpath( current_file, self._workspace_root ), - 'fileBasename': os.path.basename( current_file ), + } + + calculus = { + 'gadgetDir': lambda: install.GetGadgetDir( VIMSPECTOR_HOME, + install.GetOS() ), + 'relativeFile': lambda: relpath( current_file, + self._workspace_root ), + 'fileBasename': lambda: os.path.basename( current_file ), 'fileBasenameNoExtension': - splitext( os.path.basename( current_file ) )[ 0 ], - 'fileDirname': os.path.dirname( current_file ), - 'fileExtname': splitext( os.path.basename( current_file ) )[ 1 ], + lambda: splitext( os.path.basename( current_file ) )[ 0 ], + 'fileDirname': lambda: os.path.dirname( current_file ), + 'fileExtname': lambda: splitext( os.path.basename( current_file ) )[ 1 ], # NOTE: this is the window-local cwd for the current window, *not* Vim's # working directory. - 'cwd': os.getcwd(), + 'cwd': os.getcwd, + 'unusedLocalPort': utils.GetUnusedLocalPort, } # Pretend that vars passed to the launch command were typed in by the user # (they may have been in theory) USER_CHOICES.update( launch_variables ) - self._variables.update( launch_variables ) + variables.update( launch_variables ) - self._variables.update( + variables.update( utils.ParseVariables( adapter.get( 'variables', {} ), - self._variables, + variables, + calculus, USER_CHOICES ) ) - self._variables.update( + variables.update( utils.ParseVariables( configuration.get( 'variables', {} ), - self._variables, + variables, + calculus, USER_CHOICES ) ) utils.ExpandReferencesInDict( configuration, - self._variables, + variables, + calculus, USER_CHOICES ) utils.ExpandReferencesInDict( adapter, - self._variables, + variables, + calculus, USER_CHOICES ) if not adapter: diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index a12f3dc..d00c8d7 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -345,9 +345,9 @@ def IsCurrent( window, buf ): return vim.current.window == window and vim.current.window.buffer == buf -def ExpandReferencesInObject( obj, mapping, user_choices ): +def ExpandReferencesInObject( obj, mapping, calculus, user_choices ): if isinstance( obj, dict ): - ExpandReferencesInDict( obj, mapping, user_choices ) + ExpandReferencesInDict( obj, mapping, calculus, user_choices ) elif isinstance( obj, list ): j_offset = 0 obj_copy = list( obj ) @@ -360,6 +360,7 @@ def ExpandReferencesInObject( obj, mapping, user_choices ): # *${something} - expand list in place value = ExpandReferencesInString( obj_copy[ i ][ 1: ], mapping, + calculus, user_choices ) obj.pop( j ) j_offset -= 1 @@ -369,14 +370,18 @@ def ExpandReferencesInObject( obj, mapping, user_choices ): else: obj[ j ] = ExpandReferencesInObject( obj_copy[ i ], mapping, + calculus, user_choices ) elif isinstance( obj, str ): - obj = ExpandReferencesInString( obj, mapping, user_choices ) + obj = ExpandReferencesInString( obj, mapping, calculus, user_choices ) return obj -def ExpandReferencesInString( orig_s, mapping, user_choices ): +def ExpandReferencesInString( orig_s, + mapping, + calculus, + user_choices ): s = os.path.expanduser( orig_s ) s = os.path.expandvars( s ) @@ -393,15 +398,19 @@ def ExpandReferencesInString( orig_s, mapping, user_choices ): # HACK: This is seemingly the only way to get the key. str( e ) returns # the key surrounded by '' for unknowable reasons. key = e.args[ 0 ] - default_value = user_choices.get( key, None ) - mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ), - default_value ) - user_choices[ key ] = mapping[ key ] - _logger.debug( "Value for %s not set in %s (from %s): set to %s", - key, - s, - orig_s, - mapping[ key ] ) + + if key in calculus: + mapping[ key ] = calculus[ key ]() + else: + default_value = user_choices.get( key, None ) + mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ), + default_value ) + user_choices[ key ] = mapping[ key ] + _logger.debug( "Value for %s not set in %s (from %s): set to %s", + key, + s, + orig_s, + mapping[ key ] ) except ValueError as e: UserMessage( 'Invalid $ in string {}: {}'.format( s, e ), persist = True ) @@ -412,12 +421,18 @@ def ExpandReferencesInString( orig_s, mapping, user_choices ): # TODO: Should we just run the substitution on the whole JSON string instead? # That woul dallow expansion in bool and number values, such as ports etc. ? -def ExpandReferencesInDict( obj, mapping, user_choices ): +def ExpandReferencesInDict( obj, mapping, calculus, user_choices ): for k in obj.keys(): - obj[ k ] = ExpandReferencesInObject( obj[ k ], mapping, user_choices ) + obj[ k ] = ExpandReferencesInObject( obj[ k ], + mapping, + calculus, + user_choices ) -def ParseVariables( variables_list, mapping, user_choices ): +def ParseVariables( variables_list, + mapping, + calculus, + user_choices ): new_variables = {} new_mapping = mapping.copy() @@ -431,7 +446,10 @@ def ParseVariables( variables_list, mapping, user_choices ): if 'shell' in v: new_v = v.copy() # Bit of a hack. Allows environment variables to be used. - ExpandReferencesInDict( new_v, new_mapping, user_choices ) + ExpandReferencesInDict( new_v, + new_mapping, + calculus, + user_choices ) env = os.environ.copy() env.update( new_v.get( 'env' ) or {} ) @@ -455,6 +473,7 @@ def ParseVariables( variables_list, mapping, user_choices ): else: new_variables[ n ] = ExpandReferencesInObject( v, mapping, + calculus, user_choices ) return new_variables @@ -575,3 +594,13 @@ def GetVimspectorBase(): return base.decode( 'utf-8' ) else: return base + + +def GetUnusedLocalPort(): + import socket + sock = socket.socket() + # This tells the OS to give us any free port in the range [1024 - 65535] + sock.bind( ( '', 0 ) ) + port = sock.getsockname()[ 1 ] + sock.close() + return port From 81712b124febc8890a02d511c60c12650b967f02 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jul 2020 13:08:15 +0100 Subject: [PATCH 205/609] Fix traceback when (sometimes?) using the watch window --- python3/vimspector/variables.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index bb5b252..fe44880 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -230,7 +230,7 @@ class VariablesView( object ): scope ), { 'command': 'variables', 'arguments': { - 'variablesReference': scope.scope[ 'variablesReference' ] + 'variablesReference': scope.VariablesReference(), }, } ) @@ -295,10 +295,10 @@ class VariablesView( object ): watch.result.IsExpandedByUser() ): self._connection.DoRequest( partial( self._ConsumeVariables, self._watch.draw, - watch.result.result ), { + watch.result ), { 'command': 'variables', 'arguments': { - 'variablesReference': watch.result.result[ 'variablesReference' ] + 'variablesReference': watch.result.VariablesReference(), }, } ) From a647b659830030c4ab23b0b3f1007bdafc487579 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jul 2020 13:36:52 +0100 Subject: [PATCH 206/609] Use an unused local port for CodeLLDB --- install_gadget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 627152e..2c3a6ba 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -478,9 +478,9 @@ GADGETS = { 'type': 'CodeLLDB', "command": [ "${gadgetDir}/CodeLLDB/adapter/codelldb", - "--port", "${port}" + "--port", "${unusedLocalPort}" ], - "port": "${port}", + "port": "${unusedLocalPort}", "configuration": { "type": "lldb", "name": "lldb", From fe8d7251a4406096428da3c8a4ded6f3f853b626 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jul 2020 18:07:58 +0100 Subject: [PATCH 207/609] Add unusedLocalPort to docs --- docs/configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/configuration.md b/docs/configuration.md index 6c82584..98be766 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -364,6 +364,7 @@ The following variables are provided: * `${fileDirname}` - the current opened file's dirname * `${fileExtname}` - the current opened file's extension * `${cwd}` - the current working directory of the active window on launch +* `${unusedLocalPort}` - an unused local TCP port ## Remote Debugging Support From 9baba5afabe1fd2893c4bbfe61f3953a11192bde Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jul 2020 18:19:11 +0100 Subject: [PATCH 208/609] Document --basedir and ClearBreakpoints() and Exception Breakpoints --- README.md | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a7befe..e060e6a 100644 --- a/README.md +++ b/README.md @@ -307,6 +307,30 @@ should be): ./install_gadget.py --all --disable-tcl ``` +If you want to just add a new adapter without destroying the exisitng ones, add +`--update-gadget-config`, as in: + +```bash +$ ./install_gadget.py --enable-tcl +$ ./install_gadget.py --enable-rust --update-gadget-config +$ ./install_gadget.py --enable-java --update-gadget-config +``` + +If you want to maintain `configurations` outside of the vimspector repository +(this can be useful if you have custom gadgets or global configurations), +you can tell the installer to use a different basedir, then set +`g:vimspector_base_dir` to point to that directory, for example: + +```bash +$ ./install_gadget.py --basedir $HOME/.vim/vimspector-config --all --force-all +``` + +Then add this to your `.vimrc`: + +```viml +let g:vimspector_base_dir=expand( '$HOME/.vim/vimspector-config' ) +``` + See `--help` for more info. ## Manual gadget installation @@ -571,7 +595,7 @@ This would start the `Run Test` configuration with `${Test}` set to `'Name of the test'` and Vimspector would _not_ prompt the user to enter or confirm these things. -See [this issue](https://github.com/puremourning/vimspector/issues/97) for +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) @@ -599,6 +623,24 @@ whatever dialect the debugger understands when evaluating expressions. When using the `` mapping, the user is prompted to enter these expressions in a command line (with history). +### Exception breakpoints + +When starting debugging, you may be asekd a few questions about how to handle +exceptoins. These are "exception breakpoints" and vimspector remembers your +choices while Vim is still running. + +Typically you can accept the defaults (just keep pressing ``!) as most debug +adapter defaults are sane, but if you want to break on, say `uncaught exception` +then answer `Y` to that (for example). + +You can configure your choices in the `.vimspector.json`. See +[the configuration guide][vimspector-ref-exception] for details on that. + +### Clear breakpoints + +* Use `vimspector#ClearBreakpoints()` + to clear all breakpoints including the memory of exception breakpoint choices. + ## Stepping * Step in/out, finish, continue, pause etc. using the WinBar, or mappings. From a4abe511c78a713ec7b7b7b1f0197bee402779f8 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jul 2020 18:20:40 +0100 Subject: [PATCH 209/609] Update contents links --- README.md | 4 +++- docs/configuration.md | 56 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e060e6a..15e9872 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ For a tutorial and usage overview, take a look at the * [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) @@ -66,7 +68,7 @@ For a tutorial and usage overview, take a look at the * [Motivation](#motivation) * [License](#license) - + diff --git a/docs/configuration.md b/docs/configuration.md index 98be766..14f5047 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -11,15 +11,21 @@ for Vimspector. * [Debug adapter configuration](#debug-adapter-configuration) * [Debug profile configuration](#debug-profile-configuration) * [Replacements and variables](#replacements-and-variables) + * [The splat operator](#the-splat-operator) * [Configuration Format](#configuration-format) * [Files and locations](#files-and-locations) * [Adapter configurations](#adapter-configurations) * [Debug configurations](#debug-configurations) * [Exception breakpionts](#exception-breakpionts) * [Predefined Variables](#predefined-variables) + * [Remote Debugging Support](#remote-debugging-support) + * [Python (debugpy) Example](#python-debugpy-example) + * [C-family (gdbserver) Example](#c-family-gdbserver-example) + * [Docker Example](#docker-example) + * [Appendix: Configuration file format](#appendix-configuration-file-format) * [Appendix: Editor configuration](#appendix-editor-configuration) - + @@ -330,7 +336,7 @@ the configured response is empty string, the debug adapter default will be used. Referring to the above example, the following tells the debug adapter to use the default value for `caught` exceptoins and to break on `uncaught` exception: -``` +```json { "configurations": { "example-debug-configuration": { @@ -344,6 +350,52 @@ default value for `caught` exceptoins and to break on `uncaught` exception: ... ``` +The keys in the `exception` mapping are what Vimspector includes in the prompt. +For example, when prompted with the following: + +``` +cpp_throw: Break on C++: on throw (Y/N/default: Y)? +``` + +The exception breakpoint "type" is `cpp_throw` and the default is `Y`. + +Similarly: + +``` +cpp_catch: Break on C++: on catch (Y/N/default: N)? +``` + +The exception breakpoint "type" is `cpp_catch` and the default is `N`. + +Use the following to set the values in config and not get asked: + +```json + "configurations": { + "example-debug-configuration": { + "adapter": "example-adapter-name", + "breakpoints": { + "exception": { + "cpp_throw": "Y", + "cpp_catch": "Y" + } + }, +``` + +To just accept the defaults for these exception breakpoint types, don't specify +a value, as in : + +```json + "configurations": { + "example-debug-configuration": { + "adapter": "example-adapter-name", + "breakpoints": { + "exception": { + "cpp_throw": "", + "cpp_catch": "" + } + }, +``` + ## Predefined Variables The following variables are provided: From 9f6caadc40a8449f748c458efe9e46e8b4ca31f3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 9 Jul 2020 18:57:28 +0100 Subject: [PATCH 210/609] Pre-calculate the gadgetDir, as this is likely used every time --- python3/vimspector/debug_session.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 244a0c4..50b2b00 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -181,12 +181,11 @@ class DebugSession( object ): 'dollar': '$', # HACK. Hote '$$' also works. 'workspaceRoot': self._workspace_root, 'workspaceFolder': self._workspace_root, + 'gadgetDir': install.GetGadgetDir( VIMSPECTOR_HOME, install.GetOS() ), 'file': current_file, } calculus = { - 'gadgetDir': lambda: install.GetGadgetDir( VIMSPECTOR_HOME, - install.GetOS() ), 'relativeFile': lambda: relpath( current_file, self._workspace_root ), 'fileBasename': lambda: os.path.basename( current_file ), From 8e286be6eef68f7b14fe1bed7099ddfb9ef8de35 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 25 Jun 2020 15:38:08 +0100 Subject: [PATCH 211/609] Fix shutting down jobs when resetting - use the correct category for CleanUpCommand --- python3/vimspector/output.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index c02e84a..59d1857 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -25,6 +25,7 @@ class TabBuffer( object ): self.index = index self.flag = False self.is_job = False + self.job_category = None BUFFER_MAP = { @@ -93,7 +94,8 @@ class OutputView( object ): def Clear( self ): for category, tab_buffer in self._buffers.items(): if tab_buffer.is_job: - utils.CleanUpCommand( category, self._api_prefix ) + utils.CleanUpCommand( tab_buffer.job_category or category, + self._api_prefix ) try: vim.command( 'bdelete! {0}'.format( tab_buffer.buf.number ) ) except vim.error as e: @@ -169,6 +171,7 @@ class OutputView( object ): self._buffers[ category + '-out' ] = TabBuffer( out, len( self._buffers ) ) self._buffers[ category + '-out' ].is_job = True + self._buffers[ category + '-out' ].job_category = category self._buffers[ category + '-err' ] = TabBuffer( err, len( self._buffers ) ) self._buffers[ category + '-err' ].is_job = False From 6cfc31323467170d869f9a43463004d43d6893c2 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 10 Jul 2020 22:49:30 +0100 Subject: [PATCH 212/609] Do a better job of tidying up on test failure; note: We can still receive data callbacks after _OnExit, so just ignore that data --- autoload/vimspector/internal/job.vim | 14 +- python3/vimspector/debug_session.py | 8 +- tests/lib/plugin/shared.vim | 281 +-------------------------- 3 files changed, 15 insertions(+), 288 deletions(-) diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index a93a004..692f914 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -20,11 +20,8 @@ set cpoptions&vim " }}} function! s:_OnServerData( channel, data ) abort - if py3eval( '_vimspector_session is None' ) - call ch_log( 'Unexpected stdout data received on channel ' - \ . a:channel - \ . 'after reset: ' - \ . a:data ) + if !exists( 's:job' ) + call ch_log( 'Get data after process exit' ) return endif @@ -32,11 +29,8 @@ function! s:_OnServerData( channel, data ) abort endfunction function! s:_OnServerError( channel, data ) abort - if py3eval( '_vimspector_session is None' ) - call ch_log( 'Unexpected stderr data received on channel ' - \ . a:channel - \ . 'after reset: ' - \ . a:data ) + if !exists( 's:job' ) + call ch_log( 'Get data after process exit' ) return endif diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 50b2b00..88d1167 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -300,6 +300,10 @@ class DebugSession( object ): return wrapper def OnChannelData( self, data ): + if self._connection is None: + # Should _not_ happen, but maybe possible due to races or vim bufs? + return + self._connection.OnData( data ) @@ -969,7 +973,9 @@ class DebugSession( object ): if self._run_on_server_exit: self._logger.debug( "Running server exit handler" ) - self._run_on_server_exit() + callback = self._run_on_server_exit + self._run_on_server_exit = None + callback() else: self._logger.debug( "No server exit handler" ) diff --git a/tests/lib/plugin/shared.vim b/tests/lib/plugin/shared.vim index ee9a0af..d682810 100644 --- a/tests/lib/plugin/shared.vim +++ b/tests/lib/plugin/shared.vim @@ -6,115 +6,6 @@ if exists('*WaitFor') finish endif -" Get the name of the Python executable. -" Also keeps it in s:python. -func PythonProg() - " This test requires the Python command to run the test server. - " This most likely only works on Unix and Windows. - if has('unix') - " We also need the job feature or the pkill command to make sure the server - " can be stopped. - if !(executable('python') && (has('job') || executable('pkill'))) - return '' - endif - let s:python = 'python' - elseif has('win32') - " Use Python Launcher for Windows (py.exe) if available. - if executable('py.exe') - let s:python = 'py.exe' - elseif executable('python.exe') - let s:python = 'python.exe' - else - return '' - endif - else - return '' - endif - return s:python -endfunc - -" Run "cmd". Returns the job if using a job. -func RunCommand(cmd) - let job = 0 - if has('job') - let job = job_start(a:cmd, {'stoponexit': 'hup'}) - call job_setoptions(job, {'stoponexit': 'kill'}) - elseif has('win32') - exe 'silent !start cmd /c start "test_channel" ' . a:cmd - else - exe 'silent !' . a:cmd . '&' - endif - return job -endfunc - -" Read the port number from the Xportnr file. -func GetPort() - let l = [] - " with 200 it sometimes failed - for i in range(400) - try - let l = readfile('Xportnr') - catch - endtry - if len(l) >= 1 - break - endif - sleep 10m - endfor - call delete('Xportnr') - - if len(l) == 0 - " Can't make the connection, give up. - return 0 - endif - return l[0] -endfunc - -" Run a Python server for "cmd" and call "testfunc". -" Always kills the server before returning. -func RunServer(cmd, testfunc, args) - " The Python program writes the port number in Xportnr. - call delete('Xportnr') - - if len(a:args) == 1 - let arg = ' ' . a:args[0] - else - let arg = '' - endif - let pycmd = s:python . ' ' . a:cmd . arg - - try - let g:currentJob = RunCommand(pycmd) - - " Wait for up to 2 seconds for the port number to be there. - let port = GetPort() - if port == 0 - call assert_false(1, "Can't start " . a:cmd) - return - endif - - call call(function(a:testfunc), [port]) - catch - call assert_false(1, 'Caught exception: "' . v:exception . '" in ' . v:throwpoint) - finally - call s:kill_server(a:cmd) - endtry -endfunc - -func s:kill_server(cmd) - if has('job') - if exists('g:currentJob') - call job_stop(g:currentJob) - unlet g:currentJob - endif - elseif has('win32') - let cmd = substitute(a:cmd, '.py', '', '') - call system('taskkill /IM ' . s:python . ' /T /F /FI "WINDOWTITLE eq ' . cmd . '"') - else - call system('pkill -f ' . a:cmd) - endif -endfunc - " Wait for up to five seconds for "expr" to become true. "expr" can be a " stringified expression to evaluate, or a funcref without arguments. " Using a lambda works best. Example: @@ -180,7 +71,10 @@ func s:WaitForCommon(expr, assert, timeout) if type(a:assert) == v:t_func " Remove the errors added by the assert function. - call remove(v:errors, -1 * len( v:errors ) - errors_before ) + let errors_added = len( v:errors ) - errors_before + if errors_added > 0 + call remove(v:errors, -1 * errors_added ) + endif endif sleep 10m @@ -193,170 +87,3 @@ func s:WaitForCommon(expr, assert, timeout) return -1 " timed out endfunc - - -" Wait for up to a given milliseconds. -" With the +timers feature this waits for key-input by getchar(), Resume() -" feeds key-input and resumes process. Return time waited in milliseconds. -" Without +timers it uses simply :sleep. -func Standby(msec) - if has('timers') - let start = reltime() - let g:_standby_timer = timer_start(a:msec, function('s:feedkeys')) - call getchar() - return float2nr(reltimefloat(reltime(start)) * 1000) - else - execute 'sleep ' a:msec . 'm' - return a:msec - endif -endfunc - -func Resume() - if exists('g:_standby_timer') - call timer_stop(g:_standby_timer) - call s:feedkeys(0) - unlet g:_standby_timer - endif -endfunc - -func s:feedkeys(timer) - call feedkeys('x', 'nt') -endfunc - -" Get $VIMPROG to run Vim executable. -" The Makefile writes it as the first line in the "vimcmd" file. -func GetVimProg() - if !filereadable('vimcmd') - " Assume the script was sourced instead of running "make". - return '../vim' - endif - return readfile('vimcmd')[0] -endfunc - -let g:valgrind_cnt = 1 - -" Get the command to run Vim, with -u NONE and --not-a-term arguments. -" If there is an argument use it instead of "NONE". -func GetVimCommand(...) - if !filereadable('vimcmd') - echo 'Cannot read the "vimcmd" file, falling back to ../vim.' - let lines = ['../vim'] - else - let lines = readfile('vimcmd') - endif - if a:0 == 0 - let name = 'NONE' - else - let name = a:1 - endif - " For Unix Makefile writes the command to use in the second line of the - " "vimcmd" file, including environment options. - " Other Makefiles just write the executable in the first line, so fall back - " to that if there is no second line or it is empty. - if len(lines) > 1 && lines[1] !=# '' - let cmd = lines[1] - else - let cmd = lines[0] - endif - - let cmd = substitute(cmd, '-u \f\+', '-u ' . name, '') - if cmd !~ '-u '. name - let cmd = cmd . ' -u ' . name - endif - let cmd .= ' --not-a-term' - let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '') - - " If using valgrind, make sure every run uses a different log file. - if cmd =~# 'valgrind.*--log-file=' - let cmd = substitute(cmd, '--log-file=\(^\s*\)', '--log-file=\1.' . g:valgrind_cnt, '') - let g:valgrind_cnt += 1 - endif - - return cmd -endfunc - -" Get the command to run Vim, with --clean. -func GetVimCommandClean() - let cmd = GetVimCommand() - let cmd = substitute(cmd, '-u NONE', '--clean', '') - let cmd = substitute(cmd, '--not-a-term', '', '') - - " Optionally run Vim under valgrind - " let cmd = 'valgrind --tool=memcheck --leak-check=yes --num-callers=25 --log-file=valgrind ' . cmd - - return cmd -endfunc - -" Run Vim, using the "vimcmd" file and "-u NORC". -" "before" is a list of Vim commands to be executed before loading plugins. -" "after" is a list of Vim commands to be executed after loading plugins. -" Plugins are not loaded, unless 'loadplugins' is set in "before". -" Return 1 if Vim could be executed. -func RunVim(before, after, arguments) - return RunVimPiped(a:before, a:after, a:arguments, '') -endfunc - -func RunVimPiped(before, after, arguments, pipecmd) - let cmd = GetVimCommand() - let args = '' - if len(a:before) > 0 - call writefile(a:before, 'Xbefore.vim') - let args .= ' --cmd "so Xbefore.vim"' - endif - if len(a:after) > 0 - call writefile(a:after, 'Xafter.vim') - let args .= ' -S Xafter.vim' - endif - - exe 'silent !' . a:pipecmd . cmd . args . ' ' . a:arguments - - if len(a:before) > 0 - call delete('Xbefore.vim') - endif - if len(a:after) > 0 - call delete('Xafter.vim') - endif - return 1 -endfunc - -func CanRunGui() - return has('gui') && ($DISPLAY !=# '' || has('gui_running')) -endfunc - -func WorkingClipboard() - if !has('clipboard') - return 0 - endif - if has('x11') - return $DISPLAY !=# '' - endif - return 1 -endfunc - -" Get line "lnum" as displayed on the screen. -" Trailing white space is trimmed. -func! Screenline(lnum) - let chars = [] - for c in range(1, winwidth(0)) - call add(chars, nr2char(screenchar(a:lnum, c))) - endfor - let line = join(chars, '') - return matchstr(line, '^.\{-}\ze\s*$') -endfunc - -" Stops the shell running in terminal "buf". -func Stop_shell_in_terminal(buf) - call term_sendkeys(a:buf, "exit\r") - let job = term_getjob(a:buf) - call WaitFor({-> job_status(job) == "dead"}) -endfunc - -" Gets the text of a terminal line, using term_scrape() -func Get_terminal_text(bufnr, row) - let list = term_scrape(a:bufnr, a:row) - let text = '' - for item in list - let text .= item.chars - endfor - return text -endfunc From 5ce4147361ac81de9106557c0562bc39f9c14726 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 10:13:45 +0100 Subject: [PATCH 213/609] Manipulate buffers for variables, watches and stack trace --- python3/vimspector/stack_trace.py | 7 +-- python3/vimspector/utils.py | 15 ++++- python3/vimspector/variables.py | 96 ++++++++++++++++--------------- 3 files changed, 64 insertions(+), 54 deletions(-) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index d2c2b70..b146424 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -39,10 +39,7 @@ class StackTraceView( object ): utils.SetUpScratchBuffer( self._buf, 'vimspector.StackTrace' ) vim.current.buffer = self._buf - # FIXME: Remove all usage of "Windown" and just use buffers to prevent all - # the bugs around the window being closed. - self._win = vim.current.window - utils.SetUpUIWindow( self._win ) + utils.SetUpUIWindow( vim.current.window ) vim.command( 'nnoremap :call vimspector#GoToFrame()' ) @@ -311,4 +308,4 @@ class StackTraceView( object ): def SetSyntax( self, syntax ): self._current_syntax = utils.SetSyntax( self._current_syntax, syntax, - self._win ) + self._buf ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index d00c8d7..a868c7f 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -194,6 +194,14 @@ def LetCurrentWindow( window ): yield +@contextlib.contextmanager +def LetCurrentBuffer( buf ): + with RestoreCursorPosition(): + with RestoreCurrentBuffer( vim.current.window ): + vim.current.buffer = buf + yield + + def JumpToWindow( window ): vim.current.tabpage = window.tabpage vim.current.window = window @@ -555,8 +563,11 @@ def SetSyntax( current_syntax, syntax, *args ): if current_syntax == syntax: return - for win in args: - with LetCurrentWindow( win ): + for buf in args: + with LetCurrentBuffer( buf ): + # We use set syn= because just setting vim.Buffer.options[ 'syntax' ] + # doesn't actually trigger the Syntax autocommand, and i'm not sure that + # 'doautocmd Syntax' is the right solution or not vim.command( 'set syntax={}'.format( Escape( syntax ) ) ) return syntax diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index fe44880..6826b59 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -16,15 +16,11 @@ import abc import vim import logging -from collections import namedtuple from functools import partial import typing from vimspector import utils -View = namedtuple( 'View', [ 'win', 'lines', 'draw' ] ) - - class Expandable: """Base for anything which might contain a hierarchy of values represented by @@ -104,7 +100,6 @@ class Variable( Expandable ): self.variable = variable - class Watch: """Holds a user watch expression (DAP request) and the result (WatchResult)""" def __init__( self, expression: dict ): @@ -114,29 +109,44 @@ class Watch: self.result = None +class View: + lines: typing.Dict[ int, Expandable ] + draw: typing.Callable + buf: vim.Buffer + + def __init__( self, win: vim.Window, lines, draw ): + self.lines = lines + self.draw = draw + self.buf = win.buffer + + utils.SetUpUIWindow( win ) + + class VariablesView( object ): def __init__( self, connection, variables_win, watches_win ): self._logger = logging.getLogger( __name__ ) utils.SetUpLogging( self._logger ) - self._vars = View( variables_win, {}, self._DrawScopes ) - self._watch = View( watches_win, {}, self._DrawWatches ) self._connection = connection self._current_syntax = '' - # Allows us to hit to expand/collapse variables - with utils.LetCurrentWindow( self._vars.win ): + # Set up the "Variables" buffer in the variables_win + self._scopes: typing.List[ Scope ] = [] + self._vars = View( variables_win, {}, self._DrawScopes ) + utils.SetUpScratchBuffer( self._vars.buf, 'vimspector.Variables' ) + with utils.LetCurrentWindow( variables_win ): vim.command( 'nnoremap :call vimspector#ExpandVariable()' ) - # List of current scopes of type Scope - self._scopes: typing.List[ 'Scope' ] = [] - - # List of current Watches of type Watch - self._watches: typing.List[ 'Watch' ] = [] - - # Allows us to hit to expand/collapse variables - with utils.LetCurrentWindow( self._watch.win ): + # Set up the "Watches" buffer in the watches_win (and create a WinBar in + # there) + self._watches: typing.List[ Watch ] = [] + self._watch = View( watches_win, {}, self._DrawWatches ) + utils.SetUpPromptBuffer( self._watch.buf, + 'vimspector.Watches', + 'Expression: ', + 'vimspector#AddWatchPrompt' ) + with utils.LetCurrentWindow( watches_win ): vim.command( 'nnoremap :call vimspector#ExpandVariable()' ) vim.command( @@ -149,15 +159,7 @@ class VariablesView( object ): vim.command( 'nnoremenu 1.3 WinBar.Delete ' ':call vimspector#DeleteWatch()' ) - utils.SetUpScratchBuffer( self._vars.win.buffer, 'vimspector.Variables' ) - utils.SetUpPromptBuffer( self._watch.win.buffer, - 'vimspector.Watches', - 'Expression: ', - 'vimspector#AddWatchPrompt' ) - - utils.SetUpUIWindow( self._vars.win ) - utils.SetUpUIWindow( self._watch.win ) - + # Set the (global!) balloon expr if supported has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) ) has_balloon_term = int( vim.eval( "has( 'balloon_eval_term' )" ) ) @@ -181,10 +183,10 @@ class VariablesView( object ): self._is_term = not bool( int( vim.eval( "has( 'gui_running' )" ) ) ) def Clear( self ): - with utils.ModifiableScratchBuffer( self._vars.win.buffer ): - utils.ClearBuffer( self._vars.win.buffer ) - with utils.ModifiableScratchBuffer( self._watch.win.buffer ): - utils.ClearBuffer( self._watch.win.buffer ) + with utils.ModifiableScratchBuffer( self._vars.buf ): + utils.ClearBuffer( self._vars.buf ) + with utils.ModifiableScratchBuffer( self._watch.buf ): + utils.ClearBuffer( self._watch.buf ) self._current_syntax = '' def ConnectionUp( self, connection ): @@ -198,8 +200,8 @@ class VariablesView( object ): for k, v in self._oldoptions.items(): vim.options[ k ] = v - vim.command( 'bdelete! ' + str( self._watch.win.buffer.number ) ) - vim.command( 'bdelete! ' + str( self._vars.win.buffer.number ) ) + vim.command( 'bdelete! ' + str( self._watch.buf.number ) ) + vim.command( 'bdelete! ' + str( self._vars.buf.number ) ) def LoadScopes( self, frame ): def scopes_consumer( message ): @@ -256,8 +258,8 @@ class VariablesView( object ): self.EvaluateWatches() def DeleteWatch( self ): - if vim.current.window != self._watch.win: - utils.UserMessage( 'Not a watch window' ) + if vim.current.buffer != self._watch.buf: + utils.UserMessage( 'Not a watch buffer' ) return current_line = vim.current.window.cursor[ 0 ] @@ -305,9 +307,9 @@ class VariablesView( object ): self._DrawWatches() def ExpandVariable( self ): - if vim.current.window == self._vars.win: + if vim.current.buffer == self._vars.buf: view = self._vars - elif vim.current.window == self._watch.win: + elif vim.current.buffer == self._watch.buf: view = self._watch else: return @@ -341,7 +343,7 @@ class VariablesView( object ): assert indent > 0 for variable in variables: line = utils.AppendToBuffer( - view.win.buffer, + view.buf, '{indent}{marker}{icon} {name} ({type_}): {value}'.format( # We borrow 1 space of indent to draw the change marker indent = ' ' * ( indent - 1 ), @@ -363,8 +365,8 @@ class VariablesView( object ): # However it is pretty inefficient. self._vars.lines.clear() with utils.RestoreCursorPosition(): - with utils.ModifiableScratchBuffer( self._vars.win.buffer ): - utils.ClearBuffer( self._vars.win.buffer ) + with utils.ModifiableScratchBuffer( self._vars.buf ): + utils.ClearBuffer( self._vars.buf ) for scope in self._scopes: self._DrawScope( 0, scope ) @@ -374,11 +376,11 @@ class VariablesView( object ): # However it is pretty inefficient. self._watch.lines.clear() with utils.RestoreCursorPosition(): - with utils.ModifiableScratchBuffer( self._watch.win.buffer ): - utils.ClearBuffer( self._watch.win.buffer ) - utils.AppendToBuffer( self._watch.win.buffer, 'Watches: ----' ) + with utils.ModifiableScratchBuffer( self._watch.buf ): + utils.ClearBuffer( self._watch.buf ) + utils.AppendToBuffer( self._watch.buf, 'Watches: ----' ) for watch in self._watches: - line = utils.AppendToBuffer( self._watch.win.buffer, + line = utils.AppendToBuffer( self._watch.buf, 'Expression: ' + watch.expression[ 'expression' ] ) watch.line = line @@ -387,7 +389,7 @@ class VariablesView( object ): def _DrawScope( self, indent, scope ): icon = '+' if scope.IsExpandable() and not scope.IsExpandedByUser() else '-' - line = utils.AppendToBuffer( self._vars.win.buffer, + line = utils.AppendToBuffer( self._vars.buf, '{0}{1} Scope: {2}'.format( ' ' * indent, icon, @@ -413,7 +415,7 @@ class VariablesView( object ): icon = icon, result = watch.result.result.get( 'result', '' ) ) - line = utils.AppendToBuffer( self._watch.win.buffer, line.split( '\n' ) ) + line = utils.AppendToBuffer( self._watch.buf, line.split( '\n' ) ) self._watch.lines[ line ] = watch.result if watch.result.ShouldDrawDrillDown(): @@ -495,7 +497,7 @@ class VariablesView( object ): def SetSyntax( self, syntax ): self._current_syntax = utils.SetSyntax( self._current_syntax, syntax, - self._vars.win, - self._watch.win ) + self._vars.buf, + self._watch.buf ) # vim: sw=2 From 3643c2effd10515f29f80ba44878dfd941970a66 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 11:43:28 +0100 Subject: [PATCH 214/609] Only respond to data events for the _current_ job, not buffered data for old jobs --- autoload/vimspector/internal/job.vim | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index 692f914..20a0560 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -20,7 +20,7 @@ set cpoptions&vim " }}} function! s:_OnServerData( channel, data ) abort - if !exists( 's:job' ) + if !exists( 's:job' ) || ch_getjob( a:channel ) isnot s:job call ch_log( 'Get data after process exit' ) return endif @@ -29,7 +29,7 @@ function! s:_OnServerData( channel, data ) abort endfunction function! s:_OnServerError( channel, data ) abort - if !exists( 's:job' ) + if !exists( 's:job' ) || ch_getjob( a:channel ) isnot s:job call ch_log( 'Get data after process exit' ) return endif @@ -37,7 +37,16 @@ function! s:_OnServerError( channel, data ) abort py3 _vimspector_session.OnServerStderr( vim.eval( 'a:data' ) ) endfunction + +" FIXME: We should wait until both the exit_cb _and_ the channel closed callback +" have been received before OnServerExit? + function! s:_OnExit( channel, status ) abort + if !exists( 's:job' ) || ch_getjob( a:channel ) isnot s:job + call ch_log( 'Unexpected exit callback' ) + return + endif + echom 'Channel exit with status ' . a:status redraw if exists( 's:job' ) @@ -47,6 +56,11 @@ function! s:_OnExit( channel, status ) abort endfunction function! s:_OnClose( channel ) abort + if !exists( 's:job' ) || job_getchannel( s:job ) != a:channel + call ch_log( 'Channel closed after exit' ) + return + endif + echom 'Channel closed' redraw endfunction From cb39e2b511b1662a667c3275c439e968c6527f3e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 11:44:15 +0100 Subject: [PATCH 215/609] Allow closing of the UI windows; check valid flags and set buffers to be hidden. delete them on Reset --- python3/vimspector/output.py | 32 +++++++++++++++++-------------- python3/vimspector/stack_trace.py | 2 +- python3/vimspector/utils.py | 24 ++++++++++++----------- python3/vimspector/variables.py | 4 ++-- 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 59d1857..4599e04 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -77,9 +77,10 @@ class OutputView( object ): self._ToggleFlag( category, True ) # Scroll the buffer - with utils.RestoreCurrentWindow(): - with utils.RestoreCurrentBuffer( self._window ): - self._ShowOutput( category ) + if self._window.valid: + with utils.RestoreCurrentWindow(): + with utils.RestoreCurrentBuffer( self._window ): + self._ShowOutput( category ) def ConnectionUp( self, connection ): self._connection = connection @@ -96,18 +97,17 @@ class OutputView( object ): if tab_buffer.is_job: utils.CleanUpCommand( tab_buffer.job_category or category, self._api_prefix ) - try: - vim.command( 'bdelete! {0}'.format( tab_buffer.buf.number ) ) - except vim.error as e: - # FIXME: For now just ignore the "no buffers were deleted" error - if 'E516' not in str( e ): - raise + utils.CleanUpHiddenBuffer( tab_buffer.buf ) + # FIXME: nunmenu the WinBar ? self._buffers = {} def _ShowOutput( self, category ): + if not self._window.valid: + return + utils.JumpToWindow( self._window ) - vim.command( 'bu {0}'.format( self._buffers[ category ].buf.name ) ) + vim.current.buffer = self._buffers[ category ].buf vim.command( 'normal G' ) def ShowOutput( self, category ): @@ -146,8 +146,10 @@ class OutputView( object ): def _ToggleFlag( self, category, flag ): if self._buffers[ category ].flag != flag: self._buffers[ category ].flag = flag - with utils.LetCurrentWindow( self._window ): - self._RenderWinBar( category ) + + if self._window.valid: + with utils.LetCurrentWindow( self._window ): + self._RenderWinBar( category ) def RunJobWithOutput( self, category, cmd ): @@ -155,6 +157,9 @@ class OutputView( object ): def _CreateBuffer( self, category, file_name = None, cmd = None ): + if not self._window.valid: + return + with utils.LetCurrentWindow( self._window ): with utils.RestoreCurrentBuffer( self._window ): @@ -185,8 +190,7 @@ class OutputView( object ): utils.SetUpPromptBuffer( tab_buffer.buf, 'vimspector.Console', '> ', - 'vimspector#EvaluateConsole', - hidden=True ) + 'vimspector#EvaluateConsole' ) else: utils.SetUpHiddenBuffer( tab_buffer.buf, diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index b146424..088ac7c 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -81,7 +81,7 @@ class StackTraceView( object ): def Reset( self ): self.Clear() - # TODO: delete the buffer ? + utils.CleanUpHiddenBuffer( self._buf ) def LoadThreads( self, infer_current_frame ): pending_request = False diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index a868c7f..8d7d3d4 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -86,15 +86,17 @@ def CleanUpCommand( name, api_prefix ): name ) ) +def CleanUpHiddenBuffer( buf ): + try: + vim.command( 'bdelete! {}'.format( buf.number ) ) + except vim.error as e: + # FIXME: For now just ignore the "no buffers were deleted" error + if 'E516' not in str( e ): + raise + + def SetUpScratchBuffer( buf, name ): - buf.options[ 'buftype' ] = 'nofile' - buf.options[ 'swapfile' ] = False - buf.options[ 'modifiable' ] = False - buf.options[ 'modified' ] = False - buf.options[ 'readonly' ] = True - buf.options[ 'buflisted' ] = False - buf.options[ 'bufhidden' ] = 'wipe' - buf.name = name + SetUpHiddenBuffer( buf, name ) def SetUpHiddenBuffer( buf, name ): @@ -108,7 +110,7 @@ def SetUpHiddenBuffer( buf, name ): buf.name = name -def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ): +def SetUpPromptBuffer( buf, name, prompt, callback ): # This feature is _super_ new, so only enable when available if not Exists( '*prompt_setprompt' ): return SetUpHiddenBuffer( buf, name ) @@ -119,7 +121,8 @@ def SetUpPromptBuffer( buf, name, prompt, callback, hidden=False ): buf.options[ 'modified' ] = False buf.options[ 'readonly' ] = False buf.options[ 'buflisted' ] = False - buf.options[ 'bufhidden' ] = 'wipe' if not hidden else 'hide' + buf.options[ 'bufhidden' ] = 'hide' + buf.options[ 'textwidth' ] = 0 buf.name = name vim.eval( "prompt_setprompt( {0}, '{1}' )".format( buf.number, @@ -177,7 +180,6 @@ def RestoreCurrentWindow(): @contextlib.contextmanager def RestoreCurrentBuffer( window ): - # TODO: Don't trigger autoccommands when shifting buffers old_buffer = window.buffer try: yield diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 6826b59..528c570 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -200,8 +200,8 @@ class VariablesView( object ): for k, v in self._oldoptions.items(): vim.options[ k ] = v - vim.command( 'bdelete! ' + str( self._watch.buf.number ) ) - vim.command( 'bdelete! ' + str( self._vars.buf.number ) ) + utils.CleanUpHiddenBuffer( self._vars.buf ) + utils.CleanUpHiddenBuffer( self._watch.buf ) def LoadScopes( self, frame ): def scopes_consumer( message ): From b0d41eb34731e9b1c3e44a5fae2fce04eae0edcc Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 11:53:39 +0100 Subject: [PATCH 216/609] Scratch buffer still makes sense for the code window buffer where we ask for it from the sevrer --- python3/vimspector/stack_trace.py | 2 +- python3/vimspector/utils.py | 1 + python3/vimspector/variables.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 088ac7c..4504d10 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -36,7 +36,7 @@ class StackTraceView( object ): self._threads = [] self._sources = {} - utils.SetUpScratchBuffer( self._buf, 'vimspector.StackTrace' ) + utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' ) vim.current.buffer = self._buf utils.SetUpUIWindow( vim.current.window ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 8d7d3d4..03dbb4d 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -97,6 +97,7 @@ def CleanUpHiddenBuffer( buf ): def SetUpScratchBuffer( buf, name ): SetUpHiddenBuffer( buf, name ) + buf.options[ 'bufhidden' ] = 'wipe' def SetUpHiddenBuffer( buf, name ): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 528c570..d5fc9c2 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -133,7 +133,7 @@ class VariablesView( object ): # Set up the "Variables" buffer in the variables_win self._scopes: typing.List[ Scope ] = [] self._vars = View( variables_win, {}, self._DrawScopes ) - utils.SetUpScratchBuffer( self._vars.buf, 'vimspector.Variables' ) + utils.SetUpHiddenBuffer( self._vars.buf, 'vimspector.Variables' ) with utils.LetCurrentWindow( variables_win ): vim.command( 'nnoremap :call vimspector#ExpandVariable()' ) From bdca96f6632378e11e79d990637f622d25561c3f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 14:06:32 +0100 Subject: [PATCH 217/609] Fix up java test --- .../test/java/test_project/.vimspector.json | 10 ++++++-- support/test/java/test_project/java.vim | 24 +++++++++++++++++++ support/test/java/test_project/pom.xml | 4 ++-- .../main/java/com/vimspector/test/Base.java | 8 +++++++ .../com/vimspector/test/TestApplication.java | 10 ++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 support/test/java/test_project/java.vim create mode 100644 support/test/java/test_project/src/main/java/com/vimspector/test/Base.java diff --git a/support/test/java/test_project/.vimspector.json b/support/test/java/test_project/.vimspector.json index c19cfcb..39f4639 100644 --- a/support/test/java/test_project/.vimspector.json +++ b/support/test/java/test_project/.vimspector.json @@ -9,7 +9,10 @@ "classPaths": [ "${workspaceRoot}/target/classes" ], "args": "hello world!", "stopOnEntry": true, - "console": "integratedTerminal" + "console": "integratedTerminal", + "stepFilters": { + "skipClasses": [ "$$JDK" ] + } } }, "Java Attach": { @@ -19,7 +22,10 @@ "sourcePaths": [ "${workspaceRoot}/src/main/java" ], "stopOnEntry": true, "hostName": "localhost", - "port": "${JVMDebugPort}" + "port": "${JVMDebugPort}", + "stepFilters": { + "skipClasses": [ "$$JDK" ] + } } }, "Attach with vscode-javac": { diff --git a/support/test/java/test_project/java.vim b/support/test/java/test_project/java.vim new file mode 100644 index 0000000..96b78d8 --- /dev/null +++ b/support/test/java/test_project/java.vim @@ -0,0 +1,24 @@ +let g:ycm_java_jdtls_extension_path = [ + \ expand( ':p:h:h:h:h:h' ) . '/gadgets/macos' + \ ] + +let s:jdt_ls_debugger_port = 0 +function! s:StartDebugging() + if s:jdt_ls_debugger_port <= 0 + " Get the DAP port + let s:jdt_ls_debugger_port = youcompleteme#GetCommandResponse( + \ 'ExecuteCommand', + \ 'vscode.java.startDebugSession' ) + + if s:jdt_ls_debugger_port == '' + echom "Unable to get DAP port - is YCM initialized?" + let s:jdt_ls_debugger_port = 0 + return + endif + endif + + " Start debugging with the DAP port + call vimspector#LaunchWithSettings( { 'DAPPort': s:jdt_ls_debugger_port } ) +endfunction + +nnoremap :call StartDebugging() diff --git a/support/test/java/test_project/pom.xml b/support/test/java/test_project/pom.xml index e6dc4d3..890e7e8 100644 --- a/support/test/java/test_project/pom.xml +++ b/support/test/java/test_project/pom.xml @@ -4,7 +4,7 @@ TestApplication 1 - 11 - 11 + 8 + 8 diff --git a/support/test/java/test_project/src/main/java/com/vimspector/test/Base.java b/support/test/java/test_project/src/main/java/com/vimspector/test/Base.java new file mode 100644 index 0000000..25840d0 --- /dev/null +++ b/support/test/java/test_project/src/main/java/com/vimspector/test/Base.java @@ -0,0 +1,8 @@ +package com.vimspector.test; + +class Base { + public String DoSomething() + { + return ""; + } +} diff --git a/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java b/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java index 264c0e6..680dbb6 100644 --- a/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java +++ b/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java @@ -24,6 +24,16 @@ public class TestApplication { } } + private static class TestGeneric { + T t; + public TestGeneric( T t ) { + this.t = t; + } + public void DoSomethingUseful() { + System.out.println( t.DoSomething() ); + } + } + private static void DoGeneric( T b ) { TestGeneric foo = new TestGeneric<>( b ); foo.DoSomethingUseful(); From 43dd2827022d91307f31f59cc39e1d271996bc14 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 14:07:28 +0100 Subject: [PATCH 218/609] Fix handling data after exit - channels and neovim --- autoload/vimspector/internal/channel.vim | 8 ++++++ autoload/vimspector/internal/neochannel.vim | 8 ++++++ autoload/vimspector/internal/neojob.vim | 28 +++++++++++++++++---- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/autoload/vimspector/internal/channel.vim b/autoload/vimspector/internal/channel.vim index 62e91ec..b05d8e8 100644 --- a/autoload/vimspector/internal/channel.vim +++ b/autoload/vimspector/internal/channel.vim @@ -20,12 +20,20 @@ set cpoptions&vim " }}} function! s:_OnServerData( channel, data ) abort + if !exists( 's:ch' ) || s:ch isnot a:channel + return + endif + py3 << EOF _vimspector_session.OnChannelData( vim.eval( 'a:data' ) ) EOF endfunction function! s:_OnClose( channel ) abort + if !exists( 's:ch' ) || s:ch isnot a:channel + return + endif + echom 'Channel closed' redraw unlet s:ch diff --git a/autoload/vimspector/internal/neochannel.vim b/autoload/vimspector/internal/neochannel.vim index 511d4e5..5414568 100644 --- a/autoload/vimspector/internal/neochannel.vim +++ b/autoload/vimspector/internal/neochannel.vim @@ -22,6 +22,14 @@ set cpoptions&vim function! s:_OnEvent( chan_id, data, event ) abort + if v:exiting isnot# v:null + return + endif + + if !exists( 's:ch' ) || a:chan_id != s:ch + return + endif + if a:data == [''] echom 'Channel closed' redraw diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index 35c409a..ced61ec 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -22,6 +22,14 @@ set cpoptions&vim function! s:_OnEvent( chan_id, data, event ) abort + if v:exiting isnot# v:null + return + endif + + if !exists( 's:job' ) || a:chan_id != s:job + return + endif + " In neovim, the data argument is a list. if a:event ==# 'stdout' py3 _vimspector_session.OnChannelData( '\n'.join( vim.eval( 'a:data' ) ) ) @@ -30,9 +38,7 @@ function! s:_OnEvent( chan_id, data, event ) abort elseif a:event ==# 'exit' echom 'Channel exit with status ' . a:data redraw - if exists( 's:job' ) - unlet s:job - endif + unlet s:job " This causes terminal spam in neovim due to " https://github.com/neovim/neovim/issues/11725 py3 _vimspector_session.OnServerExit( vim.eval( 'a:data' ) ) @@ -108,10 +114,22 @@ function! vimspector#internal#neojob#Reset() abort endfunction function! s:_OnCommandEvent( category, id, data, event ) abort + if v:exiting isnot# v:null + return + endif + if a:data == [''] return endif + if !has_key( s:commands, a:category ) + return + endif + + if !has_key( s:commands[ a:category ], a:id ) + return + endif + if a:event ==# 'stdout' let buffer = s:commands[ a:category ][ a:id ].stdout elseif a:event ==# 'stderr' @@ -212,8 +230,8 @@ function! vimspector#internal#neojob#CleanUpCommand( category ) abort endif for id in keys( s:commands[ a:category ] ) - call jobstop( id ) - call jobwait( id ) + call jobstop( str2nr( id ) ) + call jobwait( [ str2nr( id ) ] ) endfor unlet! s:commands[ a:category ] endfunction From 193196cc03cf6ad536d53285e7d20e39d5a1eb71 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 14:08:12 +0100 Subject: [PATCH 219/609] Allow users to close the code window and not crash --- python3/vimspector/code.py | 42 ++++++++++++++++++++--------- python3/vimspector/debug_session.py | 7 ++--- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 364a36a..149af0d 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -56,6 +56,10 @@ class CodeView( object ): def SetCurrentFrame( self, frame ): + """Returns True if the code window was updated with the frame, False + otherwise. False means either the frame is junk, we couldn't find the file + (or don't have the data) or the code window no longer exits.""" + if self._signs[ 'vimspectorPC' ]: vim.command( 'sign unplace {} group=VimspectorCode'.format( self._signs[ 'vimspectorPC' ] ) ) @@ -67,8 +71,25 @@ class CodeView( object ): if 'path' not in frame[ 'source' ]: return False - utils.JumpToWindow( self._window ) + self._signs[ 'vimspectorPC' ] = self._next_sign_id + self._next_sign_id += 1 + try: + vim.command( 'sign place {0} group=VimspectorCode priority=20 ' + 'line={1} name=vimspectorPC ' + 'file={2}'.format( + self._signs[ 'vimspectorPC' ], + frame[ 'line' ], + frame[ 'source' ][ 'path' ] ) ) + except vim.error as e: + # Ignore 'invalid buffer name' + if 'E158' in str( e ): + pass + + if not self._window.valid: + return False + + utils.JumpToWindow( self._window ) try: utils.OpenFileInCurrentWindow( frame[ 'source' ][ 'path' ] ) except vim.error: @@ -89,16 +110,6 @@ class CodeView( object ): frame[ 'source' ][ 'path' ] ) return False - self._signs[ 'vimspectorPC' ] = self._next_sign_id - self._next_sign_id += 1 - - vim.command( 'sign place {0} group=VimspectorCode priority=20 ' - 'line={1} name=vimspectorPC ' - 'file={2}'.format( - self._signs[ 'vimspectorPC' ], - frame[ 'line' ], - frame[ 'source' ][ 'path' ] ) ) - self.current_syntax = utils.ToUnicode( vim.current.buffer.options[ 'syntax' ] ) @@ -215,8 +226,13 @@ class CodeView( object ): 'env': env, } - window_for_start = self._window - if self._terminal_window is not None: + if self._window.valid: + window_for_start = self._window + else: + # TOOD: Where? + window_for_start = vim.current.window + + if self._terminal_window is not None and self._terminal_window.valid: assert self._terminal_buffer_number if ( self._terminal_window.buffer.number == self._terminal_buffer_number and int( utils.Call( 'vimspector#internal#{}term#IsFinished'.format( diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 88d1167..32f9be7 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -518,6 +518,10 @@ class DebugSession( object ): self.SetCurrentFrame( None ) def SetCurrentFrame( self, frame ): + if not frame: + self._stackTraceView.Clear() + self._variablesView.Clear() + if not self._codeView.SetCurrentFrame( frame ): return False @@ -526,9 +530,6 @@ class DebugSession( object ): self._stackTraceView.SetSyntax( self._codeView.current_syntax ) self._variablesView.LoadScopes( frame ) self._variablesView.EvaluateWatches() - else: - self._stackTraceView.Clear() - self._variablesView.Clear() return True From 036c9b9605cddd71aa17c7ab9865eea324b9876b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 14:08:29 +0100 Subject: [PATCH 220/609] Fix neovim not having the same classes as vim --- python3/vimspector/variables.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index d5fc9c2..22444f9 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -112,9 +112,8 @@ class Watch: class View: lines: typing.Dict[ int, Expandable ] draw: typing.Callable - buf: vim.Buffer - def __init__( self, win: vim.Window, lines, draw ): + def __init__( self, win, lines, draw ): self.lines = lines self.draw = draw self.buf = win.buffer From 7c943c2ed72d31385e2ce29bebdfc9524c0d740f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 14:08:45 +0100 Subject: [PATCH 221/609] Fix the terminal window to 80 columns --- autoload/vimspector/internal/neoterm.vim | 9 +++++++-- python3/vimspector/code.py | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/autoload/vimspector/internal/neoterm.vim b/autoload/vimspector/internal/neoterm.vim index 381a6b0..c6e798e 100644 --- a/autoload/vimspector/internal/neoterm.vim +++ b/autoload/vimspector/internal/neoterm.vim @@ -46,12 +46,17 @@ function! vimspector#internal#neoterm#ResetEnvironment( env, old_env ) abort 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 = '' if get( a:opts, 'vertical', 0 ) - vnew + let mods .= 'vertical ' + let mods .= get( a:opts, 'term_cols', '' ) else - new + let mods .= get( a:opts, 'term_rows', '' ) endif + + execute mods . 'new' endif " HACK: Neovim's termopen doesn't support env diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 149af0d..b5a0e9b 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -40,7 +40,6 @@ class CodeView( object ): 'breakpoints': [] } - with utils.LetCurrentWindow( self._window ): vim.command( 'nnoremenu WinBar.Continue :call vimspector#Continue()' ) vim.command( 'nnoremenu WinBar.Next :call vimspector#StepOver()' ) @@ -221,6 +220,7 @@ class CodeView( object ): options = { 'vertical': 1, + 'term_cols': 80, 'norestore': 1, 'cwd': cwd, 'env': env, From 0de023e3c47f453c184bb6fd2f139ab882d5eca4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 14:42:35 +0100 Subject: [PATCH 222/609] Re-raise errors that aren't expectd --- autoload/vimspector/internal/neojob.vim | 2 -- python3/vimspector/code.py | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index ced61ec..f005a62 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -39,8 +39,6 @@ function! s:_OnEvent( chan_id, data, event ) abort echom 'Channel exit with status ' . a:data redraw unlet s:job - " This causes terminal spam in neovim due to - " https://github.com/neovim/neovim/issues/11725 py3 _vimspector_session.OnServerExit( vim.eval( 'a:data' ) ) endif endfunction diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index b5a0e9b..b7e88d7 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -82,8 +82,8 @@ class CodeView( object ): frame[ 'source' ][ 'path' ] ) ) except vim.error as e: # Ignore 'invalid buffer name' - if 'E158' in str( e ): - pass + if 'E158' not in str( e ): + raise if not self._window.valid: return False From 99518e7eba1adf4cf86f209efa3b03ccfd2e05a3 Mon Sep 17 00:00:00 2001 From: Wayne Bowie Date: Tue, 14 Jul 2020 09:22:55 -0500 Subject: [PATCH 223/609] Added Windows support for C# debugging --- install_gadget.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/install_gadget.py b/install_gadget.py index 2c3a6ba..f805e80 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -254,6 +254,10 @@ GADGETS = { 'file_name': 'netcoredbg-linux-master.tar.gz', 'checksum': '', }, + 'windows': { + 'file_name': 'netcoredbg-win64-master.zip', + 'checksum': '', + }, 'do': lambda name, root, gadget: installer.MakeSymlink( gadget_dir, name, From f9f0d5a83eca658fccafae2f4346360d8374068c Mon Sep 17 00:00:00 2001 From: Wayne Bowie Date: Tue, 14 Jul 2020 12:23:09 -0500 Subject: [PATCH 224/609] No longer throws a KeyError when checking for non expensive scope --- python3/vimspector/variables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 22444f9..3856347 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -221,7 +221,7 @@ class VariablesView( object ): new_scopes.append( scope ) - if not scope.scope[ 'expensive' ] and not scope.IsCollapsedByUser(): + if not scope.scope.get('expensive', '') and not scope.IsCollapsedByUser(): # Expand any non-expensive scope which is not manually collapsed scope.expanded = True From f81cc126c3dabd77e9b0e8e90d93722b02202d59 Mon Sep 17 00:00:00 2001 From: Wayne Bowie Date: Tue, 14 Jul 2020 14:04:31 -0500 Subject: [PATCH 225/609] Format fix --- python3/vimspector/variables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 3856347..1cbf599 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -221,7 +221,7 @@ class VariablesView( object ): new_scopes.append( scope ) - if not scope.scope.get('expensive', '') and not scope.IsCollapsedByUser(): + if not scope.scope.get( 'expensive' ) and not scope.IsCollapsedByUser(): # Expand any non-expensive scope which is not manually collapsed scope.expanded = True From b3c86bc757f8618bbc8ce48108f7f525647603be Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 12 Jul 2020 13:57:18 +0100 Subject: [PATCH 226/609] Use the original java test code --- .../src/main/java/com/vimspector/test/Base.java | 7 +++++-- .../com/vimspector/test/TestApplication.java | 10 ---------- .../java/com/vimspector/test/TestGeneric.java | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 support/test/java/test_project/src/main/java/com/vimspector/test/TestGeneric.java diff --git a/support/test/java/test_project/src/main/java/com/vimspector/test/Base.java b/support/test/java/test_project/src/main/java/com/vimspector/test/Base.java index 25840d0..1cd89e5 100644 --- a/support/test/java/test_project/src/main/java/com/vimspector/test/Base.java +++ b/support/test/java/test_project/src/main/java/com/vimspector/test/Base.java @@ -1,8 +1,11 @@ package com.vimspector.test; -class Base { +public class Base +{ public String DoSomething() { - return ""; + String s = new String(); + s.replace( "A", "B" ); + return s; } } diff --git a/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java b/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java index 680dbb6..264c0e6 100644 --- a/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java +++ b/support/test/java/test_project/src/main/java/com/vimspector/test/TestApplication.java @@ -24,16 +24,6 @@ public class TestApplication { } } - private static class TestGeneric { - T t; - public TestGeneric( T t ) { - this.t = t; - } - public void DoSomethingUseful() { - System.out.println( t.DoSomething() ); - } - } - private static void DoGeneric( T b ) { TestGeneric foo = new TestGeneric<>( b ); foo.DoSomethingUseful(); diff --git a/support/test/java/test_project/src/main/java/com/vimspector/test/TestGeneric.java b/support/test/java/test_project/src/main/java/com/vimspector/test/TestGeneric.java new file mode 100644 index 0000000..6cd5e01 --- /dev/null +++ b/support/test/java/test_project/src/main/java/com/vimspector/test/TestGeneric.java @@ -0,0 +1,16 @@ +package com.vimspector.test; + +class TestGeneric { + T base; + + public TestGeneric( T b ) + { + this.base = b; + } + + public String DoSomethingUseful() { + String s = "A B C" + base.DoSomething(); + + return s.replace( "B", "C" ); + } +} From 97a5d737eee3bf8d50a70fa388735e8b3ad8e920 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 12 Jul 2020 14:00:46 +0100 Subject: [PATCH 227/609] Ignore venv and test basedir --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b09d196..64341cf 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ README.md.toc.* support/test/csharp/*.exe* .neomake.log configurations/ +venv/ +test-base/ From 8d2ea44cb99ce2109dde459a4ba121404dc62c03 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 15:11:39 +0100 Subject: [PATCH 228/609] Make Restart and Stop work properly (and together) --- python3/vimspector/debug_session.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 32f9be7..1302ab6 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -62,11 +62,13 @@ class DebugSession( object ): self._run_on_server_exit = None + self._configuration = None + self._adapter = None + self._ResetServerState() def _ResetServerState( self ): self._connection = None - self._configuration = None self._init_complete = False self._launch_complete = False self._on_init_complete_handlers = [] @@ -278,11 +280,7 @@ class DebugSession( object ): start() def Restart( self ): - # TODO: There is a restart message but isn't always supported. - # FIXME: For some reason this doesn't work when run from the WinBar. It just - # beeps and doesn't display the config selector. One option is to just not - # display the selector and restart with the same opitons. - if not self._configuration or not self._adapter: + if self._configuration is None or self._adapter is None: return self.Start() self._StartWithConfiguration( self._configuration, self._adapter ) @@ -294,7 +292,8 @@ class DebugSession( object ): if not self._connection: utils.UserMessage( 'Vimspector not connected, start a debug session first', - persist=True, error=True ) + persist=True, + error=True ) return return fct( self, *args, **kwargs ) return wrapper @@ -595,6 +594,9 @@ class DebugSession( object ): "Shutting down debug adapter..." ) def handler( *args ): + self._splash_screen = utils.HideSplash( self._api_prefix, + self._splash_screen ) + if callback: self._logger.debug( "Setting server exit handler before disconnect" ) assert not self._run_on_server_exit From 0938d72a8c3097d2b83b8e7e5f7d9cd35c87edcd Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 22:00:36 +0100 Subject: [PATCH 229/609] Re-use a window if we can, as it's more efficient; don't wipe out the generated code buffers in case the code window gets used for temporary buffer switch --- python3/vimspector/stack_trace.py | 9 ++++++++- python3/vimspector/utils.py | 28 ++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 4504d10..0afeb81 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -35,6 +35,7 @@ class StackTraceView( object ): self._threads = [] self._sources = {} + self._scratch_buffers = [] utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' ) @@ -82,6 +83,11 @@ class StackTraceView( object ): def Reset( self ): self.Clear() utils.CleanUpHiddenBuffer( self._buf ) + for b in self._scratch_buffers: + utils.CleanUpHiddenBuffer( b ) + + self._scratch_buffers = [] + self._buf = None def LoadThreads( self, infer_current_frame ): pending_request = False @@ -290,7 +296,8 @@ class StackTraceView( object ): self._logger.debug( "Received source %s: %s", buf_name, msg ) buf = utils.BufferForFile( buf_name ) - utils.SetUpScratchBuffer( buf, buf_name ) + self._scratch_buffers.append( buf ) + utils.SetUpHiddenBuffer( buf, buf_name ) source[ 'path' ] = buf_name with utils.ModifiableScratchBuffer( buf ): utils.SetBufferContents( buf, msg[ 'body' ][ 'content' ] ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 03dbb4d..9fc1206 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -52,6 +52,14 @@ def BufferForFile( file_name ): return vim.buffers[ BufferNumberForFile( file_name ) ] +def WindowForBuffer( buf ): + for w in vim.current.tabpage.windows: + if w.buffer == buf: + return w + + return None + + def OpenFileInCurrentWindow( file_name ): buffer_number = BufferNumberForFile( file_name ) try: @@ -190,6 +198,18 @@ def RestoreCurrentBuffer( window ): vim.current.buffer = old_buffer +@contextlib.contextmanager +def AnyWindowForBuffer( buf ): + # Only checks the current tab page, which is what we want + current_win = WindowForBuffer( buf ) + if current_win is not None: + with LetCurrentWindow( current_win ): + yield + else: + with LetCurrentBuffer( buf ): + yield + + @contextlib.contextmanager def LetCurrentWindow( window ): with RestoreCurrentWindow(): @@ -566,11 +586,11 @@ def SetSyntax( current_syntax, syntax, *args ): if current_syntax == syntax: return + # We use set syn= because just setting vim.Buffer.options[ 'syntax' ] + # doesn't actually trigger the Syntax autocommand, and i'm not sure that + # 'doautocmd Syntax' is the right solution or not for buf in args: - with LetCurrentBuffer( buf ): - # We use set syn= because just setting vim.Buffer.options[ 'syntax' ] - # doesn't actually trigger the Syntax autocommand, and i'm not sure that - # 'doautocmd Syntax' is the right solution or not + with AnyWindowForBuffer( buf ): vim.command( 'set syntax={}'.format( Escape( syntax ) ) ) return syntax From fc7725fefb8d67b704a124ea8debb4d9afa7cffb Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 22:01:04 +0100 Subject: [PATCH 230/609] Only expand one inexpensive scope; codeLLDB lies about how expensive things are --- python3/vimspector/variables.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 1cbf599..4186f68 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -205,6 +205,7 @@ class VariablesView( object ): def LoadScopes( self, frame ): def scopes_consumer( message ): new_scopes = [] + expanded_some_scope = False for scope_body in message[ 'body' ][ 'scopes' ]: # Find it in the scopes list found = False @@ -221,9 +222,12 @@ class VariablesView( object ): new_scopes.append( scope ) - if not scope.scope.get( 'expensive' ) and not scope.IsCollapsedByUser(): - # Expand any non-expensive scope which is not manually collapsed + # Expand the first non-expensive scope which is not manually collapsed + if ( not expanded_some_scope + and not scope.scope.get( 'expensive' ) + and not scope.IsCollapsedByUser() ): scope.expanded = True + expanded_some_scope = True if scope.IsExpandedByUser(): self._connection.DoRequest( partial( self._ConsumeVariables, From 434a6f41d444db0fbad48e717e0cb938992e137e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 22:51:51 +0100 Subject: [PATCH 231/609] Only expand one cheap scope by default - don't keep others expanded from previous frames --- python3/vimspector/variables.py | 41 ++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 4186f68..eaed8bb 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -23,6 +23,11 @@ from vimspector import utils class Expandable: + EXPANDED_BY_USER = 2 + EXPANDED_BY_US = 1 + COLLAPSED_BY_USER = 0 + COLLAPSED_BY_DEFAULT = None + """Base for anything which might contain a hierarchy of values represented by a 'variablesReference' to be resolved by the 'variables' request. Records the current state expanded/collapsed. Implementations just implement @@ -32,16 +37,13 @@ class Expandable: # None is Falsy and represents collapsed _by default_. WHen set to False, # this means the user explicitly collapsed it. When True, the user expanded # it (or we expanded it by default). - self.expanded: bool = None + self.expanded: int = Expandable.COLLAPSED_BY_DEFAULT - def IsCollapsedByUser( self ): - return self.expanded is False - - def IsExpandedByUser( self ): - return self.expanded is True + def IsExpanded( self ): + return bool( self.expanded ) def ShouldDrawDrillDown( self ): - return self.IsExpandedByUser() and self.variables is not None + return self.IsExpanded() and self.variables is not None def IsExpandable( self ): return self.VariablesReference() > 0 @@ -225,11 +227,14 @@ class VariablesView( object ): # Expand the first non-expensive scope which is not manually collapsed if ( not expanded_some_scope and not scope.scope.get( 'expensive' ) - and not scope.IsCollapsedByUser() ): - scope.expanded = True + and scope.expanded is not Expandable.COLLAPSED_BY_USER ): + scope.expanded = Expandable.EXPANDED_BY_US expanded_some_scope = True + elif ( expanded_some_scope and scope.expanded is + Expandable.EXPANDED_BY_US ): + scope.expanded = Expandable.COLLAPSED_BY_DEFAULT - if scope.IsExpandedByUser(): + if scope.IsExpanded(): self._connection.DoRequest( partial( self._ConsumeVariables, self._DrawScopes, scope ), { @@ -297,7 +302,7 @@ class VariablesView( object ): watch.result = WatchResult( message[ 'body' ] ) if ( watch.result.IsExpandable() and - watch.result.IsExpandedByUser() ): + watch.result.IsExpanded() ): self._connection.DoRequest( partial( self._ConsumeVariables, self._watch.draw, watch.result ), { @@ -323,16 +328,16 @@ class VariablesView( object ): variable = view.lines[ current_line ] - if variable.expanded: + if variable.IsExpanded(): # Collapse - variable.expanded = False + variable.expanded = Expandable.COLLAPSED_BY_USER view.draw() return if not variable.IsExpandable(): return - variable.expanded = True + variable.expanded = Expandable.EXPANDED_BY_USER self._connection.DoRequest( partial( self._ConsumeVariables, view.draw, variable ), { @@ -352,7 +357,7 @@ class VariablesView( object ): indent = ' ' * ( indent - 1 ), marker = '*' if variable.changed else ' ', icon = '+' if ( variable.IsExpandable() - and not variable.IsExpandedByUser() ) else '-', + and not variable.IsExpanded() ) else '-', name = variable.variable[ 'name' ], type_ = variable.variable.get( 'type', '' ), value = variable.variable.get( 'value', @@ -390,7 +395,7 @@ class VariablesView( object ): self._DrawWatchResult( 2, watch ) def _DrawScope( self, indent, scope ): - icon = '+' if scope.IsExpandable() and not scope.IsExpandedByUser() else '-' + icon = '+' if scope.IsExpandable() and not scope.IsExpanded() else '-' line = utils.AppendToBuffer( self._vars.buf, '{0}{1} Scope: {2}'.format( @@ -409,7 +414,7 @@ class VariablesView( object ): assert indent > 0 icon = '+' if ( watch.result.IsExpandable() and - not watch.result.IsExpandedByUser() ) else '-' + not watch.result.IsExpanded() ) else '-' line = '{indent}{marker}{icon} Result: {result}'.format( # We borrow 1 space of indent to draw the change marker @@ -446,7 +451,7 @@ class VariablesView( object ): new_variables.append( variable ) - if variable.IsExpandable() and variable.IsExpandedByUser(): + if variable.IsExpandable() and variable.IsExpanded(): self._connection.DoRequest( partial( self._ConsumeVariables, draw, variable ), { From 2440a987b26b4adbe51eddb76eab310d668ba7ab Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 14 Jul 2020 20:37:35 +0100 Subject: [PATCH 232/609] Tidy unknwon types and values --- python3/vimspector/variables.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index eaed8bb..f6df06c 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -359,9 +359,9 @@ class VariablesView( object ): icon = '+' if ( variable.IsExpandable() and not variable.IsExpanded() ) else '-', name = variable.variable[ 'name' ], - type_ = variable.variable.get( 'type', '' ), + type_ = variable.variable.get( 'type', '' ), value = variable.variable.get( 'value', - '' ) ).split( '\n' ) ) + '' ) ).split( '\n' ) ) view.lines[ line ] = variable if variable.ShouldDrawDrillDown(): From a4f3fd9c5e47a561ce6ae298b06d920c768936b0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 16 Jul 2020 08:13:27 +0100 Subject: [PATCH 233/609] Disable relativenumber in utility windows; disable textwidth in neovim too --- autoload/vimspector/internal/neojob.vim | 1 + python3/vimspector/utils.py | 1 + 2 files changed, 2 insertions(+) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index f005a62..8bcae90 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -176,6 +176,7 @@ function! s:SetUpHiddenBuffer( buffer ) abort call setbufvar( a:buffer, '&bufhidden', 'hide' ) call setbufvar( a:buffer, '&wrap', 0 ) call setbufvar( a:buffer, '&swapfile', 0 ) + call setbufvar( a:buffer, '&textwidth', 0 ) call s:MakeBufferReadOnly( a:buffer ) endfunction diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 9fc1206..48991f7 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -144,6 +144,7 @@ def SetUpPromptBuffer( buf, name, prompt, callback ): def SetUpUIWindow( win ): win.options[ 'wrap' ] = False win.options[ 'number' ] = False + win.options[ 'relativenumber' ] = False win.options[ 'signcolumn' ] = 'no' win.options[ 'spell' ] = False win.options[ 'list' ] = False From c6e8e8038fde5becd183b9f1bf7d4dffea89f775 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 16:51:18 +0100 Subject: [PATCH 234/609] Remove hard-coded terminal width which doesn't work on smaller width screens --- python3/vimspector/code.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index b7e88d7..b826d21 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -220,7 +220,6 @@ class CodeView( object ): options = { 'vertical': 1, - 'term_cols': 80, 'norestore': 1, 'cwd': cwd, 'env': env, From de2a924c3883a3bbbcbeba1e772661694cc5bcd1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 11 Jul 2020 15:12:28 +0100 Subject: [PATCH 235/609] 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 236/609] 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 237/609] 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 238/609] 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 239/609] 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 240/609] 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 241/609] 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 242/609] 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 243/609] 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 244/609] 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 245/609] 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 246/609] 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 247/609] 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 248/609] 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 249/609] 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 250/609] 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 251/609] 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 252/609] 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 253/609] 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 254/609] 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 255/609] 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 256/609] 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 257/609] 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! From e885c95daae2387b66cbe05318e30870dcdd07d9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 18 Jul 2020 18:09:25 +0100 Subject: [PATCH 258/609] Put the cursor at the end of the buffer after evaluations --- python3/vimspector/output.py | 17 +++----- tests/lib/autoload/vimspector/test/signs.vim | 4 +- tests/variables.test.vim | 43 ++++++++++++++++++++ 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index c21f8f1..a359448 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -126,20 +126,13 @@ class OutputView( object ): self._ShowOutput( category ) def Evaluate( self, frame, expression ): - console = self._buffers[ 'Console' ].buf - with utils.ModifiableScratchBuffer( console ): - utils.AppendToBuffer( console, 'Evaluating: ' + expression ) + self._Print( 'Console', [ 'Evaluating: ' + expression ] ) def print_result( message ): - with utils.ModifiableScratchBuffer( console ): - utils.AppendToBuffer( console, - 'Evaluated: ' + expression ) - - result = message[ 'body' ][ 'result' ] - if result is None: - result = 'null' - - utils.AppendToBuffer( console, ' Result: ' + result ) + result = message[ 'body' ][ 'result' ] + if result is None: + result = '' + self._Print( 'Console', f' Result: { result }' ) request = { 'command': 'evaluate', diff --git a/tests/lib/autoload/vimspector/test/signs.vim b/tests/lib/autoload/vimspector/test/signs.vim index bd7a463..571353e 100644 --- a/tests/lib/autoload/vimspector/test/signs.vim +++ b/tests/lib/autoload/vimspector/test/signs.vim @@ -9,7 +9,9 @@ function! vimspector#test#signs#AssertCursorIsAtLineInBuffer( buffer, call WaitForAssert( {-> \ assert_equal( a:line, line( '.' ), 'Current line' ) \ }, 10000 ) - call assert_equal( a:column, col( '.' ), 'Current column' ) + if a:column isnot v:null + call assert_equal( a:column, col( '.' ), 'Current column' ) + endif endfunction function! vimspector#test#signs#AssertPCIsAtLineInBuffer( buffer, line ) abort diff --git a/tests/variables.test.vim b/tests/variables.test.vim index 1b10809..733061f 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -479,3 +479,46 @@ function! Test_ExpandWatch() call vimspector#test#setup#Reset() %bwipe! endfunction + + +function Test_EvaluateConsole() + 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 vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 27, 1 ) + + VimspectorEval t.i + call assert_equal( bufnr( 'vimspector.Console' ), + \ winbufnr( g:vimspector_session_windows.output ) ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ ' Result: 1' + \ ], + \ getbufline( bufnr( 'vimspector.Console' ), '$', '$' ) + \ ) + \ } ) + + let len = getbufinfo( 'vimspector.Console' )[ 0 ].linecount + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Evaluating: t.i', + \ ' Result: 1' + \ ], + \ getbufline( bufnr( 'vimspector.Console' ), len-1, '$' ) + \ ) + \ } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( + \ 'vimspector.Console', len, v:null ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction From 0c6715e95e5e2f15cb9258c8f7846844b9272c28 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 21 Jul 2020 23:24:53 +0100 Subject: [PATCH 259/609] Charityware --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 9813538..c75d71b 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ For a tutorial and usage overview, take a look at the * [FAQ](#faq) * [Motivation](#motivation) * [License](#license) + * [Sponsorship](#sponsorship) @@ -1527,6 +1528,14 @@ A message from the author about the motivation for this plugin: Copyright © 2018 Ben Jackson +# Sponsorship + +If you like Vimspector so much that you're wiling to part with your hard-earned cash, please consider donating to one of the following charities, which are meaningful to the author of Vimspector (in order of prefernce): + +* [Greyhound Rescue Wales](https://greyhoundrescuewales.co.uk) +* [Cancer Research UK](https://www.cancerresearchuk.org) +* [ICCF Holland](https://iccf.nl) + [ycmd]: https://github.com/Valloric/ycmd [gitter]: https://gitter.im/vimspector/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link [java-debug-server]: https://github.com/Microsoft/java-debug From daf854e3e1eeb51ed2b5c724b475a2aef1883b0e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 21 Jul 2020 23:26:34 +0100 Subject: [PATCH 260/609] Any charity --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c75d71b..f70f537 100644 --- a/README.md +++ b/README.md @@ -1535,6 +1535,7 @@ If you like Vimspector so much that you're wiling to part with your hard-earned * [Greyhound Rescue Wales](https://greyhoundrescuewales.co.uk) * [Cancer Research UK](https://www.cancerresearchuk.org) * [ICCF Holland](https://iccf.nl) +* Any charity of your choosing. [ycmd]: https://github.com/Valloric/ycmd [gitter]: https://gitter.im/vimspector/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link From 43d438dfc9456585a1e837e6e9b60e9b2cae5946 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 09:53:03 +0100 Subject: [PATCH 261/609] Better python diags --- .github/ISSUE_TEMPLATE/bug_report.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1aea572..887fbb1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -54,7 +54,7 @@ paste here paste here ``` -* Output of `:py3 pass`: +* Output of `:py3 print( __import__( 'sys' ).version )`: ``` paste here @@ -66,6 +66,13 @@ paste here paste here ``` +* Output of `:py3 import vimspector`: + +``` +paste here +``` + + * For neovim: output of `:checkhealth` ``` From d3fd0a38f097e0259c1fa0d4db45e72798cf24e9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 27 May 2020 21:15:00 +0100 Subject: [PATCH 262/609] Refactor installer fully into installer module --- install_gadget.py | 646 +------------------------------- python3/vimspector/gadgets.py | 490 ++++++++++++++++++++++++ python3/vimspector/installer.py | 207 +++++++++- 3 files changed, 692 insertions(+), 651 deletions(-) create mode 100644 python3/vimspector/gadgets.py diff --git a/install_gadget.py b/install_gadget.py index f805e80..af2794e 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -23,9 +23,6 @@ if sys.version_info.major < 3: import argparse import os -import string -import subprocess -import traceback import json import functools import operator @@ -35,628 +32,9 @@ import glob sys.path.insert( 1, os.path.join( os.path.dirname( __file__ ), 'python3' ) ) -from vimspector import install, installer +from vimspector import install, installer, gadgets from vimspector.vendor.json_minify import minify -GADGETS = { - 'vscode-cpptools': { - 'language': 'c', - 'download': { - 'url': 'https://github.com/Microsoft/vscode-cpptools/releases/download/' - '${version}/${file_name}', - }, - 'do': lambda name, root, gadget: InstallCppTools( name, root, gadget ), - 'all': { - 'version': '0.27.0', - "adapters": { - "vscode-cpptools": { - "name": "cppdbg", - "command": [ - "${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7" - ], - "attach": { - "pidProperty": "processId", - "pidSelect": "ask" - }, - "configuration": { - "type": "cppdbg", - "args": [], - "cwd": "${workspaceRoot}", - "environment": [], - } - }, - }, - }, - 'linux': { - 'file_name': 'cpptools-linux.vsix', - 'checksum': - '3695202e1e75a03de18049323b66d868165123f26151f8c974a480eaf0205435', - }, - 'macos': { - 'file_name': 'cpptools-osx.vsix', - 'checksum': - 'cb061e3acd7559a539e5586f8d3f535101c4ec4e8a48195856d1d39380b5cf3c', - }, - 'windows': { - 'file_name': 'cpptools-win32.vsix', - 'checksum': - 'aa294368ed16d48c59e49c8000e146eae5a19ad07b654efed5db8ec93b24229e', - "adapters": { - "vscode-cpptools": { - "name": "cppdbg", - "command": [ - "${gadgetDir}/vscode-cpptools/debugAdapters/bin/OpenDebugAD7.exe" - ], - "attach": { - "pidProperty": "processId", - "pidSelect": "ask" - }, - "configuration": { - "type": "cppdbg", - "args": [], - "cwd": "${workspaceRoot}", - "environment": [], - "MIMode": "gdb", - "MIDebuggerPath": "gdb.exe" - } - }, - }, - }, - }, - 'vscode-python': { - 'language': 'python.legacy', - 'enabled': False, - 'download': { - 'url': 'https://github.com/Microsoft/vscode-python/releases/download/' - '${version}/${file_name}', - }, - 'all': { - 'version': '2019.11.50794', - 'file_name': 'ms-python-release.vsix', - 'checksum': - '6a9edf9ecabed14aac424e6007858068204a3638bf3bb4f235bd6035d823acc6', - }, - 'adapters': { - "vscode-python": { - "name": "vscode-python", - "command": [ - "node", - "${gadgetDir}/vscode-python/out/client/debugger/debugAdapter/main.js", - ], - } - }, - }, - 'debugpy': { - 'language': 'python', - 'download': { - 'url': 'https://github.com/microsoft/debugpy/archive/${file_name}' - }, - 'all': { - 'version': '1.0.0b12', - 'file_name': 'v1.0.0b12.zip', - 'checksum': - '210632bba2221fbb841c9785a615258819ceec401d1abdbeb5f2326f12cc72a1' - }, - 'do': lambda name, root, gadget: InstallDebugpy( name, root, gadget ), - 'adapters': { - 'debugpy': { - "command": [ - sys.executable, - "${gadgetDir}/debugpy/build/lib/debugpy/adapter" - ], - "name": "debugpy", - "configuration": { - "python": sys.executable, - # Don't debug into subprocesses, as this leads to problems (vimspector - # doesn't support the custom messages) - # https://github.com/puremourning/vimspector/issues/141 - "subProcess": False, - } - } - }, - }, - 'vscode-java-debug': { - 'language': 'java', - 'enabled': False, - 'download': { - 'url': 'https://github.com/microsoft/vscode-java-debug/releases/download/' - '${version}/${file_name}', - }, - 'all': { - 'version': '0.26.0', - 'file_name': 'vscjava.vscode-java-debug-0.26.0.vsix', - 'checksum': - 'de49116ff3a3c941dad0c36d9af59baa62cd931e808a2ab392056cbb235ad5ef', - }, - 'adapters': { - "vscode-java": { - "name": "vscode-java", - "port": "${DAPPort}", - } - }, - }, - 'java-language-server': { - 'language': 'javac', - 'enabled': False, - 'download': { - 'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/' - 'publishers/georgewfraser/vsextensions/vscode-javac/${version}/' - 'vspackage', - 'target': 'georgewfraser.vscode-javac-0.2.31.vsix.gz', - 'format': 'zip.gz', - }, - 'all': { - 'version': '0.2.31', - 'file_name': 'georgewfraser.vscode-javac-0.2.31.vsix.gz', - 'checksum': - '5b0248ec1198d3ece9a9c6b9433b30c22e308f0ae6e4c7bd09cd943c454e3e1d', - }, - 'adapters': { - "vscode-javac": { - "name": "vscode-javac", - "type": "vscode-javac", - "command": [ - "${gadgetDir}/java-language-server/dist/debug_adapter_mac.sh" - ], - "attach": { - "pidSelect": "none" - } - } - }, - }, - 'tclpro': { - 'language': 'tcl', - 'repo': { - 'url': 'https://github.com/puremourning/TclProDebug', - 'ref': 'master' - }, - 'do': lambda name, root, gadget: InstallTclProDebug( name, root, gadget ), - 'adapters': { - "tclpro": { - "name": "tclpro", - "type": "tclpro", - "command": [ - "${gadgetDir}/tclpro/bin/debugadapter" - ], - "attach": { - "pidSelect": "none" - }, - "configuration": { - "target": "${file}", - "args": [ "*${args}" ], - "tclsh": "tclsh", - "cwd": "${workspaceRoot}", - "extensionDirs": [ - "${workspaceRoot}/.tclpro/extensions", - "${HOME}/.tclpro/extensions", - ] - } - } - }, - }, - 'netcoredbg': { - 'language': 'csharp', - 'enabled': False, - 'download': { - 'url': 'https://github.com/Samsung/netcoredbg/releases/download/latest/' - '${file_name}', - 'format': 'tar', - }, - 'all': { - 'version': 'master' - }, - 'macos': { - 'file_name': 'netcoredbg-osx-master.tar.gz', - 'checksum': - 'c1dc6ed58c3f5b0473cfb4985a96552999360ceb9795e42d9c9be64af054f821', - }, - 'linux': { - 'file_name': 'netcoredbg-linux-master.tar.gz', - 'checksum': '', - }, - 'windows': { - 'file_name': 'netcoredbg-win64-master.zip', - 'checksum': '', - }, - 'do': lambda name, root, gadget: installer.MakeSymlink( - gadget_dir, - name, - os.path.join( root, 'netcoredbg' ) ), - 'adapters': { - 'netcoredbg': { - "name": "netcoredbg", - "command": [ - "${gadgetDir}/netcoredbg/netcoredbg", - "--interpreter=vscode" - ], - "attach": { - "pidProperty": "processId", - "pidSelect": "ask" - }, - }, - } - }, - 'vscode-mono-debug': { - 'language': 'csharp', - 'enabled': False, - 'download': { - 'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/' - 'publishers/ms-vscode/vsextensions/mono-debug/${version}/' - 'vspackage', - 'target': 'vscode-mono-debug.vsix.gz', - 'format': 'zip.gz', - }, - 'all': { - 'file_name': 'vscode-mono-debug.vsix', - 'version': '0.15.8', - 'checksum': - '723eb2b621b99d65a24f215cb64b45f5fe694105613a900a03c859a62a810470', - }, - 'adapters': { - 'vscode-mono-debug': { - "name": "mono-debug", - "command": [ - "mono", - "${gadgetDir}/vscode-mono-debug/bin/Release/mono-debug.exe" - ], - "attach": { - "pidSelect": "none" - }, - }, - } - }, - 'vscode-bash-debug': { - 'language': 'bash', - 'download': { - 'url': 'https://github.com/rogalmic/vscode-bash-debug/releases/' - 'download/${version}/${file_name}', - }, - 'all': { - 'file_name': 'bash-debug-0.3.7.vsix', - 'version': 'v0.3.7', - 'checksum': - '7b73e5b4604375df8658fb5a72c645c355785a289aa785a986e508342c014bb4', - }, - 'do': lambda name, root, gadget: InstallBashDebug( name, root, gadget ), - 'adapters': { - "vscode-bash": { - "name": "bashdb", - "command": [ - "node", - "${gadgetDir}/vscode-bash-debug/out/bashDebug.js" - ], - "variables": { - "BASHDB_HOME": "${gadgetDir}/vscode-bash-debug/bashdb_dir" - }, - "configuration": { - "request": "launch", - "type": "bashdb", - "program": "${file}", - "args": [], - "env": {}, - "pathBash": "bash", - "pathBashdb": "${BASHDB_HOME}/bashdb", - "pathBashdbLib": "${BASHDB_HOME}", - "pathCat": "cat", - "pathMkfifo": "mkfifo", - "pathPkill": "pkill", - "cwd": "${workspaceRoot}", - "terminalKind": "integrated", - } - } - } - }, - 'vscode-go': { - 'language': 'go', - 'download': { - 'url': 'https://github.com/microsoft/vscode-go/releases/download/' - '${version}/${file_name}' - }, - 'all': { - 'version': '0.11.4', - 'file_name': 'Go-0.11.4.vsix', - 'checksum': - 'ff7d7b944da5448974cb3a0086f4a2fd48e2086742d9c013d6964283d416027e' - }, - 'adapters': { - 'vscode-go': { - 'name': 'delve', - 'command': [ - 'node', - '${gadgetDir}/vscode-go/out/src/debugAdapter/goDebug.js' - ], - }, - }, - }, - 'vscode-php-debug': { - 'language': 'php', - 'enabled': False, - 'download': { - 'url': - 'https://github.com/felixfbecker/vscode-php-debug/releases/download/' - '${version}/${file_name}', - }, - 'all': { - 'version': 'v1.13.0', - 'file_name': 'php-debug.vsix', - 'checksum': - '8a51e593458fd14623c1c89ebab87347b087d67087717f18bcf77bb788052718', - }, - 'adapters': { - 'vscode-php-debug': { - 'name': "php-debug", - 'command': [ - 'node', - "${gadgetDir}/vscode-php-debug/out/phpDebug.js", - ] - } - } - }, - 'vscode-node-debug2': { - 'language': 'node', - 'enabled': False, - 'repo': { - 'url': 'https://github.com/microsoft/vscode-node-debug2', - 'ref': 'v1.42.0', - }, - 'do': lambda name, root, gadget: InstallNodeDebug( name, root, gadget ), - 'adapters': { - 'vscode-node': { - 'name': 'node2', - 'type': 'node2', - 'command': [ - 'node', - '${gadgetDir}/vscode-node-debug2/out/src/nodeDebug.js' - ] - }, - }, - }, - 'debugger-for-chrome': { - 'language': 'chrome', - 'enabled': False, - 'download': { - 'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/' - 'publishers/msjsdiag/vsextensions/' - 'debugger-for-chrome/${version}/vspackage', - 'target': 'msjsdiag.debugger-for-chrome-4.12.0.vsix.gz', - 'format': 'zip.gz', - }, - 'all': { - 'version': '4.12.0', - 'file_name': 'msjsdiag.debugger-for-chrome-4.12.0.vsix', - 'checksum': - '0df2fe96d059a002ebb0936b0003e6569e5a5c35260dc3791e1657d27d82ccf5' - }, - 'adapters': { - 'chrome': { - 'name': 'debugger-for-chrome', - 'type': 'chrome', - 'command': [ - 'node', - '${gadgetDir}/debugger-for-chrome/out/src/chromeDebug.js' - ], - }, - }, - }, - 'CodeLLDB': { - 'language': 'rust', - 'enabled': False, - 'download': { - 'url': 'https://github.com/vadimcn/vscode-lldb/releases/download/' - '${version}/${file_name}', - }, - 'all': { - 'version': 'v1.5.3', - }, - 'macos': { - 'file_name': 'codelldb-x86_64-darwin.vsix', - 'checksum': - '7505bc1cdfcfd1cb981e2996aec62d63577440709bac31dcadb41a3b4b44631a', - 'make_executable': [ - 'adapter/codelldb', - 'lldb/bin/debugserver', - 'lldb/bin/lldb', - 'lldb/bin/lldb-argdumper', - ], - }, - 'linux': { - 'file_name': 'codelldb-x86_64-linux.vsix', - 'checksum': - 'ce7efc3e94d775368e5942a02bf5c326b6809a0b4c389f79ffa6a8f6f6b72139', - 'make_executable': [ - 'adapter/codelldb', - 'lldb/bin/lldb', - 'lldb/bin/lldb-server', - 'lldb/bin/lldb-argdumper', - ], - }, - 'windows': { - 'file_name': 'codelldb-x86_64-windows.vsix', - 'checksum': - '', - 'make_executable': [] - }, - 'adapters': { - 'CodeLLDB': { - 'name': 'CodeLLDB', - 'type': 'CodeLLDB', - "command": [ - "${gadgetDir}/CodeLLDB/adapter/codelldb", - "--port", "${unusedLocalPort}" - ], - "port": "${unusedLocalPort}", - "configuration": { - "type": "lldb", - "name": "lldb", - "cargo": {}, - "args": [], - "cwd": "${workspaceRoot}", - "env": {}, - "terminal": "integrated", - } - }, - }, - }, -} - - -def InstallGeneric( name, root, gadget ): - extension = os.path.join( root, 'extension' ) - for f in gadget.get( 'make_executable', [] ): - installer.MakeExecutable( os.path.join( extension, f ) ) - - installer.MakeExtensionSymlink( vimspector_base, name, root ) - - -def InstallCppTools( name, root, gadget ): - extension = os.path.join( root, 'extension' ) - - # It's hilarious, but the execute bits aren't set in the vsix. So they - # actually have javascript code which does this. It's just a horrible horrible - # hack that really is not funny. - installer.MakeExecutable( os.path.join( extension, - 'debugAdapters', - 'OpenDebugAD7' ) ) - with open( os.path.join( extension, 'package.json' ) ) as f: - package = json.load( f ) - runtime_dependencies = package[ 'runtimeDependencies' ] - for dependency in runtime_dependencies: - for binary in dependency.get( 'binaries' ): - file_path = os.path.abspath( os.path.join( extension, binary ) ) - if os.path.exists( file_path ): - installer.MakeExecutable( os.path.join( extension, binary ) ) - - installer.MakeExtensionSymlink( vimspector_base, name, root ) - - -def InstallBashDebug( name, root, gadget ): - installer.MakeExecutable( os.path.join( root, - 'extension', - 'bashdb_dir', - 'bashdb' ) ) - installer.MakeExtensionSymlink( vimspector_base, name, root ) - - -def InstallDebugpy( name, root, gadget ): - wd = os.getcwd() - root = os.path.join( root, 'debugpy-{}'.format( gadget[ 'version' ] ) ) - os.chdir( root ) - try: - subprocess.check_call( [ sys.executable, 'setup.py', 'build' ] ) - finally: - os.chdir( wd ) - - installer.MakeSymlink( gadget_dir, name, root ) - - -def InstallTclProDebug( name, root, gadget ): - configure = [ './configure' ] - - if OS == 'macos': - # Apple removed the headers from system frameworks because they are - # determined to make life difficult. And the TCL configure scripts are super - # old so don't know about this. So we do their job for them and try and find - # a tclConfig.sh. - # - # NOTE however that in Apple's infinite wisdom, installing the "headers" in - # the other location is actually broken because the paths in the - # tclConfig.sh are pointing at the _old_ location. You actually do have to - # run the package installation which puts the headers back in order to work. - # This is why the below list is does not contain stuff from - # /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform - # '/Applications/Xcode.app/Contents/Developer/Platforms' - # '/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System' - # '/Library/Frameworks/Tcl.framework', - # '/Applications/Xcode.app/Contents/Developer/Platforms' - # '/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System' - # '/Library/Frameworks/Tcl.framework/Versions' - # '/Current', - for p in [ '/usr/local/opt/tcl-tk/lib' ]: - if os.path.exists( os.path.join( p, 'tclConfig.sh' ) ): - configure.append( '--with-tcl=' + p ) - break - - - with installer.CurrentWorkingDir( os.path.join( root, 'lib', 'tclparser' ) ): - subprocess.check_call( configure ) - subprocess.check_call( [ 'make' ] ) - - installer.MakeSymlink( gadget_dir, name, root ) - - -def InstallNodeDebug( name, root, gadget ): - node_version = subprocess.check_output( [ 'node', '--version' ], - universal_newlines=True ).strip() - print( "Node.js version: {}".format( node_version ) ) - if list( map( int, node_version[ 1: ].split( '.' ) ) ) >= [ 12, 0, 0 ]: - print( "Can't install vscode-debug-node2:" ) - print( "Sorry, you appear to be running node 12 or later. That's not " - "compatible with the build system for this extension, and as far as " - "we know, there isn't a pre-built independent package." ) - print( "My advice is to install nvm, then do:" ) - print( " $ nvm install --lts 10" ) - print( " $ nvm use --lts 10" ) - print( " $ ./install_gadget.py --enable-node ..." ) - raise RuntimeError( 'Invalid node environent for node debugger' ) - - with installer.CurrentWorkingDir( root ): - subprocess.check_call( [ 'npm', 'install' ] ) - subprocess.check_call( [ 'npm', 'run', 'build' ] ) - installer.MakeSymlink( gadget_dir, name, root ) - - -def InstallGagdet( name, gadget, failed, all_adapters ): - try: - v = {} - v.update( gadget.get( 'all', {} ) ) - v.update( gadget.get( OS, {} ) ) - - if 'download' in gadget: - if 'file_name' not in v: - raise RuntimeError( "Unsupported OS {} for gadget {}".format( OS, - name ) ) - - destination = os.path.join( gadget_dir, 'download', name, v[ 'version' ] ) - - url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v ) - - file_path = installer.DownloadFileTo( - url, - destination, - file_name = gadget[ 'download' ].get( 'target' ), - checksum = v.get( 'checksum' ), - check_certificate = not args.no_check_certificate ) - - root = os.path.join( destination, 'root' ) - installer.ExtractZipTo( - file_path, - root, - format = gadget[ 'download' ].get( 'format', 'zip' ) ) - elif 'repo' in gadget: - url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( v ) - ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( v ) - - destination = os.path.join( gadget_dir, 'download', name ) - installer.CloneRepoTo( url, ref, destination ) - root = destination - - if 'do' in gadget: - gadget[ 'do' ]( name, root, v ) - else: - InstallGeneric( name, root, v ) - - # Allow per-OS adapter overrides. v already did that for us... - all_adapters.update( v.get( 'adapters', {} ) ) - # Add any other "all" adapters - all_adapters.update( gadget.get( 'adapters', {} ) ) - - print( "Done installing {}".format( name ) ) - except Exception as e: - traceback.print_exc() - failed.append( name ) - print( "FAILED installing {}: {}".format( name, e ) ) - - # ------------------------------------------------------------------------------ # Entry point # ------------------------------------------------------------------------------ @@ -723,7 +101,7 @@ parser.add_argument( '--sudo', "run this as root via sudo, pass this flag." ) done_languages = set() -for name, gadget in GADGETS.items(): +for name, gadget in gadgets.GADGETS.items(): lang = gadget[ 'language' ] if lang in done_languages: continue @@ -766,12 +144,8 @@ if args.basedir: vimspector_base = os.path.abspath( args.basedir ) install.MakeInstallDirs( vimspector_base ) - -OS = install.GetOS() -gadget_dir = install.GetGadgetDir( vimspector_base, OS ) - -print( 'OS = ' + OS ) -print( 'gadget_dir = ' + gadget_dir ) +installer.Configure( vimspector_base = vimspector_base, + no_check_certificate = args.no_check_certificate ) if args.force_all and not args.all: args.all = True @@ -803,7 +177,7 @@ all_adapters.update( { }, } ) -for name, gadget in GADGETS.items(): +for name, gadget in gadgets.GADGETS.items(): if not gadget.get( 'enabled', True ): if ( not args.force_all and not getattr( args, 'force_enable_' + gadget[ 'language' ] ) ): @@ -814,14 +188,14 @@ for name, gadget in GADGETS.items(): if getattr( args, 'disable_' + gadget[ 'language' ] ): continue - InstallGagdet( name, - gadget, - failed, - all_adapters ) + installer.InstallGagdet( name, + gadget, + failed, + all_adapters ) for name, gadget in CUSTOM_GADGETS.items(): - InstallGagdet( name, gadget, failed, all_adapters ) + installer.InstallGagdet( name, gadget, failed, all_adapters ) adapter_config = json.dumps ( { 'adapters': all_adapters }, indent=2, diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py new file mode 100644 index 0000000..a96c9f5 --- /dev/null +++ b/python3/vimspector/gadgets.py @@ -0,0 +1,490 @@ +# 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. + + +from vimspector import installer +import sys +import os + + +GADGETS = { + 'vscode-cpptools': { + 'language': 'c', + 'download': { + 'url': 'https://github.com/Microsoft/vscode-cpptools/releases/download/' + '${version}/${file_name}', + }, + 'do': lambda name, root, gadget: installer.InstallCppTools( name, + root, + gadget ), + 'all': { + 'version': '0.27.0', + "adapters": { + "vscode-cpptools": { + "name": "cppdbg", + "command": [ + "${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7" + ], + "attach": { + "pidProperty": "processId", + "pidSelect": "ask" + }, + "configuration": { + "type": "cppdbg", + "args": [], + "cwd": "${workspaceRoot}", + "environment": [], + } + }, + }, + }, + 'linux': { + 'file_name': 'cpptools-linux.vsix', + 'checksum': + '3695202e1e75a03de18049323b66d868165123f26151f8c974a480eaf0205435', + }, + 'macos': { + 'file_name': 'cpptools-osx.vsix', + 'checksum': + 'cb061e3acd7559a539e5586f8d3f535101c4ec4e8a48195856d1d39380b5cf3c', + }, + 'windows': { + 'file_name': 'cpptools-win32.vsix', + 'checksum': + 'aa294368ed16d48c59e49c8000e146eae5a19ad07b654efed5db8ec93b24229e', + "adapters": { + "vscode-cpptools": { + "name": "cppdbg", + "command": [ + "${gadgetDir}/vscode-cpptools/debugAdapters/bin/OpenDebugAD7.exe" + ], + "attach": { + "pidProperty": "processId", + "pidSelect": "ask" + }, + "configuration": { + "type": "cppdbg", + "args": [], + "cwd": "${workspaceRoot}", + "environment": [], + "MIMode": "gdb", + "MIDebuggerPath": "gdb.exe" + } + }, + }, + }, + }, + 'vscode-python': { + 'language': 'python.legacy', + 'enabled': False, + 'download': { + 'url': 'https://github.com/Microsoft/vscode-python/releases/download/' + '${version}/${file_name}', + }, + 'all': { + 'version': '2019.11.50794', + 'file_name': 'ms-python-release.vsix', + 'checksum': + '6a9edf9ecabed14aac424e6007858068204a3638bf3bb4f235bd6035d823acc6', + }, + 'adapters': { + "vscode-python": { + "name": "vscode-python", + "command": [ + "node", + "${gadgetDir}/vscode-python/out/client/debugger/debugAdapter/main.js", + ], + } + }, + }, + 'debugpy': { + 'language': 'python', + 'download': { + 'url': 'https://github.com/microsoft/debugpy/archive/${file_name}' + }, + 'all': { + 'version': '1.0.0b12', + 'file_name': 'v1.0.0b12.zip', + 'checksum': + '210632bba2221fbb841c9785a615258819ceec401d1abdbeb5f2326f12cc72a1' + }, + 'do': lambda name, root, gadget: installer.InstallDebugpy( name, + root, + gadget ), + 'adapters': { + 'debugpy': { + "command": [ + sys.executable, # TODO: Will this work from within Vim ? + "${gadgetDir}/debugpy/build/lib/debugpy/adapter" + ], + "name": "debugpy", + "configuration": { + "python": sys.executable, # TODO: Will this work from within Vim ? + # Don't debug into subprocesses, as this leads to problems (vimspector + # doesn't support the custom messages) + # https://github.com/puremourning/vimspector/issues/141 + "subProcess": False, + } + } + }, + }, + 'vscode-java-debug': { + 'language': 'java', + 'enabled': False, + 'download': { + 'url': 'https://github.com/microsoft/vscode-java-debug/releases/download/' + '${version}/${file_name}', + }, + 'all': { + 'version': '0.26.0', + 'file_name': 'vscjava.vscode-java-debug-0.26.0.vsix', + 'checksum': + 'de49116ff3a3c941dad0c36d9af59baa62cd931e808a2ab392056cbb235ad5ef', + }, + 'adapters': { + "vscode-java": { + "name": "vscode-java", + "port": "${DAPPort}", + } + }, + }, + 'java-language-server': { + 'language': 'javac', + 'enabled': False, + 'download': { + 'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/' + 'publishers/georgewfraser/vsextensions/vscode-javac/${version}/' + 'vspackage', + 'target': 'georgewfraser.vscode-javac-0.2.31.vsix.gz', + 'format': 'zip.gz', + }, + 'all': { + 'version': '0.2.31', + 'file_name': 'georgewfraser.vscode-javac-0.2.31.vsix.gz', + 'checksum': + '5b0248ec1198d3ece9a9c6b9433b30c22e308f0ae6e4c7bd09cd943c454e3e1d', + }, + 'adapters': { + "vscode-javac": { + "name": "vscode-javac", + "type": "vscode-javac", + "command": [ + "${gadgetDir}/java-language-server/dist/debug_adapter_mac.sh" + ], + "attach": { + "pidSelect": "none" + } + } + }, + }, + 'tclpro': { + 'language': 'tcl', + 'repo': { + 'url': 'https://github.com/puremourning/TclProDebug', + 'ref': 'master' + }, + 'do': lambda name, root, gadget: installer.InstallTclProDebug( name, + root, + gadget ), + 'adapters': { + "tclpro": { + "name": "tclpro", + "type": "tclpro", + "command": [ + "${gadgetDir}/tclpro/bin/debugadapter" + ], + "attach": { + "pidSelect": "none" + }, + "configuration": { + "target": "${file}", + "args": [ "*${args}" ], + "tclsh": "tclsh", + "cwd": "${workspaceRoot}", + "extensionDirs": [ + "${workspaceRoot}/.tclpro/extensions", + "${HOME}/.tclpro/extensions", + ] + } + } + }, + }, + 'netcoredbg': { + 'language': 'csharp', + 'enabled': False, + 'download': { + 'url': 'https://github.com/Samsung/netcoredbg/releases/download/latest/' + '${file_name}', + 'format': 'tar', + }, + 'all': { + 'version': 'master' + }, + 'macos': { + 'file_name': 'netcoredbg-osx-master.tar.gz', + 'checksum': + 'c1dc6ed58c3f5b0473cfb4985a96552999360ceb9795e42d9c9be64af054f821', + }, + 'linux': { + 'file_name': 'netcoredbg-linux-master.tar.gz', + 'checksum': '', + }, + 'windows': { + 'file_name': 'netcoredbg-win64-master.zip', + 'checksum': '', + }, + 'do': lambda name, root, gadget: installer.MakeSymlink( + name, + os.path.join( root, 'netcoredbg' ) ), + 'adapters': { + 'netcoredbg': { + "name": "netcoredbg", + "command": [ + "${gadgetDir}/netcoredbg/netcoredbg", + "--interpreter=vscode" + ], + "attach": { + "pidProperty": "processId", + "pidSelect": "ask" + }, + }, + } + }, + 'vscode-mono-debug': { + 'language': 'csharp', + 'enabled': False, + 'download': { + 'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/' + 'publishers/ms-vscode/vsextensions/mono-debug/${version}/' + 'vspackage', + 'target': 'vscode-mono-debug.vsix.gz', + 'format': 'zip.gz', + }, + 'all': { + 'file_name': 'vscode-mono-debug.vsix', + 'version': '0.15.8', + 'checksum': + '723eb2b621b99d65a24f215cb64b45f5fe694105613a900a03c859a62a810470', + }, + 'adapters': { + 'vscode-mono-debug': { + "name": "mono-debug", + "command": [ + "mono", + "${gadgetDir}/vscode-mono-debug/bin/Release/mono-debug.exe" + ], + "attach": { + "pidSelect": "none" + }, + }, + } + }, + 'vscode-bash-debug': { + 'language': 'bash', + 'download': { + 'url': 'https://github.com/rogalmic/vscode-bash-debug/releases/' + 'download/${version}/${file_name}', + }, + 'all': { + 'file_name': 'bash-debug-0.3.7.vsix', + 'version': 'v0.3.7', + 'checksum': + '7b73e5b4604375df8658fb5a72c645c355785a289aa785a986e508342c014bb4', + }, + 'do': lambda name, root, gadget: installer.InstallBashDebug( name, + root, + gadget ), + 'adapters': { + "vscode-bash": { + "name": "bashdb", + "command": [ + "node", + "${gadgetDir}/vscode-bash-debug/out/bashDebug.js" + ], + "variables": { + "BASHDB_HOME": "${gadgetDir}/vscode-bash-debug/bashdb_dir" + }, + "configuration": { + "request": "launch", + "type": "bashdb", + "program": "${file}", + "args": [], + "env": {}, + "pathBash": "bash", + "pathBashdb": "${BASHDB_HOME}/bashdb", + "pathBashdbLib": "${BASHDB_HOME}", + "pathCat": "cat", + "pathMkfifo": "mkfifo", + "pathPkill": "pkill", + "cwd": "${workspaceRoot}", + "terminalKind": "integrated", + } + } + } + }, + 'vscode-go': { + 'language': 'go', + 'download': { + 'url': 'https://github.com/microsoft/vscode-go/releases/download/' + '${version}/${file_name}' + }, + 'all': { + 'version': '0.11.4', + 'file_name': 'Go-0.11.4.vsix', + 'checksum': + 'ff7d7b944da5448974cb3a0086f4a2fd48e2086742d9c013d6964283d416027e' + }, + 'adapters': { + 'vscode-go': { + 'name': 'delve', + 'command': [ + 'node', + '${gadgetDir}/vscode-go/out/src/debugAdapter/goDebug.js' + ], + }, + }, + }, + 'vscode-php-debug': { + 'language': 'php', + 'enabled': False, + 'download': { + 'url': + 'https://github.com/felixfbecker/vscode-php-debug/releases/download/' + '${version}/${file_name}', + }, + 'all': { + 'version': 'v1.13.0', + 'file_name': 'php-debug.vsix', + 'checksum': + '8a51e593458fd14623c1c89ebab87347b087d67087717f18bcf77bb788052718', + }, + 'adapters': { + 'vscode-php-debug': { + 'name': "php-debug", + 'command': [ + 'node', + "${gadgetDir}/vscode-php-debug/out/phpDebug.js", + ] + } + } + }, + 'vscode-node-debug2': { + 'language': 'node', + 'enabled': False, + 'repo': { + 'url': 'https://github.com/microsoft/vscode-node-debug2', + 'ref': 'v1.42.0', + }, + 'do': lambda name, root, gadget: installer.InstallNodeDebug( name, + root, + gadget ), + 'adapters': { + 'vscode-node': { + 'name': 'node2', + 'type': 'node2', + 'command': [ + 'node', + '${gadgetDir}/vscode-node-debug2/out/src/nodeDebug.js' + ] + }, + }, + }, + 'debugger-for-chrome': { + 'language': 'chrome', + 'enabled': False, + 'download': { + 'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/' + 'publishers/msjsdiag/vsextensions/' + 'debugger-for-chrome/${version}/vspackage', + 'target': 'msjsdiag.debugger-for-chrome-4.12.0.vsix.gz', + 'format': 'zip.gz', + }, + 'all': { + 'version': '4.12.0', + 'file_name': 'msjsdiag.debugger-for-chrome-4.12.0.vsix', + 'checksum': + '0df2fe96d059a002ebb0936b0003e6569e5a5c35260dc3791e1657d27d82ccf5' + }, + 'adapters': { + 'chrome': { + 'name': 'debugger-for-chrome', + 'type': 'chrome', + 'command': [ + 'node', + '${gadgetDir}/debugger-for-chrome/out/src/chromeDebug.js' + ], + }, + }, + }, + 'CodeLLDB': { + 'language': 'rust', + 'enabled': False, + 'download': { + 'url': 'https://github.com/vadimcn/vscode-lldb/releases/download/' + '${version}/${file_name}', + }, + 'all': { + 'version': 'v1.5.3', + }, + 'macos': { + 'file_name': 'codelldb-x86_64-darwin.vsix', + 'checksum': + '7505bc1cdfcfd1cb981e2996aec62d63577440709bac31dcadb41a3b4b44631a', + 'make_executable': [ + 'adapter/codelldb', + 'lldb/bin/debugserver', + 'lldb/bin/lldb', + 'lldb/bin/lldb-argdumper', + ], + }, + 'linux': { + 'file_name': 'codelldb-x86_64-linux.vsix', + 'checksum': + 'ce7efc3e94d775368e5942a02bf5c326b6809a0b4c389f79ffa6a8f6f6b72139', + 'make_executable': [ + 'adapter/codelldb', + 'lldb/bin/lldb', + 'lldb/bin/lldb-server', + 'lldb/bin/lldb-argdumper', + ], + }, + 'windows': { + 'file_name': 'codelldb-x86_64-windows.vsix', + 'checksum': + '', + 'make_executable': [] + }, + 'adapters': { + 'CodeLLDB': { + 'name': 'CodeLLDB', + 'type': 'CodeLLDB', + "command": [ + "${gadgetDir}/CodeLLDB/adapter/codelldb", + "--port", "${unusedLocalPort}" + ], + "port": "${unusedLocalPort}", + "configuration": { + "type": "lldb", + "name": "lldb", + "cargo": {}, + "args": [], + "cwd": "${workspaceRoot}", + "env": {}, + "terminal": "integrated", + } + }, + }, + }, +} diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 1afea27..c767aa5 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -16,22 +16,195 @@ # limitations under the License. from urllib import request -import io import contextlib -import zipfile -import gzip -import shutil -import tarfile -import hashlib -import time -import ssl -import subprocess import functools +import gzip +import hashlib +import io import os +import shutil +import ssl +import string +import subprocess import sys +import tarfile +import time +import traceback +import zipfile +import json from vimspector import install +class Options: + vimspector_base = None + no_check_certificate = False + + +options = Options() + + +def Configure( **kwargs ): + for k, v in kwargs.items(): + setattr( options, k, v ) + + +def InstallGeneric( name, root, gadget ): + extension = os.path.join( root, 'extension' ) + for f in gadget.get( 'make_executable', [] ): + MakeExecutable( os.path.join( extension, f ) ) + + MakeExtensionSymlink( name, root ) + + +def InstallCppTools( name, root, gadget ): + extension = os.path.join( root, 'extension' ) + + # It's hilarious, but the execute bits aren't set in the vsix. So they + # actually have javascript code which does this. It's just a horrible horrible + # hack that really is not funny. + MakeExecutable( os.path.join( extension, 'debugAdapters', 'OpenDebugAD7' ) ) + with open( os.path.join( extension, 'package.json' ) ) as f: + package = json.load( f ) + runtime_dependencies = package[ 'runtimeDependencies' ] + for dependency in runtime_dependencies: + for binary in dependency.get( 'binaries' ): + file_path = os.path.abspath( os.path.join( extension, binary ) ) + if os.path.exists( file_path ): + MakeExecutable( os.path.join( extension, binary ) ) + + MakeExtensionSymlink( name, root ) + + +def InstallBashDebug( name, root, gadget ): + MakeExecutable( os.path.join( root, + 'extension', + 'bashdb_dir', + 'bashdb' ) ) + MakeExtensionSymlink( name, root ) + + +def InstallDebugpy( name, root, gadget ): + wd = os.getcwd() + root = os.path.join( root, 'debugpy-{}'.format( gadget[ 'version' ] ) ) + os.chdir( root ) + try: + subprocess.check_call( [ sys.executable, 'setup.py', 'build' ] ) + finally: + os.chdir( wd ) + + MakeSymlink( name, root ) + + +def InstallTclProDebug( name, root, gadget ): + configure = [ './configure' ] + + if install.GetOS() == 'macos': + # Apple removed the headers from system frameworks because they are + # determined to make life difficult. And the TCL configure scripts are super + # old so don't know about this. So we do their job for them and try and find + # a tclConfig.sh. + # + # NOTE however that in Apple's infinite wisdom, installing the "headers" in + # the other location is actually broken because the paths in the + # tclConfig.sh are pointing at the _old_ location. You actually do have to + # run the package installation which puts the headers back in order to work. + # This is why the below list is does not contain stuff from + # /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform + # '/Applications/Xcode.app/Contents/Developer/Platforms' + # '/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System' + # '/Library/Frameworks/Tcl.framework', + # '/Applications/Xcode.app/Contents/Developer/Platforms' + # '/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System' + # '/Library/Frameworks/Tcl.framework/Versions' + # '/Current', + for p in [ '/usr/local/opt/tcl-tk/lib' ]: + if os.path.exists( os.path.join( p, 'tclConfig.sh' ) ): + configure.append( '--with-tcl=' + p ) + break + + + with CurrentWorkingDir( os.path.join( root, 'lib', 'tclparser' ) ): + subprocess.check_call( configure ) + subprocess.check_call( [ 'make' ] ) + + MakeSymlink( name, root ) + + +def InstallNodeDebug( name, root, gadget ): + node_version = subprocess.check_output( [ 'node', '--version' ], + universal_newlines=True ).strip() + print( "Node.js version: {}".format( node_version ) ) + if list( map( int, node_version[ 1: ].split( '.' ) ) ) >= [ 12, 0, 0 ]: + print( "Can't install vscode-debug-node2:" ) + print( "Sorry, you appear to be running node 12 or later. That's not " + "compatible with the build system for this extension, and as far as " + "we know, there isn't a pre-built independent package." ) + print( "My advice is to install nvm, then do:" ) + print( " $ nvm install --lts 10" ) + print( " $ nvm use --lts 10" ) + print( " $ ./install_gadget.py --enable-node ..." ) + raise RuntimeError( 'Invalid node environent for node debugger' ) + + with CurrentWorkingDir( root ): + subprocess.check_call( [ 'npm', 'install' ] ) + subprocess.check_call( [ 'npm', 'run', 'build' ] ) + MakeSymlink( name, root ) + + +def InstallGagdet( name, gadget, failed, all_adapters ): + try: + v = {} + v.update( gadget.get( 'all', {} ) ) + v.update( gadget.get( install.GetOS(), {} ) ) + + if 'download' in gadget: + if 'file_name' not in v: + raise RuntimeError( "Unsupported OS {} for gadget {}".format( + install.GetOS(), + name ) ) + + destination = os.path.join( _GetGadgetDir(), + 'download', + name, v[ 'version' ] ) + + url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v ) + + file_path = DownloadFileTo( + url, + destination, + file_name = gadget[ 'download' ].get( 'target' ), + checksum = v.get( 'checksum' ), + check_certificate = not options.no_check_certificate ) + + root = os.path.join( destination, 'root' ) + ExtractZipTo( + file_path, + root, + format = gadget[ 'download' ].get( 'format', 'zip' ) ) + elif 'repo' in gadget: + url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( v ) + ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( v ) + + destination = os.path.join( _GetGadgetDir(), 'download', name ) + CloneRepoTo( url, ref, destination ) + root = destination + + if 'do' in gadget: + gadget[ 'do' ]( name, root, v ) + else: + InstallGeneric( name, root, v ) + + # Allow per-OS adapter overrides. v already did that for us... + all_adapters.update( v.get( 'adapters', {} ) ) + # Add any other "all" adapters + all_adapters.update( gadget.get( 'adapters', {} ) ) + + print( "Done installing {}".format( name ) ) + except Exception as e: + traceback.print_exc() + failed.append( name ) + print( "FAILED installing {}: {}".format( name, e ) ) + @contextlib.contextmanager def CurrentWorkingDir( d ): @@ -212,14 +385,18 @@ def ExtractZipTo( file_path, destination, format ): subprocess.check_call( [ 'tar', 'zxvf', file_path ] ) -def MakeExtensionSymlink( vimspector_base, name, root ): - MakeSymlink( install.GetGadgetDir( vimspector_base, - install.GetOS() ), - name, - os.path.join( root, 'extension' ) ), +def _GetGadgetDir(): + return install.GetGadgetDir( options.vimspector_base, install.GetOS() ) -def MakeSymlink( in_folder, link, pointing_to ): +def MakeExtensionSymlink( name, root ): + MakeSymlink( name, os.path.join( root, 'extension' ) ), + + +def MakeSymlink( link, pointing_to, in_folder = None ): + if not in_folder: + in_folder = _GetGadgetDir() + RemoveIfExists( os.path.join( in_folder, link ) ) in_folder = os.path.abspath( in_folder ) From 6b89df173fe84c6f8a6663ebb57c7f124aa32c59 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 27 May 2020 21:22:08 +0100 Subject: [PATCH 263/609] Remove pointless calls to GetOS() everywhere --- python3/vimspector/debug_session.py | 7 +++---- python3/vimspector/developer.py | 3 +-- python3/vimspector/install.py | 10 ++++------ python3/vimspector/installer.py | 19 ++++++++++--------- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 9ff680a..bd32cf1 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -51,8 +51,7 @@ class DebugSession( object ): self._logger.info( "API is: {}".format( api_prefix ) ) self._logger.info( 'VIMSPECTOR_HOME = %s', VIMSPECTOR_HOME ) self._logger.info( 'gadgetDir = %s', - install.GetGadgetDir( VIMSPECTOR_HOME, - install.GetOS() ) ) + install.GetGadgetDir( VIMSPECTOR_HOME ) ) self._uiTab = None self._stackTraceView = None @@ -91,7 +90,7 @@ class DebugSession( object ): configurations = {} adapters = {} - glob.glob( install.GetGadgetDir( VIMSPECTOR_HOME, install.GetOS() ) ) + glob.glob( install.GetGadgetDir( VIMSPECTOR_HOME ) ) for gadget_config_file in PathsToAllGadgetConfigs( VIMSPECTOR_HOME, current_file ): self._logger.debug( f'Reading gadget config: {gadget_config_file}' ) @@ -192,7 +191,7 @@ class DebugSession( object ): 'dollar': '$', # HACK. Hote '$$' also works. 'workspaceRoot': self._workspace_root, 'workspaceFolder': self._workspace_root, - 'gadgetDir': install.GetGadgetDir( VIMSPECTOR_HOME, install.GetOS() ), + 'gadgetDir': install.GetGadgetDir( VIMSPECTOR_HOME ), 'file': current_file, } diff --git a/python3/vimspector/developer.py b/python3/vimspector/developer.py index 4945e6a..49e96c3 100644 --- a/python3/vimspector/developer.py +++ b/python3/vimspector/developer.py @@ -23,8 +23,7 @@ from vimspector import install, utils def SetUpDebugpy( wait=False, port=5678 ): sys.path.insert( 1, - os.path.join( install.GetGadgetDir( utils.GetVimspectorBase(), - install.GetOS() ), + os.path.join( install.GetGadgetDir( utils.GetVimspectorBase() ), 'debugpy', 'build', 'lib' ) ) diff --git a/python3/vimspector/install.py b/python3/vimspector/install.py index 4726a5b..7dc0897 100644 --- a/python3/vimspector/install.py +++ b/python3/vimspector/install.py @@ -38,18 +38,16 @@ def MakeInstallDirs( vimspector_base ): mkdirs( GetConfigDirForFiletype( vimspector_base, '_all' ) ) -def GetGadgetDir( vimspector_base, OS ): - return os.path.join( os.path.abspath( vimspector_base ), 'gadgets', OS ) +def GetGadgetDir( vimspector_base ): + return os.path.join( os.path.abspath( vimspector_base ), 'gadgets', GetOS() ) def GetGadgetConfigFile( vimspector_base ): - return os.path.join( GetGadgetDir( vimspector_base, GetOS() ), - '.gadgets.json' ) + return os.path.join( GetGadgetDir( vimspector_base ), '.gadgets.json' ) def GetGadgetConfigDir( vimspector_base ): - return os.path.join( GetGadgetDir( vimspector_base, GetOS() ), - '.gadgets.d' ) + return os.path.join( GetGadgetDir( vimspector_base ), '.gadgets.d' ) def GetConfigDirForFiletype( vimspector_base, filetype ): diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index c767aa5..9e42fee 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -163,9 +163,11 @@ def InstallGagdet( name, gadget, failed, all_adapters ): install.GetOS(), name ) ) - destination = os.path.join( _GetGadgetDir(), - 'download', - name, v[ 'version' ] ) + destination = os.path.join( + install.GetGadgetDir( options.vimspector_base ), + 'download', + name, + v[ 'version' ] ) url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v ) @@ -185,7 +187,10 @@ def InstallGagdet( name, gadget, failed, all_adapters ): url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( v ) ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( v ) - destination = os.path.join( _GetGadgetDir(), 'download', name ) + destination = os.path.join( + install.GetGadgetDir( options.vimspector_base ), + 'download', + name ) CloneRepoTo( url, ref, destination ) root = destination @@ -385,17 +390,13 @@ def ExtractZipTo( file_path, destination, format ): subprocess.check_call( [ 'tar', 'zxvf', file_path ] ) -def _GetGadgetDir(): - return install.GetGadgetDir( options.vimspector_base, install.GetOS() ) - - def MakeExtensionSymlink( name, root ): MakeSymlink( name, os.path.join( root, 'extension' ) ), def MakeSymlink( link, pointing_to, in_folder = None ): if not in_folder: - in_folder = _GetGadgetDir() + in_folder = install.GetGadgetDir( options.vimspector_base ) RemoveIfExists( os.path.join( in_folder, link ) ) From 8f3de079bcc09222b6cf39ebe5d4da2dfaf40efa Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 27 May 2020 21:25:17 +0100 Subject: [PATCH 264/609] Use --install to run_tests instead of manually running the installer --- azure-pipelines.yml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5fb2b08..d5e36a2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -45,20 +45,17 @@ stages: - task: CacheBeta@0 inputs: - key: v1 | gadgets | $(Agent.OS) | install_gadget.py + key: v2 | gadgets | $(Agent.OS) | python3/vimspector/installer.py path: gadgets/linux/download displayName: Cache gadgets - - bash: python3 install_gadget.py --all - displayName: 'Install gadgets - python3' - - bash: vim --version displayName: 'Print vim version information' - bash: | eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) export GOPATH=$HOME/go - ./run_tests --report messages --quiet + ./run_tests --install --report messages --quiet displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: gdb @@ -92,17 +89,14 @@ stages: - task: CacheBeta@0 inputs: - key: v1 | gadgets | $(Agent.OS) | install_gadget.py + key: v2 | gadgets | $(Agent.OS) | python3/vimspector/installer.py path: gadgets/macos/download displayName: Cache gadgets - - bash: python3 install_gadget.py --all - displayName: 'Install gadgets - python3' - - bash: vim --version displayName: 'Print vim version information' - - bash: ./run_tests --report messages --quiet + - bash: ./run_tests --install --report messages --quiet displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: lldb From f945dbcfdda670b0a6009da30b773b02566840ec Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 27 May 2020 21:47:17 +0100 Subject: [PATCH 265/609] Move gadget config file writing too --- install_gadget.py | 24 ++++-------------------- python3/vimspector/installer.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index af2794e..5cfc395 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -163,19 +163,8 @@ for custom_file_name in functools.reduce( operator.add, failed = [] -if args.update_gadget_config: - with open( install.GetGadgetConfigFile( vimspector_base ), 'r' ) as f: - all_adapters = json.load( f ).get( 'adapters', {} ) -else: - all_adapters = {} - -# Include "built-in" adapter for multi-session mode -all_adapters.update( { - 'multi-session': { - 'port': '${port}', - 'host': '${host}' - }, -} ) +all_adapters = installer.ReadAdapters( + read_existing = args.update_gadget_config ) for name, gadget in gadgets.GADGETS.items(): if not gadget.get( 'enabled', True ): @@ -197,17 +186,12 @@ for name, gadget in gadgets.GADGETS.items(): for name, gadget in CUSTOM_GADGETS.items(): installer.InstallGagdet( name, gadget, failed, all_adapters ) -adapter_config = json.dumps ( { 'adapters': all_adapters }, - indent=2, - sort_keys=True ) - if args.no_gadget_config: print( "" ) print( "Would write the following gadgets: " ) - print( adapter_config ) + installer.WriteAdapters( all_adapters, to_file = sys.stdout ) else: - with open( install.GetGadgetConfigFile( vimspector_base ), 'w' ) as f: - f.write( adapter_config ) + installer.WriteAdapters( all_adapters ) if failed: raise RuntimeError( 'Failed to install gadgets: {}'.format( diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 9e42fee..8bf78b0 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -211,6 +211,38 @@ def InstallGagdet( name, gadget, failed, all_adapters ): print( "FAILED installing {}: {}".format( name, e ) ) +def ReadAdapters( read_existing = True ): + if read_existing: + with open( install.GetGadgetConfigFile( options.vimspector_base ), + 'r' ) as f: + all_adapters = json.load( f ).get( 'adapters', {} ) + else: + all_adapters = {} + + # Include "built-in" adapter for multi-session mode + all_adapters.update( { + 'multi-session': { + 'port': '${port}', + 'host': '${host}' + }, + } ) + + return all_adapters + + +def WriteAdapters( all_adapters, to_file=None ): + adapter_config = json.dumps ( { 'adapters': all_adapters }, + indent=2, + sort_keys=True ) + + if to_file: + to_file.write( adapter_config ) + else: + with open( install.GetGadgetConfigFile( options.vimspector_base ), + 'w' ) as f: + f.write( adapter_config ) + + @contextlib.contextmanager def CurrentWorkingDir( d ): cur_d = os.getcwd() From 025d193493f864d97f88ac0e58796625ab0213ea Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 3 Jul 2020 18:47:48 +0100 Subject: [PATCH 266/609] Add VimspectorInstall command with sort-of completion --- autoload/vimspector.vim | 46 +++++++++++++++++++++++++++++++++ plugin/vimspector.vim | 5 ++++ python3/vimspector/installer.py | 33 ++++++++++++++++++++++- 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index a7c6e4b..3d0ea05 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -150,6 +150,52 @@ function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort \ "\n" ) endfunction +function! vimspector#Install( ... ) abort + if a:0 < 1 + return + endif + + let gadgets = a:000 + let force = a:1 ==# '--force' + if force + let gadgets = a:000[ 1: ] + endif + + py3 << EOF +from vimspector import installer as vimspector_installer +from vimspector import utils as vimspector_utils +vimspector_installer.Configure( + vimspector_base = vimspector_utils.GetVimspectorBase() ) +vimspector_installer.Install( vim.eval( 'gadgets'), vim.eval( 'force' ) ) +EOF +endfunction + +function! vimspector#CompleteInstall( ArgLead, CmdLine, CursorPos ) abort + let words = split( a:CmdLine ) + let done_options = v:false + for word in words + if ! word =~# '^--' + let done_options = v:true + break + endif + endfor + + let options = [] + + if !done_options + call extend( options, [ '--force' ] ) + endif + + py3 from vimspector import installer as vimspector_installer + call extend( options, [ 'all' ] ) + call extend( + \ options, + \ py3eval( + \ '[ g[ "language" ] for g in vimspector_installer.GADGETS.values() ]' ) ) + + return join( options, "\n" ) +endfunction + " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index fa8312c..5a31021 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -97,6 +97,11 @@ command! -bar \ VimspectorReset \ call vimspector#Reset() +" Installer commands +command! -bar -nargs=* -complete=custom,vimspector#CompleteInstall + \ VimspectorInstall + \ call vimspector#Install( ) + " Dummy autocommands so that we can call this whenever augroup VimspectorUserAutoCmds au! diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 8bf78b0..89844d3 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -15,6 +15,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +# TODO: Chnage `print` to some other mechanism that can be displayed in a vim +# buffer? + from urllib import request import contextlib import functools @@ -33,7 +36,8 @@ import traceback import zipfile import json -from vimspector import install +from vimspector import install, gadgets + class Options: vimspector_base = None @@ -48,6 +52,33 @@ def Configure( **kwargs ): setattr( options, k, v ) +def Install( languages, force ): + all_enabled = 'all' in languages + force_all = all_enabled and force + + install.MakeInstallDirs( options.vimspector_base ) + all_adapters = ReadAdapters() + failed = [] + + for name, gadget in gadgets.GADGETS.items(): + if not gadget.get( 'enabled', True ): + if ( not force_all + and not ( force and gadget[ 'language' ] in languages ) ): + continue + else: + if not all_enabled and not gadget[ 'language' ] in languages: + continue + + InstallGagdet( name, + gadget, + failed, + all_adapters ) + + WriteAdapters( all_adapters ) + + return failed + + def InstallGeneric( name, root, gadget ): extension = os.path.join( root, 'extension' ) for f in gadget.get( 'make_executable', [] ): From 23e5f6bbf4f18623c53c2343080f82123ef25859 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 21 Jul 2020 16:43:39 +0100 Subject: [PATCH 267/609] Switch to running the actual install_gadget.py This re-uses the OutputView code to run the installer script. Refactor to remove connection from the base OutputView (and other places, it wasn't used - only used after ConnectionUp). This also consolidates the stdout and stderr buffers for running jobs. The distinction was always arbitrary and probably an error, based on the fact that they were separate in the APIs not based on usability. --- autoload/vimspector.vim | 47 ++------- autoload/vimspector/internal/job.vim | 13 +-- autoload/vimspector/internal/neojob.vim | 12 +-- autoload/vimspector/internal/state.vim | 15 +-- install_gadget.py | 15 ++- plugin/vimspector.vim | 3 +- python3/vimspector/debug_session.py | 9 +- python3/vimspector/installer.py | 106 ++++++++++++++++---- python3/vimspector/output.py | 128 ++++++++++++++---------- python3/vimspector/stack_trace.py | 4 +- python3/vimspector/utils.py | 47 +++++---- python3/vimspector/variables.py | 4 +- 12 files changed, 236 insertions(+), 167 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 3d0ea05..4e96968 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -132,6 +132,14 @@ function! vimspector#ShowOutput( category ) abort py3 _vimspector_session.ShowOutput( vim.eval( 'a:category' ) ) endfunction +function! vimspector#ShowOutputInWindow( win_id, category ) abort + py3 <:p:h:h' ) let s:mappings = get( g:, 'vimspector_enable_mappings', '' ) @@ -98,7 +99,7 @@ command! -bar \ call vimspector#Reset() " Installer commands -command! -bar -nargs=* -complete=custom,vimspector#CompleteInstall +command! -bar -nargs=* \ VimspectorInstall \ call vimspector#Install( ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index bd32cf1..17d6303 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -528,7 +528,6 @@ class DebugSession( object ): 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 ) # Watches @@ -546,17 +545,15 @@ class DebugSession( object ): with utils.LetCurrentWindow( stack_trace_window ): vim.command( f'{ one_third }wincmd _' ) - self._variablesView = variables.VariablesView( self._connection, - vars_window, + self._variablesView = variables.VariablesView( vars_window, watch_window ) # Output/logging vim.current.window = code_window vim.command( f'rightbelow { settings.Int( "bottombar_height", 10 ) }new' ) output_window = vim.current.window - self._outputView = output.OutputView( self._connection, - output_window, - self._api_prefix ) + 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 diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 89844d3..29ae91f 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -36,7 +36,9 @@ import traceback import zipfile import json -from vimspector import install, gadgets +from vimspector import install + +OUTPUT_VIEW = None class Options: @@ -52,31 +54,94 @@ def Configure( **kwargs ): setattr( options, k, v ) -def Install( languages, force ): - all_enabled = 'all' in languages - force_all = all_enabled and force +def PathToAnyWorkingPython3(): + # We can't rely on sys.executable because it's usually 'vim' (fixme, not with + # neovim?) + paths = os.environ[ 'PATH' ].split( os.pathsep ) - install.MakeInstallDirs( options.vimspector_base ) - all_adapters = ReadAdapters() - failed = [] + if install.GetOS() == 'windows': + paths.insert( 0, os.getcwd() ) + candidates = [ os.path.join( sys.exec_prefix, 'python.exe' ), + 'python.exe' ] + else: + candidates = [ os.path.join( sys.exec_prefix, 'bin', 'python3' ), + 'python3', + 'python' ] - for name, gadget in gadgets.GADGETS.items(): - if not gadget.get( 'enabled', True ): - if ( not force_all - and not ( force and gadget[ 'language' ] in languages ) ): + for candidate in candidates: + for path in paths: + filename = os.path.abspath( os.path.join( path, candidate ) ) + if not os.path.isfile( filename ): continue - else: - if not all_enabled and not gadget[ 'language' ] in languages: + if not os.access( filename, os.F_OK | os.X_OK ): continue - InstallGagdet( name, - gadget, - failed, - all_adapters ) + return filename - WriteAdapters( all_adapters ) + raise RuntimeError( "Unable to find a working python3" ) + + +def RunInstaller( api_prefix, *args ): + from vimspector import utils, output, settings + import vim + + vimspector_home = utils.GetVimString( vim.vars, 'vimspector_home' ) + vimspector_base_dir = utils.GetVimspectorBase() + + # TODO: Translate the arguments to something more user-friendly than -- args + global OUTPUT_VIEW + + if OUTPUT_VIEW: + OUTPUT_VIEW.Reset() + OUTPUT_VIEW = None + + with utils.RestoreCurrentWindow(): + vim.command( f'botright { settings.Int( "bottombar_height", 10 ) }new' ) + win = vim.current.window + OUTPUT_VIEW = output.OutputView( win, api_prefix ) + + cmd = [ + PathToAnyWorkingPython3(), + '-u', + os.path.join( vimspector_home, 'install_gadget.py' ), + '--update-gadget-config', + ] + if not vimspector_base_dir == vimspector_home: + cmd.extend( '--basedir', vimspector_base_dir ) + cmd.extend( args ) + + OUTPUT_VIEW.RunJobWithOutput( 'Installer', cmd ) + OUTPUT_VIEW.ShowOutput( 'Installer' ) + + +# def Install( languages, force ): +# all_enabled = 'all' in languages +# force_all = all_enabled and force +# +# install.MakeInstallDirs( options.vimspector_base ) +# all_adapters = ReadAdapters() +# succeeded = [] +# failed = [] +# +# for name, gadget in gadgets.GADGETS.items(): +# if not gadget.get( 'enabled', True ): +# if ( not force_all +# and not ( force and gadget[ 'language' ] in languages ) ): +# continue +# else: +# if not all_enabled and not gadget[ 'language' ] in languages: +# continue +# +# InstallGagdet( name, +# gadget, +# succeeded, +# failed, +# all_adapters ) +# +# WriteAdapters( all_adapters ) +# +# return succeeded, failed - return failed def InstallGeneric( name, root, gadget ): @@ -182,7 +247,7 @@ def InstallNodeDebug( name, root, gadget ): MakeSymlink( name, root ) -def InstallGagdet( name, gadget, failed, all_adapters ): +def InstallGagdet( name, gadget, succeeded, failed, all_adapters ): try: v = {} v.update( gadget.get( 'all', {} ) ) @@ -235,6 +300,7 @@ def InstallGagdet( name, gadget, failed, all_adapters ): # Add any other "all" adapters all_adapters.update( gadget.get( 'adapters', {} ) ) + succeeded.append( name ) print( "Done installing {}".format( name ) ) except Exception as e: traceback.print_exc() diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index a359448..2e2412b 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -25,7 +25,6 @@ class TabBuffer( object ): self.index = index self.flag = False self.is_job = False - self.job_category = None BUFFER_MAP = { @@ -40,18 +39,24 @@ def CategoryToBuffer( category ): return BUFFER_MAP.get( category, category ) +VIEWS = set() + + +def ShowOutputInWindow( win_id, category ): + for view in VIEWS: + if view._window.valid and utils.WindowID( view._window ) == win_id: + view.ShowOutput( category ) + return + + raise ValueError( f'Unable to find output object for win id {win_id}!' ) + + class OutputView( object ): - def __init__( self, connection, window, api_prefix ): + def __init__( self, window, api_prefix ): self._window = window - self._connection = connection self._buffers = {} self._api_prefix = api_prefix - - for b in set( BUFFER_MAP.values() ): - self._CreateBuffer( b ) - - self._CreateBuffer( 'Vimspector', file_name = utils.LOG_FILE ) - self._ShowOutput( 'Console' ) + VIEWS.add( self ) def Print( self, categroy, text ): self._Print( 'server', text.splitlines() ) @@ -82,22 +87,23 @@ class OutputView( object ): with utils.RestoreCurrentBuffer( self._window ): self._ShowOutput( category ) - def ConnectionUp( self, connection ): - self._connection = connection - - def ConnectionClosed( self ): - # Don't clear because output is probably still useful - self._connection = None - def Reset( self ): self.Clear() + VIEWS.remove( self ) + + + def _CleanUpBuffer( self, category, tab_buffer = None ): + if tab_buffer is None: + tab_buffer = self._buffers[ category ] + + if tab_buffer.is_job: + utils.CleanUpCommand( category, self._api_prefix ) + utils.CleanUpHiddenBuffer( tab_buffer.buf ) + def Clear( self ): for category, tab_buffer in self._buffers.items(): - if tab_buffer.is_job: - utils.CleanUpCommand( tab_buffer.job_category or category, - self._api_prefix ) - utils.CleanUpHiddenBuffer( tab_buffer.buf ) + self._CleanUpBuffer( category, tab_buffer ) # FIXME: nunmenu the WinBar ? self._buffers = {} @@ -125,28 +131,6 @@ class OutputView( object ): self._ToggleFlag( category, False ) self._ShowOutput( category ) - def Evaluate( self, frame, expression ): - self._Print( 'Console', [ 'Evaluating: ' + expression ] ) - - def print_result( message ): - result = message[ 'body' ][ 'result' ] - if result is None: - result = '' - self._Print( 'Console', f' Result: { result }' ) - - request = { - 'command': 'evaluate', - 'arguments': { - 'expression': expression, - 'context': 'repl', - } - } - - if frame: - request[ 'arguments' ][ 'frameId' ] = frame[ 'id' ] - - self._connection.DoRequest( print_result, request ) - def _ToggleFlag( self, category, flag ): if self._buffers[ category ].flag != flag: self._buffers[ category ].flag = flag @@ -178,16 +162,10 @@ class OutputView( object ): cmd = [ 'tail', '-F', '-n', '+1', '--', file_name ] if cmd is not None: - out, err = utils.SetUpCommandBuffer( cmd, category, self._api_prefix ) - self._buffers[ category + '-out' ] = TabBuffer( out, - len( self._buffers ) ) - self._buffers[ category + '-out' ].is_job = True - self._buffers[ category + '-out' ].job_category = category - self._buffers[ category + '-err' ] = TabBuffer( err, - len( self._buffers ) ) - self._buffers[ category + '-err' ].is_job = False - self._RenderWinBar( category + '-out' ) - self._RenderWinBar( category + '-err' ) + out = utils.SetUpCommandBuffer( cmd, category, self._api_prefix ) + self._buffers[ category ] = TabBuffer( out, len( self._buffers ) ) + self._buffers[ category ].is_job = True + self._RenderWinBar( category ) else: vim.command( 'enew' ) tab_buffer = TabBuffer( vim.current.buffer, len( self._buffers ) ) @@ -218,10 +196,52 @@ class OutputView( object ): raise vim.command( "nnoremenu 1.{0} WinBar.{1}{2} " - ":call vimspector#ShowOutput( '{1}' )".format( + ":call vimspector#ShowOutputInWindow( {3}, '{1}' )".format( tab_buffer.index, utils.Escape( category ), - '*' if tab_buffer.flag else '' ) ) + '*' if tab_buffer.flag else '', + utils.WindowID( self._window ) ) ) def GetCategories( self ): return list( self._buffers.keys() ) + + +class DAPOutputView( OutputView ): + def __init__( self, *args ): + super().__init__( *args ) + + self._connection = None + for b in set( BUFFER_MAP.values() ): + self._CreateBuffer( b ) + + self._CreateBuffer( 'Vimspector', file_name = utils.LOG_FILE ) + self._ShowOutput( 'Console' ) + + def ConnectionUp( self, connection ): + self._connection = connection + + def ConnectionClosed( self ): + # Don't clear because output is probably still useful + self._connection = None + + def Evaluate( self, frame, expression ): + self._Print( 'Console', [ 'Evaluating: ' + expression ] ) + + def print_result( message ): + result = message[ 'body' ][ 'result' ] + if result is None: + result = '' + self._Print( 'Console', f' Result: { result }' ) + + request = { + 'command': 'evaluate', + 'arguments': { + 'expression': expression, + 'context': 'repl', + } + } + + if frame: + request[ 'arguments' ][ 'frameId' ] = frame[ 'id' ] + + self._connection.DoRequest( print_result, request ) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index d5ea0b5..6caccc8 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -21,13 +21,13 @@ from vimspector import utils class StackTraceView( object ): - def __init__( self, session, connection, win ): + def __init__( self, session, win ): self._logger = logging.getLogger( __name__ ) utils.SetUpLogging( self._logger ) self._buf = win.buffer self._session = session - self._connection = connection + self._connection = None self._current_thread = None self._current_frame = None diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index bfdddcc..ab6f733 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -72,20 +72,18 @@ def OpenFileInCurrentWindow( file_name ): def SetUpCommandBuffer( cmd, name, api_prefix ): - bufs = vim.eval( - 'vimspector#internal#{}job#StartCommandWithLog( {}, "{}" )'.format( - api_prefix, - json.dumps( cmd ), - name ) ) + buf = Call( f'vimspector#internal#{api_prefix}job#StartCommandWithLog', + cmd, + name ) - if bufs is None: + if buf is None: raise RuntimeError( "Unable to start job {}: {}".format( cmd, name ) ) - elif not all( int( b ) > 0 for b in bufs ): + elif int( buf ) <= 0: raise RuntimeError( "Unable to get all streams for job {}: {}".format( name, cmd ) ) - return [ vim.buffers[ int( b ) ] for b in bufs ] + return vim.buffers[ int( buf ) ] def CleanUpCommand( name, api_prefix ): @@ -558,7 +556,6 @@ def Call( vimscript_function, *args ): call += 'g:' + arg_name call += ')' - _logger.debug( 'Calling: {}'.format( call ) ) return vim.eval( call ) @@ -633,16 +630,26 @@ def HideSplash( api_prefix, splash ): return None +def GetVimString( vim_dict, name, default=None ): + + # FIXME: use 'encoding' ? + try: + value = vim_dict[ name ] + except KeyError: + return default + + if isinstance( value, bytes ): + return value.decode( 'utf-8' ) + return value + + def GetVimspectorBase(): - base = vim.vars.get( 'vimspector_base_dir' ) - if base is None: - return os.path.abspath( os.path.join( os.path.dirname( __file__ ), - '..', - '..' ) ) - elif isinstance( base, bytes ): - return base.decode( 'utf-8' ) - else: - return base + return GetVimString( vim.vars, + 'vimspector_base_dir', + os.path.abspath( + os.path.join( os.path.dirname( __file__ ), + '..', + '..' ) ) ) def GetUnusedLocalPort(): @@ -655,5 +662,7 @@ def GetUnusedLocalPort(): return port -def WindowID( window, tab ): +def WindowID( window, tab=None ): + if tab is None: + tab = window.tabpage return int( Call( 'win_getid', window.number, tab.number ) ) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index f6df06c..ffd641d 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -124,11 +124,11 @@ class View: class VariablesView( object ): - def __init__( self, connection, variables_win, watches_win ): + def __init__( self, variables_win, watches_win ): self._logger = logging.getLogger( __name__ ) utils.SetUpLogging( self._logger ) - self._connection = connection + self._connection = None self._current_syntax = '' # Set up the "Variables" buffer in the variables_win From 0140a607b1b6f6abd43eb87edcf55eac84166b92 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 21 Jul 2020 19:11:31 +0100 Subject: [PATCH 268/609] Raise autocommand when installer completes. use this in testing --- autoload/vimspector/internal/job.vim | 11 ++++++++++ python3/vimspector/installer.py | 28 ++++++++++++++++++------- python3/vimspector/output.py | 31 +++++++++++++++------------- python3/vimspector/utils.py | 15 +++++++++++++- run_tests | 23 +++++++++++++++++++-- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index 834f83c..51ed137 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -146,6 +146,14 @@ function! vimspector#internal#job#Reset() abort call vimspector#internal#job#StopDebugSession() endfunction +function! s:_OnCommandExit( category, ch, code ) abort + py3 << EOF +from vimspector import utils as vimspector_utils +vimspector_utils.OnCommandWithLogComplete( vim.eval( 'a:category' ), + int( vim.eval( 'a:code' ) ) ) +EOF +endfunction + function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort if ! exists( 's:commands' ) let s:commands = {} @@ -165,8 +173,11 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort \ 'out_io': 'buffer', \ 'in_io': 'null', \ 'err_io': 'buffer', + \ 'out_msg': 0, + \ 'err_msg': 0, \ 'out_name': buf, \ 'err_name': buf, + \ 'exit_cb': funcref( 's:_OnCommandExit', [ a:category ] ), \ 'out_modifiable': 0, \ 'err_modifiable': 0, \ 'stoponexit': 'kill' diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 29ae91f..eefbd80 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -107,10 +107,22 @@ def RunInstaller( api_prefix, *args ): '--update-gadget-config', ] if not vimspector_base_dir == vimspector_home: - cmd.extend( '--basedir', vimspector_base_dir ) + cmd.extend( [ '--basedir', vimspector_base_dir ] ) cmd.extend( args ) - OUTPUT_VIEW.RunJobWithOutput( 'Installer', cmd ) + def handler( exit_code ): + if exit_code == 0: + utils.UserMessage( "Vimspector installation complete!" ) + vim.command( 'doautocmd User VimspectorInstallSuccess' ) + else: + utils.UserMessage( 'Vimspector installation reported errors', + error = True ) + vim.command( 'silent doautocmd User VimspectorInstallFailed' ) + + + OUTPUT_VIEW.RunJobWithOutput( 'Installer', + cmd, + completion_handler = handler ) OUTPUT_VIEW.ShowOutput( 'Installer' ) @@ -309,12 +321,14 @@ def InstallGagdet( name, gadget, succeeded, failed, all_adapters ): def ReadAdapters( read_existing = True ): + all_adapters = {} if read_existing: - with open( install.GetGadgetConfigFile( options.vimspector_base ), - 'r' ) as f: - all_adapters = json.load( f ).get( 'adapters', {} ) - else: - all_adapters = {} + try: + with open( install.GetGadgetConfigFile( options.vimspector_base ), + 'r' ) as f: + all_adapters = json.load( f ).get( 'adapters', {} ) + except OSError: + pass # Include "built-in" adapter for multi-session mode all_adapters.update( { diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 2e2412b..4b89d95 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -92,18 +92,11 @@ class OutputView( object ): VIEWS.remove( self ) - def _CleanUpBuffer( self, category, tab_buffer = None ): - if tab_buffer is None: - tab_buffer = self._buffers[ category ] - - if tab_buffer.is_job: - utils.CleanUpCommand( category, self._api_prefix ) - utils.CleanUpHiddenBuffer( tab_buffer.buf ) - - def Clear( self ): for category, tab_buffer in self._buffers.items(): - self._CleanUpBuffer( category, tab_buffer ) + if tab_buffer.is_job: + utils.CleanUpCommand( category, self._api_prefix ) + utils.CleanUpHiddenBuffer( tab_buffer.buf ) # FIXME: nunmenu the WinBar ? self._buffers = {} @@ -140,11 +133,17 @@ class OutputView( object ): self._RenderWinBar( category ) - def RunJobWithOutput( self, category, cmd ): - self._CreateBuffer( category, cmd = cmd ) + def RunJobWithOutput( self, category, cmd, completion_handler = None ): + self._CreateBuffer( category, + cmd = cmd, + completion_handler = completion_handler ) - def _CreateBuffer( self, category, file_name = None, cmd = None ): + def _CreateBuffer( self, + category, + file_name = None, + cmd = None, + completion_handler = None ): win = self._window if not win.valid: # We need to borrow the current window @@ -162,7 +161,11 @@ class OutputView( object ): cmd = [ 'tail', '-F', '-n', '+1', '--', file_name ] if cmd is not None: - out = utils.SetUpCommandBuffer( cmd, category, self._api_prefix ) + out = utils.SetUpCommandBuffer( + cmd, + category, + self._api_prefix, + completion_handler = completion_handler ) self._buffers[ category ] = TabBuffer( out, len( self._buffers ) ) self._buffers[ category ].is_job = True self._RenderWinBar( category ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index ab6f733..cf4a69d 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -71,7 +71,20 @@ def OpenFileInCurrentWindow( file_name ): return vim.buffers[ buffer_number ] -def SetUpCommandBuffer( cmd, name, api_prefix ): +COMMAND_HANDLERS = {} + + +def OnCommandWithLogComplete( name, exit_code ): + cb = COMMAND_HANDLERS.get( name ) + if cb: + cb( exit_code ) + else: + UserMessage( f'Job complete: { name } (exit status: { exit_code })' ) + + +def SetUpCommandBuffer( cmd, name, api_prefix, completion_handler = None ): + COMMAND_HANDLERS[ name ] = completion_handler + buf = Call( f'vimspector#internal#{api_prefix}job#StartCommandWithLog', cmd, name ) diff --git a/run_tests b/run_tests index c03c7b9..3c327bb 100755 --- a/run_tests +++ b/run_tests @@ -25,6 +25,11 @@ while [ -n "$1" ]; do INSTALL=1 shift ;; + "--install-method") + shift + INSTALL=$1 + shift + ;; "--report") shift VIMSPECTOR_TEST_STDOUT=$1 @@ -71,8 +76,22 @@ if [ "${out_fd}" = "1" ]; then exec 3>&1 fi -if [ $INSTALL = 1 ]; then - python3 $(dirname $0)/install_gadget.py --basedir ${BASEDIR} --all +if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "script" ]; then + if ! python3 $(dirname $0)/install_gadget.py --basedir ${BASEDIR} --all; then + echo "Script installation reported errors" >&2 + exit 1 + fi +fi + +if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "vim" ]; then + if ! $RUN_VIM -u $(dirname $0)/tests/vimrc \ + --cmd "${BASEDIR_CMD}" \ + -c 'autocmd User VimspectorInstallSuccess qa!' \ + -c 'autocmd User VimspectorInstallFailed cquit!' \ + -c "VimspectorInstall --all"; then + echo "Vim installation reported errors" >&2 + exit 1 + fi fi if [ -z "$VIMSPECTOR_MIMODE" ]; then From ca4ab52f8d1fe56005b0c5573a1efc532e952449 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 21 Jul 2020 19:23:38 +0100 Subject: [PATCH 269/609] Fix regression: Don't render winbar if the window isn't valid --- python3/vimspector/output.py | 3 +++ python3/vimspector/utils.py | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 4b89d95..4cd7c64 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -186,6 +186,9 @@ class OutputView( object ): self._RenderWinBar( category ) def _RenderWinBar( self, category ): + if not self._window.valid: + return + tab_buffer = self._buffers[ category ] try: diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index cf4a69d..d2870c6 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -195,9 +195,8 @@ def RestoreCurrentWindow(): try: yield finally: - if old_tabpage.valid: + if old_tabpage.valid and old_window.valid: vim.current.tabpage = old_tabpage - if old_window.valid: vim.current.window = old_window From 05bbafd60c18529902abfdde05722df04593ea05 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 21 Jul 2020 21:55:31 +0100 Subject: [PATCH 270/609] Close the intaller output when complete --- autoload/vimspector.vim | 24 ++++++++++++++--------- plugin/vimspector.vim | 3 ++- python3/vimspector/installer.py | 34 +++++++++++++++++++++++++++++---- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 4e96968..1dd22f1 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -133,11 +133,10 @@ function! vimspector#ShowOutput( category ) abort endfunction function! vimspector#ShowOutputInWindow( win_id, category ) abort - py3 < ) + " Dummy autocommands so that we can call this whenever augroup VimspectorUserAutoCmds au! diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index eefbd80..33a6a76 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -85,12 +85,13 @@ def RunInstaller( api_prefix, *args ): from vimspector import utils, output, settings import vim + args = GadgetListToInstallerArgs( *args ) + vimspector_home = utils.GetVimString( vim.vars, 'vimspector_home' ) vimspector_base_dir = utils.GetVimspectorBase() # TODO: Translate the arguments to something more user-friendly than -- args global OUTPUT_VIEW - if OUTPUT_VIEW: OUTPUT_VIEW.Reset() OUTPUT_VIEW = None @@ -112,10 +113,14 @@ def RunInstaller( api_prefix, *args ): def handler( exit_code ): if exit_code == 0: - utils.UserMessage( "Vimspector installation complete!" ) - vim.command( 'doautocmd User VimspectorInstallSuccess' ) + global OUTPUT_VIEW + if OUTPUT_VIEW: + OUTPUT_VIEW.Reset() + OUTPUT_VIEW = None + utils.UserMessage( "Vimspector gadget installation complete!" ) + vim.command( 'silent doautocmd User VimspectorInstallSuccess' ) else: - utils.UserMessage( 'Vimspector installation reported errors', + utils.UserMessage( 'Vimspector gadget installation reported errors', error = True ) vim.command( 'silent doautocmd User VimspectorInstallFailed' ) @@ -126,6 +131,27 @@ def RunInstaller( api_prefix, *args ): OUTPUT_VIEW.ShowOutput( 'Installer' ) +def GadgetListToInstallerArgs( *gadget_list ): + installer_args = [] + from vimspector import gadgets + for name in gadget_list: + if name.startswith( '-' ): + installer_args.append( name ) + continue + + try: + gadget = gadgets.GADGETS[ name ] + except KeyError: + continue + + if not gadget.get( 'enabled', True ): + installer_args.append( f'--force-enable-{ gadget[ "language" ] }' ) + else: + installer_args.append( f'--enable-{ gadget[ "language" ] }' ) + + return installer_args + + # def Install( languages, force ): # all_enabled = 'all' in languages # force_all = all_enabled and force From cd5ca37ce1b1da2b10e462785b75d1c6f7151a4c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 21 Jul 2020 22:48:26 +0100 Subject: [PATCH 271/609] Neovim support --- autoload/vimspector/internal/job.vim | 9 +- autoload/vimspector/internal/neojob.vim | 112 +++++++++++++----------- 2 files changed, 66 insertions(+), 55 deletions(-) diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index 51ed137..474eb7c 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -147,11 +147,10 @@ function! vimspector#internal#job#Reset() abort endfunction function! s:_OnCommandExit( category, ch, code ) abort - py3 << EOF -from vimspector import utils as vimspector_utils -vimspector_utils.OnCommandWithLogComplete( vim.eval( 'a:category' ), - int( vim.eval( 'a:code' ) ) ) -EOF + py3 __import__( "vimspector", + \ fromlist = [ "utils" ] ).utils.OnCommandWithLogComplete( + \ vim.eval( 'a:category' ), + \ int( vim.eval( 'a:code' ) ) ) endfunction function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index 9d0f79f..0cefc63 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -116,58 +116,65 @@ function! s:_OnCommandEvent( category, id, data, event ) abort return endif - if a:data == [''] - return - endif - - if !has_key( s:commands, a:category ) - return - endif - - if !has_key( s:commands[ a:category ], a:id ) - return - endif - - if a:event ==# 'stdout' - let buffer = s:commands[ a:category ][ a:id ].stdout - elseif a:event ==# 'stderr' - let buffer = s:commands[ a:category ][ a:id ].stderr - endif - - try - call bufload( buffer ) - catch /E325/ - " Ignore E325/ATTENTION - endtry - - - let numlines = py3eval( "len( vim.buffers[ int( vim.eval( 'buffer' ) ) ] )" ) - let last_line = getbufline( buffer, '$' )[ 0 ] - - call s:MakeBufferWritable( buffer ) - try - if numlines == 1 && last_line ==# '' - call setbufline( buffer, 1, a:data[ 0 ] ) - else - call setbufline( buffer, '$', last_line . a:data[ 0 ] ) + if a:event ==# 'stdout' || a:event ==# 'stderr' + if a:data == [''] + return endif - call appendbufline( buffer, '$', a:data[ 1: ] ) - finally - call s:MakeBufferReadOnly( buffer ) - call setbufvar( buffer, '&modified', 0 ) - endtry + if !has_key( s:commands, a:category ) + return + endif + + if !has_key( s:commands[ a:category ], a:id ) + return + endif + + if a:event ==# 'stdout' + let buffer = s:commands[ a:category ][ a:id ].stdout + elseif a:event ==# 'stderr' + let buffer = s:commands[ a:category ][ a:id ].stderr + endif - " if the buffer is visible, scroll it - let w = bufwinnr( buffer ) - if w > 0 - let cw = winnr() try - execute w . 'wincmd w' - normal! Gz. - finally - execute cw . 'wincmd w' + call bufload( buffer ) + catch /E325/ + " Ignore E325/ATTENTION endtry + + + let numlines = py3eval( "len( vim.buffers[ int( vim.eval( 'buffer' ) ) ] )" ) + let last_line = getbufline( buffer, '$' )[ 0 ] + + call s:MakeBufferWritable( buffer ) + try + if numlines == 1 && last_line ==# '' + call setbufline( buffer, 1, a:data[ 0 ] ) + else + call setbufline( buffer, '$', last_line . a:data[ 0 ] ) + endif + + call appendbufline( buffer, '$', a:data[ 1: ] ) + finally + call s:MakeBufferReadOnly( buffer ) + call setbufvar( buffer, '&modified', 0 ) + endtry + + " if the buffer is visible, scroll it + let w = bufwinnr( buffer ) + if w > 0 + let cw = winnr() + try + execute w . 'wincmd w' + normal! Gz- + finally + execute cw . 'wincmd w' + endtry + endif + elseif a:event ==# 'exit' + py3 __import__( "vimspector", + \ fromlist = [ "utils" ] ).utils.OnCommandWithLogComplete( + \ vim.eval( 'a:category' ), + \ int( vim.eval( 'a:data' ) ) ) endif endfunction @@ -210,7 +217,9 @@ function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort \ 'on_stdout': funcref( 's:_OnCommandEvent', \ [ a:category ] ), \ 'on_stderr': funcref( 's:_OnCommandEvent', - \ [ a:category ] ) + \ [ a:category ] ), + \ 'on_exit': funcref( 's:_OnCommandEvent', + \ [ a:category ] ), \ } ) let s:commands[ a:category ][ id ] = { @@ -227,8 +236,11 @@ function! vimspector#internal#neojob#CleanUpCommand( category ) abort endif for id in keys( s:commands[ a:category ] ) - call jobstop( str2nr( id ) ) - call jobwait( [ str2nr( id ) ] ) + let id = str2nr( id ) + if jobwait( [ id ], 0 )[ 0 ] == -1 + call jobstop( id ) + endif + call jobwait( [ id ], -1 ) endfor unlet! s:commands[ a:category ] endfunction From 375ff4aa2727b4c529c16a8f333e8a01d2d5f690 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 21 Jul 2020 23:18:07 +0100 Subject: [PATCH 272/609] Suggest installing gadget if possible --- python3/vimspector/debug_session.py | 21 ++++++++++++- python3/vimspector/installer.py | 48 +++++++++++------------------ 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 17d6303..cc23a47 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -30,7 +30,8 @@ from vimspector import ( breakpoints, stack_trace, utils, variables, - settings ) + settings, + installer ) from vimspector.vendor.json_minify import minify # We cache this once, and don't allow it to change (FIXME?) @@ -151,6 +152,24 @@ class DebugSession( object ): adapter_dict = adapters.get( adapter ) if adapter_dict is None: + suggested_gadgets = installer.FindGadgetForAdapter( adapter ) + if suggested_gadgets: + response = utils.AskForInput( + f"The specified adapter '{adapter}' is not " + "installed. Would you like to install the following gadgets? ", + ' '.join( suggested_gadgets ) ) + if response: + new_launch_variables = dict( launch_variables ) + new_launch_variables[ 'configuration' ] = configuration_name + + installer.RunInstaller( + self._api_prefix, + *shlex.split( response ), + then = lambda: self.Start( new_launch_variables ) ) + return + elif response is None: + return + utils.UserMessage( f"The specified adapter '{adapter}' is not " "available. Did you forget to run " "'install_gadget.py'?", diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 33a6a76..e287adc 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -36,7 +36,7 @@ import traceback import zipfile import json -from vimspector import install +from vimspector import install, gadgets OUTPUT_VIEW = None @@ -81,7 +81,7 @@ def PathToAnyWorkingPython3(): raise RuntimeError( "Unable to find a working python3" ) -def RunInstaller( api_prefix, *args ): +def RunInstaller( api_prefix, *args, **kwargs ): from vimspector import utils, output, settings import vim @@ -119,6 +119,8 @@ def RunInstaller( api_prefix, *args ): OUTPUT_VIEW = None utils.UserMessage( "Vimspector gadget installation complete!" ) vim.command( 'silent doautocmd User VimspectorInstallSuccess' ) + if 'then' in kwargs: + kwargs[ 'then' ]() else: utils.UserMessage( 'Vimspector gadget installation reported errors', error = True ) @@ -133,7 +135,6 @@ def RunInstaller( api_prefix, *args ): def GadgetListToInstallerArgs( *gadget_list ): installer_args = [] - from vimspector import gadgets for name in gadget_list: if name.startswith( '-' ): installer_args.append( name ) @@ -152,34 +153,21 @@ def GadgetListToInstallerArgs( *gadget_list ): return installer_args -# def Install( languages, force ): -# all_enabled = 'all' in languages -# force_all = all_enabled and force -# -# install.MakeInstallDirs( options.vimspector_base ) -# all_adapters = ReadAdapters() -# succeeded = [] -# failed = [] -# -# for name, gadget in gadgets.GADGETS.items(): -# if not gadget.get( 'enabled', True ): -# if ( not force_all -# and not ( force and gadget[ 'language' ] in languages ) ): -# continue -# else: -# if not all_enabled and not gadget[ 'language' ] in languages: -# continue -# -# InstallGagdet( name, -# gadget, -# succeeded, -# failed, -# all_adapters ) -# -# WriteAdapters( all_adapters ) -# -# return succeeded, failed +def FindGadgetForAdapter( adapter_name ): + candidates = [] + for name, gadget in gadgets.GADGETS.items(): + v = {} + v.update( gadget.get( 'all', {} ) ) + v.update( gadget.get( install.GetOS(), {} ) ) + adapters = {} + adapters.update( v.get( 'adapters', {} ) ) + adapters.update( gadget.get( 'adapters', {} ) ) + + if adapter_name in adapters: + candidates.append( name ) + + return candidates def InstallGeneric( name, root, gadget ): From 98bef3db030d056f80d4a8939822d63fc6bf7cd1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 09:36:32 +0100 Subject: [PATCH 273/609] Fix - don't switch windows/buffers to create a new hidden buffer --- autoload/vimspector.vim | 83 +++++++++++++++++++- autoload/vimspector/internal/state.vim | 20 +++-- python3/vimspector/installer.py | 1 - python3/vimspector/output.py | 102 ++++++++++++------------- python3/vimspector/utils.py | 7 ++ 5 files changed, 155 insertions(+), 58 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 1dd22f1..8ad19e2 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -20,29 +20,47 @@ set cpoptions&vim " }}} -call vimspector#internal#state#Reset() +let s:enabled = vimspector#internal#state#Reset() function! vimspector#Launch() abort + if !s:enabled + return + endif py3 _vimspector_session.Start() endfunction function! vimspector#LaunchWithSettings( settings ) abort + if !s:enabled + return + endif py3 _vimspector_session.Start( launch_variables = vim.eval( 'a:settings' ) ) endfunction function! vimspector#Reset() abort + if !s:enabled + return + endif py3 _vimspector_session.Reset() endfunction function! vimspector#Restart() abort + if !s:enabled + return + endif py3 _vimspector_session.Restart() endfunction function! vimspector#ClearBreakpoints() abort + if !s:enabled + return + endif py3 _vimspector_session.ClearBreakpoints() endfunction function! vimspector#ToggleBreakpoint( ... ) abort + if !s:enabled + return + endif if a:0 == 0 let options = {} else @@ -52,6 +70,9 @@ function! vimspector#ToggleBreakpoint( ... ) abort endfunction function! vimspector#AddFunctionBreakpoint( function, ... ) abort + if !s:enabled + return + endif if a:0 == 0 let options = {} else @@ -62,42 +83,72 @@ function! vimspector#AddFunctionBreakpoint( function, ... ) abort endfunction function! vimspector#StepOver() abort + if !s:enabled + return + endif py3 _vimspector_session.StepOver() endfunction function! vimspector#StepInto() abort + if !s:enabled + return + endif py3 _vimspector_session.StepInto() endfunction function! vimspector#StepOut() abort + if !s:enabled + return + endif py3 _vimspector_session.StepOut() endfunction function! vimspector#Continue() abort + if !s:enabled + return + endif py3 _vimspector_session.Continue() endfunction function! vimspector#Pause() abort + if !s:enabled + return + endif py3 _vimspector_session.Pause() endfunction function! vimspector#Stop() abort + if !s:enabled + return + endif py3 _vimspector_session.Stop() endfunction function! vimspector#ExpandVariable() abort + if !s:enabled + return + endif py3 _vimspector_session.ExpandVariable() endfunction function! vimspector#DeleteWatch() abort + if !s:enabled + return + endif py3 _vimspector_session.DeleteWatch() endfunction function! vimspector#GoToFrame() abort + if !s:enabled + return + endif py3 _vimspector_session.ExpandFrameOrThread() endfunction function! vimspector#AddWatch( ... ) abort + if !s:enabled + return + endif if a:0 == 0 let expr = input( 'Enter watch expression: ' ) else @@ -112,27 +163,42 @@ function! vimspector#AddWatch( ... ) abort endfunction function! vimspector#AddWatchPrompt( expr ) abort + if !s:enabled + return + endif stopinsert setlocal nomodified call vimspector#AddWatch( a:expr ) endfunction function! vimspector#Evaluate( expr ) abort + if !s:enabled + return + endif py3 _vimspector_session.ShowOutput( 'Console' ) py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) ) endfunction function! vimspector#EvaluateConsole( expr ) abort + if !s:enabled + return + endif stopinsert setlocal nomodified py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) ) endfunction function! vimspector#ShowOutput( category ) abort + if !s:enabled + return + endif py3 _vimspector_session.ShowOutput( vim.eval( 'a:category' ) ) endfunction function! vimspector#ShowOutputInWindow( win_id, category ) abort + if !s:enabled + return + endif py3 __import__( 'vimspector', \ fromlist = [ 'output' ] ).output.ShowOutputInWindow( \ int( vim.eval( 'a:win_id' ) ), @@ -140,16 +206,25 @@ function! vimspector#ShowOutputInWindow( win_id, category ) abort endfunction function! vimspector#ListBreakpoints() abort + if !s:enabled + return + endif py3 _vimspector_session.ListBreakpoints() endfunction function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort + if !s:enabled + return + endif let buffers = py3eval( '_vimspector_session.GetOutputBuffers() ' \ . ' if _vimspector_session else []' ) return join( buffers, "\n" ) endfunction function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort + if !s:enabled + return + endif return join( py3eval( '_vimspector_session.GetCompletionsSync( ' \.' vim.eval( "a:CmdLine" ),' \.' int( vim.eval( "a:CursorPos" ) ) )' @@ -158,6 +233,9 @@ function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort endfunction function! vimspector#Install( ... ) abort + if !s:enabled + return + endif if a:0 < 1 return endif @@ -169,6 +247,9 @@ function! vimspector#Install( ... ) abort endfunction function! vimspector#CompleteInstall( ArgLead, CmdLine, CursorPos ) abort + if !s:enabled + return + endif return py3eval( '"\n".join(' \ . '__import__( "vimspector", fromlist = [ "gadgets" ] )' \ . '.gadgets.GADGETS.keys() ' diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index a03d9a3..b4f18aa 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -25,11 +25,21 @@ if has( 'nvim' ) endif function! vimspector#internal#state#Reset() abort - py3 << EOF -import vim -from vimspector import debug_session -_vimspector_session = debug_session.DebugSession( vim.eval( 's:prefix' ) ) -EOF + try + py3 import vim + py3 _vimspector_session = __import__( + \ "vimspector", + \ fromlist=[ "debug_session" ] ).debug_session.DebugSession( + \ vim.eval( 's:prefix' ) ) + catch /.*/ + echohl WarningMsg + echom 'Exception while loading vimspector:' v:exception + echom 'Vimspector unavailable: Requires Vim compiled with Python 3.6' + echohl None + return v:false + endtry + + return v:true endfunction function! vimspector#internal#state#GetAPIPrefix() abort diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index e287adc..f52d667 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -90,7 +90,6 @@ def RunInstaller( api_prefix, *args, **kwargs ): vimspector_home = utils.GetVimString( vim.vars, 'vimspector_home' ) vimspector_base_dir = utils.GetVimspectorBase() - # TODO: Translate the arguments to something more user-friendly than -- args global OUTPUT_VIEW if OUTPUT_VIEW: OUTPUT_VIEW.Reset() diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 4cd7c64..3a2d5cb 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -52,6 +52,8 @@ def ShowOutputInWindow( win_id, category ): class OutputView( object ): + """Container for a 'tabbed' window of buffers that can be used to display + files or the output of commands.""" def __init__( self, window, api_prefix ): self._window = window self._buffers = {} @@ -144,75 +146,73 @@ class OutputView( object ): file_name = None, cmd = None, completion_handler = None ): - win = self._window - if not win.valid: - # We need to borrow the current window - win = vim.current.window + if file_name is not None: + assert cmd is None + if install.GetOS() == "windows": + # FIXME: Can't display fiels in windows (yet?) + return - with utils.LetCurrentWindow( win ): - with utils.RestoreCurrentBuffer( win ): + cmd = [ 'tail', '-F', '-n', '+1', '--', file_name ] - if file_name is not None: - assert cmd is None - if install.GetOS() == "windows": - # FIXME: Can't display fiels in windows (yet?) - return + if cmd is not None: + out = utils.SetUpCommandBuffer( + cmd, + category, + self._api_prefix, + completion_handler = completion_handler ) + self._buffers[ category ] = TabBuffer( out, len( self._buffers ) ) + self._buffers[ category ].is_job = True + self._RenderWinBar( category ) + else: + if category == 'Console': + name = 'vimspector.Console' + else: + name = 'vimspector.Output:{0}'.format( category ) - cmd = [ 'tail', '-F', '-n', '+1', '--', file_name ] + tab_buffer = TabBuffer( utils.NewEmptyBuffer(), len( self._buffers ) ) + self._buffers[ category ] = tab_buffer - if cmd is not None: - out = utils.SetUpCommandBuffer( - cmd, - category, - self._api_prefix, - completion_handler = completion_handler ) - self._buffers[ category ] = TabBuffer( out, len( self._buffers ) ) - self._buffers[ category ].is_job = True - self._RenderWinBar( category ) - else: - vim.command( 'enew' ) - tab_buffer = TabBuffer( vim.current.buffer, len( self._buffers ) ) - self._buffers[ category ] = tab_buffer - if category == 'Console': - utils.SetUpPromptBuffer( tab_buffer.buf, - 'vimspector.Console', - '> ', - 'vimspector#EvaluateConsole' ) - else: - utils.SetUpHiddenBuffer( - tab_buffer.buf, - 'vimspector.Output:{0}'.format( category ) ) + if category == 'Console': + utils.SetUpPromptBuffer( tab_buffer.buf, + name, + '> ', + 'vimspector#EvaluateConsole' ) + else: + utils.SetUpHiddenBuffer( tab_buffer.buf, name ) - self._RenderWinBar( category ) + self._RenderWinBar( category ) def _RenderWinBar( self, category ): if not self._window.valid: return - tab_buffer = self._buffers[ category ] + with utils.LetCurrentWindow( self._window ): + tab_buffer = self._buffers[ category ] - try: - if tab_buffer.flag: - vim.command( 'nunmenu WinBar.{}'.format( utils.Escape( category ) ) ) - else: - vim.command( 'nunmenu WinBar.{}*'.format( utils.Escape( category ) ) ) - except vim.error as e: - # E329 means the menu doesn't exist; ignore that. - if 'E329' not in str( e ): - raise + try: + if tab_buffer.flag: + vim.command( 'nunmenu WinBar.{}'.format( utils.Escape( category ) ) ) + else: + vim.command( 'nunmenu WinBar.{}*'.format( utils.Escape( category ) ) ) + except vim.error as e: + # E329 means the menu doesn't exist; ignore that. + if 'E329' not in str( e ): + raise - vim.command( "nnoremenu 1.{0} WinBar.{1}{2} " - ":call vimspector#ShowOutputInWindow( {3}, '{1}' )".format( - tab_buffer.index, - utils.Escape( category ), - '*' if tab_buffer.flag else '', - utils.WindowID( self._window ) ) ) + vim.command( + "nnoremenu 1.{0} WinBar.{1}{2} " + ":call vimspector#ShowOutputInWindow( {3}, '{1}' )".format( + tab_buffer.index, + utils.Escape( category ), + '*' if tab_buffer.flag else '', + utils.WindowID( self._window ) ) ) def GetCategories( self ): return list( self._buffers.keys() ) class DAPOutputView( OutputView ): + """Specialised OutputView which adds the DAP Console (REPL)""" def __init__( self, *args ): super().__init__( *args ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index d2870c6..fe11631 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -52,6 +52,12 @@ def BufferForFile( file_name ): return vim.buffers[ BufferNumberForFile( file_name ) ] +def NewEmptyBuffer(): + bufnr = int( vim.eval( 'bufadd("")' ) ) + Call( 'bufload', bufnr ) + return vim.buffers[ bufnr ] + + def WindowForBuffer( buf ): for w in vim.current.tabpage.windows: if w.buffer == buf: @@ -353,6 +359,7 @@ def AskForInput( prompt, default_value = None ): def AppendToBuffer( buf, line_or_lines, modified=False ): + line = 1 try: # After clearing the buffer (using buf[:] = None) there is always a single # empty line in the buffer object and no "is empty" method. From 8275d2fafb130c3c189aca28c2cb80edd575a66c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 11:53:51 +0100 Subject: [PATCH 274/609] README updates --- README.md | 188 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 104 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index f70f537..3fc4722 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,12 @@ For a tutorial and usage overview, take a look at the * [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) + * [Supported languages](#supported-languages) * [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) @@ -66,7 +63,7 @@ For a tutorial and usage overview, take a look at the * [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) + * [Changing the terminal size](#changing-the-terminal-size) * [Advanced UI customisation](#advanced-ui-customisation) * [Example](#example) * [FAQ](#faq) @@ -74,7 +71,7 @@ For a tutorial and usage overview, take a look at the * [License](#license) * [Sponsorship](#sponsorship) - + @@ -91,6 +88,12 @@ But for now, here's a (rather old) screenshot of Vimsepctor debugging Vim: ![vimspector-vim-screenshot](https://puremourning.github.io/vimspector-web/img/vimspector-overview.png) +And a couple of brief demos: + +[![asciicast](https://asciinema.org/a/VmptWmFHTNLPfK3DVsrR2bv8S.svg)](https://asciinema.org/a/VmptWmFHTNLPfK3DVsrR2bv8S) + +[![asciicast](https://asciinema.org/a/1wZJSoCgs3AvjkhKwetJOJhDh.svg)](https://asciinema.org/a/1wZJSoCgs3AvjkhKwetJOJhDh) + ## Supported debugging features - flexible configuration syntax that can be checked in to source control @@ -108,31 +111,32 @@ But for now, here's a (rather old) screenshot of Vimsepctor debugging Vim: - logging/stdout display - simple stable API for custom tooling (e.g. integrate with language server) -## Supported languages: +For other languages, you'll need some other way to install the gadget. -The following languages are used frequently by the author and are known to work -with little effort, and are supported as first-class languages. +## Supported languages -- C, C++, etc. (languages supported by gdb or lldb) -- Python 2 and Python 3 -- TCL -- Bash scripts -- Java +The following table lists the languages that are "built-in" (along with their +runtime dependencies). They are categorised by their level of support: -## Languages known to work +* `Tested` : Fully supported, Vimspector regression tests cover them +* `Supported` : Fully supported, frequently used and manually tested +* `Experimental`: Working, but not frequently used and rarely tested +* `Legacy`: No longer supported, please migrate your config -The following languages are used frequently by the author, but require some sort -of hackery that makes it challenging to support generally. These languages are -on a best-efforts basis: - -- C# (c-sharp) using dotnet core -- Go (requires separate installation of [Delve][]) -- Node.js (requires node <12 for installation) -- Anything running in chrome (i.e. javascript). - -## Languages known not to work - -- C# (c-sharp) using mono debug adapter (vimspector unable to set breakpoints) +| Language | Status | Switch (for `install_gadget.py`) | Adapter (for `:VimspectorInstall`) | Dependencies | +|------------------|--------------|----------------------------------|------------------------------------|--------------------------------------------| +| C, C++, etc. | Tested | `--all` or `--enable-c` | vscode-cpptools | mono-core | +| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | +| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] | +| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | +| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | +| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | +| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | +| Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | +| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | +| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | +| Rust (CodeLLDB) | Experimental | `--force-enable-rust` | CodeLLDB | Python 3 | +| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | ## Other languages @@ -224,33 +228,6 @@ The following features are not implemented for Windows: * Tailing the vimspector log in the Output Window. -## Language dependencies - -The debug adapters themselves have certain runtime dependencies. They are -categorised as follows: - -* `Tested` : Fully supported, Vimspector regression tests cover them -* `Supported` : Fully supported, frequently used and manually tested -* `Experimental`: Working, but not frequently used and rarely tested -* `Legacy`: No longer supported, please migrate your config - -| Language | Status | Switch | Adapter | Dependencies | -|------------------|--------------|--------------------------------|---------------------|--------------------------------------------| -| C, C++, etc. | Tested | `--all` or `--enable-c` | vscode-cpptools | mono-core | -| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | -| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] | -| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | -| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | -| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | -| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | -| Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | -| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | -| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | -| Rust (CodeLLDB) | Experimental | `--force-enable-rust` | CodeLLDB | Python 3 | -| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | - -For other languages, you'll need some other way to install the gadget. - ## Clone the plugin There are many Vim plugin managers, and I'm not going to state a particular @@ -281,10 +258,27 @@ See support/doc/example_vimrc.vim. ## Install some gadgets -There are a couple of ways of doing this, but ***using `install_gadget.py` is -highly recommended*** where that's an option. +Vimspector is a generic client for Debug Adapters. Debug Adapters (referred to +as 'gadgets' or 'adapters') are what actually do the work of talking to the real +debugers. -For supported languages, `install_gadget.py` will: +In order for Vimspector to be useful, you need to have some adapters installed. + +There are a few ways to do this: + +* Using `:VimspectorInstall ` (use TAB `wildmenu` to see the + options, also accepts any `install_gadget.py` option) +* Using `python3 install_gadget.py ` (use `--help` to see all options) +* When attempting to launch a debug configuration, if the configured adapter + can't be found, vimspector might suggest installing one. + +Here's a demo: + +[![asciicast](https://asciinema.org/a/M3kShmfAZ8I5YewTCCKezzrr9.svg)](https://asciinema.org/a/M3kShmfAZ8I5YewTCCKezzrr9) + +Both `install_gadget.py` and `:VimspectorInstall` do the same set of things, +though the default behaviours are slightly different. For supported languages, +they will: * Download the relevant debug adapter at a version that's been tested from the internet, either as a 'vsix' (Visusal Studio plugin), or clone from GitHub. If @@ -298,25 +292,29 @@ For supported languages, `install_gadget.py` will: To install the tested debug adapter for a language, run: -``` -./install_gadget.py --enable- -``` +| To install | Script | Command | +| --- | --- | --- | +| `` | | `:VimspectorInstall ` | +| ``, ``, ... | | `:VimspectorInstall ...` | +| `` | `./install_gadget.py --enable- ...` | `:VimspectorInstall --enable- ...` | +| Supported adapters | `./install_gadget.py --all` | `:VimspectorInstall --all` | +| Supported adapters, but not TCL | `./install_gadget.py --all --disable-tcl` | `:VimspectorInstall --all --disable-tcl` | +| Supported and experimental adapters | `./install_gadget.py --all --force-all` | `:VimspectorInstall --all` | +| Adapter for specific debug config | | Suggested by Vimspector when starting debugging | -Or to install all supported gagtets: +`"VimspectorInstall` runs `install_gadget.py` in the background with some of +the options defaulted. -``` -./install_gadget.py --all -``` +Here's a demo: -To install everything other than TCL (because TCL is sadly not as popular as it -should be): +[![asciicast](https://asciinema.org/a/mJQmMAuQG4rOp5DWq1IhDvQty.svg)](https://asciinema.org/a/mJQmMAuQG4rOp5DWq1IhDvQty) -``` -./install_gadget.py --all --disable-tcl -``` +By default `install_gadget.py` will overwrite your `.gadgets.json` with the set +of adapters just installed, whereas `:VimspectorInstall` will _update_ it, +overwriting only newly changed or installed adapters. -If you want to just add a new adapter without destroying the exisitng ones, add -`--update-gadget-config`, as in: +If you want to just add a new adapter using the script without destroying the +exisitng ones, add `--update-gadget-config`, as in: ```bash $ ./install_gadget.py --enable-tcl @@ -339,10 +337,16 @@ Then add this to your `.vimrc`: let g:vimspector_base_dir=expand( '$HOME/.vim/vimspector-config' ) ``` -See `--help` for more info. +When usnig `:VimspectorInstall`, the `g:vimspector_base_dir` setting is +respected unless `--basedir` is manually added (not recommended). + +See `--help` for more info on the various options. ## Manual gadget installation +If the language you want to debug is not in the supported list above, you can +probably still make it work, but it's more effort. + You essentially need to get a working installation of the debug adapter, find out how to start it, and configure that in an `adapters` entry in either your `.vimspector.json` or in `.gadgets.json`. @@ -352,7 +356,9 @@ its extension manager to install the relevant extension. You can then configure the adapter manually in the `adapters` section of your `.vimspector.json` or in a `gadgets.json`. -PRs are always welcome to add configuration to do this to `install_gadget.py`. +PRs are always welcome to add supported languages (which roughly translates to +updating `python/vimspector/gadgets.py` and testing it). + ### The gadget directory @@ -415,7 +421,8 @@ Example: } ``` -The gadget file is automatically written by `install_gadget.py`. +The gadget file is automatically written by `install_gadget.py` (or +`:VimspectorInstall`). Vimspector will also load any fies matching: `/gadgets//.gadgets.d/*.json`. These have the same @@ -865,9 +872,9 @@ an example of getting Vimspector to remotely launch and attach. ## Python * Python: [debugpy][] -* Requires `install_gadget.py --enable-python`, ideally requires a working - compiler and the python development headers/libs to build a C python extension - for performance. +* Install with `install_gadget.py --enable-python` or `:VimspectorInstall + debugpy`, ideally requires a working compiler and the python development + headers/libs to build a C python extension for performance. * Full options: https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings @@ -978,7 +985,8 @@ See [my fork of TclProDebug](https://github.com/puremourning/TclProDebug) for in * C# - dotnet core -Requires `install_gadget.py --force-enable-csharp` +Install with `install_gadget.py --force-enable-csharp` or `:VimspectorInstall +netcoredbg` ```json { @@ -998,7 +1006,8 @@ Requires `install_gadget.py --force-enable-csharp` * C# - mono -Requires `install_gadget.py --force-enable-csharp`. +Install with `install_gadget.py --force-enable-csharp` or `:VimspectorInstall +vscode-mono-debug`. ***Known not to work.*** @@ -1029,7 +1038,7 @@ Requires `install_gadget.py --force-enable-csharp`. Requires: -* `install_gadget.py --enable-go` +* `install_gadget.py --enable-go` or `:VimspectorInstall vscode-go` * [Delve][delve-install] installed, e.g. `go get -u github.com/go-delve/delve/cmd/dlv` * Delve to be in your PATH, or specify the `dlvToolPath` launch option @@ -1057,7 +1066,8 @@ https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug Requires: * (optional) Xdebug helper for chrome https://chrome.google.com/webstore/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc -* `install_gadget.py --force-enable-php` +* `install_gadget.py --force-enable-php` or `:VimspectorInstall + vscode-php-debug` * configured php xdebug extension ```ini zend_extension=xdebug.so @@ -1161,7 +1171,8 @@ https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome It allows you to debug scripts running inside chrome from within Vim. -* `./install_gadget.py --force-enable-chrome` +* `./install_gadget.py --force-enable-chrome` or `:VimspectorInstall + debugger-for-chrome` * Example: `support/test/chrome` ```json @@ -1195,7 +1206,8 @@ use it with Vimspector. * Set up [YCM for java][YcmJava]. * Get Vimspector to download the java debug plugin: - `install_gadget.py --force-enable-java ` + `install_gadget.py --force-enable-java ` or + `:VimspectorInstall java-debug-adapter` * Configure Vimspector for your project using the `vscode-java` adapter, e.g.: ```json @@ -1266,7 +1278,7 @@ Rust is supported with any gdb/lldb-based debugger. So it works fine with `vscode-cpptools` and `lldb-vscode` above. However, support for rust is best in [`CodeLLDB`](https://github.com/vadimcn/vscode-lldb#features). -* `./install_gadget.py --force-enable-rust` +* `./install_gadget.py --force-enable-rust` or `:VimspectorInstall CodeLLDB` * Example: `support/test/rust/vimspector_test` ```json @@ -1478,6 +1490,14 @@ hi link jsonCommentError Comment hi link jsonComment Comment ``` +7. What is the difference between a `gadget` and an `adapter`? A gadget is + somethin you install with `:VimspectorInstall` or `install_gadget.py`, an + `adapter` is something that Vimspector talks to (actually it's the Vimsepctor + config describing that thing). These are _usually_ one-to-one, + but in theory a single gadget can supply multiple `adapter` configs. + Typically this happens when a `gadget` supplies different `adapter` config + for, say remote debugging, or debugging in a container, etc. + # Motivation A message from the author about the motivation for this plugin: From 4144631d0367e43f99d436c8f5872a4484ce7994 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 12:50:44 +0100 Subject: [PATCH 275/609] Add :VimspectorUpdate --- README.md | 5 +++++ autoload/vimspector.vim | 11 +++++++++++ plugin/vimspector.vim | 4 ++++ python3/vimspector/installer.py | 13 +++++++++++++ 4 files changed, 33 insertions(+) diff --git a/README.md b/README.md index 3fc4722..4bd72c9 100644 --- a/README.md +++ b/README.md @@ -453,6 +453,11 @@ which can be used to check everything is working. This is used by the regression tests in CI so should always work, and is a good way to check if the problem is your configuration rather than a bug. +## Upgrade + +After updating the Vimspector code (either via `git pull` or whatever pacakge +manager), run `:VimspectorUpdate` to update any already-installed gadets. + # About ## Background diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 8ad19e2..791c262 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -256,6 +256,17 @@ function! vimspector#CompleteInstall( ArgLead, CmdLine, CursorPos ) abort \ . ')' ) endfunction +function! vimspector#Update() abort + if !s:enabled + return + endif + + let prefix = vimspector#internal#state#GetAPIPrefix() + py3 __import__( 'vimspector', + \ fromlist = [ 'installer' ] ).installer.RunUpdate( + \ vim.eval( 'prefix' ) ) +endfunction + " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 338d034..be51c27 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -102,6 +102,10 @@ command! -bar command! -bar -nargs=* -complete=custom,vimspector#CompleteInstall \ VimspectorInstall \ call vimspector#Install( ) +command! -bar -nargs=0 + \ VimspectorUpdate + \ call vimspector#Update() + " Dummy autocommands so that we can call this whenever diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index f52d667..53a48a6 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -132,6 +132,19 @@ def RunInstaller( api_prefix, *args, **kwargs ): OUTPUT_VIEW.ShowOutput( 'Installer' ) +def RunUpdate( api_prefix ): + from vimspector import utils + Configure( vimspector_base = utils.GetVimspectorBase() ) + + current_adapters = ReadAdapters( read_existing = True ) + adapters = [] + for adapter_name in current_adapters.keys(): + adapters.extend( FindGadgetForAdapter( adapter_name ) ) + + if adapters: + RunInstaller( api_prefix, *adapters ) + + def GadgetListToInstallerArgs( *gadget_list ): installer_args = [] for name in gadget_list: From 000f7a923220deea4aa98300413fc289743a0b9f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 14:40:23 +0100 Subject: [PATCH 276/609] Prettify the output with some syntax and quiet option --- README.md | 13 ++- autoload/vimspector.vim | 15 +++- install_gadget.py | 18 ++++- plugin/vimspector.vim | 7 +- python3/vimspector/installer.py | 139 +++++++++++++++++++------------- python3/vimspector/output.py | 16 ++-- python3/vimspector/utils.py | 3 +- syntax/vimspector-installer.vim | 13 +++ 8 files changed, 146 insertions(+), 78 deletions(-) create mode 100644 syntax/vimspector-installer.vim diff --git a/README.md b/README.md index 4bd72c9..d20f046 100644 --- a/README.md +++ b/README.md @@ -268,13 +268,16 @@ There are a few ways to do this: * Using `:VimspectorInstall ` (use TAB `wildmenu` to see the options, also accepts any `install_gadget.py` option) -* Using `python3 install_gadget.py ` (use `--help` to see all options) +* Alternatively, using `python3 install_gadget.py ` (use `--help` to see + all options) * When attempting to launch a debug configuration, if the configured adapter can't be found, vimspector might suggest installing one. +* Use `:VimspectorUpdate` to install the latest supported versions of the + gadgets. -Here's a demo: +Here's a demo of doing somee installs and an upgrade: -[![asciicast](https://asciinema.org/a/M3kShmfAZ8I5YewTCCKezzrr9.svg)](https://asciinema.org/a/M3kShmfAZ8I5YewTCCKezzrr9) +[![asciicast](https://asciinema.org/a/Hfu4ZvuyTZun8THNen9FQbTay.svg)](https://asciinema.org/a/Hfu4ZvuyTZun8THNen9FQbTay) Both `install_gadget.py` and `:VimspectorInstall` do the same set of things, though the default behaviours are slightly different. For supported languages, @@ -305,10 +308,6 @@ To install the tested debug adapter for a language, run: `"VimspectorInstall` runs `install_gadget.py` in the background with some of the options defaulted. -Here's a demo: - -[![asciicast](https://asciinema.org/a/mJQmMAuQG4rOp5DWq1IhDvQty.svg)](https://asciinema.org/a/mJQmMAuQG4rOp5DWq1IhDvQty) - By default `install_gadget.py` will overwrite your `.gadgets.json` with the set of adapters just installed, whereas `:VimspectorInstall` will _update_ it, overwriting only newly changed or installed adapters. diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 791c262..1ada223 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -256,7 +256,7 @@ function! vimspector#CompleteInstall( ArgLead, CmdLine, CursorPos ) abort \ . ')' ) endfunction -function! vimspector#Update() abort +function! vimspector#Update( ... ) abort if !s:enabled return endif @@ -264,9 +264,20 @@ function! vimspector#Update() abort let prefix = vimspector#internal#state#GetAPIPrefix() py3 __import__( 'vimspector', \ fromlist = [ 'installer' ] ).installer.RunUpdate( - \ vim.eval( 'prefix' ) ) + \ vim.eval( 'prefix' ), + \ *vim.eval( 'a:000' ) ) endfunction +function! vimspector#AbortInstall() abort + if !s:enabled + return + endif + + let prefix = vimspector#internal#state#GetAPIPrefix() + py3 __import__( 'vimspector', fromlist = [ 'installer' ] ).installer.Abort() +endfunction + + " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/install_gadget.py b/install_gadget.py index c030699..44dbfe4 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -69,6 +69,14 @@ parser.add_argument( '--force-all', action = 'store_true', help = 'Enable all unsupported completers' ) +parser.add_argument( '--quiet', + action = 'store_true', + help = 'Suppress installation output' ) + +parser.add_argument( '--verbose', + action = 'store_true', + help = 'Force installation output' ) + parser.add_argument( '--basedir', action = 'store', help = 'Advanced option. ' @@ -145,6 +153,7 @@ if args.basedir: install.MakeInstallDirs( vimspector_base ) installer.Configure( vimspector_base = vimspector_base, + quiet = args.quiet and not args.verbose, no_check_certificate = args.no_check_certificate ) if args.force_all and not args.all: @@ -202,8 +211,11 @@ if args.basedir: "let g:vimspector_base_dir='" + vimspector_base + "'" ) if succeeded: - print( "The following adapters were installed successfully: {}".format( - ','.join( succeeded ) ) ) + print( "Done. The following adapters were installed successfully:\n - {}".format( + '\n - '.join( succeeded ) ) ) if failed: - sys.exit( 'Failed to install adapters: {}'.format( ','.join( failed ) ) ) + sys.exit( 'Failed to install adapters:\n * {}{}'.format( + '\n * '.join( failed ), + "\nRe-run with --verbose for more info on failures" + if args.quiet and not args.verbose else '' ) ) diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index be51c27..afc1c08 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -102,9 +102,12 @@ command! -bar command! -bar -nargs=* -complete=custom,vimspector#CompleteInstall \ VimspectorInstall \ call vimspector#Install( ) -command! -bar -nargs=0 +command! -bar -nargs=* \ VimspectorUpdate - \ call vimspector#Update() + \ call vimspector#Update( ) +command! -bar -nargs=* + \ VimspectorAbortInstall + \ call vimspector#AbortInstall( ) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 53a48a6..28624af 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -44,6 +44,7 @@ OUTPUT_VIEW = None class Options: vimspector_base = None no_check_certificate = False + quiet = False options = Options() @@ -54,6 +55,23 @@ def Configure( **kwargs ): setattr( options, k, v ) +def Print( *args, **kwargs ): + if not options.quiet: + print( *args, **kwargs ) + + +def CheckCall( *args, **kwargs ): + if options.quiet: + out = subprocess.PIPE + else: + out = sys.stdout + + kwargs[ 'stdout' ] = out + kwargs[ 'stderr' ] = subprocess.STDOUT + + subprocess.check_call( *args, **kwargs ) + + def PathToAnyWorkingPython3(): # We can't rely on sys.executable because it's usually 'vim' (fixme, not with # neovim?) @@ -91,9 +109,7 @@ def RunInstaller( api_prefix, *args, **kwargs ): vimspector_base_dir = utils.GetVimspectorBase() global OUTPUT_VIEW - if OUTPUT_VIEW: - OUTPUT_VIEW.Reset() - OUTPUT_VIEW = None + _ResetInstaller() with utils.RestoreCurrentWindow(): vim.command( f'botright { settings.Int( "bottombar_height", 10 ) }new' ) @@ -104,6 +120,7 @@ def RunInstaller( api_prefix, *args, **kwargs ): PathToAnyWorkingPython3(), '-u', os.path.join( vimspector_home, 'install_gadget.py' ), + '--quiet', '--update-gadget-config', ] if not vimspector_base_dir == vimspector_home: @@ -112,10 +129,7 @@ def RunInstaller( api_prefix, *args, **kwargs ): def handler( exit_code ): if exit_code == 0: - global OUTPUT_VIEW - if OUTPUT_VIEW: - OUTPUT_VIEW.Reset() - OUTPUT_VIEW = None + _ResetInstaller() utils.UserMessage( "Vimspector gadget installation complete!" ) vim.command( 'silent doautocmd User VimspectorInstallSuccess' ) if 'then' in kwargs: @@ -128,21 +142,37 @@ def RunInstaller( api_prefix, *args, **kwargs ): OUTPUT_VIEW.RunJobWithOutput( 'Installer', cmd, - completion_handler = handler ) + completion_handler = handler, + syntax = 'vimspector-installer' ) OUTPUT_VIEW.ShowOutput( 'Installer' ) -def RunUpdate( api_prefix ): +def RunUpdate( api_prefix, *args ): from vimspector import utils Configure( vimspector_base = utils.GetVimspectorBase() ) + args = list( args ) current_adapters = ReadAdapters( read_existing = True ) - adapters = [] for adapter_name in current_adapters.keys(): - adapters.extend( FindGadgetForAdapter( adapter_name ) ) + args.extend( FindGadgetForAdapter( adapter_name ) ) - if adapters: - RunInstaller( api_prefix, *adapters ) + if args: + RunInstaller( api_prefix, *args ) + + +def _ResetInstaller(): + global OUTPUT_VIEW + if OUTPUT_VIEW: + OUTPUT_VIEW.Reset() + OUTPUT_VIEW = None + + +def Abort(): + _ResetInstaller() + from vimspector import utils + utils.UserMessage( 'Vimspector installation aborted', + persist = True, + error = True ) def GadgetListToInstallerArgs( *gadget_list ): @@ -222,7 +252,7 @@ def InstallDebugpy( name, root, gadget ): root = os.path.join( root, 'debugpy-{}'.format( gadget[ 'version' ] ) ) os.chdir( root ) try: - subprocess.check_call( [ sys.executable, 'setup.py', 'build' ] ) + CheckCall( [ sys.executable, 'setup.py', 'build' ] ) finally: os.chdir( wd ) @@ -258,8 +288,8 @@ def InstallTclProDebug( name, root, gadget ): with CurrentWorkingDir( os.path.join( root, 'lib', 'tclparser' ) ): - subprocess.check_call( configure ) - subprocess.check_call( [ 'make' ] ) + CheckCall( configure ) + CheckCall( [ 'make' ] ) MakeSymlink( name, root ) @@ -267,26 +297,27 @@ def InstallTclProDebug( name, root, gadget ): def InstallNodeDebug( name, root, gadget ): node_version = subprocess.check_output( [ 'node', '--version' ], universal_newlines=True ).strip() - print( "Node.js version: {}".format( node_version ) ) + Print( "Node.js version: {}".format( node_version ) ) if list( map( int, node_version[ 1: ].split( '.' ) ) ) >= [ 12, 0, 0 ]: - print( "Can't install vscode-debug-node2:" ) - print( "Sorry, you appear to be running node 12 or later. That's not " + Print( "Can't install vscode-debug-node2:" ) + Print( "Sorry, you appear to be running node 12 or later. That's not " "compatible with the build system for this extension, and as far as " "we know, there isn't a pre-built independent package." ) - print( "My advice is to install nvm, then do:" ) - print( " $ nvm install --lts 10" ) - print( " $ nvm use --lts 10" ) - print( " $ ./install_gadget.py --enable-node ..." ) - raise RuntimeError( 'Invalid node environent for node debugger' ) + Print( "My advice is to install nvm, then do:" ) + Print( " $ nvm install --lts 10" ) + Print( " $ nvm use --lts 10" ) + Print( " $ ./install_gadget.py --enable-node ..." ) + raise RuntimeError( 'Node 10 is required to install node debugger (sadly)' ) with CurrentWorkingDir( root ): - subprocess.check_call( [ 'npm', 'install' ] ) - subprocess.check_call( [ 'npm', 'run', 'build' ] ) + CheckCall( [ 'npm', 'install' ] ) + CheckCall( [ 'npm', 'run', 'build' ] ) MakeSymlink( name, root ) def InstallGagdet( name, gadget, succeeded, failed, all_adapters ): try: + print( f"Installing {name}..." ) v = {} v.update( gadget.get( 'all', {} ) ) v.update( gadget.get( install.GetOS(), {} ) ) @@ -339,11 +370,12 @@ def InstallGagdet( name, gadget, succeeded, failed, all_adapters ): all_adapters.update( gadget.get( 'adapters', {} ) ) succeeded.append( name ) - print( "Done installing {}".format( name ) ) + print( f" - Done installing {name}" ) except Exception as e: - traceback.print_exc() + if not options.quiet: + traceback.print_exc() failed.append( name ) - print( "FAILED installing {}: {}".format( name, e ) ) + print( f" - FAILED installing {name}: {e}".format( name, e ) ) def ReadAdapters( read_existing = True ): @@ -392,7 +424,7 @@ def CurrentWorkingDir( d ): def MakeExecutable( file_path ): # TODO: import stat and use them by _just_ adding the X bit. - print( 'Making executable: {}'.format( file_path ) ) + Print( 'Making executable: {}'.format( file_path ) ) os.chmod( file_path, 0o755 ) @@ -409,7 +441,7 @@ def WithRetry( f ): return f( *args, **kwargs ) except Exception as e: thrown = e - print( "Failed - {}, will retry in {} seconds".format( e, timeout ) ) + Print( "Failed - {}, will retry in {} seconds".format( e, timeout ) ) time.sleep( timeout ) raise thrown @@ -437,13 +469,13 @@ def DownloadFileTo( url, if os.path.exists( file_path ): if checksum: if ValidateCheckSumSHA256( file_path, checksum ): - print( "Checksum matches for {}, using it".format( file_path ) ) + Print( "Checksum matches for {}, using it".format( file_path ) ) return file_path else: - print( "Checksum doesn't match for {}, removing it".format( + Print( "Checksum doesn't match for {}, removing it".format( file_path ) ) - print( "Removing existing {}".format( file_path ) ) + Print( "Removing existing {}".format( file_path ) ) os.remove( file_path ) r = request.Request( url, headers = { 'User-Agent': 'Vimspector' } ) @@ -470,7 +502,7 @@ def DownloadFileTo( url, GetChecksumSHA254( file_path ), checksum ) ) else: - print( "Checksum for {}: {}".format( file_path, + Print( "Checksum for {}: {}".format( file_path, GetChecksumSHA254( file_path ) ) ) return file_path @@ -488,7 +520,7 @@ def ValidateCheckSumSHA256( file_path, checksum ): def RemoveIfExists( destination ): if os.path.islink( destination ): - print( "Removing file {}".format( destination ) ) + Print( "Removing file {}".format( destination ) ) os.remove( destination ) return @@ -499,21 +531,21 @@ def RemoveIfExists( destination ): return "{}.{}".format( destination, N ) while os.path.isdir( BackupDir() ): - print( "Removing old dir {}".format( BackupDir() ) ) + Print( "Removing old dir {}".format( BackupDir() ) ) try: shutil.rmtree( BackupDir() ) - print ( "OK, removed it" ) + Print ( "OK, removed it" ) break except OSError: - print ( "FAILED" ) + Print ( "FAILED" ) N = N + 1 if os.path.exists( destination ): - print( "Removing dir {}".format( destination ) ) + Print( "Removing dir {}".format( destination ) ) try: shutil.rmtree( destination ) except OSError: - print( "FAILED, moving {} to dir {}".format( destination, BackupDir() ) ) + Print( "FAILED, moving {} to dir {}".format( destination, BackupDir() ) ) os.rename( destination, BackupDir() ) @@ -534,7 +566,7 @@ class ModePreservingZipFile( zipfile.ZipFile ): def ExtractZipTo( file_path, destination, format ): - print( "Extracting {} to {}".format( file_path, destination ) ) + Print( "Extracting {} to {}".format( file_path, destination ) ) RemoveIfExists( destination ) if format == 'zip': @@ -556,7 +588,7 @@ def ExtractZipTo( file_path, destination, format ): # windows-generated tar files os.makedirs( destination ) with CurrentWorkingDir( destination ): - subprocess.check_call( [ 'tar', 'zxvf', file_path ] ) + CheckCall( [ 'tar', 'zxvf', file_path ] ) def MakeExtensionSymlink( name, root ): @@ -580,12 +612,7 @@ def MakeSymlink( link, pointing_to, in_folder = None ): link_path = os.path.abspath( link_path ) if os.path.isdir( link_path ): os.rmdir( link_path ) - subprocess.check_call( [ 'cmd.exe', - '/c', - 'mklink', - '/J', - link_path, - pointing_to ] ) + CheckCall( [ 'cmd.exe', '/c', 'mklink', '/J', link_path, pointing_to ] ) else: os.symlink( pointing_to_relative, link_path ) @@ -593,13 +620,10 @@ def MakeSymlink( link, pointing_to, in_folder = None ): def CloneRepoTo( url, ref, destination ): RemoveIfExists( destination ) git_in_repo = [ 'git', '-C', destination ] - subprocess.check_call( [ 'git', 'clone', url, destination ] ) - subprocess.check_call( git_in_repo + [ 'checkout', ref ] ) - subprocess.check_call( git_in_repo + [ 'submodule', 'sync', '--recursive' ] ) - subprocess.check_call( git_in_repo + [ 'submodule', - 'update', - '--init', - '--recursive' ] ) + CheckCall( [ 'git', 'clone', url, destination ] ) + CheckCall( git_in_repo + [ 'checkout', ref ] ) + CheckCall( git_in_repo + [ 'submodule', 'sync', '--recursive' ] ) + CheckCall( git_in_repo + [ 'submodule', 'update', '--init', '--recursive' ] ) def AbortIfSUperUser( force_sudo ): @@ -613,4 +637,5 @@ def AbortIfSUperUser( force_sudo ): print( "*** RUNNING AS SUPER USER DUE TO force_sudo! " " All bets are off. ***" ) else: - sys.exit( "This script should *not* be run as super user. Aborting." ) + raise RuntimeError( + "This script should *not* be run as super user. Aborting." ) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 3a2d5cb..1d349aa 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -25,6 +25,7 @@ class TabBuffer( object ): self.index = index self.flag = False self.is_job = False + self.syntax = None BUFFER_MAP = { @@ -135,17 +136,16 @@ class OutputView( object ): self._RenderWinBar( category ) - def RunJobWithOutput( self, category, cmd, completion_handler = None ): - self._CreateBuffer( category, - cmd = cmd, - completion_handler = completion_handler ) + def RunJobWithOutput( self, category, cmd, **kwargs ): + self._CreateBuffer( category, cmd = cmd, **kwargs ) def _CreateBuffer( self, category, file_name = None, cmd = None, - completion_handler = None ): + completion_handler = None, + syntax = None ): if file_name is not None: assert cmd is None if install.GetOS() == "windows": @@ -182,6 +182,12 @@ class OutputView( object ): self._RenderWinBar( category ) + self._buffers[ category ].syntax = utils.SetSyntax( + self._buffers[ category ].syntax, + syntax, + self._buffers[ category ].buf ) + + def _RenderWinBar( self, category ): if not self._window.valid: return diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index fe11631..21ac3ff 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -621,8 +621,7 @@ def SetSyntax( current_syntax, syntax, *args ): # doesn't actually trigger the Syntax autocommand, and i'm not sure that # 'doautocmd Syntax' is the right solution or not for buf in args: - with AnyWindowForBuffer( buf ): - vim.command( 'set syntax={}'.format( Escape( syntax ) ) ) + Call( 'setbufvar', buf.number, '&syntax', syntax ) return syntax diff --git a/syntax/vimspector-installer.vim b/syntax/vimspector-installer.vim new file mode 100644 index 0000000..98ea48b --- /dev/null +++ b/syntax/vimspector-installer.vim @@ -0,0 +1,13 @@ +if exists( 'b:current_syntax' ) + finish +endif + +let b:current_syntax = 'vimspector-installer' + +syn keyword VimspectorInstalling Installing +syn keyword VimspectorDone Done +syn keyword VimspectorError Failed FAILED + +hi default link VimspectorInstalling Constant +hi default link VimspectorDone DiffAdd +hi default link VimspectorError WarningMsg From 625da3fcbeb86a5f3718b1d6a4ddc83581810e81 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 14:57:26 +0100 Subject: [PATCH 277/609] Tarballs still require no installation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d20f046..37b05c5 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,8 @@ In order for Vimspector to be useful, you need to have some adapters installed. There are a few ways to do this: +* If you downloaded a tarball, gadgets for main supported langauges are already + installed for you. * Using `:VimspectorInstall ` (use TAB `wildmenu` to see the options, also accepts any `install_gadget.py` option) * Alternatively, using `python3 install_gadget.py ` (use `--help` to see From d7eff46e0b902ff32839efd14121d01a5be1b8a3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 14:59:54 +0100 Subject: [PATCH 278/609] Vint the syntax file too --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d5e36a2..24932f2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -28,7 +28,7 @@ stages: - bash: pip3 install -r dev_requirements.txt displayName: "Install requirements" - - bash: $HOME/.local/bin/vint autoload/ compiler/ plugin/ tests/ + - bash: $HOME/.local/bin/vint autoload/ compiler/ plugin/ tests/ syntax/ displayName: "Run vint" - job: 'linux' From 2d6cada5a9ecddc13845caf45c492d070c7b7267 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 15:04:37 +0100 Subject: [PATCH 279/609] Azure - check gadgets.py --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 24932f2..6584cf9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -45,7 +45,7 @@ stages: - task: CacheBeta@0 inputs: - key: v2 | gadgets | $(Agent.OS) | python3/vimspector/installer.py + key: v2 | gadgets | $(Agent.OS) | python3/vimspector/gagdets.py path: gadgets/linux/download displayName: Cache gadgets @@ -89,7 +89,7 @@ stages: - task: CacheBeta@0 inputs: - key: v2 | gadgets | $(Agent.OS) | python3/vimspector/installer.py + key: v2 | gadgets | $(Agent.OS) | python3/vimspector/gagdets.py path: gadgets/macos/download displayName: Cache gadgets From 8a6d56d3e1ac3439f1906cd6878ceb542c509728 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 15:04:48 +0100 Subject: [PATCH 280/609] Run the upate in CI too --- azure-pipelines.yml | 4 ++-- run_tests | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6584cf9..0c4d9e6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -55,7 +55,7 @@ stages: - bash: | eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) export GOPATH=$HOME/go - ./run_tests --install --report messages --quiet + ./run_tests --install --update --report messages --quiet displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: gdb @@ -96,7 +96,7 @@ stages: - bash: vim --version displayName: 'Print vim version information' - - bash: ./run_tests --install --report messages --quiet + - bash: ./run_tests --install --update --report messages --quiet displayName: 'Run the tests' env: VIMSPECTOR_MIMODE: lldb diff --git a/run_tests b/run_tests index 3c327bb..44e09f3 100755 --- a/run_tests +++ b/run_tests @@ -2,6 +2,7 @@ BASEDIR=$(dirname $0) INSTALL=0 +UPDATE=0 RUN_VIM="vim -N --clean --not-a-term" RUN_TEST="${RUN_VIM} -S lib/run_test.vim" BASEDIR_CMD='py3 pass' @@ -30,6 +31,10 @@ while [ -n "$1" ]; do INSTALL=$1 shift ;; + "--update") + UPDATE=1 + shift + ;; "--report") shift VIMSPECTOR_TEST_STDOUT=$1 @@ -94,6 +99,18 @@ if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "vim" ]; then fi fi +if [ "$UPDATE" = "1" ]; then + if ! $RUN_VIM -u $(dirname $0)/tests/vimrc \ + --cmd "${BASEDIR_CMD}" \ + -c 'autocmd User VimspectorInstallSuccess qa!' \ + -c 'autocmd User VimspectorInstallFailed cquit!' \ + -c "VimspectorUpdate"; then + echo "Vim update reported errors" >&2 + exit 1 + fi +fi + + if [ -z "$VIMSPECTOR_MIMODE" ]; then if which lldb >/dev/null 2>&1; then export VIMSPECTOR_MIMODE=lldb From e603520860a216899a3b0135086221cfa4dbc7ca Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 15:14:59 +0100 Subject: [PATCH 281/609] FixUp: Flake8 --- install_gadget.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install_gadget.py b/install_gadget.py index 44dbfe4..4b42984 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -211,7 +211,8 @@ if args.basedir: "let g:vimspector_base_dir='" + vimspector_base + "'" ) if succeeded: - print( "Done. The following adapters were installed successfully:\n - {}".format( + print( "Done" ) + print( "The following adapters were installed successfully:\n - {}".format( '\n - '.join( succeeded ) ) ) if failed: From 8d1c723b283669d49bf1c218f18f7b121dcfc446 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 15:16:09 +0100 Subject: [PATCH 282/609] FixUp: Azure --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0c4d9e6..ade040c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -45,7 +45,7 @@ stages: - task: CacheBeta@0 inputs: - key: v2 | gadgets | $(Agent.OS) | python3/vimspector/gagdets.py + key: v2 | gadgets | $(Agent.OS) | python3/vimspector/gadgets.py path: gadgets/linux/download displayName: Cache gadgets @@ -89,7 +89,7 @@ stages: - task: CacheBeta@0 inputs: - key: v2 | gadgets | $(Agent.OS) | python3/vimspector/gagdets.py + key: v2 | gadgets | $(Agent.OS) | python3/vimspector/gadgets.py path: gadgets/macos/download displayName: Cache gadgets From 2ea112ded9845bacac5fda3e0c2bb97c1df9af12 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 15:48:16 +0100 Subject: [PATCH 283/609] No args for VimspectorAbortInstall --- plugin/vimspector.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index afc1c08..3e937da 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -105,9 +105,9 @@ command! -bar -nargs=* -complete=custom,vimspector#CompleteInstall command! -bar -nargs=* \ VimspectorUpdate \ call vimspector#Update( ) -command! -bar -nargs=* +command! -bar -nargs=0 \ VimspectorAbortInstall - \ call vimspector#AbortInstall( ) + \ call vimspector#AbortInstall() From c50c99ef342181de7558b3173b082239552b4bae Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 16:01:44 +0100 Subject: [PATCH 284/609] Don't spam echo when jobs finish, revert sudo exit --- python3/vimspector/installer.py | 6 +----- python3/vimspector/utils.py | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 28624af..e610fe4 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -15,9 +15,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# TODO: Chnage `print` to some other mechanism that can be displayed in a vim -# buffer? - from urllib import request import contextlib import functools @@ -637,5 +634,4 @@ def AbortIfSUperUser( force_sudo ): print( "*** RUNNING AS SUPER USER DUE TO force_sudo! " " All bets are off. ***" ) else: - raise RuntimeError( - "This script should *not* be run as super user. Aborting." ) + sys.exit( "This script should *not* be run as super user. Aborting." ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 21ac3ff..ad5bc9a 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -84,8 +84,6 @@ def OnCommandWithLogComplete( name, exit_code ): cb = COMMAND_HANDLERS.get( name ) if cb: cb( exit_code ) - else: - UserMessage( f'Job complete: { name } (exit status: { exit_code })' ) def SetUpCommandBuffer( cmd, name, api_prefix, completion_handler = None ): From 26d7e95adc62100981759d04c85c455b9832268b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 19:21:47 +0100 Subject: [PATCH 285/609] Fix windows installer issues - use symlinks not junctions and fix dumb use of PIPE with check_call --- autoload/vimspector/internal/job.vim | 1 - python3/vimspector/installer.py | 23 +++++++++++------------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/autoload/vimspector/internal/job.vim b/autoload/vimspector/internal/job.vim index 474eb7c..206baf0 100644 --- a/autoload/vimspector/internal/job.vim +++ b/autoload/vimspector/internal/job.vim @@ -170,7 +170,6 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort \ a:cmd, \ { \ 'out_io': 'buffer', - \ 'in_io': 'null', \ 'err_io': 'buffer', \ 'out_msg': 0, \ 'err_msg': 0, diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index e610fe4..4175946 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -59,14 +59,13 @@ def Print( *args, **kwargs ): def CheckCall( *args, **kwargs ): if options.quiet: - out = subprocess.PIPE + try: + subprocess.check_output( *args, stderr=subprocess.STDOUT, **kwargs ) + except subprocess.CalledProcessError as e: + print( e.output.decode( 'utf-8' ) ) + raise else: - out = sys.stdout - - kwargs[ 'stdout' ] = out - kwargs[ 'stderr' ] = subprocess.STDOUT - - subprocess.check_call( *args, **kwargs ) + subprocess.check_call( *args, **kwargs ) def PathToAnyWorkingPython3(): @@ -477,7 +476,7 @@ def DownloadFileTo( url, r = request.Request( url, headers = { 'User-Agent': 'Vimspector' } ) - print( "Downloading {} to {}/{}".format( url, destination, file_name ) ) + Print( "Downloading {} to {}/{}".format( url, destination, file_name ) ) if not check_certificate: context = ssl.create_default_context() @@ -517,7 +516,7 @@ def ValidateCheckSumSHA256( file_path, checksum ): def RemoveIfExists( destination ): if os.path.islink( destination ): - Print( "Removing file {}".format( destination ) ) + Print( "Removing link {}".format( destination ) ) os.remove( destination ) return @@ -533,8 +532,8 @@ def RemoveIfExists( destination ): shutil.rmtree( BackupDir() ) Print ( "OK, removed it" ) break - except OSError: - Print ( "FAILED" ) + except OSError as e: + Print ( f"FAILED to remove {BackupDir()}: {e}" ) N = N + 1 if os.path.exists( destination ): @@ -609,7 +608,7 @@ def MakeSymlink( link, pointing_to, in_folder = None ): link_path = os.path.abspath( link_path ) if os.path.isdir( link_path ): os.rmdir( link_path ) - CheckCall( [ 'cmd.exe', '/c', 'mklink', '/J', link_path, pointing_to ] ) + CheckCall( [ 'cmd.exe', '/c', 'mklink', '/D', link_path, pointing_to ] ) else: os.symlink( pointing_to_relative, link_path ) From 29cb5c914b8f1d62915c50771cf4b9e06107cd70 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 22:09:49 +0100 Subject: [PATCH 286/609] Add bang versions of install commands to leave the output open --- README.md | 21 +++++++++++++++++++-- autoload/vimspector.vim | 6 ++++-- plugin/vimspector.vim | 8 ++++---- python3/vimspector/debug_session.py | 1 + python3/vimspector/installer.py | 9 +++++---- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 37b05c5..93dd747 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,12 @@ For a tutorial and usage overview, take a look at the * [Windows differences](#windows-differences) * [Clone the plugin](#clone-the-plugin) * [Install some gadgets](#install-some-gadgets) + * [VimspectorInstall and VimspectorUpdate commands](#vimspectorinstall-and-vimspectorupdate-commands) + * [install_gadget.py](#install_gadgetpy) * [Manual gadget installation](#manual-gadget-installation) * [The gadget directory](#the-gadget-directory) * [Trying it out](#trying-it-out) + * [Upgrade](#upgrade) * [About](#about) * [Background](#background) * [Status](#status) @@ -71,7 +74,7 @@ For a tutorial and usage overview, take a look at the * [License](#license) * [Sponsorship](#sponsorship) - + @@ -307,9 +310,23 @@ To install the tested debug adapter for a language, run: | Supported and experimental adapters | `./install_gadget.py --all --force-all` | `:VimspectorInstall --all` | | Adapter for specific debug config | | Suggested by Vimspector when starting debugging | -`"VimspectorInstall` runs `install_gadget.py` in the background with some of +### VimspectorInstall and VimspectorUpdate commands + +`:VimspectorInstall` runs `install_gadget.py` in the background with some of the options defaulted. +`:VimspectorUpdate` runs `install_gadget.py` to re-install (i.e. update) any +gadgets already installed in your `.gadgets.json`. + +The output is minimal, to see the full output add `--verbose` to the command, as +in `:VimspectorInstall --verbose ...` or `:VimspectorUpdate --verbose ...`. + +If the installation is successful, the output window is closed (and the output +lost forever). Use a `!` to keep it open (e.g. `:VimspectorInstall! --verbose +--all` or `:VimspectorUpdate!` (etc.). + +### install\_gadget.py + By default `install_gadget.py` will overwrite your `.gadgets.json` with the set of adapters just installed, whereas `:VimspectorInstall` will _update_ it, overwriting only newly changed or installed adapters. diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 1ada223..afbd52c 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -232,7 +232,7 @@ function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort \ "\n" ) endfunction -function! vimspector#Install( ... ) abort +function! vimspector#Install( bang, ... ) abort if !s:enabled return endif @@ -243,6 +243,7 @@ function! vimspector#Install( ... ) abort py3 __import__( 'vimspector', \ fromlist = [ 'installer' ] ).installer.RunInstaller( \ vim.eval( 'prefix' ), + \ vim.eval( 'a:bang' ) == '!', \ *vim.eval( 'a:000' ) ) endfunction @@ -256,7 +257,7 @@ function! vimspector#CompleteInstall( ArgLead, CmdLine, CursorPos ) abort \ . ')' ) endfunction -function! vimspector#Update( ... ) abort +function! vimspector#Update( bang, ... ) abort if !s:enabled return endif @@ -265,6 +266,7 @@ function! vimspector#Update( ... ) abort py3 __import__( 'vimspector', \ fromlist = [ 'installer' ] ).installer.RunUpdate( \ vim.eval( 'prefix' ), + \ vim.eval( 'a:bang' ) == '!', \ *vim.eval( 'a:000' ) ) endfunction diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 3e937da..416eb04 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -99,12 +99,12 @@ command! -bar \ call vimspector#Reset() " Installer commands -command! -bar -nargs=* -complete=custom,vimspector#CompleteInstall +command! -bar -bang -nargs=* -complete=custom,vimspector#CompleteInstall \ VimspectorInstall - \ call vimspector#Install( ) -command! -bar -nargs=* + \ call vimspector#Install( , ) +command! -bar -bang -nargs=* \ VimspectorUpdate - \ call vimspector#Update( ) + \ call vimspector#Update( , ) command! -bar -nargs=0 \ VimspectorAbortInstall \ call vimspector#AbortInstall() diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index cc23a47..15ff90c 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -164,6 +164,7 @@ class DebugSession( object ): installer.RunInstaller( self._api_prefix, + False, # Don't leave open *shlex.split( response ), then = lambda: self.Start( new_launch_variables ) ) return diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 4175946..52497b5 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -95,7 +95,7 @@ def PathToAnyWorkingPython3(): raise RuntimeError( "Unable to find a working python3" ) -def RunInstaller( api_prefix, *args, **kwargs ): +def RunInstaller( api_prefix, leave_open, *args, **kwargs ): from vimspector import utils, output, settings import vim @@ -125,7 +125,8 @@ def RunInstaller( api_prefix, *args, **kwargs ): def handler( exit_code ): if exit_code == 0: - _ResetInstaller() + if not leave_open: + _ResetInstaller() utils.UserMessage( "Vimspector gadget installation complete!" ) vim.command( 'silent doautocmd User VimspectorInstallSuccess' ) if 'then' in kwargs: @@ -143,7 +144,7 @@ def RunInstaller( api_prefix, *args, **kwargs ): OUTPUT_VIEW.ShowOutput( 'Installer' ) -def RunUpdate( api_prefix, *args ): +def RunUpdate( api_prefix, leave_open, *args ): from vimspector import utils Configure( vimspector_base = utils.GetVimspectorBase() ) @@ -153,7 +154,7 @@ def RunUpdate( api_prefix, *args ): args.extend( FindGadgetForAdapter( adapter_name ) ) if args: - RunInstaller( api_prefix, *args ) + RunInstaller( api_prefix, leave_open, *args ) def _ResetInstaller(): From 8438dd40a12ca7d9e06c60c924da2fe268fd8fb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adelar=20da=20Silva=20Queir=C3=B3z?= Date: Wed, 22 Jul 2020 19:17:39 -0300 Subject: [PATCH 287/609] Spelling correction Just a spelling correction --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 93dd747..3c2e2a6 100644 --- a/README.md +++ b/README.md @@ -473,7 +473,7 @@ your configuration rather than a bug. ## Upgrade -After updating the Vimspector code (either via `git pull` or whatever pacakge +After updating the Vimspector code (either via `git pull` or whatever package manager), run `:VimspectorUpdate` to update any already-installed gadets. # About From 8f5b928e4b8dfd150f153ada38d11cad7a8cd794 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 23 Jul 2020 16:25:04 +0100 Subject: [PATCH 288/609] Allow a statically configured list of gadgets Useful for storing config in source control --- README.md | 13 +++++++++++++ autoload/vimspector.vim | 3 --- python3/vimspector/installer.py | 20 ++++++++++++++------ python3/vimspector/settings.py | 9 ++++++++- python3/vimspector/utils.py | 30 ++++++++++++++++++++++-------- 5 files changed, 57 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 3c2e2a6..db6e025 100644 --- a/README.md +++ b/README.md @@ -325,6 +325,19 @@ If the installation is successful, the output window is closed (and the output lost forever). Use a `!` to keep it open (e.g. `:VimspectorInstall! --verbose --all` or `:VimspectorUpdate!` (etc.). +If you know in advance which gadgets you want to install, for example so that +you can reproduce your config from source control, you can set +`g:vimspector_install_gadgets` to a list of gadgets. This will be used when: + +* Running `:VimspectorInstall` with no arguments, or +* Running `:VimspectorUpdate` + +For example: + +```viml +let g:vimspector_install_gadgets = [ 'debugpy', 'vscode-cpptools', 'CodeLLDB' ] +``` + ### install\_gadget.py By default `install_gadget.py` will overwrite your `.gadgets.json` with the set diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index afbd52c..3b5dd75 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -236,9 +236,6 @@ function! vimspector#Install( bang, ... ) abort if !s:enabled return endif - if a:0 < 1 - return - endif let prefix = vimspector#internal#state#GetAPIPrefix() py3 __import__( 'vimspector', \ fromlist = [ 'installer' ] ).installer.RunInstaller( diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 52497b5..3e4440d 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -99,9 +99,15 @@ def RunInstaller( api_prefix, leave_open, *args, **kwargs ): from vimspector import utils, output, settings import vim + if not args: + args = settings.List( 'install_gadgets' ) + + if not args: + return + args = GadgetListToInstallerArgs( *args ) - vimspector_home = utils.GetVimString( vim.vars, 'vimspector_home' ) + vimspector_home = utils.GetVimValue( vim.vars, 'vimspector_home' ) vimspector_base_dir = utils.GetVimspectorBase() global OUTPUT_VIEW @@ -145,16 +151,18 @@ def RunInstaller( api_prefix, leave_open, *args, **kwargs ): def RunUpdate( api_prefix, leave_open, *args ): - from vimspector import utils + from vimspector import utils, settings Configure( vimspector_base = utils.GetVimspectorBase() ) - args = list( args ) + insatller_args = list( args ) + insatller_args.extend( settings.List( 'install_gadgets' ) ) + current_adapters = ReadAdapters( read_existing = True ) for adapter_name in current_adapters.keys(): - args.extend( FindGadgetForAdapter( adapter_name ) ) + insatller_args.extend( FindGadgetForAdapter( adapter_name ) ) - if args: - RunInstaller( api_prefix, leave_open, *args ) + if insatller_args: + RunInstaller( api_prefix, leave_open, *insatller_args ) def _ResetInstaller(): diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index c365499..08f3066 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -16,11 +16,18 @@ import vim import builtins +from vimspector import utils def Get( option: str, default=None, cls=str ): - return cls( vim.vars.get( f'vimspector_{ option }', default ) ) + return cls( utils.GetVimValue( vim.vars, + f'vimspector_{ option }', + default ) ) def Int( option: str, default=0 ): return Get( option, default=default, cls=builtins.int ) + + +def List( option: str, default=[] ): + return utils.GetVimList( vim.vars, f'vimspector_{ option }', default ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index ad5bc9a..0aea216 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -23,7 +23,7 @@ import string import functools import subprocess import shlex - +import collections LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log' ) ) @@ -646,7 +646,7 @@ def HideSplash( api_prefix, splash ): return None -def GetVimString( vim_dict, name, default=None ): +def GetVimValue( vim_dict, name, default=None ): # FIXME: use 'encoding' ? try: @@ -659,13 +659,27 @@ def GetVimString( vim_dict, name, default=None ): return value +def GetVimList( vim_dict, name, default=None ): + try: + value = vim_dict[ name ] + except KeyError: + return default + + if not isinstance( value, collections.abc.Iterable ): + raise ValueError( f"Expected a list for { name }, but found " + f"{ type( value ) }" ) + + return [ i.decode( 'utf-8' ) if isinstance( i, bytes ) else i for i in value ] + + + def GetVimspectorBase(): - return GetVimString( vim.vars, - 'vimspector_base_dir', - os.path.abspath( - os.path.join( os.path.dirname( __file__ ), - '..', - '..' ) ) ) + return GetVimValue( vim.vars, + 'vimspector_base_dir', + os.path.abspath( + os.path.join( os.path.dirname( __file__ ), + '..', + '..' ) ) ) def GetUnusedLocalPort(): From f9d20b9537bf2521d8ca4d35b32155b0a9a99e95 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 24 Jul 2020 17:01:16 +0100 Subject: [PATCH 289/609] Upgrade gadgets when they change This adds a --upgrade option to install_gadget.py and makes VimspectorUpdate only update things which have changed. To do this, we record the gadget spec in a manfiest file and compare it with the current spec when in upgrade mode. 'Changed' in this case means that the gadget spec has changed from the last time the installer was run. It does _not_ actually check the presence of the gadget. --- install_gadget.py | 21 +++- python3/vimspector/gadgets.py | 5 +- python3/vimspector/install.py | 5 + python3/vimspector/installer.py | 193 +++++++++++++++++++++----------- syntax/vimspector-installer.vim | 8 ++ 5 files changed, 162 insertions(+), 70 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 4b42984..52621c3 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -69,6 +69,10 @@ parser.add_argument( '--force-all', action = 'store_true', help = 'Enable all unsupported completers' ) +parser.add_argument( '--upgrade', + action = 'store_true', + help = 'Only update adapters changed from the manifest' ) + parser.add_argument( '--quiet', action = 'store_true', help = 'Suppress installation output' ) @@ -175,6 +179,7 @@ failed = [] succeeded = [] all_adapters = installer.ReadAdapters( read_existing = args.update_gadget_config ) +manifest = installer.Manifest() for name, gadget in gadgets.GADGETS.items(): if not gadget.get( 'enabled', True ): @@ -187,15 +192,27 @@ for name, gadget in gadgets.GADGETS.items(): if getattr( args, 'disable_' + gadget[ 'language' ] ): continue + if not args.upgrade: + manifest.Clear( name ) + installer.InstallGagdet( name, gadget, + manifest, succeeded, failed, all_adapters ) for name, gadget in CUSTOM_GADGETS.items(): - installer.InstallGagdet( name, gadget, succeeded, failed, all_adapters ) + if not args.upgrade: + manifest.Clear( name ) + + installer.InstallGagdet( name, + gadget, + manifest, + succeeded, + failed, + all_adapters ) if args.no_gadget_config: print( "" ) @@ -204,6 +221,8 @@ if args.no_gadget_config: else: installer.WriteAdapters( all_adapters ) +manifest.Write() + if args.basedir: print( "" ) print( "***NOTE***: You set --basedir to " + args.basedir + diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index a96c9f5..7e1d619 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -234,8 +234,7 @@ GADGETS = { }, 'macos': { 'file_name': 'netcoredbg-osx-master.tar.gz', - 'checksum': - 'c1dc6ed58c3f5b0473cfb4985a96552999360ceb9795e42d9c9be64af054f821', + 'checksum': '', }, 'linux': { 'file_name': 'netcoredbg-linux-master.tar.gz', @@ -385,7 +384,7 @@ GADGETS = { 'enabled': False, 'repo': { 'url': 'https://github.com/microsoft/vscode-node-debug2', - 'ref': 'v1.42.0', + 'ref': 'v1.42.5' }, 'do': lambda name, root, gadget: installer.InstallNodeDebug( name, root, diff --git a/python3/vimspector/install.py b/python3/vimspector/install.py index 7dc0897..d6ceb78 100644 --- a/python3/vimspector/install.py +++ b/python3/vimspector/install.py @@ -42,6 +42,11 @@ def GetGadgetDir( vimspector_base ): return os.path.join( os.path.abspath( vimspector_base ), 'gadgets', GetOS() ) +def GetManifestFile( vimspector_base ): + return os.path.join( GetGadgetDir( vimspector_base ), + '.gadgets.manifest.json' ) + + def GetGadgetConfigFile( vimspector_base ): return os.path.join( GetGadgetDir( vimspector_base ), '.gadgets.json' ) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 3e4440d..485aed5 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -162,6 +162,7 @@ def RunUpdate( api_prefix, leave_open, *args ): insatller_args.extend( FindGadgetForAdapter( adapter_name ) ) if insatller_args: + insatller_args.append( '--upgrade' ) RunInstaller( api_prefix, leave_open, *insatller_args ) @@ -217,6 +218,91 @@ def FindGadgetForAdapter( adapter_name ): return candidates +class Manifest: + manifest: dict + + def __init__( self ): + self.manifest = {} + self.Read() + + def Read( self ): + try: + with open( install.GetManifestFile( options.vimspector_base ), 'r' ) as f: + self.manifest = json.load( f ) + except OSError: + pass + + def Write( self ): + with open( install.GetManifestFile( options.vimspector_base ), 'w' ) as f: + json.dump( self.manifest, f ) + + + def Clear( self, name: str ): + try: + del self.manifest[ name ] + except KeyError: + pass + + + def Update( self, name: str, gadget_spec: dict ): + self.manifest[ name ] = gadget_spec + + + def RequiresUpdate( self, name: str, gadget_spec: dict ): + try: + current_spec = self.manifest[ name ] + except KeyError: + # It's new. + return True + + # If anything changed in the spec, update + if not current_spec == gadget_spec: + return True + + # Always update if the version string is 'master'. Probably a git repo + # that pulls master (which tbh we shouldn't have) + if current_spec.get( 'version' ) in ( 'master', '' ): + return True + if current_spec.get( 'repo', {} ).get( 'ref' ) == 'master': + return True + + return False + + +def ReadAdapters( read_existing = True ): + all_adapters = {} + if read_existing: + try: + with open( install.GetGadgetConfigFile( options.vimspector_base ), + 'r' ) as f: + all_adapters = json.load( f ).get( 'adapters', {} ) + except OSError: + pass + + # Include "built-in" adapter for multi-session mode + all_adapters.update( { + 'multi-session': { + 'port': '${port}', + 'host': '${host}' + }, + } ) + + return all_adapters + + +def WriteAdapters( all_adapters, to_file=None ): + adapter_config = json.dumps ( { 'adapters': all_adapters }, + indent=2, + sort_keys=True ) + + if to_file: + to_file.write( adapter_config ) + else: + with open( install.GetGadgetConfigFile( options.vimspector_base ), + 'w' ) as f: + f.write( adapter_config ) + + def InstallGeneric( name, root, gadget ): extension = os.path.join( root, 'extension' ) for f in gadget.get( 'make_executable', [] ): @@ -300,52 +386,57 @@ def InstallTclProDebug( name, root, gadget ): def InstallNodeDebug( name, root, gadget ): - node_version = subprocess.check_output( [ 'node', '--version' ], - universal_newlines=True ).strip() - Print( "Node.js version: {}".format( node_version ) ) - if list( map( int, node_version[ 1: ].split( '.' ) ) ) >= [ 12, 0, 0 ]: - Print( "Can't install vscode-debug-node2:" ) - Print( "Sorry, you appear to be running node 12 or later. That's not " - "compatible with the build system for this extension, and as far as " - "we know, there isn't a pre-built independent package." ) - Print( "My advice is to install nvm, then do:" ) - Print( " $ nvm install --lts 10" ) - Print( " $ nvm use --lts 10" ) - Print( " $ ./install_gadget.py --enable-node ..." ) - raise RuntimeError( 'Node 10 is required to install node debugger (sadly)' ) - with CurrentWorkingDir( root ): CheckCall( [ 'npm', 'install' ] ) CheckCall( [ 'npm', 'run', 'build' ] ) MakeSymlink( name, root ) -def InstallGagdet( name, gadget, succeeded, failed, all_adapters ): +def InstallGagdet( name: str, + gadget: dict, + manifest: Manifest, + succeeded: list, + failed: list, + all_adapters: dict ): + try: - print( f"Installing {name}..." ) - v = {} - v.update( gadget.get( 'all', {} ) ) - v.update( gadget.get( install.GetOS(), {} ) ) + # Spec is an os-specific definition of the gadget + spec = {} + spec.update( gadget.get( 'all', {} ) ) + spec.update( gadget.get( install.GetOS(), {} ) ) + + def save_adapters(): + # allow per-os adapter overrides. v already did that for us... + all_adapters.update( spec.get( 'adapters', {} ) ) + # add any other "all" adapters + all_adapters.update( gadget.get( 'adapters', {} ) ) if 'download' in gadget: - if 'file_name' not in v: + if 'file_name' not in spec: raise RuntimeError( "Unsupported OS {} for gadget {}".format( install.GetOS(), name ) ) + print( f"Installing {name}@{spec[ 'version' ]}..." ) + spec[ 'download' ] = gadget[ 'download' ] + if not manifest.RequiresUpdate( name, spec ): + save_adapters() + print( " - Skip - up to date" ) + return + destination = os.path.join( install.GetGadgetDir( options.vimspector_base ), 'download', name, - v[ 'version' ] ) + spec[ 'version' ] ) - url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( v ) + url = string.Template( gadget[ 'download' ][ 'url' ] ).substitute( spec ) file_path = DownloadFileTo( url, destination, file_name = gadget[ 'download' ].get( 'target' ), - checksum = v.get( 'checksum' ), + checksum = spec.get( 'checksum' ), check_certificate = not options.no_check_certificate ) root = os.path.join( destination, 'root' ) @@ -354,8 +445,15 @@ def InstallGagdet( name, gadget, succeeded, failed, all_adapters ): root, format = gadget[ 'download' ].get( 'format', 'zip' ) ) elif 'repo' in gadget: - url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( v ) - ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( v ) + url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( spec ) + ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( spec ) + + print( f"Installing {name}@{gadget[ 'repo' ][ 'ref' ]}..." ) + spec[ 'repo' ] = gadget[ 'repo' ] + if not manifest.RequiresUpdate( name, spec ): + save_adapters() + print( " - Skip - up to date" ) + return destination = os.path.join( install.GetGadgetDir( options.vimspector_base ), @@ -365,15 +463,12 @@ def InstallGagdet( name, gadget, succeeded, failed, all_adapters ): root = destination if 'do' in gadget: - gadget[ 'do' ]( name, root, v ) + gadget[ 'do' ]( name, root, spec ) else: - InstallGeneric( name, root, v ) - - # Allow per-OS adapter overrides. v already did that for us... - all_adapters.update( v.get( 'adapters', {} ) ) - # Add any other "all" adapters - all_adapters.update( gadget.get( 'adapters', {} ) ) + InstallGeneric( name, root, spec ) + save_adapters() + manifest.Update( name, spec ) succeeded.append( name ) print( f" - Done installing {name}" ) except Exception as e: @@ -383,40 +478,6 @@ def InstallGagdet( name, gadget, succeeded, failed, all_adapters ): print( f" - FAILED installing {name}: {e}".format( name, e ) ) -def ReadAdapters( read_existing = True ): - all_adapters = {} - if read_existing: - try: - with open( install.GetGadgetConfigFile( options.vimspector_base ), - 'r' ) as f: - all_adapters = json.load( f ).get( 'adapters', {} ) - except OSError: - pass - - # Include "built-in" adapter for multi-session mode - all_adapters.update( { - 'multi-session': { - 'port': '${port}', - 'host': '${host}' - }, - } ) - - return all_adapters - - -def WriteAdapters( all_adapters, to_file=None ): - adapter_config = json.dumps ( { 'adapters': all_adapters }, - indent=2, - sort_keys=True ) - - if to_file: - to_file.write( adapter_config ) - else: - with open( install.GetGadgetConfigFile( options.vimspector_base ), - 'w' ) as f: - f.write( adapter_config ) - - @contextlib.contextmanager def CurrentWorkingDir( d ): cur_d = os.getcwd() diff --git a/syntax/vimspector-installer.vim b/syntax/vimspector-installer.vim index 98ea48b..4eb6170 100644 --- a/syntax/vimspector-installer.vim +++ b/syntax/vimspector-installer.vim @@ -4,10 +4,18 @@ endif let b:current_syntax = 'vimspector-installer' +syn match VimspectorGadget /[^ ]*\ze@/ +syn match VimspectorGadgetVersion /@\@<=[^ ]*\ze\.\.\./ + + syn keyword VimspectorInstalling Installing syn keyword VimspectorDone Done +syn keyword VimspectorSkip Skip syn keyword VimspectorError Failed FAILED hi default link VimspectorInstalling Constant hi default link VimspectorDone DiffAdd +hi default link VimspectorSkip DiffAdd hi default link VimspectorError WarningMsg +hi default link VimspectorGadget String +hi default link VimspectorGadgetVersion Identifier From 1e43cd4870c90b5716c0187796067e8cccd38052 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 24 Jul 2020 21:27:28 +0100 Subject: [PATCH 290/609] Use a junction, as symlinks require escalated priviledges on some windows --- python3/vimspector/installer.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 485aed5..5a4c60a 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -331,10 +331,7 @@ def InstallCppTools( name, root, gadget ): def InstallBashDebug( name, root, gadget ): - MakeExecutable( os.path.join( root, - 'extension', - 'bashdb_dir', - 'bashdb' ) ) + MakeExecutable( os.path.join( root, 'extension', 'bashdb_dir', 'bashdb' ) ) MakeExtensionSymlink( name, root ) @@ -585,10 +582,12 @@ def ValidateCheckSumSHA256( file_path, checksum ): def RemoveIfExists( destination ): - if os.path.islink( destination ): - Print( "Removing link {}".format( destination ) ) + try: os.remove( destination ) + Print( "Removed file {}".format( destination ) ) return + except OSError: + pass N = 1 @@ -678,7 +677,7 @@ def MakeSymlink( link, pointing_to, in_folder = None ): link_path = os.path.abspath( link_path ) if os.path.isdir( link_path ): os.rmdir( link_path ) - CheckCall( [ 'cmd.exe', '/c', 'mklink', '/D', link_path, pointing_to ] ) + CheckCall( [ 'cmd.exe', '/c', 'mklink', '/J', link_path, pointing_to ] ) else: os.symlink( pointing_to_relative, link_path ) From 917b737486f8f04494baaccfa39ecaa36b6a9251 Mon Sep 17 00:00:00 2001 From: raochsinai Date: Sat, 25 Jul 2020 03:50:45 +0800 Subject: [PATCH 291/609] Custom text of Winbar buttons. --- support/custom_ui_vimrc | 14 ++++++++++++ tests/ui.test.vim | 49 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index d319b28..944ff93 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -46,8 +46,22 @@ function s:SetUpTerminal() execute cols . 'wincmd |' endfunction +function! s:CustomiseWinBar() + call win_gotoid( g:vimspector_session_windows.code) + aunmenu WinBar + nnoremenu WinBar.▷\ ᶠ⁵ :call vimspector#Continue() + nnoremenu WinBar.↷\ ᶠ¹⁰ :call vimspector#StepOver() + nnoremenu WinBar.↓\ ᶠ¹¹ :call vimspector#StepInto() + nnoremenu WinBar.↑\ ˢᶠ¹¹ :call vimspector#StepOut() + nnoremenu WinBar.❘❘\ ᶠ⁶ :call vimspector#Pause() + nnoremenu WinBar.□\ ˢᶠ⁵ :call vimspector#Stop() + nnoremenu WinBar.⟲\ ᶜˢᶠ⁵ :call vimspector#Restart() + nnoremenu WinBar.✕\ ᶠ⁸ :call vimspector#Reset() +endfunction + augroup TestUICustomistaion autocmd! autocmd User VimspectorUICreated call s:CustomiseUI() autocmd User VimspectorTerminalOpened call s:SetUpTerminal() + autocmd User VimspectorUICreated call s:CustomiseWinBar() augroup END diff --git a/tests/ui.test.vim b/tests/ui.test.vim index 02bde3e..99bf90a 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -378,3 +378,52 @@ function! Test_CustomUI() call vimspector#test#setup#Reset() %bwipe! endfunction + + +function! s:CustomWinBar() + call win_gotoid( g:vimspector_session_windows.code) + aunmenu WinBar + nnoremenu WinBar.▷\ ᶠ⁵ :call vimspector#Continue() + nnoremenu WinBar.↷\ ᶠ¹⁰ :call vimspector#StepOver() + nnoremenu WinBar.↓\ ᶠ¹¹ :call vimspector#StepInto() + nnoremenu WinBar.↑\ ˢᶠ¹¹ :call vimspector#StepOut() + nnoremenu WinBar.❘❘\ ᶠ⁶ :call vimspector#Pause() + nnoremenu WinBar.□\ ˢᶠ⁵ :call vimspector#Stop() + nnoremenu WinBar.⟲\ ᶜˢᶠ⁵ :call vimspector#Restart() + nnoremenu WinBar.✕\ ᶠ⁸ :call vimspector#Reset() +endfunction + + +function! Test_CustomWinBar() + augroup TestCustomWinBar + au! + au User VimspectorUICreated + \ call win_execute( g:vimspector_session_windows.watches, 'q' ) + au User VimspectorUICreated call s:CustomWinBar() + 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.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! TestCustomWinBar + call vimspector#test#setup#Reset() + %bwipe! +endfunction From cb0eee180f8e66d583a0bf64f7fd09aff55a6e6e Mon Sep 17 00:00:00 2001 From: raochsinai Date: Sat, 25 Jul 2020 17:03:04 +0800 Subject: [PATCH 292/609] Correct test function for CustomWinBar in ui.test.vim. --- tests/ui.test.vim | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/tests/ui.test.vim b/tests/ui.test.vim index 99bf90a..b6c8feb 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -397,31 +397,13 @@ endfunction function! Test_CustomWinBar() augroup TestCustomWinBar au! - au User VimspectorUICreated - \ call win_execute( g:vimspector_session_windows.watches, 'q' ) au User VimspectorUICreated call s:CustomWinBar() 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.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 ) ) + \ ['▷ ᶠ⁵', '↷ ᶠ¹⁰', '↓ ᶠ¹¹', '↑ ˢᶠ¹¹', '❘❘ ᶠ⁶', '□ ˢᶠ⁵', '⟲ ᶜˢᶠ⁵', '✕ ᶠ⁸'], + \ menu_info( 'WinBar' ).submenus ) au! TestCustomWinBar call vimspector#test#setup#Reset() From aa26d4bc1ae3d58ff750ddffc78281e4c61a8e3b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 22 Jul 2020 23:58:12 +0100 Subject: [PATCH 293/609] Add ability to specify defaults for variables --- docs/configuration.md | 34 ++++++++- docs/custom_gadget_file.md | 25 +++++++ python3/vimspector/utils.py | 74 +++++++++++++++++-- .../python/simple_python/.vimspector.json | 18 +++++ 4 files changed, 143 insertions(+), 8 deletions(-) create mode 100644 docs/custom_gadget_file.md diff --git a/docs/configuration.md b/docs/configuration.md index 134b049..54a2f54 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -12,6 +12,7 @@ for Vimspector. * [Debug profile configuration](#debug-profile-configuration) * [Replacements and variables](#replacements-and-variables) * [The splat operator](#the-splat-operator) + * [Default values](#default-values) * [Configuration Format](#configuration-format) * [Files and locations](#files-and-locations) * [Adapter configurations](#adapter-configurations) @@ -28,7 +29,7 @@ for Vimspector. * [Appendix: Configuration file format](#appendix-configuration-file-format) * [Appendix: Editor configuration](#appendix-editor-configuration) - + @@ -204,6 +205,37 @@ You can also combine with static values: This would yield the intuitive result: `[ "First", "one", "two three", "four", "Last" ]` +### Default values + +You can specify replacesments with default values. In this case if the user has +not specified a value, they are prompted but with the default value +pre-populated, allowing them to just press return to accept the default. + +The syntax is `${variableName:default value}`. The default value can contain any +character, but to include a `}` you must escape it with a backslash. To include +a backslash in the JSON you must write `\\`, as in: + +```json + { "key": "${value:default {\\} stuff}" } +``` + +The default value can also be a replacement variable. However, this _must_ be a +veriable that's already defined, such as one of the [predefined +variables](#predefined-variables), or one speified in a `variables` block. In +order to reference them, you _must_ use `${var}` syntax and you _must_ escape +the closing `}`. For example, the is a common and useful case: + +```json + { + "configuration": { + "program": "${script:${file\\}}" + } + } +``` + +This will prompt the user to specify `script`, but it will default to the path +to the current file. + ## Configuration Format All Vimspector configuration is defined in a JSON object. The complete diff --git a/docs/custom_gadget_file.md b/docs/custom_gadget_file.md new file mode 100644 index 0000000..428b7d9 --- /dev/null +++ b/docs/custom_gadget_file.md @@ -0,0 +1,25 @@ +--- +title: Configuration +--- + +This document describes how to use vimspector's `install_gadget.py` to install +custom debug adapters. This can be useful as a way to get an adapter working +that isn't officially supported by Vimspector, but otherwise can be made to work +by simply downloading the VScode extension into the gadget directory. + +## Usage + +``` +./install_gadget.py --enable-custom=/path/to/a.json \ + --enable-custom=/path/to/b.json` +``` + +This tells `install_gadget.py` to read `a.json` and `b.json` as _gadget +definitions_ and download/unpack the specified gadgets into the gadget dir, just +like the supported adapters. + +## Gadget Definitions + +A _gadget definition_ is a file containing a single JSON object definition, +describing the debug adapter and how to download and install it. This mechanism +is crude but can be effective. diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 0aea216..9381407 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -19,11 +19,11 @@ import os import contextlib import vim import json -import string import functools import subprocess import shlex import collections +import re LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log' ) ) @@ -435,6 +435,60 @@ def ExpandReferencesInObject( obj, mapping, calculus, user_choices ): return obj +# Based on the python standard library string.Template().substitue, enhanced to +# add ${name:default} parsing, and to remove the unnecessary generality. +VAR_MATCH = re.compile( + r""" + \$(?: # A dollar, followed by... + (?P\$) | # Another doller = escaped + (?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 + : # then a colon + (?P(?:[^}]|\})*) # then anything up to }, or a \} + )} | # + (?P) # or Something else - invalid + ) + """, + re.IGNORECASE | re.VERBOSE ) + + +class MissingSubstitution( Exception ): + def __init__( self, name, default_value = None ): + self.name = name + self.default_value = default_value + + +def _Substitute( template, mapping ): + def convert( mo ): + # Check the most common path first. + named = mo.group( 'named' ) or mo.group( 'braced' ) + if named is not None: + if named not in mapping: + raise MissingSubstitution( named ) + return str( mapping[ named ] ) + + if mo.group( 'escaped' ) is not None: + return '$' + + if mo.group( 'braceddefault' ) is not None: + named = mo.group( 'defname' ) + if named not in mapping: + '' + raise MissingSubstitution( + named, + mo.group( 'default' ).replace( '\\}', '}' ) ) + return str( mapping[ named ] ) + + if mo.group( 'invalid' ) is not None: + raise ValueError( f"Invalid placeholder in string { template }" ) + + raise ValueError( 'Unrecognized named group in pattern', VAR_MATCH ) + + return VAR_MATCH.sub( convert, template ) + + def ExpandReferencesInString( orig_s, mapping, calculus, @@ -449,17 +503,23 @@ def ExpandReferencesInString( orig_s, ++bug_catcher try: - s = string.Template( s ).substitute( mapping ) + s = _Substitute( s, mapping ) break - except KeyError as e: - # HACK: This is seemingly the only way to get the key. str( e ) returns - # the key surrounded by '' for unknowable reasons. - key = e.args[ 0 ] + except MissingSubstitution as e: + key = e.name if key in calculus: mapping[ key ] = calculus[ key ]() else: - default_value = user_choices.get( key, None ) + default_value = user_choices.get( key ) + # Allow _one_ level of additional substitution. This allows a very real + # use case of "program": ${prgram:${file\\}} + if default_value is None and e.default_value is not None: + try: + default_value = _Substitute( e.default_value, mapping ) + except MissingSubstitution: + default_value = e.default_value + mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ), default_value ) diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 3d51d2c..6a1b838 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -50,6 +50,24 @@ } } }, + "run - default": { + "adapter": "debugpy", + "configuration": { + "request": "launch", + "type": "python", + "cwd": "${workspaceRoot}", + "program": "${program:${file\\}}", + "stopOnEntry": false, + "console": "integratedTerminal" + }, + "breakpoints": { + "exception": { + "raised": "N", + "uncaught": "", + "userUnhandled": "" + } + } + }, "run - main.py": { "adapter": "debugpy", "configuration": { From f6158d0ffb3519554e4c10412e475379d64fc28c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 23 Jul 2020 15:51:57 +0100 Subject: [PATCH 294/609] Make installer quiet too --- run_tests | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/run_tests b/run_tests index 44e09f3..af0a598 100755 --- a/run_tests +++ b/run_tests @@ -3,6 +3,7 @@ BASEDIR=$(dirname $0) INSTALL=0 UPDATE=0 +INSTALLER_ARGS='' RUN_VIM="vim -N --clean --not-a-term" RUN_TEST="${RUN_VIM} -S lib/run_test.vim" BASEDIR_CMD='py3 pass' @@ -48,6 +49,7 @@ while [ -n "$1" ]; do # old on macOS out_fd=3 exec 3>/dev/null + INSTALLER_ARGS="${INSTALLER_ARGS} --quiet" ;; "--") shift @@ -82,7 +84,10 @@ if [ "${out_fd}" = "1" ]; then fi if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "script" ]; then - if ! python3 $(dirname $0)/install_gadget.py --basedir ${BASEDIR} --all; then + if ! python3 $(dirname $0)/install_gadget.py \ + --basedir ${BASEDIR} \ + ${INSTALLER_ARGS} \ + --all; then echo "Script installation reported errors" >&2 exit 1 fi From 6593f383cff7482a080539dacf598fd462c41feb Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 24 Jul 2020 23:15:39 +0100 Subject: [PATCH 295/609] Use any python for debugpy --- python3/vimspector/developer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/developer.py b/python3/vimspector/developer.py index 49e96c3..42de1af 100644 --- a/python3/vimspector/developer.py +++ b/python3/vimspector/developer.py @@ -17,7 +17,7 @@ import sys import os -from vimspector import install, utils +from vimspector import install, utils, installer def SetUpDebugpy( wait=False, port=5678 ): @@ -32,7 +32,7 @@ def SetUpDebugpy( wait=False, port=5678 ): exe = sys.executable try: # debugpy uses sys.executable (which is `vim`, so we hack it) - sys.executable = 'python3' + sys.executable = installer.PathToAnyWorkingPython3() debugpy.listen( port ) finally: sys.executable = exe From 10e9a75fc7cf646ef26b5f8de2702e76c4aa5576 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 25 Jul 2020 09:56:23 +0100 Subject: [PATCH 296/609] Tidy UI - use some unicode symbols and organise winbar --- python3/vimspector/breakpoints.py | 6 +++--- python3/vimspector/code.py | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 10b89e8..59d1370 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -53,13 +53,13 @@ class ProjectBreakpoints( object ): self._next_sign_id = 1 if not utils.SignDefined( 'vimspectorBP' ): - vim.command( 'sign define vimspectorBP text==> texthl=Error' ) + vim.command( 'sign define vimspectorBP text=\\ ● texthl=WarningMsg' ) if not utils.SignDefined( 'vimspectorBPCond' ): - vim.command( 'sign define vimspectorBPCond text=?> texthl=Error' ) + vim.command( 'sign define vimspectorBPCond text=\\ ◆ texthl=WarningMsg' ) if not utils.SignDefined( 'vimspectorBPDisabled' ): - vim.command( 'sign define vimspectorBPDisabled text=!> texthl=Warning' ) + vim.command( 'sign define vimspectorBPDisabled text=\\ ● texthl=LineNr' ) def ConnectionUp( self, connection ): diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index a4f2514..8e33ece 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -41,17 +41,17 @@ class CodeView( object ): } with utils.LetCurrentWindow( self._window ): - vim.command( 'nnoremenu WinBar.Continue :call vimspector#Continue()' ) - vim.command( 'nnoremenu WinBar.Next :call vimspector#StepOver()' ) - vim.command( 'nnoremenu WinBar.Step :call vimspector#StepInto()' ) - vim.command( 'nnoremenu WinBar.Finish :call vimspector#StepOut()' ) - vim.command( 'nnoremenu WinBar.Pause :call vimspector#Pause()' ) - vim.command( 'nnoremenu WinBar.Stop :call vimspector#Stop()' ) - vim.command( 'nnoremenu WinBar.Restart :call vimspector#Restart()' ) - vim.command( 'nnoremenu WinBar.Reset :call vimspector#Reset()' ) + vim.command( 'nnoremenu WinBar.■\\ Stop :call vimspector#Stop()' ) + vim.command( 'nnoremenu WinBar.▶\\ Cont :call vimspector#Continue()' ) + vim.command( 'nnoremenu WinBar.▷\\ Pause :call vimspector#Pause()' ) + vim.command( 'nnoremenu WinBar.↷\\ Next :call vimspector#StepOver()' ) + vim.command( 'nnoremenu WinBar.→\\ Step :call vimspector#StepInto()' ) + vim.command( 'nnoremenu WinBar.←\\ Out :call vimspector#StepOut()' ) + vim.command( 'nnoremenu WinBar.⟲: :call vimspector#Restart()' ) + vim.command( 'nnoremenu WinBar.✕ :call vimspector#Reset()' ) if not utils.SignDefined( 'vimspectorPC' ): - vim.command( 'sign define vimspectorPC text=-> texthl=Search' ) + vim.command( 'sign define vimspectorPC text=\\ ▶ texthl=MatchParen' ) def SetCurrentFrame( self, frame ): From d86b42bf5b06ed09e12c75622d2a13fc75893959 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 27 Jul 2020 22:49:13 +0100 Subject: [PATCH 297/609] Allow VimspectorShowOutput with no argument --- autoload/vimspector.vim | 8 ++++++-- plugin/vimspector.vim | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 3b5dd75..929a8b3 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -188,11 +188,15 @@ function! vimspector#EvaluateConsole( expr ) abort py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) ) endfunction -function! vimspector#ShowOutput( category ) abort +function! vimspector#ShowOutput( ... ) abort if !s:enabled return endif - py3 _vimspector_session.ShowOutput( vim.eval( 'a:category' ) ) + if a:0 == 1 + py3 _vimspector_session.ShowOutput( vim.eval( 'a:1' ) ) + else + py3 _vimspector_session.ShowOutput( 'Console' ) + endif endfunction function! vimspector#ShowOutputInWindow( win_id, category ) abort diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 416eb04..0aa5f13 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -88,7 +88,7 @@ endif command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr \ VimspectorWatch \ call vimspector#AddWatch( ) -command! -bar -nargs=1 -complete=custom,vimspector#CompleteOutput +command! -bar -nargs=? -complete=custom,vimspector#CompleteOutput \ VimspectorShowOutput \ call vimspector#ShowOutput( ) command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr From 3a160aa77a471629bbb69db2da4430bc798be59e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 27 Jul 2020 22:49:50 +0100 Subject: [PATCH 298/609] Fix: Crash when deleting a watch that never got a result --- python3/vimspector/variables.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index ffd641d..d82090a 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -106,6 +106,7 @@ class Watch: """Holds a user watch expression (DAP request) and the result (WatchResult)""" def __init__( self, expression: dict ): self.result: WatchResult + self.line = None self.expression = expression self.result = None From 750e68bdf589cf3b839f42dfefd8d7a2221b21b7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 2 Aug 2020 18:16:32 +0100 Subject: [PATCH 299/609] Require minimal reproduction in contributing guide. --- CONTRIBUTING.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aec70ef..9c9d8fb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,6 +45,7 @@ of the following reasons: * The [issue template][issue-template] was not completed * The issue or comment is off-topic * The issue does not represent a Vimspector bug or feature request +* The issue cannot be reasonably reproduced using the minimal vimrc * etc. Issue titles are important. It's not usually helpful to write a title like @@ -60,6 +61,24 @@ diagnostics without having to spend time asking for them, and waiting for the response. This means *you get a better answer, faster*. So it's worth it, honestly. +### Reproduce your issue with the minimal vimrc + +Many problems can be caused by unexpected configuration or other plugins. +Therefore when raising an issue, you must attempt to reproduce your issue +with the minimal vimrc provided, and to provide any additional changes required +to that file in order to reproduce it. The purpose of this is to ensure that +the issue is not a conflict with another plugin, or a problem unique to your +configuration. + +If your issue does _not_ reproduce with the minimal vimrc, then you must say so +in the issue report. + +The minimal vimrc is in `support/test/minimal_vimrc` and can be used as follows: + +``` +vim -Nu /path/to/vimspector/support/minimal_vimrc +``` + ## Pull Requests When contributing pull requests, I ask that: From 7824bb29b2a1047c923171277529d467a2c86313 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 2 Aug 2020 18:25:06 +0100 Subject: [PATCH 300/609] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 55 ++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 887fbb1..1788c04 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,35 +7,50 @@ assignees: '' --- -**Describe the bug** -A clear and concise description of what the bug is. +> DO NOT DELETE THIS TEMPLATE. IF YOU DELETE THIS TEMPLATE AND DO NOT COMPLETE IT, YOUR ISSUE WILL BE CLOSED. -**To Reproduce** -List of steps to reproduce +### Describe the bug -Vimspector config file: +> Provide A clear and concise description of what the bug is. + +### Minimal reproduciton + +> Please answer the following questions + +* Does your issue reproduce using `vim -Nu /path/to/vimspector/support/minimal_vimrc` ? \[Yes/No] +* If you are using Neovim, does your issue reproduce using Vim? \[Yes/No] + +> List of steps to reproduce: + +> 1. Run `vim -Nu /path/to/vimspector/support/minimal_vimrc` +> 2. Open _this project_... +> 3. Press _this sequence of keys_ + +> Use the following Vimspector config file: ``` paste .vimspector.json here ``` -**Expected behavior** -A clear and concise description of what you expected to happen. +### Expected behaviour -**Actual behaviour** -What actually happened, including output, log files etc. +> Provide A clear and concise description of what you expected to happen. -Please include: -* Vimspector log (~/.vimspector.log) -* Output from any or all UI diagnostic tabs (Server, etc.) +### Actual behaviour -**Environemnt** +> What actually happened, including output, log files etc. -NOTE: NeoVim is supported only on a best-effort basis. Please check the README +> Please include: +> * Vimspector log (~/.vimspector.log) +> * Output from any or all UI diagnostic tabs (Server, etc.) + +### Environemnt + +***NOTE***: NeoVim is supported only on a best-effort basis. Please check the README for limitations of neovim. Don't be offended if I ask you to reproduce issues in Vim. -NOTE: Windows support is experimental and best-efrort only. If you find an +***NOTE***: Windows support is experimental and best-efrort only. If you find an issue related to Windows or windows-isms, consider sending a PR or discussing on Gitter rather than raising an issue. @@ -80,6 +95,14 @@ paste here ``` * Operating system: and version + +### Declaration + +> Please complete the following declaration. If this declaration is not completed, your issue may be closed without comment. + +* I have read and understood [CONTRIBUTING.md](https://github.com/puremourning/vimspector/blob/master/CONTRIBUTING.md) \[Yes/No] + + +### Additional information -**Additional context** Add any other context about the problem here. From be44a22903d4bbf8993c440d45cb99e0c28570dc Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 5 Aug 2020 22:19:38 +0100 Subject: [PATCH 301/609] Allow coercing a type in vimspector config Initially I considered using #i, #s, etc. to coerce to specific types, but then it wasn't clear of the semantics (particularly for bool, where JSON bool true/false, Python bool True/False). But it turns out that we can just coerce any key from a JSON string. Users can _probably_ type JSON strings for most things, or use variables to run scripts to generate them, this allows essentially complete flexibility to define the data types needed to populate the launch spec. The purpose of this is to allow some level of automated setup by requesting data from the user and then (subsequently) saving the flattneed config to the vimspector config file. --- python3/vimspector/utils.py | 24 +++++++++++++++++-- .../python/simple_python/.vimspector.json | 6 +++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 9381407..02b0c85 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -24,6 +24,7 @@ import subprocess import shlex import collections import re +import typing LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log' ) ) @@ -540,14 +541,30 @@ def ExpandReferencesInString( orig_s, return s +def CoerceType( mapping: typing.Dict[ str, typing.Any ], key: str ): + DICT_TYPES = { + 'json': json.loads, + } + + parts = key.split( '#' ) + if len( parts ) > 1 and parts[ -1 ] in DICT_TYPES.keys(): + value = mapping.pop( key ) + + new_type = parts[ -1 ] + key = '#'.join( parts[ 0 : -1 ] ) + + mapping[ key ] = DICT_TYPES[ new_type ]( value ) + + # TODO: Should we just run the substitution on the whole JSON string instead? # That woul dallow expansion in bool and number values, such as ports etc. ? def ExpandReferencesInDict( obj, mapping, calculus, user_choices ): - for k in obj.keys(): + for k in list( obj.keys() ): obj[ k ] = ExpandReferencesInObject( obj[ k ], mapping, calculus, user_choices ) + CoerceType( obj, k ) def ParseVariables( variables_list, @@ -560,9 +577,10 @@ def ParseVariables( variables_list, if not isinstance( variables_list, list ): variables_list = [ variables_list ] + variables: typing.Dict[ str, typing.Any ] for variables in variables_list: new_mapping.update( new_variables ) - for n, v in variables.items(): + for n, v in list( variables.items() ): if isinstance( v, dict ): if 'shell' in v: new_v = v.copy() @@ -597,6 +615,8 @@ def ParseVariables( variables_list, calculus, user_choices ) + CoerceType( new_variables, n ) + return new_variables diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 6a1b838..a25a75b 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -57,8 +57,10 @@ "type": "python", "cwd": "${workspaceRoot}", "program": "${program:${file\\}}", - "stopOnEntry": false, - "console": "integratedTerminal" + "stopOnEntry#json": "${StopOnEntry:true}", + "console": "integratedTerminal", + "args#json": "${args:[]}", + "env#json": "${env:{\\}}" }, "breakpoints": { "exception": { From ca63c08d6a64085250d10a655b6729995939fe7b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 5 Aug 2020 22:24:35 +0100 Subject: [PATCH 302/609] Add a way to force a str in case #json is really required in a key --- python3/vimspector/utils.py | 1 + support/test/python/simple_python/.vimspector.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 02b0c85..bc7c168 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -544,6 +544,7 @@ def ExpandReferencesInString( orig_s, def CoerceType( mapping: typing.Dict[ str, typing.Any ], key: str ): DICT_TYPES = { 'json': json.loads, + 's': str } parts = key.split( '#' ) diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index a25a75b..044c669 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -60,7 +60,8 @@ "stopOnEntry#json": "${StopOnEntry:true}", "console": "integratedTerminal", "args#json": "${args:[]}", - "env#json": "${env:{\\}}" + "env#json": "${env:{\\}}", + "igored#json#s": "string not json" }, "breakpoints": { "exception": { From 57f1c128c563c9eefe2fd416b0c69add50143dfe Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 13 Aug 2020 17:30:07 +0100 Subject: [PATCH 303/609] update doc for coercing types --- docs/configuration.md | 122 +++++++++++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 26 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 54a2f54..0ced4e6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -36,7 +36,7 @@ for Vimspector. ## Concepts As Vimspector supports debugging arbitrary projects, you need to tell it a few -deatils about what you want to debug, and how to go about doing that. +details about what you want to debug, and how to go about doing that. In order to debug things, Vimspector requires a Debug Adapter which bridges between Vimspector and the actual debugger tool. Vimspector can be used with any @@ -160,7 +160,7 @@ the following variable substitutions: provide a value interactively when starting debugging. Vimspector remembers what they said and provides it as the default should they debug again. * `${SecretToken}` - this variable is provided by the configuration's - `variables` block. Its value is taken from the `strip`ped result of running + `variables` block. Its value is taken from the `strip`'d result of running the shell command. Note these variables can be supplied by both the debug and adapter configurations and can be either static strings or shell commands. @@ -177,8 +177,8 @@ example: "args": [ "one", "two three", "four" ], ``` -To help with this sort of case, vimspector supports a 'splat' operator for -replacement variables apperaing within lists. The syntax is: `"*${var}`, which +To help with this sort of case, Vimspector supports a 'splat' operator for +replacement variables operating within lists. The syntax is: `"*${var}`, which means roughly "splice the contents of `${var}` into the list at this position". `${var}` is parsed like a shell command (using python's `shlex` parser) and each word is added as a list item. @@ -207,7 +207,7 @@ This would yield the intuitive result: ### Default values -You can specify replacesments with default values. In this case if the user has +You can specify replacements with default values. In this case if the user has not specified a value, they are prompted but with the default value pre-populated, allowing them to just press return to accept the default. @@ -220,8 +220,8 @@ a backslash in the JSON you must write `\\`, as in: ``` The default value can also be a replacement variable. However, this _must_ be a -veriable that's already defined, such as one of the [predefined -variables](#predefined-variables), or one speified in a `variables` block. In +variable that's already defined, such as one of the [predefined +variables](#predefined-variables), or one specified in a `variables` block. In order to reference them, you _must_ use `${var}` syntax and you _must_ escape the closing `}`. For example, the is a common and useful case: @@ -236,6 +236,76 @@ the closing `}`. For example, the is a common and useful case: This will prompt the user to specify `script`, but it will default to the path to the current file. +### Coercing Types + +Sometimes, you want to provide an option for a boolean parameter, or want to +allow the user to specify more than just strings. Vimspector allows you to do +this, ensuring that the resulting JSON is valid. This is done by interpreting a +value as a JSON string and substituting the resulting JSON value in its place. + +This is easier to explain with an example. Let's say we want to offer the +ability to break on entry, as an option for the user. The launch configuration +requires `stopOnEntry` to be a bool. This doesn't work: + +```json + "stopOnEntry": "${StopOnEntry}" +``` + +The reason is that if the user types `true`, the resulting object is: + +```json + "stopOnEntry": "true" +``` + +The problem being that is a string, not a boolean. So Vimspector allows you to +re-interpret the string as a JSON value and use that instead. To do this, add +`#json` to the key's name. You can even add a default, like this: + +```json + "stopOnEntry#json": "${stopOnEntry:true}" +``` + +If the user accepts the default, the resulting string `"true"` is coerced to a +JSON value `true`, and the suffix is stripped fom the key, resulting in the +following: + +```json + "stopOnEntry#json": true +``` + +Which is what we need. + +If you happen to have a key that already ends in `#json` (unlikely!), then you +can force Vimspector to treat the value as a string by appending `#s`, as in: + +```json + "unlikelyKeyName#json#s": "this is a string, not JSON data" +``` + +#### Advanced usage + +The most common usage for this is for number and bool types, but it works for +objects too. If you want to be able to specify a whole object (e.g. a whole +`env` dict), then you can do that too: + +```json + "env#json": "${Environment:{\\}}" +``` + +The default value here is `{}` (note the `}` must be escaped!). The user can +then enter something like `{ "MYVAR": "MyValue", "OTHER": "Other" }` and the +resulting object would be: + +```json + "env": { + "MYVAR": "MyValue", + "OTHER": "Other" + } +``` + +It also works for lists, though [the splat operator](#the-splat-operator) +is usually more convenient for that. + ## Configuration Format All Vimspector configuration is defined in a JSON object. The complete @@ -268,7 +338,7 @@ abbreviations: * `` means the path to the Vimspector installation (such as `$HOME/.vim/pack/vimspector/start/vimspector`) * `` is either `macos` or `linux` depending on the host operating system. -* `` is the Vim fileytype. Where multiple filetypes are in effect, +* `` is the Vim filetype. Where multiple filetypes are in effect, typically all filetypes are checked. ## Adapter configurations @@ -323,7 +393,7 @@ read and no further searching is done. Only a single `.vimspector.json` is read. If one is found, the location of this file is used for `${workspaceRoot}` and other workspace-relative paths. -In addition, users can create filetype-specific configurations in the vimspector +In addition, users can create filetype-specific configurations in the Vimspector installation directory. This can be useful where the parameters for the debug session for a particular filetype are always known in advance, or can always be entered by the user. This allows for debugging to "just work" without any @@ -389,7 +459,7 @@ As noted, you can specify a default configuration with `"default": true`: } ``` -If multiple conifigurations are found with `default` set to `true`, then the +If multiple configurations are found with `default` set to `true`, then the user is prompted anyway. #### Preventing automatic selection @@ -412,10 +482,10 @@ central (as opposed to project-local) directory. For example: Setting `autoselect` to `false` overrides setting `default` to `true`. -### Exception breakpionts +### Exception Breakpoints Debug adapters have arbitrary configuration for exception breakpoints. Normally -this is presented as a series of question to the user on startnig the debug +this is presented as a series of question to the user on starting the debug session. The question includes the name of the exception breakpoint option, the default and the list of valid responses (usually `Y` or `N`). @@ -425,7 +495,7 @@ and configure the response `exception` mapping in the `breakpoints` mapping. If the configured response is empty string, the debug adapter default will be used. Referring to the above example, the following tells the debug adapter to use the -default value for `caught` exceptoins and to break on `uncaught` exception: +default value for `caught` exceptions and to break on `uncaught` exception: ```json { @@ -458,7 +528,7 @@ cpp_catch: Break on C++: on catch (Y/N/default: N)? The exception breakpoint "type" is `cpp_catch` and the default is `N`. -Use the following to set the values in config and not get asked: +Use the following to set the values in configuration and not get asked: ```json "configurations": { @@ -497,26 +567,26 @@ The following variables are provided: found * `${workspaceFolder}` - the path of the folder where `.vimspector.json` was found -* `${gadgetDir}` - path to the OS-specifc gadget dir (`/gadgets/`) * `${file}` - the current opened file -* `${relativeFile}` - the current opened file relative to workspaceRoot -* `${fileBasename}` - the current opened file's basename -* `${fileBasenameNoExtension}` - the current opened file's basename with no +* `${relativeFile}` - the current opened file relative to `workspaceRoot` +* `${fileBasename}` - the current opened file's `basename` +* `${fileBasenameNoExtension}` - the current opened file's `basename` with no file extension -* `${fileDirname}` - the current opened file's dirname +* `${fileDirname}` - the current opened file's `dirname` * `${fileExtname}` - the current opened file's extension * `${cwd}` - the current working directory of the active window on launch * `${unusedLocalPort}` - an unused local TCP port ## Remote Debugging Support -Vimspector has in-built support for exectuting remote debuggers (such as +Vimspector has in-built support for executing remote debuggers (such as `gdbserver`, `debugpy`, `llvm-server` etc.). This is useful for environments 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 +In order for it to work, you have to set up paswordless SSH between the local and remote machines/accounts. Then just tell Vimspector how to remotely launch and/or attach to the app. @@ -527,12 +597,12 @@ research that. Vimspector's tools are intended to automate your existing process for setting this up rather than to offer batteries-included approach. Ultimately, all -vimspector is going to do is run your commands over SSH, or docker, and +Vimspector is going to do is run your commands over SSH, or docker, and co-ordinate with the adapter. ### Python (debugpy) Example -Here is some examples using the vimspector built-in +Here is some examples using the Vimspector built-in remote support (using SSH) to remotely launch and attach a python application and connect to it using debugpy. @@ -662,10 +732,10 @@ Vimspector then orchestrates the various tools to set you up. ### C-family (gdbserver) Example -This example uses vimspector to remotely launch or attach to a binary using +This example uses Vimspector to remotely launch or attach to a binary using `gdbserver` and then instructs vscode-cpptools to attach to that `gdbserver`. -The appraoch is very similar to the above for python, just that we use gdbserver +The approach is very similar to the above for python, just that we use gdbserver and have to tell cpptools a few more options. ```json @@ -752,7 +822,7 @@ and have to tell cpptools a few more options. ### Docker Example -This example uses vimspector to remotely launch or attach to a docker container +This example uses Vimspector to remotely launch or attach to a docker container port. ``` json From 51bbadc4f5dd5fad91d9ada321ea1ce12a2256d1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 15 Aug 2020 16:39:32 +0100 Subject: [PATCH 304/609] Update website dependencies --- docs/Gemfile.lock | 56 ++++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 416db83..ce0ce98 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.3.1) + activesupport (6.0.3.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -16,9 +16,9 @@ GEM colorator (1.1.0) commonmarker (0.17.13) ruby-enum (~> 0.5) - concurrent-ruby (1.1.6) - dnsruby (1.61.3) - addressable (~> 2.5) + concurrent-ruby (1.1.7) + dnsruby (1.61.4) + simpleidn (~> 0.1) em-websocket (0.5.1) eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) @@ -26,14 +26,14 @@ GEM ffi (>= 1.3.0) eventmachine (1.2.7) execjs (2.7.0) - faraday (1.0.0) + faraday (1.0.1) multipart-post (>= 1.2, < 3) - ffi (1.12.2) + ffi (1.13.1) forwardable-extended (2.6.0) gemoji (3.0.1) - github-pages (204) + github-pages (207) github-pages-health-check (= 1.16.1) - jekyll (= 3.8.5) + jekyll (= 3.9.0) jekyll-avatar (= 0.7.0) jekyll-coffeescript (= 1.1.1) jekyll-commonmark-ghpages (= 0.1.6) @@ -67,12 +67,13 @@ GEM jekyll-theme-time-machine (= 0.1.1) jekyll-titles-from-headings (= 0.5.3) jemoji (= 0.11.1) - kramdown (= 1.17.0) + kramdown (= 2.3.0) + kramdown-parser-gfm (= 1.1.0) liquid (= 4.0.3) mercenary (~> 0.3) minima (= 2.5.1) nokogiri (>= 1.10.4, < 2.0) - rouge (= 3.13.0) + rouge (= 3.19.0) terminal-table (~> 1.4) github-pages-health-check (1.16.1) addressable (~> 2.3) @@ -80,20 +81,20 @@ GEM octokit (~> 4.0) public_suffix (~> 3.0) typhoeus (~> 1.3) - html-pipeline (2.12.3) + html-pipeline (2.14.0) activesupport (>= 2) nokogiri (>= 1.4) http_parser.rb (0.6.0) i18n (0.9.5) concurrent-ruby (~> 1.0) - jekyll (3.8.5) + jekyll (3.9.0) addressable (~> 2.4) colorator (~> 1.0) em-websocket (~> 0.5) i18n (~> 0.7) jekyll-sass-converter (~> 1.0) jekyll-watch (~> 2.0) - kramdown (~> 1.14) + kramdown (>= 1.17, < 3) liquid (~> 4.0) mercenary (~> 0.3.3) pathutil (~> 0.9) @@ -191,7 +192,10 @@ GEM gemoji (~> 3.0) html-pipeline (~> 2.2) jekyll (>= 3.0, < 5.0) - kramdown (1.17.0) + kramdown (2.3.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) liquid (4.0.3) listen (3.2.1) rb-fsevent (~> 0.10, >= 0.10.3) @@ -204,21 +208,22 @@ GEM jekyll-seo-tag (~> 2.1) minitest (5.14.1) multipart-post (2.1.1) - nokogiri (1.10.7) + nokogiri (1.10.10) mini_portile2 (~> 2.4.0) - octokit (4.15.0) + octokit (4.18.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (3.1.1) - rb-fsevent (0.10.3) + rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) - rouge (3.13.0) - ruby-enum (0.7.2) + rexml (3.2.4) + rouge (3.19.0) + ruby-enum (0.8.0) i18n - rubyzip (2.2.0) + rubyzip (2.3.0) safe_yaml (1.0.5) sass (3.7.4) sass-listen (~> 4.0.0) @@ -228,15 +233,20 @@ GEM sawyer (0.8.2) addressable (>= 2.3.5) faraday (> 0.8, < 2.0) + simpleidn (0.1.1) + unf (~> 0.1.4) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) thread_safe (0.3.6) - typhoeus (1.3.1) + typhoeus (1.4.0) ethon (>= 0.9.0) tzinfo (1.2.7) thread_safe (~> 0.1) - unicode-display_width (1.6.1) - zeitwerk (2.3.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.7) + unicode-display_width (1.7.0) + zeitwerk (2.4.0) PLATFORMS ruby From 213a02dcbe61d2513772236c12a2a5fa457d8707 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 15 Aug 2020 16:41:48 +0100 Subject: [PATCH 305/609] Remove junk website stuff --- support/site/jekyll/.gitignore | 2 -- support/site/jekyll/Gemfile | 2 -- support/site/jekyll/Gemfile.lock | 61 -------------------------------- 3 files changed, 65 deletions(-) delete mode 100644 support/site/jekyll/.gitignore delete mode 100644 support/site/jekyll/Gemfile delete mode 100644 support/site/jekyll/Gemfile.lock diff --git a/support/site/jekyll/.gitignore b/support/site/jekyll/.gitignore deleted file mode 100644 index 011d9d8..0000000 --- a/support/site/jekyll/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -vendor/ -.bundle diff --git a/support/site/jekyll/Gemfile b/support/site/jekyll/Gemfile deleted file mode 100644 index 2c01300..0000000 --- a/support/site/jekyll/Gemfile +++ /dev/null @@ -1,2 +0,0 @@ -source 'https://rubygems.org' -gem 'jekyll', '3.8.5' diff --git a/support/site/jekyll/Gemfile.lock b/support/site/jekyll/Gemfile.lock deleted file mode 100644 index 2367501..0000000 --- a/support/site/jekyll/Gemfile.lock +++ /dev/null @@ -1,61 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) - colorator (1.1.0) - concurrent-ruby (1.1.5) - em-websocket (0.5.1) - eventmachine (>= 0.12.9) - http_parser.rb (~> 0.6.0) - eventmachine (1.2.7) - ffi (1.11.1) - forwardable-extended (2.6.0) - http_parser.rb (0.6.0) - i18n (0.9.5) - concurrent-ruby (~> 1.0) - jekyll (3.8.5) - addressable (~> 2.4) - colorator (~> 1.0) - em-websocket (~> 0.5) - i18n (~> 0.7) - jekyll-sass-converter (~> 1.0) - jekyll-watch (~> 2.0) - kramdown (~> 1.14) - liquid (~> 4.0) - mercenary (~> 0.3.3) - pathutil (~> 0.9) - rouge (>= 1.7, < 4) - safe_yaml (~> 1.0) - jekyll-sass-converter (1.5.2) - sass (~> 3.4) - jekyll-watch (2.2.1) - listen (~> 3.0) - kramdown (1.17.0) - liquid (4.0.3) - listen (3.2.0) - rb-fsevent (~> 0.10, >= 0.10.3) - rb-inotify (~> 0.9, >= 0.9.10) - mercenary (0.3.6) - pathutil (0.16.2) - forwardable-extended (~> 2.6) - public_suffix (4.0.1) - rb-fsevent (0.10.3) - rb-inotify (0.10.0) - ffi (~> 1.0) - rouge (3.12.0) - safe_yaml (1.0.5) - sass (3.7.4) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - -PLATFORMS - ruby - -DEPENDENCIES - jekyll (= 3.8.5) - -BUNDLED WITH - 2.0.2 From 67a380c9faae622fe29058817a2385adb34ba775 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 21 Aug 2020 17:44:35 +0100 Subject: [PATCH 306/609] Fix homebrew installation --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ade040c..3cbf7b8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -79,7 +79,7 @@ stages: brew update for p in macvim node@10 tcl-tk llvm; do brew install $p - brew outdated $p || brew install $p + brew outdated $p || brew upgrade $p done brew link --force --overwrite node@10 displayName: 'Install vim and node' From cc06605b40ccd231e556d08b42e482eb44c9ad7e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 21 Aug 2020 17:49:07 +0100 Subject: [PATCH 307/609] try with the latest node, as we no longer require node 10 --- azure-pipelines.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3cbf7b8..f9e4bc7 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -75,9 +75,8 @@ stages: vmImage: 'macOS-10.14' steps: - bash: | - brew unlink node@6 brew update - for p in macvim node@10 tcl-tk llvm; do + for p in macvim node tcl-tk llvm; do brew install $p brew outdated $p || brew upgrade $p done From 044e27144e3084a9651a6d4a6f11ec1d68c29427 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 21 Aug 2020 17:54:12 +0100 Subject: [PATCH 308/609] Force link latest node --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f9e4bc7..fc4e995 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -80,7 +80,7 @@ stages: brew install $p brew outdated $p || brew upgrade $p done - brew link --force --overwrite node@10 + brew link --force --overwrite node displayName: 'Install vim and node' - bash: go get -u github.com/go-delve/delve/cmd/dlv From 3573439a5d89a86101f0d716a7586d888820daaf Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 21 Aug 2020 18:05:57 +0100 Subject: [PATCH 309/609] apparently /usr/local/node is not writable; make it --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fc4e995..5064c34 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -80,6 +80,7 @@ stages: brew install $p brew outdated $p || brew upgrade $p done + find /usr/local/include/node -type d -exec chmod +w {} \; brew link --force --overwrite node displayName: 'Install vim and node' From dc57ed7a677ded461f929fea92151c406c000bf1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 21 Aug 2020 18:14:59 +0100 Subject: [PATCH 310/609] Use the node installer task --- azure-pipelines.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5064c34..9b09c49 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -74,13 +74,19 @@ stages: pool: vmImage: 'macOS-10.14' steps: + # Node.js tool installer + # Finds or downloads and caches the specified version spec of Node.js and adds it to the PATH + - task: NodeTool@0 + inputs: + versionSpec: '>=12.0.0' + - bash: | brew update - for p in macvim node tcl-tk llvm; do + brew doctor + for p in macvim tcl-tk llvm; do brew install $p brew outdated $p || brew upgrade $p done - find /usr/local/include/node -type d -exec chmod +w {} \; brew link --force --overwrite node displayName: 'Install vim and node' From d263568ef74214b0f613dec0cec8610c6320c43f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 21 Aug 2020 18:19:45 +0100 Subject: [PATCH 311/609] DOn't link node that you didn't install --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9b09c49..a97e6b2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -76,7 +76,8 @@ stages: steps: # Node.js tool installer # Finds or downloads and caches the specified version spec of Node.js and adds it to the PATH - - task: NodeTool@0 + - displayName: Install node + task: NodeTool@0 inputs: versionSpec: '>=12.0.0' @@ -87,8 +88,7 @@ stages: brew install $p brew outdated $p || brew upgrade $p done - brew link --force --overwrite node - displayName: 'Install vim and node' + displayName: 'Install vim and deps' - bash: go get -u github.com/go-delve/delve/cmd/dlv displayName: 'Install Delve for Go' From e90093870e69ff0cae7bff18d8c456878280a532 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 21 Aug 2020 18:28:20 +0100 Subject: [PATCH 312/609] task does not support displayName --- azure-pipelines.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a97e6b2..7eb6946 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -76,8 +76,7 @@ stages: steps: # Node.js tool installer # Finds or downloads and caches the specified version spec of Node.js and adds it to the PATH - - displayName: Install node - task: NodeTool@0 + - task: NodeTool@0 inputs: versionSpec: '>=12.0.0' From 9de4d07955f6cc007fb074b61b48956b49658dcb Mon Sep 17 00:00:00 2001 From: tamago324 Date: Sun, 23 Aug 2020 12:02:06 +0900 Subject: [PATCH 313/609] Fix schema url --- docs/configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 0ced4e6..2bb70ab 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1012,8 +1012,8 @@ This configuration can be adapted to any other LSP-based editor configuration and is provided just as an example. [dap]: https://microsoft.github.io/debug-adapter-protocol/ -[schema]: http://puremourning.github.io/vimspector/vimspector.schema.json -[gadget-schema]: http://puremourning.github.io/vimspector/gadgets.schema.json +[schema]: http://puremourning.github.io/vimspector/schema/vimspector.schema.json +[gadget-schema]: http://puremourning.github.io/vimspector/schema/gadgets.schema.json [YouCompleteMe]: https://github.com/ycm-core/YouCompleteMe [lsp-examples]: https://github.com/ycm-core/lsp-examples [vscode-json]: https://github.com/vscode-langservers/vscode-json-languageserver From 3c1ac36e6e31be642780e33353a97dec41e2c054 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 23 Aug 2020 12:27:03 +0100 Subject: [PATCH 314/609] Add some instructions for cutomising the unicode signs --- README.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index db6e025..1f21681 100644 --- a/README.md +++ b/README.md @@ -1353,17 +1353,31 @@ Vimsector uses them, they will not be replaced. So to customise the signs, define them in your `vimrc`. * `vimspectorBP`: A breakpoint. +* `vimspectorBPCond`: A conditional breakpoint. * `vimspectorBPDisabled`: A disabled breakpoint * `vimspectorPC` The program counter, i.e. current line. -For example, to use some unicode symbols, you could put this in your `vimrc`: +The default symbols are the equivalent of something like the following: ```viml -sign define vimspectorBP text=🔴 texthl=Normal -sign define vimspectorBPDisabled text=🔵 texthl=Normal -sign define vimspectorPC text=🔶 texthl=SpellBad +sign define vimspectorBP text=\ ● texthl=WarningMsg +sign define vimspectorBPCond text=\ ◆ texthl=WarningMsg +sign define vimspectorBPDisabled text=\ ● texthl=LineNr +sign define vimspectorPC text=\ ▶ texthl=MatchParen ``` +If the signs don't display properly, your font probably doesn't contain these +glyphs. You can easily change them by deifining the sign in your vimrc. For +example, you could put this in your `vimrc` to use some simple ASCII symbols: + +```viml +sign define vimspectorBP text=>> texthl=WarningMsg +sign define vimspectorBPCond text=>> texthl=WarningMsg +sign define vimspectorBPDisabled text=>> texthl=LIneNr +sign define vimspectorPC text=-> texthl=MatchParen +``` + + ## Changing the default window sizes > ***Please Note***: This cusomiation API is ***unstable***, meaning that it may @@ -1463,6 +1477,50 @@ In addition, the following key is added when triggering the * `g:vimspector_session_windows.terminal`: Window ID of the terminal window +## Customising the WinBar + +You can even customise the WinBar buttons by simply running the usual `menu` +(and `unmanu`) commands. + +By default, Vimspector uses something a bit like this: + +```viml +nnoremenu WinBar.■\ Stop :call vimspector#Stop() +nnoremenu WinBar.▶\ Cont :call vimspector#Continue() +nnoremenu WinBar.▷\ Pause :call vimspector#Pause() +nnoremenu WinBar.↷\ Next :call vimspector#StepOver() +nnoremenu WinBar.→\ Step :call vimspector#StepInto() +nnoremenu WinBar.←\ Out :call vimspector#StepOut() +nnoremenu WinBar.⟲: :call vimspector#Restart() +nnoremenu WinBar.✕ :call vimspector#Reset() +``` + +If you prefer a different layout or if the unicode symbols don't render +correctly in your font, you can customise this in the `VimspectorUICreated` +autocommand, for example: + +```viml +func! CustomiseUI() + call win_gotoid( g:vimspector_session_windows.code ) + " Clear the existing WinBar created by Vimspector + nunmenu WinBar + " Cretae our own WinBar + nnoremenu WinBar.Kill :call vimspector#Stop() + nnoremenu WinBar.Continue :call vimspector#Continue() + nnoremenu WinBar.Pause :call vimspector#Pause() + nnoremenu WinBar.Step\ Over :call vimspector#StepOver() + nnoremenu WinBar.Step\ In :call vimspector#StepInto() + nnoremenu WinBar.Step\ Out :call vimspector#StepOut() + nnoremenu WinBar.Restart :call vimspector#Restart() + nnoremenu WinBar.Exit :call vimspector#Reset() +endfunction + +augroup MyVimspectorUICustomistaion + autocmd! + autocmd User VimspectorUICreated call s:CustomiseUI() +augroup END +``` + ## Example There is some example code in `support/custom_ui_vimrc` showing how you can use @@ -1533,6 +1591,8 @@ hi link jsonComment Comment but in theory a single gadget can supply multiple `adapter` configs. Typically this happens when a `gadget` supplies different `adapter` config for, say remote debugging, or debugging in a container, etc. +8. The signs and winbar display funny symbols. How do i fix them? See + [this](#changing-the-default-signs) and [this](#customising-the-winbar) # Motivation From dae576090064fc882767eeeca38f06a7ac42c86f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 23 Aug 2020 12:52:03 +0100 Subject: [PATCH 315/609] Ensure signs are only padded when 1 display cell --- python3/vimspector/breakpoints.py | 12 +++++++++--- python3/vimspector/code.py | 4 +++- python3/vimspector/utils.py | 10 ++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 59d1370..917804b 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -53,13 +53,19 @@ class ProjectBreakpoints( object ): self._next_sign_id = 1 if not utils.SignDefined( 'vimspectorBP' ): - vim.command( 'sign define vimspectorBP text=\\ ● texthl=WarningMsg' ) + utils.DefineSign( 'vimspectorBP', + text = '●', + texthl = 'WarningMsg' ) if not utils.SignDefined( 'vimspectorBPCond' ): - vim.command( 'sign define vimspectorBPCond text=\\ ◆ texthl=WarningMsg' ) + utils.DefineSign( 'vimspectorBPCond', + text = '◆', + texthl = 'WarningMsg' ) if not utils.SignDefined( 'vimspectorBPDisabled' ): - vim.command( 'sign define vimspectorBPDisabled text=\\ ● texthl=LineNr' ) + utils.DefineSign( 'vimspectorBPDisabled', + text = '●', + texthl = 'LineNr' ) def ConnectionUp( self, connection ): diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 8e33ece..d8bd208 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -51,7 +51,9 @@ class CodeView( object ): vim.command( 'nnoremenu WinBar.✕ :call vimspector#Reset()' ) if not utils.SignDefined( 'vimspectorPC' ): - vim.command( 'sign define vimspectorPC text=\\ ▶ texthl=MatchParen' ) + utils.DefineSign( 'vimspectorPC', + text = '▶', + texthl = 'MatchParen' ) def SetCurrentFrame( self, frame ): diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index bc7c168..af76150 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -777,3 +777,13 @@ def WindowID( window, tab=None ): if tab is None: tab = window.tabpage return int( Call( 'win_getid', window.number, tab.number ) ) + + +def DefineSign( name, text, texthl, col = 'right' ): + if col == 'right': + if int( Call( 'strdisplaywidth', text ) ) < 2: + text = ' ' + text + + text = text.replace( ' ', r'\ ' ) + + vim.command( f'sign define { name } text={ text } texthl={ texthl }' ) From 24193a17ffe1796dac455f13842d622d01ace6f1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 23 Aug 2020 18:01:46 +0100 Subject: [PATCH 316/609] Fix minor readme errors --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1f21681..4fe0286 100644 --- a/README.md +++ b/README.md @@ -1371,9 +1371,9 @@ glyphs. You can easily change them by deifining the sign in your vimrc. For example, you could put this in your `vimrc` to use some simple ASCII symbols: ```viml -sign define vimspectorBP text=>> texthl=WarningMsg -sign define vimspectorBPCond text=>> texthl=WarningMsg -sign define vimspectorBPDisabled text=>> texthl=LIneNr +sign define vimspectorBP text==> texthl=WarningMsg +sign define vimspectorBPCond text=?> texthl=WarningMsg +sign define vimspectorBPDisabled text=!> texthl=LineNr sign define vimspectorPC text=-> texthl=MatchParen ``` From ef94b1bc496e298a4f287f617c2981cd5b240214 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 24 Aug 2020 18:31:47 +0100 Subject: [PATCH 317/609] Fix neovim (again) - incompatible exception behabiour Neovim does not raise: * KeyError when accessing a vim dict * KeyboardInterrupt when ctrl-c at a prompt Instead it raises some internal subclass of vim.errro which cannot easily be identified, so we just catch any vim.error. --- python3/vimspector/breakpoints.py | 13 +++++-------- python3/vimspector/utils.py | 8 ++++---- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 917804b..b2632e9 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -348,14 +348,11 @@ class ProjectBreakpoints( object ): f"Invalid value for exception breakpoint filter '{f}': " f"'{result}'. Must be boolean, 'Y', 'N' or '' (default)" ) else: - try: - result = utils.AskForInput( - "{}: Break on {} (Y/N/default: {})? ".format( f[ 'filter' ], - f[ 'label' ], - default_value ), - default_value ) - except KeyboardInterrupt: - result = '' + result = utils.AskForInput( + "{}: Break on {} (Y/N/default: {})? ".format( f[ 'filter' ], + f[ 'label' ], + default_value ), + default_value ) if result == 'Y': exception_filters.append( f[ 'filter' ] ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index af76150..1b7f6b4 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -339,7 +339,7 @@ def SelectFromList( prompt, options ): if selection < 0 or selection >= len( options ): return None return options[ selection ] - except KeyboardInterrupt: + except ( KeyboardInterrupt, vim.error ): return None @@ -353,7 +353,7 @@ def AskForInput( prompt, default_value = None ): try: return vim.eval( "input( '{}' {} )".format( Escape( prompt ), default_option ) ) - except KeyboardInterrupt: + except ( KeyboardInterrupt, vim.error ): return None @@ -732,7 +732,7 @@ def GetVimValue( vim_dict, name, default=None ): # FIXME: use 'encoding' ? try: value = vim_dict[ name ] - except KeyError: + except ( KeyError, vim.error ): return default if isinstance( value, bytes ): @@ -743,7 +743,7 @@ def GetVimValue( vim_dict, name, default=None ): def GetVimList( vim_dict, name, default=None ): try: value = vim_dict[ name ] - except KeyError: + except ( KeyError, vim.error ): return default if not isinstance( value, collections.abc.Iterable ): From 51cc6c4d3ac770445a083b726869303f8047dbd4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 27 Aug 2020 18:09:48 +0100 Subject: [PATCH 318/609] Add VimspectorToggleLog --- CONTRIBUTING.md | 7 +++++++ README.md | 10 ++++++++++ autoload/vimspector.vim | 7 +++++++ plugin/vimspector.vim | 3 +++ python3/vimspector/debug_session.py | 26 +++++++++++++++++++++++++- python3/vimspector/output.py | 6 ++++-- 6 files changed, 56 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9c9d8fb..cfa6c9d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,6 +33,13 @@ work, please always include _all_ of the diagnostics requested in the [issue template][issue-template]. Please do not be offended if your request is ignored if it does not include the requested diagnostics. +The Vimspector log file contains a full trace of the communication between +Vimspector and the debug adapter. This is the primary source of diagnostic +information when something goes wrong that's not a clear Vim traceback. + +If you just want to see the Vimspector log file, use `:VimspectorToggleLog`, +which will tail it in a little window (doesn't work on Windows). + ## Issues The GitHub issue tracker is for *bug reports* and *features requests* for the diff --git a/README.md b/README.md index 4fe0286..1ee251b 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ For a tutorial and usage overview, take a look at the * [Stack Traces](#stack-traces) * [Program Output](#program-output) * [Console](#console) + * [Log View](#log-view) * [Closing debugger](#closing-debugger) * [Debug adapter configuration](#debug-adapter-configuration) * [C, C , Rust, etc.](#c-c-rust-etc) @@ -779,6 +780,15 @@ NOTE: See also [Watches](#watches) above. If the output window is closed, a new one can be opened with `:VimspectorShowOutput Console`. +### Log View + +The Vimspector log file contains a full trace of the communication between +Vimspector and the debug adapter. This is the primary source of diagnostic +information when something goes wrong that's not a Vim traceback. + +If you just want to see the Vimspector log file, use `:VimspectorToggleLog`, +which will tail it in a little window (doesn't work on Windows). + ## Closing debugger To close the debugger, use: diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 929a8b3..302b34d 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -209,6 +209,13 @@ function! vimspector#ShowOutputInWindow( win_id, category ) abort \ vim.eval( 'a:category' ) ) endfunction +function! vimspector#ToggleLog() abort + if !s:enabled + return + endif + py3 _vimspector_session.ToggleLog() +endfunction + function! vimspector#ListBreakpoints() abort if !s:enabled return diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 0aa5f13..b1e9481 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -91,6 +91,9 @@ command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr command! -bar -nargs=? -complete=custom,vimspector#CompleteOutput \ VimspectorShowOutput \ call vimspector#ShowOutput( ) +command! -bar + \ VimspectorToggleLog + \ call vimspector#ToggleLog() command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr \ VimspectorEval \ call vimspector#Evaluate( ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 15ff90c..13d4b1e 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -55,6 +55,7 @@ class DebugSession( object ): install.GetGadgetDir( VIMSPECTOR_HOME ) ) self._uiTab = None + self._logView = None self._stackTraceView = None self._variablesView = None self._outputView = None @@ -332,12 +333,15 @@ class DebugSession( object ): return wrapper return decorator + def _HasUI( self ): + return self._uiTab and self._uiTab.valid + def RequiresUI( otherwise=None ): """Decorator, call fct if self._connected else echo warning""" def decorator( fct ): @functools.wraps( fct ) def wrapper( self, *args, **kwargs ): - if not self._uiTab or not self._uiTab.valid: + if not self._HasUI(): utils.UserMessage( 'Vimspector is not active', persist=False, @@ -492,6 +496,26 @@ class DebugSession( object ): def ExpandFrameOrThread( self ): self._stackTraceView.ExpandFrameOrThread() + def ToggleLog( self ): + if self._HasUI(): + return self.ShowOutput( 'Vimspector' ) + + if self._logView and self._logView.WindowIsValid(): + self._logView.Reset() + self._logView = None + return + + if self._logView: + self._logView.Reset() + + # TODO: The UI code is too scattered. Re-organise into a UI class that + # just deals with these thigns like window layout and custmisattion. + vim.command( f'botright { settings.Int( "bottombar_height", 10 ) }new' ) + win = vim.current.window + self._logView = output.OutputView( win, self._api_prefix ) + self._logView.AddLogFileView() + self._logView.ShowOutput( 'Vimspector' ) + @RequiresUI() def ShowOutput( self, category ): if not self._outputView.WindowIsValid(): diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 1d349aa..43dd240 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -187,7 +187,6 @@ class OutputView( object ): syntax, self._buffers[ category ].buf ) - def _RenderWinBar( self, category ): if not self._window.valid: return @@ -216,6 +215,9 @@ class OutputView( object ): def GetCategories( self ): return list( self._buffers.keys() ) + def AddLogFileView( self, file_name = utils.LOG_FILE ): + self._CreateBuffer( 'Vimspector', file_name = file_name ) + class DAPOutputView( OutputView ): """Specialised OutputView which adds the DAP Console (REPL)""" @@ -226,7 +228,7 @@ class DAPOutputView( OutputView ): for b in set( BUFFER_MAP.values() ): self._CreateBuffer( b ) - self._CreateBuffer( 'Vimspector', file_name = utils.LOG_FILE ) + self.AddLogFileView() self._ShowOutput( 'Console' ) def ConnectionUp( self, connection ): From 95fa0c5105c2a75cd0fc91b2424ff6fcf014e891 Mon Sep 17 00:00:00 2001 From: tamago324 Date: Sat, 29 Aug 2020 15:17:24 +0900 Subject: [PATCH 319/609] Fix balloon display ... --- python3/vimspector/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 1b7f6b4..e3a0347 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -625,6 +625,9 @@ def DisplayBaloon( is_term, display ): if not is_term: display = '\n'.join( display ) + # Remove balloon + vim.eval( "balloon_show( '' )") + vim.eval( "balloon_show( {0} )".format( json.dumps( display ) ) ) From 9b7540fbf696e6c3bb587883cc00ca032b4c81b4 Mon Sep 17 00:00:00 2001 From: tamago324 Date: Sat, 29 Aug 2020 15:22:19 +0900 Subject: [PATCH 320/609] Format flake8 --- 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 e3a0347..ec0f7fb 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -626,7 +626,7 @@ def DisplayBaloon( is_term, display ): display = '\n'.join( display ) # Remove balloon - vim.eval( "balloon_show( '' )") + vim.eval( "balloon_show( '' )" ) vim.eval( "balloon_show( {0} )".format( json.dumps( display ) ) ) From 8b488d479d7640b8592e05bdbd76d4107f66e5ba Mon Sep 17 00:00:00 2001 From: tamago324 Date: Sat, 29 Aug 2020 22:19:46 +0900 Subject: [PATCH 321/609] To enable the Windows GUI to display the balloon correctly Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685 --- python3/vimspector/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index ec0f7fb..ec2d54d 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -624,9 +624,9 @@ def ParseVariables( variables_list, def DisplayBaloon( is_term, display ): if not is_term: display = '\n'.join( display ) - - # Remove balloon - vim.eval( "balloon_show( '' )" ) + # To enable the Windows GUI to display the balloon correctly + # Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685 + vim.eval( "balloon_show( '' )" ) vim.eval( "balloon_show( {0} )".format( json.dumps( display ) ) ) From c1b544fb3c4667a36a91661945aefb16e5a95a42 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 1 Sep 2020 13:37:45 +0100 Subject: [PATCH 322/609] Allow customisation of the signs Too many plugins use the default priority of 10 so they race/chase. Allow uses to configure the priorities and make sure that the defaults are documented. --- README.md | 46 +++++- compiler/vimspector_test.vim | 87 ++++++++--- python3/vimspector/breakpoints.py | 40 +++-- python3/vimspector/code.py | 43 +++--- python3/vimspector/settings.py | 6 + python3/vimspector/signs.py | 39 +++++ python3/vimspector/utils.py | 17 --- support/custom_ui_vimrc | 7 + tests/breakpoints.test.vim | 148 ++++++++++++++++--- tests/language_go.test.vim | 3 +- tests/language_python.test.vim | 6 +- tests/lib/autoload/vimspector/test/signs.vim | 41 ++++- 12 files changed, 370 insertions(+), 113 deletions(-) create mode 100644 python3/vimspector/signs.py diff --git a/README.md b/README.md index 1ee251b..d354108 100644 --- a/README.md +++ b/README.md @@ -66,16 +66,18 @@ 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) + * [Sign priority](#sign-priority) * [Changing the default window sizes](#changing-the-default-window-sizes) * [Changing the terminal size](#changing-the-terminal-size) * [Advanced UI customisation](#advanced-ui-customisation) + * [Customising the WinBar](#customising-the-winbar) * [Example](#example) * [FAQ](#faq) * [Motivation](#motivation) * [License](#license) * [Sponsorship](#sponsorship) - + @@ -1362,10 +1364,13 @@ Vimsector uses the following signs internally. If they are defined before Vimsector uses them, they will not be replaced. So to customise the signs, define them in your `vimrc`. -* `vimspectorBP`: A breakpoint. -* `vimspectorBPCond`: A conditional breakpoint. -* `vimspectorBPDisabled`: A disabled breakpoint -* `vimspectorPC` The program counter, i.e. current line. + +| Sign | Description | Priority | +|------------------------|-------------------------------------|----------| +| `vimspectorBP` | Line breakpoint | 9 | +| `vimspectorBPCond` | Conditional line breakpiont | 9 | +| `vimspectorBPDisabled` | Disabled breakpoint | 9 | +| `vimspectorPC` | Program counter (i.e. current line) | 20 | The default symbols are the equivalent of something like the following: @@ -1387,6 +1392,37 @@ sign define vimspectorBPDisabled text=!> texthl=LineNr sign define vimspectorPC text=-> texthl=MatchParen ``` +## Sign priority + +Many different plugins provide signs for various purposes. Examples include +diagnostic signs for code errors, etc. Vim provides only a single priority to +determine which sign should be displayed when multiple signs are placed at a +single line. If you are finding that other signs are interfering with +vimspector's (or vice-versa), you can customise the priority used by vimspector +by setting the following dictionary: + +```viml +let g:vimspector_sign_priority = { + \ '': , + \ } +``` + +For example: + +```viml +let g:vimspector_sign_priority = { + \ 'vimspectorBP': 3, + \ 'vimspectorBPCond': 2, + \ 'vimspectorBPDisabled': 1, + \ 'vimspectorPC': 999, + \ } +``` + +All keys are optional. If a sign is not customised, the default priority it used +(as shown above). + +See `:help sign-priority`. The default priority is 10, larger numbers override +smaller ones. ## Changing the default window sizes diff --git a/compiler/vimspector_test.vim b/compiler/vimspector_test.vim index 5264b4b..d0c6def 100644 --- a/compiler/vimspector_test.vim +++ b/compiler/vimspector_test.vim @@ -37,49 +37,68 @@ if ! exists( ':' . s:make_cmd ) endif function! VimGetCurrentFunction() - echom s:GetCurrentFunction() + echom s:GetCurrentFunction()[ 0 ] endfunction function! s:GetCurrentFunction() " Store the cursor position; we'll need to reset it - let [ l:buf, l:row, l:col, l:offset ] = getpos( '.' ) + let [ buf, row, col, offset ] = getpos( '.' ) - let l:test_function = '' + let [ test_function, test_function_line ] = [ v:null, -1 ] - let l:pattern = '\V\C\s\*function!\?\s\+\(\<\w\+\>\)\.\*\$' + let pattern = '\V\C\s\*func\%\(tion\)\?!\?\s\+\(\<\w\+\>\)\.\*\$' - let l:lnum = prevnonblank( '.' ) + let lnum = prevnonblank( '.' ) " Find the top-level method and class - while l:lnum > 0 - call cursor( l:lnum, 1 ) - let l:lnum = search( l:pattern, 'bcnWz' ) + while lnum > 0 + call cursor( lnum, 1 ) + let lnum = search( pattern, 'bcnWz' ) - if l:lnum <= 0 - call cursor( l:row, l:col ) - return l:test_function + if lnum <= 0 + call cursor( row, col ) + return [ test_function, test_function_line ] endif - let l:this_decl = substitute( getline( l:lnum ), l:pattern, '\1', '' ) - let l:this_decl_is_test = match( l:this_decl, '\V\C\^Test_' ) >= 0 + let this_decl = substitute( getline( lnum ), pattern, '\1', '' ) + let this_decl_is_test = match( this_decl, '\V\C\^Test_' ) >= 0 - if l:this_decl_is_test - let l:test_function = l:this_decl + if this_decl_is_test + let [ test_function, test_function_line ] = [ this_decl, lnum ] - if indent( l:lnum ) == 0 - call cursor( l:row, l:col ) - return l:test_function + if indent( lnum ) == 0 + call cursor( row, col ) + return [ test_function, test_function_line ] endif endif - let l:lnum = prevnonblank( l:lnum - 1 ) + let lnum = prevnonblank( lnum - 1 ) endwhile + return [ v:null, -1 ] endfunction +function! s:RunTestUnderCursorInVimspector() + update + let l:test_func_name = s:GetCurrentFunction()[ 0 ] + + if l:test_func_name ==# '' + echo 'No test method found' + return + endif + + echo "Running test '" . l:test_func_name . "'" + + call vimspector#LaunchWithSettings( { + \ 'configuration': 'Run test', + \ 'TestFunction': l:test_func_name + \ } ) +endfunction + + function! s:RunTestUnderCursor() update - let l:test_func_name = s:GetCurrentFunction() + let l:test_func_name = s:GetCurrentFunction()[ 0 ] if l:test_func_name ==# '' echo 'No test method found' @@ -132,10 +151,36 @@ if ! has( 'gui_running' ) nnoremap  :call RunAllTests() " † is right-option+t nnoremap † :call RunTestUnderCursor() + nnoremap † :call RunTestUnderCursorInVimspector() " å is the right-option+q nnoremap å :cfirst " å is the right-option+a - nnoremap œ :cnext + nnoremap œ :FuncLine " Ω is the right-option+z nnoremap Ω :cprevious endif + +function! s:GoToCurrentFunctionLine( ... ) + if a:0 < 1 + call inputsave() + let lnum = str2nr( input( 'Enter line num: ' ) ) + call inputrestore() + else + let lnum = a:1 + endif + + let [ f, l ] = s:GetCurrentFunction() + if f is v:null + return + endif + + let lnum += l + + echo 'Function' f 'at line' l '(jump to line ' lnum . ')' + + call cursor( [ lnum, indent( lnum ) ] ) +endfunction + +command! -buffer -nargs=? -bar + \ FuncLine + \ :call s:GoToCurrentFunctionLine( ) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index b2632e9..6539c81 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -21,7 +21,7 @@ import os import logging import json -from vimspector import utils +from vimspector import utils, signs class ServerBreakpointHandler( object ): @@ -52,18 +52,18 @@ class ProjectBreakpoints( object ): self._next_sign_id = 1 - if not utils.SignDefined( 'vimspectorBP' ): - utils.DefineSign( 'vimspectorBP', + if not signs.SignDefined( 'vimspectorBP' ): + signs.DefineSign( 'vimspectorBP', text = '●', texthl = 'WarningMsg' ) - if not utils.SignDefined( 'vimspectorBPCond' ): - utils.DefineSign( 'vimspectorBPCond', + if not signs.SignDefined( 'vimspectorBPCond' ): + signs.DefineSign( 'vimspectorBPCond', text = '◆', texthl = 'WarningMsg' ) - if not utils.SignDefined( 'vimspectorBPDisabled' ): - utils.DefineSign( 'vimspectorBPDisabled', + if not signs.SignDefined( 'vimspectorBPDisabled' ): + signs.DefineSign( 'vimspectorBPDisabled', text = '●', texthl = 'LineNr' ) @@ -130,8 +130,7 @@ class ProjectBreakpoints( object ): for bp in breakpoints: self._SignToLine( file_name, bp ) if 'sign_id' in bp: - vim.command( 'sign unplace {0} group=VimspectorBP'.format( - bp[ 'sign_id' ] ) ) + signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) self._line_breakpoints = defaultdict( list ) self._func_breakpoints = [] @@ -157,8 +156,7 @@ class ProjectBreakpoints( object ): action = 'Disable' else: if 'sign_id' in bp: - vim.command( 'sign unplace {0} group=VimspectorBP'.format( - bp[ 'sign_id' ] ) ) + signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) del self._line_breakpoints[ file_name ][ index ] action = 'Delete' break @@ -249,8 +247,7 @@ class ProjectBreakpoints( object ): for bp in line_breakpoints: self._SignToLine( file_name, bp ) if 'sign_id' in bp: - vim.command( 'sign unplace {0} group=VimspectorBP'.format( - bp[ 'sign_id' ] ) ) + signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) del bp[ 'sign_id' ] if bp[ 'state' ] != 'ENABLED': @@ -374,8 +371,7 @@ class ProjectBreakpoints( object ): for bp in line_breakpoints: self._SignToLine( file_name, bp ) if 'sign_id' in bp: - vim.command( 'sign unplace {0} group=VimspectorBP '.format( - bp[ 'sign_id' ] ) ) + signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) else: bp[ 'sign_id' ] = self._next_sign_id self._next_sign_id += 1 @@ -384,12 +380,12 @@ class ProjectBreakpoints( object ): else 'vimspectorBPCond' if 'condition' in bp[ 'options' ] else 'vimspectorBP' ) - vim.command( - 'sign place {0} group=VimspectorBP line={1} name={2} file={3}'.format( - bp[ 'sign_id' ] , - bp[ 'line' ], - sign, - file_name ) ) + signs.PlaceSign( bp[ 'sign_id' ], + 'VimspectorBP', + sign, + 9, + file_name, + bp[ 'line' ] ) def _SignToLine( self, file_name, bp ): @@ -398,7 +394,7 @@ class ProjectBreakpoints( object ): signs = vim.eval( "sign_getplaced( '{}', {} )".format( utils.Escape( file_name ), - json.dumps( { 'id': file_name, 'group': 'VimspectorBP', } ) ) ) + json.dumps( { 'id': bp[ 'sign_id' ], 'group': 'VimspectorBP', } ) ) ) if len( signs ) == 1 and len( signs[ 0 ][ 'signs' ] ) == 1: bp[ 'line' ] = int( signs[ 0 ][ 'signs' ][ 0 ][ 'lnum' ] ) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index d8bd208..3cbe77b 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 +from vimspector import utils, settings, signs class CodeView( object ): @@ -50,8 +50,8 @@ class CodeView( object ): vim.command( 'nnoremenu WinBar.⟲: :call vimspector#Restart()' ) vim.command( 'nnoremenu WinBar.✕ :call vimspector#Reset()' ) - if not utils.SignDefined( 'vimspectorPC' ): - utils.DefineSign( 'vimspectorPC', + if not signs.SignDefined( 'vimspectorPC' ): + signs.DefineSign( 'vimspectorPC', text = '▶', texthl = 'MatchParen' ) @@ -62,8 +62,7 @@ class CodeView( object ): (or don't have the data) or the code window no longer exits.""" if self._signs[ 'vimspectorPC' ]: - vim.command( 'sign unplace {} group=VimspectorCode'.format( - self._signs[ 'vimspectorPC' ] ) ) + signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' ) self._signs[ 'vimspectorPC' ] = None if not frame or not frame.get( 'source' ): @@ -76,12 +75,12 @@ class CodeView( object ): self._next_sign_id += 1 try: - vim.command( 'sign place {0} group=VimspectorCode priority=20 ' - 'line={1} name=vimspectorPC ' - 'file={2}'.format( - self._signs[ 'vimspectorPC' ], - frame[ 'line' ], - frame[ 'source' ][ 'path' ] ) ) + signs.PlaceSign( self._signs[ 'vimspectorPC' ], + 'VimspectorCode', + 'vimspectorPC', + 20, + frame[ 'source' ][ 'path' ], + frame[ 'line' ] ) except vim.error as e: # Ignore 'invalid buffer name' if 'E158' not in str( e ): @@ -118,8 +117,7 @@ class CodeView( object ): def Clear( self ): if self._signs[ 'vimspectorPC' ]: - vim.command( 'sign unplace {} group=VimspectorCode'.format( - self._signs[ 'vimspectorPC' ] ) ) + signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' ) self._signs[ 'vimspectorPC' ] = None self._UndisplaySigns() @@ -163,7 +161,7 @@ class CodeView( object ): def _UndisplaySigns( self ): for sign_id in self._signs[ 'breakpoints' ]: - vim.command( 'sign unplace {} group=VimspectorCode'.format( sign_id ) ) + signs.UnplaceSign( sign_id, 'VimspectorCode' ) self._signs[ 'breakpoints' ] = [] @@ -182,16 +180,13 @@ class CodeView( object ): sign_id = self._next_sign_id self._next_sign_id += 1 self._signs[ 'breakpoints' ].append( sign_id ) - vim.command( - 'sign place {0} group=VimspectorCode priority=9 ' - 'line={1} ' - 'name={2} ' - 'file={3}'.format( - sign_id, - breakpoint[ 'line' ], - 'vimspectorBP' if breakpoint[ 'verified' ] - else 'vimspectorBPDisabled', - file_name ) ) + signs.PlaceSign( sign_id, + 'VimspectorCode', + 'vimspectorBP' if breakpoint[ 'verified' ] + else 'vimspectorBPDisabled', + 9, + file_name, + breakpoint[ 'line' ] ) def BreakpointsAsQuickFix( self ): diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index 08f3066..d186e42 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -31,3 +31,9 @@ def Int( option: str, default=0 ): def List( option: str, default=[] ): return utils.GetVimList( vim.vars, f'vimspector_{ option }', default ) + + +def Dict( option: str, default={} ): + return utils.GetVimValue( vim.vars, + f'vimspector_{ option }', + default ) diff --git a/python3/vimspector/signs.py b/python3/vimspector/signs.py new file mode 100644 index 0000000..ac98983 --- /dev/null +++ b/python3/vimspector/signs.py @@ -0,0 +1,39 @@ +import vim + +from vimspector import settings, utils + + +def SignDefined( name ): + if utils.Exists( "*sign_getdefined" ): + return int( + vim.eval( f"len( sign_getdefined( '{ utils.Escape( name ) }' ) )" ) + ) + + return False + + +def DefineSign( name, text, texthl, col = 'right' ): + if col == 'right': + if int( utils.Call( 'strdisplaywidth', text ) ) < 2: + text = ' ' + text + + text = text.replace( ' ', r'\ ' ) + + vim.command( f'sign define { name } text={ text } texthl={ texthl }' ) + + +def PlaceSign( sign_id, group, name, priority, file, line ): + priority = settings.Dict( 'sign_priority' ).get( name, priority ) + + cmd = ( f'sign place { sign_id } ' + f'group={ group } ' + f'name={ name } ' + f'priority={ priority } ' + f'line={ line } ' + f'file={ file }' ) + + vim.command( cmd ) + + +def UnplaceSign( sign_id, group ): + vim.command( f'sign unplace { sign_id } group={ group }' ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index ec2d54d..b850090 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -660,13 +660,6 @@ def Call( vimscript_function, *args ): return vim.eval( call ) -def SignDefined( name ): - if Exists( "*sign_getdefined" ): - return int( vim.eval( f"len( sign_getdefined( '{ Escape( name ) }' ) )" ) ) - - return False - - MEMO = {} @@ -780,13 +773,3 @@ def WindowID( window, tab=None ): if tab is None: tab = window.tabpage return int( Call( 'win_getid', window.number, tab.number ) ) - - -def DefineSign( name, text, texthl, col = 'right' ): - if col == 'right': - if int( Call( 'strdisplaywidth', text ) ) < 2: - text = ' ' + text - - text = text.replace( ' ', r'\ ' ) - - vim.command( f'sign define { name } text={ text } texthl={ texthl }' ) diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index 944ff93..95c3360 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -65,3 +65,10 @@ augroup TestUICustomistaion autocmd User VimspectorTerminalOpened call s:SetUpTerminal() autocmd User VimspectorUICreated call s:CustomiseWinBar() augroup END + +let g:vimspector_sign_priority = { + \ 'vimspectorBP': 3, + \ 'vimspectorBPCond': 2, + \ 'vimspectorBPDisabled': 1, + \ 'vimspectorPC': 999, + \ } diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index acc10e2..ca57157 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -53,14 +53,16 @@ function! Test_Signs_Placed_Using_API_Are_Shown() call assert_true( exists( '*vimspector#ToggleBreakpoint' ) ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', \ line( '.' ), - \ 'vimspectorBP' ) + \ 'vimspectorBP', + \ 9 ) " Disable breakpoint call vimspector#ToggleBreakpoint() call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ line( '.' ), - \ 'vimspectorBPDisabled' ) + \ 'vimspectorBPDisabled', + \ 9 ) " Remove breakpoint call vimspector#ToggleBreakpoint() @@ -92,14 +94,16 @@ function! Test_Use_Mappings_HUMAN() call feedkeys( "\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', \ 15, - \ 'vimspectorBP' ) + \ 'vimspectorBP', + \ 9 ) " Disable the breakpoint call feedkeys( "\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 15, - \ 'vimspectorBPDisabled' ) + \ 'vimspectorBPDisabled', + \ 9 ) " Delete the breakpoint call feedkeys( "\", 'xt' ) @@ -110,7 +114,8 @@ function! Test_Use_Mappings_HUMAN() call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 15, - \ 'vimspectorBP' ) + \ 'vimspectorBP', + \ 9 ) " Here we go. Start Debugging call feedkeys( "\", 'xt' ) @@ -184,7 +189,8 @@ function Test_DisableBreakpointWhileDebugging() \ vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorCode', \ 16, - \ 'vimspectorBP' ) + \ 'vimspectorBP', + \ 9 ) \ } ) " Remove the breakpoint @@ -200,7 +206,8 @@ function Test_DisableBreakpointWhileDebugging() \ vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorCode', \ 16, - \ 'vimspectorBP' ) + \ 'vimspectorBP', + \ 9 ) \ } ) " Run to breakpoint @@ -224,7 +231,8 @@ function Test_DisableBreakpointWhileDebugging() call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 16, - \ 'vimspectorBP' ) + \ 'vimspectorBP', + \ 9 ) " Disable the breakpoint call setpos( '.', [ bufnr( 'simple.cpp' ), 16, 1 ] ) @@ -232,7 +240,8 @@ function Test_DisableBreakpointWhileDebugging() call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 16, - \ 'vimspectorBPDisabled' ) + \ 'vimspectorBPDisabled', + \ 9 ) " And delete it call feedkeys( "\", 'xt' ) @@ -266,14 +275,16 @@ function! Test_Insert_Code_Above_Breakpoint() call feedkeys( "\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', \ 25, - \ 'vimspectorBP' ) + \ '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' ) + \ 'vimspectorBP', + \ 9 ) " CHeck that we break at the right point call setpos( '.', [ 0, 1, 1 ] ) @@ -286,12 +297,14 @@ function! Test_Insert_Code_Above_Breakpoint() call setpos( '.', [ 0, 26, 1 ] ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', \ 26, - \ 'vimspectorBP' ) + \ 'vimspectorBP', + \ 9 ) call feedkeys( "\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 26, - \ 'vimspectorBPDisabled' ) + \ 'vimspectorBPDisabled', + \ 9 ) " Delete it call feedkeys( "\", 'xt' ) call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 26 ) @@ -314,14 +327,16 @@ function! Test_Conditional_Line_Breakpoint() call feedkeys( "\\\argc==0\\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', \ 16, - \ 'vimspectorBPCond' ) + \ 'vimspectorBPCond', + \ 9 ) " Disable the breakpoint call feedkeys( "\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 16, - \ 'vimspectorBPDisabled' ) + \ 'vimspectorBPDisabled', + \ 9 ) " Delete the breakpoint call feedkeys( "\", 'xt' ) @@ -335,20 +350,23 @@ function! Test_Conditional_Line_Breakpoint() call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 16, - \ 'vimspectorBPCond' ) + \ 'vimspectorBPCond', + \ 9 ) call setpos( '.', [ 0, 9, 1 ] ) call vimspector#ToggleBreakpoint() call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 9, - \ 'vimspectorBP' ) + \ 'vimspectorBP', + \ 9 ) call setpos( '.', [ 0, 17, 1 ] ) call vimspector#ToggleBreakpoint( { 'condition': 'argc == 1' } ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 17, - \ 'vimspectorBPCond' ) + \ 'vimspectorBPCond', + \ 9 ) call setpos( '.', [ 0, 1, 1 ] ) @@ -385,7 +403,8 @@ function! Test_Conditional_Line_Breakpoint_Hit() call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 14, - \ 'vimspectorBPCond' ) + \ 'vimspectorBPCond', + \ 9 ) call vimspector#LaunchWithSettings( { 'configuration': 'run' } ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 14, 1 ) @@ -526,3 +545,94 @@ function! Test_ListBreakpoints() call vimspector#test#setup#Reset() %bwipe! endfunction + +function! Test_Custom_Breakpoint_Priority() + let g:vimspector_sign_priority = { + \ 'vimspectorPC': 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, + \ 'vimspectorPC', + \ 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, + \ 'vimspectorPC', + \ 1 ) + + + call vimspector#test#setup#Reset() + lcd - + %bwipeout! +endfunction + +function! TearDown_Test_Custom_Breakpoint_Priority() + unlet! g:vimspector_sign_priority +endfunction diff --git a/tests/language_go.test.vim b/tests/language_go.test.vim index 7fb9c9d..b6a7da4 100644 --- a/tests/language_go.test.vim +++ b/tests/language_go.test.vim @@ -23,7 +23,8 @@ function! Test_Go_Simple() call feedkeys( "\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', \ 4, - \ 'vimspectorBP' ) + \ 'vimspectorBP', + \ 9 ) call setpos( '.', [ 0, 1, 1 ] ) diff --git a/tests/language_python.test.vim b/tests/language_python.test.vim index dccdc72..cc49adb 100644 --- a/tests/language_python.test.vim +++ b/tests/language_python.test.vim @@ -23,7 +23,8 @@ function! Test_Python_Simple() call feedkeys( "\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', \ 6, - \ 'vimspectorBP' ) + \ 'vimspectorBP', + \ 9 ) call setpos( '.', [ 0, 1, 1 ] ) @@ -79,7 +80,8 @@ function! Test_Python_Remote_Attach() call feedkeys( "\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', \ 6, - \ 'vimspectorBP' ) + \ 'vimspectorBP', + \ 9 ) call setpos( '.', [ 0, 1, 1 ] ) diff --git a/tests/lib/autoload/vimspector/test/signs.vim b/tests/lib/autoload/vimspector/test/signs.vim index 571353e..926a972 100644 --- a/tests/lib/autoload/vimspector/test/signs.vim +++ b/tests/lib/autoload/vimspector/test/signs.vim @@ -46,7 +46,8 @@ endfunction function! vimspector#test#signs#AssertSignGroupSingletonAtLine( group, \ line, - \ sign_name ) + \ sign_name, + \ priority ) \ abort let signs = sign_getplaced( '%', { @@ -62,10 +63,46 @@ function! vimspector#test#signs#AssertSignGroupSingletonAtLine( group, \ 'Num signs in ' . a:group . ' at ' . a:line ) || \ assert_equal( a:sign_name, \ signs[ 0 ].signs[ 0 ].name, - \ 'Sign in group ' . a:group . ' at ' . a:line ) + \ 'Sign in group ' . a:group . ' at ' . a:line ) || + \ assert_equal( a:priority, + \ signs[ 0 ].signs[ 0 ].priority, + \ 'Sign priority in group ' . a:group . ' at ' . a:line ) endfunction +function! vimspector#test#signs#AssertSignAtLine( group, line, sign_name, priority ) abort + + let signs = sign_getplaced( '%', { + \ 'group': a:group, + \ 'lnum': a:line, + \ } ) + + let errors_before = v:errors + let result = 1 + let errors = [ 'No signs were found' ] + + for sign in signs[ 0 ].signs + let v:errors = [] + + let result = + \ assert_equal( a:sign_name, + \ sign.name, + \ 'Sign in group ' . a:group . ' at ' . a:line ) || + \ assert_equal( a:priority, + \ sign.priority, + \ 'Sign priority in group ' . a:group . ' at ' . a:line ) + if result + let errors = v:errors + else + let errors = [] + break + endif + endfor + + let v:errors = errors_before + errors + return result +endfunction + function! vimspector#test#signs#AssertSignGroupEmptyAtLine( group, line ) abort let signs = sign_getplaced( '%', { \ 'group': a:group, From f538102d33d81614636558ba8179fb75cf54e8d0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 1 Sep 2020 15:01:24 +0100 Subject: [PATCH 323/609] Don't leak buffers when creating output view --- python3/vimspector/output.py | 21 ++++++++ tests/tabpage.test.vim | 100 +++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 43dd240..90df09d 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -17,6 +17,7 @@ from vimspector import utils, install import vim import json +import typing class TabBuffer( object ): @@ -55,6 +56,8 @@ def ShowOutputInWindow( win_id, category ): class OutputView( object ): """Container for a 'tabbed' window of buffers that can be used to display files or the output of commands.""" + _buffers: typing.Dict[ str, TabBuffer ] + def __init__( self, window, api_prefix ): self._window = window self._buffers = {} @@ -146,6 +149,17 @@ class OutputView( object ): cmd = None, completion_handler = None, syntax = None ): + + buf_to_delete = None + if ( not self._buffers + and self._window is not None + and self._window.valid + and not self._window.buffer.name ): + # If there's an empty buffer in the current window that we're not using, + # delete it. We could try and use it, but that complicates the call to + # SetUpCommandBuffer + buf_to_delete = self._window.buffer + if file_name is not None: assert cmd is None if install.GetOS() == "windows": @@ -160,6 +174,7 @@ class OutputView( object ): category, self._api_prefix, completion_handler = completion_handler ) + self._buffers[ category ] = TabBuffer( out, len( self._buffers ) ) self._buffers[ category ].is_job = True self._RenderWinBar( category ) @@ -170,6 +185,7 @@ class OutputView( object ): name = 'vimspector.Output:{0}'.format( category ) tab_buffer = TabBuffer( utils.NewEmptyBuffer(), len( self._buffers ) ) + self._buffers[ category ] = tab_buffer if category == 'Console': @@ -187,6 +203,11 @@ class OutputView( object ): syntax, self._buffers[ category ].buf ) + if buf_to_delete: + with utils.RestoreCurrentWindow(): + self._ShowOutput( category ) + utils.CleanUpHiddenBuffer( buf_to_delete ) + def _RenderWinBar( self, category ): if not self._window.valid: return diff --git a/tests/tabpage.test.vim b/tests/tabpage.test.vim index e08a8c0..6338d9c 100644 --- a/tests/tabpage.test.vim +++ b/tests/tabpage.test.vim @@ -49,3 +49,103 @@ function! Test_Step_With_Different_Tabpage() %bwipeout! endfunction +function! Test_All_Buffers_Deleted_NoHidden() + set nohidden + lcd testdata/cpp/simple + edit simple.cpp + + let opts = #{ buflisted: v:true } + + let buffers_before = getbufinfo( opts ) + + call setpos( '.', [ 0, 15, 1 ] ) + call vimspector#ToggleBreakpoint() + call vimspector#Launch() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( + \ 'simple.cpp', + \ 15 ) + + call vimspector#Reset() + call vimspector#test#setup#WaitForReset() + + let buffers_after = getbufinfo( opts ) + + call assert_equal( len( buffers_before ), len( buffers_after ) ) + + set hidden& + lcd - + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_All_Buffers_Deleted_Hidden() + set hidden + lcd testdata/cpp/simple + edit simple.cpp + + let opts = #{ buflisted: v:true } + + let buffers_before = getbufinfo( opts ) + + call setpos( '.', [ 0, 15, 1 ] ) + call vimspector#ToggleBreakpoint() + call vimspector#Launch() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( + \ 'simple.cpp', + \ 15 ) + + call vimspector#Reset() + call vimspector#test#setup#WaitForReset() + + let buffers_after = getbufinfo( opts ) + + call assert_equal( len( buffers_before ), len( buffers_after ) ) + + set hidden& + lcd - + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_All_Buffers_Deleted_ToggleLog() + set hidden + let buffers_before = getbufinfo( #{ buflisted: 1 } ) + VimspectorToggleLog + VimspectorToggleLog + let buffers_after = getbufinfo( #{ buflisted: 1 } ) + call assert_equal( len( buffers_before ), len( buffers_after ) ) + + call vimspector#test#setup#Reset() + set hidden& + %bwipe! +endfunction + +let g:vimspector_test_install_done = 0 + +function! Test_All_Buffers_Deleted_Installer() + set hidden + let buffers_before = getbufinfo( #{ buflisted: 1 } ) + + augroup Test_All_Buffers_Deleted_Installer + au! + au User VimspectorInstallSuccess let g:vimspector_test_install_done = 1 + au User VimspectorInstallFailed let g:vimspector_test_install_done = 1 + augroup END + + VimspectorUpdate + + " The test timeout will take care of this taking too long + call WaitForAssert( + \ { -> assert_equal( 1, g:vimspector_test_install_done ) }, + \ 120000 ) + + let buffers_after = getbufinfo( #{ buflisted: 1 } ) + call assert_equal( len( buffers_before ), len( buffers_after ) ) + + call vimspector#test#setup#Reset() + set hidden& + au! Test_All_Buffers_Deleted_Installer + %bwipe! +endfunction From b2456b587f6c71110bda0c92449ef1eca07364e9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 1 Sep 2020 16:31:04 +0100 Subject: [PATCH 324/609] Centralise the default settings --- python3/vimspector/breakpoints.py | 1 - python3/vimspector/code.py | 8 ++--- python3/vimspector/debug_session.py | 8 ++--- python3/vimspector/installer.py | 2 +- python3/vimspector/settings.py | 54 ++++++++++++++++++++++++----- python3/vimspector/signs.py | 4 +-- python3/vimspector/utils.py | 1 - 7 files changed, 55 insertions(+), 23 deletions(-) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 6539c81..6e59149 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -383,7 +383,6 @@ class ProjectBreakpoints( object ): signs.PlaceSign( bp[ 'sign_id' ], 'VimspectorBP', sign, - 9, file_name, bp[ 'line' ] ) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 3cbe77b..bab847c 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -78,7 +78,6 @@ class CodeView( object ): signs.PlaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode', 'vimspectorPC', - 20, frame[ 'source' ][ 'path' ], frame[ 'line' ] ) except vim.error as e: @@ -184,7 +183,6 @@ class CodeView( object ): 'VimspectorCode', 'vimspectorBP' if breakpoint[ 'verified' ] else 'vimspectorBPDisabled', - 9, file_name, breakpoint[ 'line' ] ) @@ -248,9 +246,9 @@ class CodeView( object ): 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 ) + - settings.Int( 'code_minwidth' ), + settings.Int( 'terminal_maxwidth' ) ), + settings.Int( 'terminal_minwidth' ) ) buffer_number = int( diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 13d4b1e..5a542c6 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -510,7 +510,7 @@ class DebugSession( object ): # TODO: The UI code is too scattered. Re-organise into a UI class that # just deals with these thigns like window layout and custmisattion. - vim.command( f'botright { settings.Int( "bottombar_height", 10 ) }new' ) + vim.command( f'botright { settings.Int( "bottombar_height" ) }new' ) win = vim.current.window self._logView = output.OutputView( win, self._api_prefix ) self._logView.AddLogFileView() @@ -525,7 +525,7 @@ class DebugSession( object ): # 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' ) + vim.command( f'botright { settings.Int( "bottombar_height" ) }new' ) self._outputView.UseWindow( vim.current.window ) vim.vars[ 'vimspector_session_windows' ][ 'output' ] = utils.WindowID( vim.current.window, @@ -568,7 +568,7 @@ class DebugSession( object ): # Call stack vim.command( - f'topleft vertical { settings.Int( "sidebar_width", 50 ) }new' ) + f'topleft vertical { settings.Int( "sidebar_width" ) }new' ) stack_trace_window = vim.current.window one_third = int( vim.eval( 'winheight( 0 )' ) ) / 3 self._stackTraceView = stack_trace.StackTraceView( self, @@ -594,7 +594,7 @@ class DebugSession( object ): # Output/logging vim.current.window = code_window - vim.command( f'rightbelow { settings.Int( "bottombar_height", 10 ) }new' ) + vim.command( f'rightbelow { settings.Int( "bottombar_height" ) }new' ) output_window = vim.current.window self._outputView = output.DAPOutputView( output_window, self._api_prefix ) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 5a4c60a..ac0c266 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -114,7 +114,7 @@ def RunInstaller( api_prefix, leave_open, *args, **kwargs ): _ResetInstaller() with utils.RestoreCurrentWindow(): - vim.command( f'botright { settings.Int( "bottombar_height", 10 ) }new' ) + vim.command( f'botright { settings.Int( "bottombar_height" ) }new' ) win = vim.current.window OUTPUT_VIEW = output.OutputView( win, api_prefix ) diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index d186e42..c80f682 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -18,22 +18,58 @@ import vim import builtins from vimspector import utils +DEFAULTS = { + # UI + 'bottombar_height': 10, + 'sidebar_width': 50, + 'code_minwidth': 82, + 'terminal_maxwidth': 80, + 'terminal_minwidth': 10, + + # Signs + 'sign_priority': { + 'vimspectorPC': 20, + 'vimspectorBP': 9, + 'vimspectorBPCond': 9, + 'vimspectorBPDisabled': 9, + }, + + # Installer + 'install_gadgets': [], +} + def Get( option: str, default=None, cls=str ): return cls( utils.GetVimValue( vim.vars, f'vimspector_{ option }', - default ) ) + DEFAULTS.get( option, cls() ) ) ) -def Int( option: str, default=0 ): - return Get( option, default=default, cls=builtins.int ) +def Int( option: str ): + return Get( option, cls=builtins.int ) -def List( option: str, default=[] ): - return utils.GetVimList( vim.vars, f'vimspector_{ option }', default ) +def List( option: str ): + return utils.GetVimList( vim.vars, + f'vimspector_{ option }', + DEFAULTS.get( option, [] ) ) -def Dict( option: str, default={} ): - return utils.GetVimValue( vim.vars, - f'vimspector_{ option }', - default ) +# FIXME: +# In Vim, we must use vim.Dictionary because this sorts out the annoying +# keys-as-bytes discrepancy, making things awkward. That said, we still have the +# problem where the _values_ are potentially bytes. It's very tempting to just +# make a deep copy to antive str type here. +# Of course in neovim, it's totally different and you actually get a dict type +# back (though for once, neovim is making life somewhat easier for a change). +DICT_TYPE = dict +if hasattr( vim, 'Dictionary' ): + DICT_TYPE = vim.Dictionary + + +def Dict( option: str ): + d = DICT_TYPE( DEFAULTS.get( option, {} ) ) + d.update( utils.GetVimValue( vim.vars, + f'vimspector_{ option }', + {} ) ) + return d diff --git a/python3/vimspector/signs.py b/python3/vimspector/signs.py index ac98983..a1faa92 100644 --- a/python3/vimspector/signs.py +++ b/python3/vimspector/signs.py @@ -22,8 +22,8 @@ def DefineSign( name, text, texthl, col = 'right' ): vim.command( f'sign define { name } text={ text } texthl={ texthl }' ) -def PlaceSign( sign_id, group, name, priority, file, line ): - priority = settings.Dict( 'sign_priority' ).get( name, priority ) +def PlaceSign( sign_id, group, name, file, line ): + priority = settings.Dict( 'sign_priority' )[ name ] cmd = ( f'sign place { sign_id } ' f'group={ group } ' diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index b850090..17c8166 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -749,7 +749,6 @@ def GetVimList( vim_dict, name, default=None ): return [ i.decode( 'utf-8' ) if isinstance( i, bytes ) else i for i in value ] - def GetVimspectorBase(): return GetVimValue( vim.vars, 'vimspector_base_dir', From dffd65f241ea727399248fedb80bee6b84ffa424 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 1 Sep 2020 16:30:16 +0100 Subject: [PATCH 325/609] Use CursorLine highlihgt to highlight current PC line --- README.md | 4 +- python3/vimspector/code.py | 3 +- python3/vimspector/settings.py | 2 +- python3/vimspector/signs.py | 8 +++- tests/breakpoints.test.vim | 86 ++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d354108..674bb0b 100644 --- a/README.md +++ b/README.md @@ -1370,7 +1370,7 @@ define them in your `vimrc`. | `vimspectorBP` | Line breakpoint | 9 | | `vimspectorBPCond` | Conditional line breakpiont | 9 | | `vimspectorBPDisabled` | Disabled breakpoint | 9 | -| `vimspectorPC` | Program counter (i.e. current line) | 20 | +| `vimspectorPC` | Program counter (i.e. current line) | 200 | The default symbols are the equivalent of something like the following: @@ -1378,7 +1378,7 @@ The default symbols are the equivalent of something like the following: sign define vimspectorBP text=\ ● texthl=WarningMsg sign define vimspectorBPCond text=\ ◆ texthl=WarningMsg sign define vimspectorBPDisabled text=\ ● texthl=LineNr -sign define vimspectorPC text=\ ▶ texthl=MatchParen +sign define vimspectorPC text=\ ▶ texthl=MatchParen linehl=CursorLine ``` If the signs don't display properly, your font probably doesn't contain these diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index bab847c..cb55af7 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -53,7 +53,8 @@ class CodeView( object ): if not signs.SignDefined( 'vimspectorPC' ): signs.DefineSign( 'vimspectorPC', text = '▶', - texthl = 'MatchParen' ) + texthl = 'MatchParen', + linehl = 'CursorLine' ) def SetCurrentFrame( self, frame ): diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index c80f682..04d9834 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -28,7 +28,7 @@ DEFAULTS = { # Signs 'sign_priority': { - 'vimspectorPC': 20, + 'vimspectorPC': 200, 'vimspectorBP': 9, 'vimspectorBPCond': 9, 'vimspectorBPDisabled': 9, diff --git a/python3/vimspector/signs.py b/python3/vimspector/signs.py index a1faa92..0c5e704 100644 --- a/python3/vimspector/signs.py +++ b/python3/vimspector/signs.py @@ -12,14 +12,18 @@ def SignDefined( name ): return False -def DefineSign( name, text, texthl, col = 'right' ): +def DefineSign( name, text, texthl, col = 'right', **kwargs ): if col == 'right': if int( utils.Call( 'strdisplaywidth', text ) ) < 2: text = ' ' + text text = text.replace( ' ', r'\ ' ) - vim.command( f'sign define { name } text={ text } texthl={ texthl }' ) + cmd = f'sign define { name } text={ text } texthl={ texthl }' + for key, value in kwargs.items(): + cmd += f' { key }={ value }' + + vim.command( cmd ) def PlaceSign( sign_id, group, name, file, line ): diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index ca57157..fed7f2a 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -633,6 +633,92 @@ function! Test_Custom_Breakpoint_Priority() %bwipeout! 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, + \ 'vimspectorPC', + \ 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, + \ 'vimspectorPC', + \ 200 ) + + + call vimspector#test#setup#Reset() + lcd - + %bwipeout! +endfunction + function! TearDown_Test_Custom_Breakpoint_Priority() unlet! g:vimspector_sign_priority endfunction From b22d8e570e50a6a45cfa835f7d93b65b019c66ed Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 1 Sep 2020 17:06:52 +0100 Subject: [PATCH 326/609] Try to determine what is flaking the tests; make sure to reset the priorities --- tests/breakpoints.test.vim | 4 +--- tests/tabpage.test.vim | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index fed7f2a..67bb47f 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -631,6 +631,7 @@ function! Test_Custom_Breakpoint_Priority() call vimspector#test#setup#Reset() lcd - %bwipeout! + unlet! g:vimspector_sign_priority endfunction function! Test_Custom_Breakpoint_Priority_Partial() @@ -717,8 +718,5 @@ function! Test_Custom_Breakpoint_Priority_Partial() call vimspector#test#setup#Reset() lcd - %bwipeout! -endfunction - -function! TearDown_Test_Custom_Breakpoint_Priority() unlet! g:vimspector_sign_priority endfunction diff --git a/tests/tabpage.test.vim b/tests/tabpage.test.vim index 6338d9c..6772c66 100644 --- a/tests/tabpage.test.vim +++ b/tests/tabpage.test.vim @@ -71,7 +71,12 @@ function! Test_All_Buffers_Deleted_NoHidden() let buffers_after = getbufinfo( opts ) - call assert_equal( len( buffers_before ), len( buffers_after ) ) + if assert_equal( len( buffers_before ), len( buffers_after ) ) + call assert_report( 'Expected ' + \ . string( buffers_before ) + \ . ' but found ' + \ . string( buffers_after ) ) + endif set hidden& lcd - @@ -101,7 +106,12 @@ function! Test_All_Buffers_Deleted_Hidden() let buffers_after = getbufinfo( opts ) - call assert_equal( len( buffers_before ), len( buffers_after ) ) + if assert_equal( len( buffers_before ), len( buffers_after ) ) + call assert_report( 'Expected ' + \ . string( buffers_before ) + \ . ' but found ' + \ . string( buffers_after ) ) + endif set hidden& lcd - @@ -115,7 +125,13 @@ function! Test_All_Buffers_Deleted_ToggleLog() VimspectorToggleLog VimspectorToggleLog let buffers_after = getbufinfo( #{ buflisted: 1 } ) - call assert_equal( len( buffers_before ), len( buffers_after ) ) + + if assert_equal( len( buffers_before ), len( buffers_after ) ) + call assert_report( 'Expected ' + \ . string( buffers_before ) + \ . ' but found ' + \ . string( buffers_after ) ) + endif call vimspector#test#setup#Reset() set hidden& @@ -142,7 +158,12 @@ function! Test_All_Buffers_Deleted_Installer() \ 120000 ) let buffers_after = getbufinfo( #{ buflisted: 1 } ) - call assert_equal( len( buffers_before ), len( buffers_after ) ) + if assert_equal( len( buffers_before ), len( buffers_after ) ) + call assert_report( 'Expected ' + \ . string( buffers_before ) + \ . ' but found ' + \ . string( buffers_after ) ) + endif call vimspector#test#setup#Reset() set hidden& From 733843a6d47ef6f17166d59e6f50d3abc66f9a9b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 3 Sep 2020 17:46:06 +0100 Subject: [PATCH 327/609] Support completion for console and watches. Add omnifunc for prompt buffers This synchronous completion can be used with any completion system including built-in CTRL-X CTRL-O. The filetype of the prompt buffers is set to VimspectorPrompt so that it can be identified by completion systems. For example, this works well with YCM: let g:ycm_semantic_triggers = { \ 'VimspectorPrompt': [ '.', '->', ':', '<' ] \ } --- autoload/vimspector.vim | 103 +++++++++++++++++++++++++++- python3/vimspector/debug_session.py | 3 +- python3/vimspector/output.py | 3 +- python3/vimspector/utils.py | 9 ++- python3/vimspector/variables.py | 3 +- 5 files changed, 113 insertions(+), 8 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 302b34d..df0b6a9 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -232,17 +232,114 @@ function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort return join( buffers, "\n" ) endfunction +py3 < s:latest_completion_request.start_pos + " fix up the text (insert anything that is already present in the line + " that would be erased by the fixed-up earlier start position) + " + " both start_pos and item.start are 1-based + let item.text = s:latest_completion_request.text[ + \ s:latest_completion_request.start_pos + pfxlen - 1 : + \ item.start + pfxlen - 1 ] . item.text + endif + + call add( items, { 'word': item.text, + \ 'abbr': item.label, + \ 'menu': get( item, 'type', '' ), + \ 'icase': 1, + \ } ) + endfor + let s:latest_completion_request = {} + return { 'words': items, 'refresh': 'always' } + endif +endfunction + +function! vimspector#OmniFuncWatch( find_start, query ) abort + return vimspector#CompleteFuncSync( 'Expression: ', a:find_start, a:query ) +endfunction + +function! vimspector#OmniFuncConsole( find_start, query ) abort + return vimspector#CompleteFuncSync( '> ', a:find_start, a:query ) +endfunction + function! vimspector#Install( bang, ... ) abort if !s:enabled return diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 5a542c6..6a40294 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -554,8 +554,7 @@ class DebugSession( object ): # TODO: # - start / length # - sortText - return [ i.get( 'text' ) or i[ 'label' ] - for i in response[ 'body' ][ 'targets' ] ] + return response[ 'body' ][ 'targets' ] def _SetUpUI( self ): diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 90df09d..71c81e9 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -192,7 +192,8 @@ class OutputView( object ): utils.SetUpPromptBuffer( tab_buffer.buf, name, '> ', - 'vimspector#EvaluateConsole' ) + 'vimspector#EvaluateConsole', + 'vimspector#OmniFuncConsole' ) else: utils.SetUpHiddenBuffer( tab_buffer.buf, name ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 17c8166..2874635 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -135,7 +135,7 @@ def SetUpHiddenBuffer( buf, name ): buf.name = name -def SetUpPromptBuffer( buf, name, prompt, callback ): +def SetUpPromptBuffer( buf, name, prompt, callback, omnifunc ): # This feature is _super_ new, so only enable when available if not Exists( '*prompt_setprompt' ): return SetUpHiddenBuffer( buf, name ) @@ -148,6 +148,7 @@ def SetUpPromptBuffer( buf, name, prompt, callback ): buf.options[ 'buflisted' ] = False buf.options[ 'bufhidden' ] = 'hide' buf.options[ 'textwidth' ] = 0 + buf.options[ 'omnifunc' ] = omnifunc buf.name = name vim.eval( "prompt_setprompt( {0}, '{1}' )".format( buf.number, @@ -156,6 +157,12 @@ def SetUpPromptBuffer( buf, name, prompt, callback ): buf.number, Escape( callback ) ) ) + # This serves a few purposes, mainly to ensure that completion systems have + # something to work with. In particular it makes YCM use its identifier engine + # and you can config ycm to trigger semantic (annoyingly, synchronously) using + # some let g:ycm_auto_trggier + Call( 'setbufvar', buf.number, '&filetype', 'VimspectorPrompt' ) + def SetUpUIWindow( win ): win.options[ 'wrap' ] = False diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index d82090a..5694e46 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -147,7 +147,8 @@ class VariablesView( object ): utils.SetUpPromptBuffer( self._watch.buf, 'vimspector.Watches', 'Expression: ', - 'vimspector#AddWatchPrompt' ) + 'vimspector#AddWatchPrompt', + 'vimspector#OmniFuncWatch' ) with utils.LetCurrentWindow( watches_win ): vim.command( 'nnoremap :call vimspector#ExpandVariable()' ) From 97bdb0d0cc35f7508fb3ce07a88f302b813a11e1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 3 Sep 2020 22:09:22 +0100 Subject: [PATCH 328/609] Show launch failure reason in the splash --- autoload/vimspector/internal/neopopup.vim | 47 +++++++++++++++-------- python3/vimspector/debug_session.py | 17 +++++++- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/autoload/vimspector/internal/neopopup.vim b/autoload/vimspector/internal/neopopup.vim index 148107c..fe5fe05 100644 --- a/autoload/vimspector/internal/neopopup.vim +++ b/autoload/vimspector/internal/neopopup.vim @@ -26,24 +26,39 @@ set cpoptions&vim let s:db = {} let s:next_id = 0 + +function! s:MessageToList( message ) abort + if type( a:message ) == type( [] ) + let message = a:message + else + let message = [ a:message ] + endif + return message +endfunction + +function! s:GetSplashConfig( message ) abort + let l = max( map( a:message, 'len( v:val )' ) ) + let h = len( a:message ) + + return { 'relative': 'editor', + \ 'width': l, + \ 'height': h, + \ 'col': ( &columns / 2 ) - ( l / 2 ), + \ 'row': ( &lines / 2 ) - h / 2, + \ 'anchor': 'NW', + \ 'style': 'minimal', + \ 'focusable': v:false, + \ } +endfunction + function! vimspector#internal#neopopup#DisplaySplash( message ) abort + let message = s:MessageToList( a:message ) let buf = nvim_create_buf(v:false, v:true) - call nvim_buf_set_lines(buf, 0, -1, v:true, [ a:message ] ) + call nvim_buf_set_lines(buf, 0, -1, v:true, message ) - let l = len( a:message ) - - let opts = { - \ 'relative': 'editor', - \ 'width': l, - \ 'height': 1, - \ 'col': ( &columns / 2 ) - ( l / 2 ), - \ 'row': &lines / 2, - \ 'anchor': 'NW', - \ 'style': 'minimal', - \ 'focusable': v:false, - \ } - let win = nvim_open_win(buf, 0, opts) + let win = nvim_open_win(buf, 0, s:GetSplashConfig( message ) ) call nvim_win_set_option(win, 'wrap', v:false) + call nvim_win_set_option(win, 'colorcolumn', '') let id = s:next_id let s:next_id += 1 @@ -53,7 +68,9 @@ endfunction function! vimspector#internal#neopopup#UpdateSplash( id, message ) abort let splash = s:db[ a:id ] - call nvim_buf_set_lines(splash.buf, 0, -1, v:true, [ a:message ] ) + let message = s:MessageToList( a:message ) + call nvim_buf_set_lines( splash.buf, 0, -1, v:true, message ) + call nvim_win_set_config( splash.win, s:GetSplashConfig( message ) ) return a:id endfunction diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 6a40294..3956d7f 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -940,13 +940,26 @@ class DebugSession( object ): if 'name' not in launch_config: launch_config[ 'name' ] = 'test' + def failure_handler( reason, msg ): + text = [ + 'Launch Failed', + '', + reason, + '', + 'Use :VimspectorReset to close' + ] + self._splash_screen = utils.DisplaySplash( self._api_prefix, + self._splash_screen, + text ) + + self._connection.DoRequest( lambda msg: self._OnLaunchComplete(), { 'command': launch_config[ 'request' ], 'arguments': launch_config - } - ) + }, + failure_handler ) def _OnLaunchComplete( self ): From 3aa949431e6e114e40d93f52db401cd0017e88fc Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 3 Sep 2020 22:09:59 +0100 Subject: [PATCH 329/609] Upgrade chrome debugger to 4.12.10 --- python3/vimspector/gadgets.py | 8 ++++---- support/test/chrome/run_server | 3 +++ support/test/chrome/www/js/test.js | 9 ++++++++- 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100755 support/test/chrome/run_server diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 7e1d619..21073f1 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -407,14 +407,14 @@ GADGETS = { 'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/' 'publishers/msjsdiag/vsextensions/' 'debugger-for-chrome/${version}/vspackage', - 'target': 'msjsdiag.debugger-for-chrome-4.12.0.vsix.gz', + 'target': 'msjsdiag.debugger-for-chrome-4.12.10.vsix.gz', 'format': 'zip.gz', }, 'all': { - 'version': '4.12.0', - 'file_name': 'msjsdiag.debugger-for-chrome-4.12.0.vsix', + 'version': '4.12.10', + 'file_name': 'msjsdiag.debugger-for-chrome-4.12.10.vsix', 'checksum': - '0df2fe96d059a002ebb0936b0003e6569e5a5c35260dc3791e1657d27d82ccf5' + '' }, 'adapters': { 'chrome': { diff --git a/support/test/chrome/run_server b/support/test/chrome/run_server new file mode 100755 index 0000000..9e5b569 --- /dev/null +++ b/support/test/chrome/run_server @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +php -S localhost:1234 -t www diff --git a/support/test/chrome/www/js/test.js b/support/test/chrome/www/js/test.js index 273804e..9c1c4a1 100644 --- a/support/test/chrome/www/js/test.js +++ b/support/test/chrome/www/js/test.js @@ -6,5 +6,12 @@ $( document ).ready( function() { return msg; }; - alert( 'test: ' + getMessage() ); + var obj = { + test: getMessage(), + toast: function() { return 'egg'; }, + spam: 'ham' + }; + + alert( 'test: ' + obj.test ); + alert( 'toast: ' + obj.toast() ); } ); From 2710ee2bfa2387c7f62f1fc8847a486827b07db8 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 3 Sep 2020 23:44:18 +0100 Subject: [PATCH 330/609] When the start parameter is missing, the behabiour is arbitary It seems that the behaviour of the start parameter being missing is server (or perhaps a specific client) dependent. The specification clearely says that it should be inserted at the column of the original request, but the servers clearly expect either for that column to be the beginning of an identifier or for the client to ignore the spec and request from that position anyway. Reading the VSCode code, we see that the 'word' before the cursor is guessed, and if only if BOTH 'start' AND 'length' are supplied, then they are used to determine where insertion starts, otherwise the current 'word' is used. Unclear what 'word' means in the specific contexts, but we're relying on iskeyword. --- autoload/vimspector.vim | 51 ++++++++++++++++++++++++------ support/test/node/simple/simple.js | 9 +++++- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index df0b6a9..f7486f6 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -19,6 +19,13 @@ let s:save_cpo = &cpoptions set cpoptions&vim " }}} +function! s:Debug( ... ) + py3 < len( a:query ) + call s:Debug( 'Rejecting %s, length is greater than %s', + \ item, + \ len( a:query ) ) + continue + endif + call add( items, { 'word': item.text, \ 'abbr': item.label, \ 'menu': get( item, 'type', '' ), @@ -328,6 +359,8 @@ function! vimspector#CompleteFuncSync( prompt, find_start, query ) abort \ } ) endfor let s:latest_completion_request = {} + + call s:Debug( 'Items: %s', items ) return { 'words': items, 'refresh': 'always' } endif endfunction diff --git a/support/test/node/simple/simple.js b/support/test/node/simple/simple.js index fc6e558..77b8e32 100644 --- a/support/test/node/simple/simple.js +++ b/support/test/node/simple/simple.js @@ -1,3 +1,10 @@ var msg = 'Hello, world!' -console.log( "OK stuff happened" ) +var obj = { + test: 'testing', + toast: function() { + return 'toasty' . this.test; + } +} + +console.log( "OK stuff happened " + obj.toast() ) From 3a79ce9ab7acb4fc37ff7ffa8b4bd3bf9e24771d Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 3 Sep 2020 23:51:44 +0100 Subject: [PATCH 331/609] Add a bash test script --- support/test/bash/test_script | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 support/test/bash/test_script diff --git a/support/test/bash/test_script b/support/test/bash/test_script new file mode 100644 index 0000000..4e27bd9 --- /dev/null +++ b/support/test/bash/test_script @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +function Test() { + echo $1 +} + +for i in "$@"; do + Test $i +done From 0867edd81c8ad95f64ca8c1f2427217f1a4e3db2 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 4 Sep 2020 00:37:39 +0100 Subject: [PATCH 332/609] FixUp: Correct return values from omnifunc --- autoload/vimspector.vim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index f7486f6..a25e13f 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -266,14 +266,17 @@ let s:latest_completion_request = {} function! vimspector#CompleteFuncSync( prompt, find_start, query ) abort if py3eval( 'not _vimspector_session' ) - return [] + if a:find_start + return -3 + endif + return v:none endif if a:find_start " We're busy if !empty( s:latest_completion_request ) - return -1 + return -3 endif let line = getline( line( '.' ) )[ len( a:prompt ) : ] From 710cffe2da0baac57c97fff727c2e0c21c5ffba5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 4 Sep 2020 00:48:40 +0100 Subject: [PATCH 333/609] update docs --- README.md | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 674bb0b..61422aa 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,11 @@ For a tutorial and usage overview, take a look at the * [Stepping](#stepping) * [Variables and scopes](#variables-and-scopes) * [Watches](#watches) + * [Watch autocompletion](#watch-autocompletion) * [Stack Traces](#stack-traces) * [Program Output](#program-output) * [Console](#console) + * [Console autocompletion](#console-autocompletion) * [Log View](#log-view) * [Closing debugger](#closing-debugger) * [Debug adapter configuration](#debug-adapter-configuration) @@ -77,7 +79,7 @@ For a tutorial and usage overview, take a look at the * [License](#license) * [Sponsorship](#sponsorship) - + @@ -109,10 +111,10 @@ And a couple of brief demos: - launch and attach - remote launch, remote attach - locals and globals display -- watch expressions +- watch expressions with autocompletion - call stack display and navigation - variable value display hover -- interactive debug console +- interactive debug console with autocompletion - launch debugee within Vim's embedded terminal - logging/stdout display - simple stable API for custom tooling (e.g. integrate with language server) @@ -742,6 +744,22 @@ to add a new watch expression. The watches are represented by the buffer `vimspector.StackTrace`. +### Watch autocompletion + +The watch prompt buffer has its `omnifunc` set to a function that will +calcualte completion for the current expression. This is trivailly used with +`` (see `:help ins-completion`), or integrated with your +favourite completion system. The filetype in the buffer is set to +`VimspectorPrompt`. + +For YouCompleteMe, the following config works well: + +```viml +let g:ycm_semantic_triggers = { + \ 'VimspectorPrompt': [ '.', '->', ':', '<' ] +} +``` + ## Stack Traces * In the threads window, use `` to expand/collapse. @@ -782,6 +800,22 @@ NOTE: See also [Watches](#watches) above. If the output window is closed, a new one can be opened with `:VimspectorShowOutput Console`. +### Console autocompletion + +The console prompt buffer has its `omnifunc` set to a function that will +calcualte completion for the current command/expression. This is trivailly used +with `` (see `:help ins-completion`), or integrated with your +favourite completion system. The filetype in the buffer is set to +`VimspectorPrompt`. + +For YouCompleteMe, the following config works well: + +```viml +let g:ycm_semantic_triggers = { + \ 'VimspectorPrompt': [ '.', '->', ':', '<' ] +} +``` + ### Log View The Vimspector log file contains a full trace of the communication between From 1ace7b648ead7d7890e5c0794c813bfd34ea08db Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 4 Sep 2020 00:49:37 +0100 Subject: [PATCH 334/609] FixUp: vint --- autoload/vimspector.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index a25e13f..b853681 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -19,7 +19,7 @@ let s:save_cpo = &cpoptions set cpoptions&vim " }}} -function! s:Debug( ... ) +function! s:Debug( ... ) abort py3 < Date: Fri, 4 Sep 2020 00:52:36 +0100 Subject: [PATCH 335/609] Remove slow debugging code --- autoload/vimspector.vim | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index b853681..1a141d6 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -324,13 +324,13 @@ function! vimspector#CompleteFuncSync( prompt, find_start, query ) abort let s:latest_completion_request.start_pos = start_pos let s:latest_completion_request.prompt = a:prompt - call s:Debug( 'FindStart: %s', { - \ 'line': line, - \ 'col': col, - \ 'prompt': len( a:prompt ), - \ 'start_pos': start_pos, - \ 'returning': ( start_pos + len( a:prompt ) ) - 1, - \ } ) + " call s:Debug( 'FindStart: %s', { + " \ 'line': line, + " \ 'col': col, + " \ 'prompt': len( a:prompt ), + " \ 'start_pos': start_pos, + " \ 'returning': ( start_pos + len( a:prompt ) ) - 1, + " \ } ) " start_pos is 1-based and the return of findstart is 0-based return ( start_pos + len( a:prompt ) ) - 1 @@ -349,9 +349,9 @@ function! vimspector#CompleteFuncSync( prompt, find_start, query ) abort endif if item.length > len( a:query ) - call s:Debug( 'Rejecting %s, length is greater than %s', - \ item, - \ len( a:query ) ) + " call s:Debug( 'Rejecting %s, length is greater than %s', + " \ item, + " \ len( a:query ) ) continue endif @@ -363,7 +363,7 @@ function! vimspector#CompleteFuncSync( prompt, find_start, query ) abort endfor let s:latest_completion_request = {} - call s:Debug( 'Items: %s', items ) + " call s:Debug( 'Items: %s', items ) return { 'words': items, 'refresh': 'always' } endif endfunction From 4e5011fe1b0a998e201ed4ea6e4d24572506020a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 4 Sep 2020 01:18:59 +0100 Subject: [PATCH 336/609] Make command line completion work too --- autoload/vimspector.vim | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 1a141d6..c1f3640 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -240,25 +240,38 @@ function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort endfunction py3 < Date: Fri, 4 Sep 2020 01:29:08 +0100 Subject: [PATCH 337/609] Fix flake when terminal takes a while to close --- tests/tabpage.test.vim | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/tests/tabpage.test.vim b/tests/tabpage.test.vim index 6772c66..1a5b1bc 100644 --- a/tests/tabpage.test.vim +++ b/tests/tabpage.test.vim @@ -71,12 +71,8 @@ function! Test_All_Buffers_Deleted_NoHidden() let buffers_after = getbufinfo( opts ) - if assert_equal( len( buffers_before ), len( buffers_after ) ) - call assert_report( 'Expected ' - \ . string( buffers_before ) - \ . ' but found ' - \ . string( buffers_after ) ) - endif + call WaitForAssert( {-> + \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) set hidden& lcd - @@ -106,12 +102,8 @@ function! Test_All_Buffers_Deleted_Hidden() let buffers_after = getbufinfo( opts ) - if assert_equal( len( buffers_before ), len( buffers_after ) ) - call assert_report( 'Expected ' - \ . string( buffers_before ) - \ . ' but found ' - \ . string( buffers_after ) ) - endif + call WaitForAssert( {-> + \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) set hidden& lcd - @@ -126,12 +118,8 @@ function! Test_All_Buffers_Deleted_ToggleLog() VimspectorToggleLog let buffers_after = getbufinfo( #{ buflisted: 1 } ) - if assert_equal( len( buffers_before ), len( buffers_after ) ) - call assert_report( 'Expected ' - \ . string( buffers_before ) - \ . ' but found ' - \ . string( buffers_after ) ) - endif + call WaitForAssert( {-> + \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) call vimspector#test#setup#Reset() set hidden& @@ -158,12 +146,8 @@ function! Test_All_Buffers_Deleted_Installer() \ 120000 ) let buffers_after = getbufinfo( #{ buflisted: 1 } ) - if assert_equal( len( buffers_before ), len( buffers_after ) ) - call assert_report( 'Expected ' - \ . string( buffers_before ) - \ . ' but found ' - \ . string( buffers_after ) ) - endif + call WaitForAssert( {-> + \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) call vimspector#test#setup#Reset() set hidden& From f1cc01a39950d18310bad2d14d7f8e7c52a1e7d0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 4 Sep 2020 12:57:13 +0100 Subject: [PATCH 338/609] FixUp: Actually wait for buffers to close --- tests/tabpage.test.vim | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/tabpage.test.vim b/tests/tabpage.test.vim index 1a5b1bc..6ae0dcf 100644 --- a/tests/tabpage.test.vim +++ b/tests/tabpage.test.vim @@ -69,10 +69,8 @@ function! Test_All_Buffers_Deleted_NoHidden() call vimspector#Reset() call vimspector#test#setup#WaitForReset() - let buffers_after = getbufinfo( opts ) - call WaitForAssert( {-> - \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) + \ assert_equal( len( buffers_before ), len( getbufinfo( opts ) ) ) } ) set hidden& lcd - @@ -100,10 +98,8 @@ function! Test_All_Buffers_Deleted_Hidden() call vimspector#Reset() call vimspector#test#setup#WaitForReset() - let buffers_after = getbufinfo( opts ) - call WaitForAssert( {-> - \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) + \ assert_equal( len( buffers_before ), len( getbufinfo( opts ) ) ) } ) set hidden& lcd - @@ -116,10 +112,10 @@ function! Test_All_Buffers_Deleted_ToggleLog() let buffers_before = getbufinfo( #{ buflisted: 1 } ) VimspectorToggleLog VimspectorToggleLog - let buffers_after = getbufinfo( #{ buflisted: 1 } ) call WaitForAssert( {-> - \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) + \ assert_equal( len( buffers_before ), + \ len( getbufinfo( #{ buflisted: 1 } ) ) ) } ) call vimspector#test#setup#Reset() set hidden& @@ -145,9 +141,9 @@ function! Test_All_Buffers_Deleted_Installer() \ { -> assert_equal( 1, g:vimspector_test_install_done ) }, \ 120000 ) - let buffers_after = getbufinfo( #{ buflisted: 1 } ) call WaitForAssert( {-> - \ assert_equal( len( buffers_before ), len( buffers_after ) ) } ) + \ assert_equal( len( buffers_before ), + \ len( getbufinfo( #{ buflisted: 1 } ) ) ) } ) call vimspector#test#setup#Reset() set hidden& From 3e452b73d1887635cd27c52a8859c71f614e5daa Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 4 Sep 2020 16:39:19 +0100 Subject: [PATCH 339/609] Migrate linting to GitHub actions --- .github/workflows/build.yaml | 32 ++++++++++++++++++++++++++++++++ azure-pipelines.yml | 24 ------------------------ 2 files changed, 32 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/build.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..b5ffad6 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,32 @@ +name: Build +on: + push: + branches: + - master + pull_request: + branches: + - master + +defaults: + run: + shell: bash + +jobs: + PythonLint: + runs-on: ubuntu-16.04 + container: 'puremourning/vimspector:test' + steps: + - uses: actions/checkout@v2 + - name: 'Insatll requirements' + run: pip3 install --user -r dev_requirements.txt + - name: 'Run flake8' + run: '$HOME/.local/bin/flake8 python3/ *.py' + VimscriptLint: + runs-on: 'ubuntu-16.04' + container: 'puremourning/vimspector:test' + steps: + - uses: actions/checkout@v2 + - name: 'Install requirements' + run: pip3 install --user -r dev_requirements.txt + - name: 'Run vint' + run: $HOME/.local/bin/vint autoload/ compiler/ plugin/ tests/ syntax/ diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7eb6946..e3f5f0d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -7,30 +7,6 @@ pr: stages: - stage: Build jobs: - - job: 'PythonLint' - displayName: "Python Lint" - pool: - vmImage: 'ubuntu-16.04' - container: 'puremourning/vimspector:test' - steps: - - bash: pip3 install -r dev_requirements.txt - displayName: "Install requirements" - - - bash: '$HOME/.local/bin/flake8 python3/ *.py' - displayName: "Run flake8" - - - job: 'Vimscript' - displayName: "Vimscript Lint" - pool: - vmImage: 'ubuntu-16.04' - container: 'puremourning/vimspector:test' - steps: - - bash: pip3 install -r dev_requirements.txt - displayName: "Install requirements" - - - bash: $HOME/.local/bin/vint autoload/ compiler/ plugin/ tests/ syntax/ - displayName: "Run vint" - - job: 'linux' pool: vmImage: 'ubuntu-16.04' From 8b8be9f5b59b3b86a10768ac9cd33a23e95d3d72 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 4 Sep 2020 17:51:01 +0100 Subject: [PATCH 340/609] Port builds to GitHub Actions too --- .github/workflows/build.yaml | 145 ++++++++++++++++++++++++++++++++--- azure-pipelines.yml | 128 ------------------------------- 2 files changed, 135 insertions(+), 138 deletions(-) delete mode 100644 azure-pipelines.yml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b5ffad6..febe8d7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,17 +16,142 @@ jobs: runs-on: ubuntu-16.04 container: 'puremourning/vimspector:test' steps: - - uses: actions/checkout@v2 - - name: 'Insatll requirements' - run: pip3 install --user -r dev_requirements.txt - - name: 'Run flake8' - run: '$HOME/.local/bin/flake8 python3/ *.py' + - uses: actions/checkout@v2 + - name: 'Insatll requirements' + run: pip3 install --user -r dev_requirements.txt + - name: 'Run flake8' + run: '$HOME/.local/bin/flake8 python3/ *.py' VimscriptLint: runs-on: 'ubuntu-16.04' container: 'puremourning/vimspector:test' steps: - - uses: actions/checkout@v2 - - name: 'Install requirements' - run: pip3 install --user -r dev_requirements.txt - - name: 'Run vint' - run: $HOME/.local/bin/vint autoload/ compiler/ plugin/ tests/ syntax/ + - uses: actions/checkout@v2 + - name: 'Install requirements' + run: pip3 install --user -r dev_requirements.txt + - name: 'Run vint' + run: $HOME/.local/bin/vint autoload/ compiler/ plugin/ tests/ syntax/ + + Linux: + runs-on: 'ubuntu-16.04' + container: + image: 'puremourning/vimspector:test' + options: --cap-add=SYS_PTRACE --security-opt seccomp=unconfined + steps: + - uses: actions/checkout@v2 + - run: | + eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) + go get -u github.com/go-delve/delve/cmd/dlv + name: 'Install Delve for Go' + + - uses: actions/cache@v2 + with: + key: v1-gadgets-${{ runner.os }}-${{ hashFiles( 'python3/vimspector/gadgets.py' ) }} + path: gadgets/linux/download + name: Cache gadgets + + - run: vim --version + name: 'Print vim version information' + + - run: | + eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) + export GOPATH=$HOME/go + ./run_tests --install --update --report messages --quiet + name: 'Run the tests' + env: + VIMSPECTOR_MIMODE: gdb + + - run: ./make_package linux ${{ github.run_id }} + name: 'Package' + + # TODO: test the tarball + + - name: "Upload package" + uses: actions/upload-artifact@v2 + with: + name: 'package-linux' + path: 'package/linux-${{ github.run_id }}.tar.gz' + + MacOS: + runs-on: 'macos-10.15' + steps: + - uses: actions/checkout@v2 + - run: | + brew update + brew doctor || true + for p in macvim tcl-tk llvm; do + brew install $p + brew outdated $p || brew upgrade $p + done + name: 'Install vim and deps' + + - run: go get -u github.com/go-delve/delve/cmd/dlv + name: 'Install Delve for Go' + + - uses: actions/cache@v2 + with: + key: v1-gadgets-${{ runner.os }}-${{ hashFiles( 'python3/vimspector/gadgets.py' ) }} + path: gadgets/macos/download + name: Cache gadgets + + - run: vim --version + name: 'Print vim version information' + + - run: ./run_tests --install --update --report messages --quiet + name: 'Run the tests' + env: + VIMSPECTOR_MIMODE: lldb + + - run: ./make_package macos ${{ github.run_id }} + name: 'Package' + + # TODO: test the tarball + + - name: "Upload package" + uses: actions/upload-artifact@v2 + with: + name: 'package-macos' + path: 'package/macos-${{ github.run_id }}.tar.gz' + + PublishRelease: + runs-on: 'ubuntu-16.04' + needs: + - Linux + - MacOS + if: github.ref == 'refs/heads/master' + steps: + - name: 'Download artifacts' + id: download_artifacts + uses: actions/download-artifact@v2 + + - name: 'Create Release' + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Build ${{ github.run_id }} + draft: false + prerelease: true + + - name: 'Upload Linux Package' + id: upload-release-asset-linux + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ${{ steps.download_artifacts.outputs.download-path }}/package-linux + asset_name: linux-${{ github.run_id }}.tar.gz + asset_content_type: application/gzip + + - name: 'Upload MacOS Package' + id: upload-release-asset-macos + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ${{ steps.download_artifacts.outputs.download-path }}/package-macos + asset_name: macos-${{ github.run_id }}.tar.gz + asset_content_type: application/gzip diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index e3f5f0d..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,128 +0,0 @@ -trigger: -- master - -pr: -- master - -stages: -- stage: Build - jobs: - - job: 'linux' - pool: - vmImage: 'ubuntu-16.04' - container: - image: 'puremourning/vimspector:test' - options: --cap-add=SYS_PTRACE --security-opt seccomp=unconfined - steps: - - bash: | - eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) - go get -u github.com/go-delve/delve/cmd/dlv - displayName: 'Install Delve for Go' - - - task: CacheBeta@0 - inputs: - key: v2 | gadgets | $(Agent.OS) | python3/vimspector/gadgets.py - path: gadgets/linux/download - displayName: Cache gadgets - - - bash: vim --version - displayName: 'Print vim version information' - - - bash: | - eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) - export GOPATH=$HOME/go - ./run_tests --install --update --report messages --quiet - displayName: 'Run the tests' - env: - VIMSPECTOR_MIMODE: gdb - - - bash: ./make_package linux $(Build.SourceVersion) - displayName: 'Package' - - # TODO: test the tarball - - - task: PublishPipelineArtifact@0 - inputs: - artifactName: 'package-linux' - targetPath: 'package/linux-$(Build.SourceVersion).tar.gz' - - - job: 'macos' - pool: - vmImage: 'macOS-10.14' - steps: - # Node.js tool installer - # Finds or downloads and caches the specified version spec of Node.js and adds it to the PATH - - task: NodeTool@0 - inputs: - versionSpec: '>=12.0.0' - - - bash: | - brew update - brew doctor - for p in macvim tcl-tk llvm; do - brew install $p - brew outdated $p || brew upgrade $p - done - displayName: 'Install vim and deps' - - - bash: go get -u github.com/go-delve/delve/cmd/dlv - displayName: 'Install Delve for Go' - - - task: CacheBeta@0 - inputs: - key: v2 | gadgets | $(Agent.OS) | python3/vimspector/gadgets.py - path: gadgets/macos/download - displayName: Cache gadgets - - - bash: vim --version - displayName: 'Print vim version information' - - - bash: ./run_tests --install --update --report messages --quiet - displayName: 'Run the tests' - env: - VIMSPECTOR_MIMODE: lldb - - - bash: ./make_package macos $(Build.SourceVersion) - displayName: 'Package' - - # TODO: test the tarball - - - task: PublishPipelineArtifact@0 - inputs: - artifactName: 'package-macos' - targetPath: 'package/macos-$(Build.SourceVersion).tar.gz' - -- stage: "Publish" - dependsOn: - - "Build" - condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) - jobs: - - job: 'Publish' - pool: - vmImage: 'ubuntu-16.04' - steps: - - task: DownloadPipelineArtifact@0 - inputs: - artifactName: 'package-linux' - targetPath: $(Build.ArtifactStagingDirectory) - - task: DownloadPipelineArtifact@0 - inputs: - artifactName: 'package-macos' - targetPath: $(Build.ArtifactStagingDirectory) - - task: GitHubRelease@0 - inputs: - gitHubConnection: puremourning - repositoryName: '$(Build.Repository.Name)' - action: 'create' # Options: create, edit, delete - target: '$(Build.SourceVersion)' # Required when action == Create || Action == Edit - tagSource: 'manual' # Required when action == Create# Options: auto, manual - tag: "$(Build.BuildId)" - #title: # Optional - #releaseNotesSource: 'file' # Optional. Options: file, input - #releaseNotesFile: # Optional - #releaseNotes: # Optional - #assets: '$(Build.ArtifactStagingDirectory)/*' # Optional - #assetUploadMode: 'delete' # Optional. Options: delete, replace - #isDraft: false # Optional - isPreRelease: true # Optional - #addChangeLog: true # Optional From 99d20bb2773f6986b00dd51fbd5e68520308ff07 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 11 Sep 2020 15:01:37 +0100 Subject: [PATCH 341/609] Update mergify config for GHA --- .mergify.yml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.mergify.yml b/.mergify.yml index a5fcafb..09d3d83 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -3,10 +3,14 @@ pull_request_rules: conditions: - author=puremourning - base=master + # Review - status-success=code-review/reviewable - - status-success=puremourning.vimspector # Azure pipeline - - "#changes-requested-reviews-by=0" + # CI https://doc.mergify.io/conditions.html#github-actions + - status-success=PythonLint + - status-success=VimscriptLint + - status-success=MacOS + actions: &merge-actions merge: @@ -18,12 +22,16 @@ pull_request_rules: conditions: - author!=puremourning - base=master + # Review - status-success=code-review/reviewable - - status-success=puremourning.vimspector # Azure pipeline - - approved-reviews-by=puremourning - - "#approved-reviews-by>=1" - "#changes-requested-reviews-by=0" + - approved-reviews-by=puremourning + # CI https://doc.mergify.io/conditions.html#github-actions + - status-success=PythonLint + - status-success=VimscriptLint + - status-success=Linux + - status-success=MacOS actions: <<: *merge-actions comment: From db4e60aca5f990b0c6f8062c291669ebc5ace471 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 3 Sep 2020 14:28:25 +0100 Subject: [PATCH 342/609] Add a manual test for the calculated variables --- tests/testdata/cpp/simple/.vimspector.json | 24 ++++++++++++++++++++++ tests/testdata/cpp/simple/Makefile | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/testdata/cpp/simple/.vimspector.json b/tests/testdata/cpp/simple/.vimspector.json index 93a7a60..57eefad 100644 --- a/tests/testdata/cpp/simple/.vimspector.json +++ b/tests/testdata/cpp/simple/.vimspector.json @@ -1,4 +1,5 @@ { + "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json", "configurations": { "run-to-entry": { "adapter": "vscode-cpptools", @@ -44,6 +45,29 @@ "swift_throw": "" } } + }, + "calculate-some-variable": { + "adapter": "vscode-cpptools", + "variables": { + "SIMPLE": "This is some text containing: $HOME", + "CALCULATED_LIST": { + "shell": [ "uuidgen" ] + }, + "CALCULATED_STR": { + "shell": [ "uuidgen" ] + } + }, + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/${fileBasenameNoExtension}", + "MImode": "${VIMSPECTOR_MIMODE}", + "externalConsole": false, + "args": [ + "CALCULATED_LIST", "${CALCULATED_LIST}", + "SIMPLE", "${SIMPLE}", + "CALCULATED_STR", "${CALCULATED_STR}" + ] + } } } } diff --git a/tests/testdata/cpp/simple/Makefile b/tests/testdata/cpp/simple/Makefile index 0e8d8c8..85690c5 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 struct +TARGETS=simple variables struct printer all: $(TARGETS) From 778745b5c248e2c1a15ae49d3902c03f16f84c48 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 5 Sep 2020 13:44:15 +0100 Subject: [PATCH 343/609] Add a test for ruby --- support/test/ruby/.bundle/config | 2 ++ support/test/ruby/.gitignore | 2 ++ support/test/ruby/.vimspector.json | 15 +++++++++++++++ support/test/ruby/Gemfile | 11 +++++++++++ support/test/ruby/test.rb | 1 + 5 files changed, 31 insertions(+) create mode 100644 support/test/ruby/.bundle/config create mode 100644 support/test/ruby/.gitignore create mode 100644 support/test/ruby/.vimspector.json create mode 100644 support/test/ruby/Gemfile create mode 100644 support/test/ruby/test.rb diff --git a/support/test/ruby/.bundle/config b/support/test/ruby/.bundle/config new file mode 100644 index 0000000..2369228 --- /dev/null +++ b/support/test/ruby/.bundle/config @@ -0,0 +1,2 @@ +--- +BUNDLE_PATH: "vendor/bundle" diff --git a/support/test/ruby/.gitignore b/support/test/ruby/.gitignore new file mode 100644 index 0000000..fc0674a --- /dev/null +++ b/support/test/ruby/.gitignore @@ -0,0 +1,2 @@ +Gemfile.lock +vendor/ diff --git a/support/test/ruby/.vimspector.json b/support/test/ruby/.vimspector.json new file mode 100644 index 0000000..418eb02 --- /dev/null +++ b/support/test/ruby/.vimspector.json @@ -0,0 +1,15 @@ +{ + "configurations": { + "launch current file": { + "adapter": "cust_vscode-ruby", + "configuration": { + "request": "launch", + "program": "${file}", + "args": [ "*${args}" ], + "useBundler": true, + "trace": true, + "showDebuggerOutput": true + } + } + } +} diff --git a/support/test/ruby/Gemfile b/support/test/ruby/Gemfile new file mode 100644 index 0000000..d4580eb --- /dev/null +++ b/support/test/ruby/Gemfile @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } + +# gem "rails" + +gem "ruby-debug-ide", "~> 0.7.2" + +gem "debase", "~> 0.2.4" diff --git a/support/test/ruby/test.rb b/support/test/ruby/test.rb new file mode 100644 index 0000000..5209e0f --- /dev/null +++ b/support/test/ruby/test.rb @@ -0,0 +1 @@ +print 'hello' 'world' From 853af58124623c7bb5ef5bf4a1b920d5ba39a322 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 11 Sep 2020 15:43:41 +0100 Subject: [PATCH 344/609] Update badge and upload logs if tests fail --- .github/workflows/build.yaml | 16 ++++++++++++++++ README.md | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index febe8d7..cddbdc0 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -57,9 +57,17 @@ jobs: export GOPATH=$HOME/go ./run_tests --install --update --report messages --quiet name: 'Run the tests' + id: run_tests env: VIMSPECTOR_MIMODE: gdb + - name: "Upload test logs" + uses: actions/upload-artifact@v2 + if: steps.run_tests.outcome == 'failure' + with: + name: 'test-logs-${{ runner.os }}' + path: 'tests/logs' + - run: ./make_package linux ${{ github.run_id }} name: 'Package' @@ -98,9 +106,17 @@ jobs: - run: ./run_tests --install --update --report messages --quiet name: 'Run the tests' + id: run_tests env: VIMSPECTOR_MIMODE: lldb + - name: "Upload test logs" + uses: actions/upload-artifact@v2 + if: steps.run_tests.outcome == 'failure' + with: + name: 'test-logs-${{ runner.os }}' + path: 'tests/logs' + - run: ./make_package macos ${{ github.run_id }} name: 'Package' diff --git a/README.md b/README.md index 61422aa..251e923 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ For a tutorial and usage overview, take a look at the [Vimspector website][website] -[![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) +![Build](https://github.com/puremourning/vimspector/workflows/Build/badge.svg?branch=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) From 277994e27c8036799a7ea599d00489bbe7608351 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 11 Sep 2020 15:56:04 +0100 Subject: [PATCH 345/609] Add missing test file --- tests/testdata/cpp/simple/.gitignore | 1 + tests/testdata/cpp/simple/printer.cpp | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/testdata/cpp/simple/printer.cpp diff --git a/tests/testdata/cpp/simple/.gitignore b/tests/testdata/cpp/simple/.gitignore index fec4c0b..d3a7590 100644 --- a/tests/testdata/cpp/simple/.gitignore +++ b/tests/testdata/cpp/simple/.gitignore @@ -1,3 +1,4 @@ simple variables struct +printer diff --git a/tests/testdata/cpp/simple/printer.cpp b/tests/testdata/cpp/simple/printer.cpp new file mode 100644 index 0000000..1e4a0b7 --- /dev/null +++ b/tests/testdata/cpp/simple/printer.cpp @@ -0,0 +1,21 @@ +#include +#include + +int main( int argc, char** argv ) +{ + if ( ( argc - 1 ) % 2 ) + { + std::cerr << "Unbalanced arguments\n"; + return 1; + } + + for ( int i=1; i < argc; i += 2 ) + { + std::cout << argv[ i ] + << ": " + << argv[ i + 1 ] + << '\n'; + } + + return 0; +} From 2e3bbef6df9f20d4db6667617b119e975aa1bbc4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 11 Sep 2020 16:14:26 +0100 Subject: [PATCH 346/609] The steps context doesn't seem to work --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index cddbdc0..6f07c9a 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -63,7 +63,7 @@ jobs: - name: "Upload test logs" uses: actions/upload-artifact@v2 - if: steps.run_tests.outcome == 'failure' + if: failure() with: name: 'test-logs-${{ runner.os }}' path: 'tests/logs' @@ -112,7 +112,7 @@ jobs: - name: "Upload test logs" uses: actions/upload-artifact@v2 - if: steps.run_tests.outcome == 'failure' + if: failure() with: name: 'test-logs-${{ runner.os }}' path: 'tests/logs' From a037e4d31a70e269b390414da0057d862c1bf9b4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 11 Sep 2020 16:25:00 +0100 Subject: [PATCH 347/609] We need to globstar the test log dirs --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6f07c9a..91acfc7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -66,7 +66,7 @@ jobs: if: failure() with: name: 'test-logs-${{ runner.os }}' - path: 'tests/logs' + path: 'tests/logs/**' - run: ./make_package linux ${{ github.run_id }} name: 'Package' @@ -115,7 +115,7 @@ jobs: if: failure() with: name: 'test-logs-${{ runner.os }}' - path: 'tests/logs' + path: 'tests/logs/**' - run: ./make_package macos ${{ github.run_id }} name: 'Package' From 1db1bd6d5bd9b514c0a1dc948a0de9886b0e1637 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 11 Sep 2020 16:44:57 +0100 Subject: [PATCH 348/609] Need to specify the exact asset path --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 91acfc7..5e4a806 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -157,8 +157,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ${{ steps.download_artifacts.outputs.download-path }}/package-linux - asset_name: linux-${{ github.run_id }}.tar.gz + asset_path: ${{ steps.download_artifacts.outputs.download-path }}/package-linux/linux-${{ github.run_id }}.tar.gz + asset_name: vimspector-linux-${{ github.run_id }}.tar.gz asset_content_type: application/gzip - name: 'Upload MacOS Package' @@ -168,6 +168,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ${{ steps.download_artifacts.outputs.download-path }}/package-macos - asset_name: macos-${{ github.run_id }}.tar.gz + asset_path: ${{ steps.download_artifacts.outputs.download-path }}/package-macos/macos-${{ github.run_id }}.tar.gz + asset_name: vimspector-macos-${{ github.run_id }}.tar.gz asset_content_type: application/gzip From e76d6a5c97bde5959c066d07152ad0fb37f49c3a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 11 Sep 2020 16:50:10 +0100 Subject: [PATCH 349/609] Use a sensible tag --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5e4a806..3a7ac8d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -145,7 +145,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: ${{ github.ref }} + tag_name: ${{ github.run_id }} release_name: Build ${{ github.run_id }} draft: false prerelease: true From 173f2d6f375bc58bffc6b871c5786ba183af09ee Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 11 Sep 2020 17:06:05 +0100 Subject: [PATCH 350/609] CI: maybe this is the pattern we need ? --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 3a7ac8d..75da533 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -66,7 +66,7 @@ jobs: if: failure() with: name: 'test-logs-${{ runner.os }}' - path: 'tests/logs/**' + path: 'tests/logs/**/*' - run: ./make_package linux ${{ github.run_id }} name: 'Package' @@ -115,7 +115,7 @@ jobs: if: failure() with: name: 'test-logs-${{ runner.os }}' - path: 'tests/logs/**' + path: 'tests/logs/**/*' - run: ./make_package macos ${{ github.run_id }} name: 'Package' From 4424969028ce4b8a869650b5deae1aa68f4a7706 Mon Sep 17 00:00:00 2001 From: "Ben.Jackson" Date: Fri, 18 Sep 2020 18:27:44 +0100 Subject: [PATCH 351/609] Fix installation issues on windows - find npm.cmd --- .vimspector.json | 9 ++++ python3/vimspector/installer.py | 78 ++++++++++++++++++++++++--------- 2 files changed, 66 insertions(+), 21 deletions(-) diff --git a/.vimspector.json b/.vimspector.json index 4a9889e..7e50c43 100644 --- a/.vimspector.json +++ b/.vimspector.json @@ -9,6 +9,15 @@ "configuration": { "request": "attach" } + }, + "Python: Run current script": { + "adapter": "debugpy", + "configuration": { + "request": "launch", + "program": "${file}", + "args": [ "*${args:--update-gadget-config}" ], + "justMyCode#json": "${justMyCode:true}" + } } } } diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index ac0c266..36f7bec 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -57,32 +57,32 @@ def Print( *args, **kwargs ): print( *args, **kwargs ) -def CheckCall( *args, **kwargs ): - if options.quiet: - try: - subprocess.check_output( *args, stderr=subprocess.STDOUT, **kwargs ) - except subprocess.CalledProcessError as e: - print( e.output.decode( 'utf-8' ) ) - raise - else: - subprocess.check_call( *args, **kwargs ) +class MissingExecutable( Exception ): + pass -def PathToAnyWorkingPython3(): - # We can't rely on sys.executable because it's usually 'vim' (fixme, not with - # neovim?) +def GetPATHAsList(): paths = os.environ[ 'PATH' ].split( os.pathsep ) - if install.GetOS() == 'windows': paths.insert( 0, os.getcwd() ) - candidates = [ os.path.join( sys.exec_prefix, 'python.exe' ), - 'python.exe' ] - else: - candidates = [ os.path.join( sys.exec_prefix, 'bin', 'python3' ), - 'python3', - 'python' ] + return paths + + +def FindExecutable( executable: str, paths=None ): + if not paths: + paths = GetPATHAsList() + + if install.GetOS() == 'windows': + extensions = [ '.exe', '.bat', '.cmd' ] + else: + extensions = [ '' ] + + for extension in extensions: + if executable.endswith( extension ): + candidate = executable + else: + candidate = executable + extension - for candidate in candidates: for path in paths: filename = os.path.abspath( os.path.join( path, candidate ) ) if not os.path.isfile( filename ): @@ -92,6 +92,42 @@ def PathToAnyWorkingPython3(): return filename + raise MissingExecutable( f"Unable to find executable { executable } in path" ) + + + +def CheckCall( cmd, *args, **kwargs ): + cmd[ 0 ] = FindExecutable( cmd[ 0 ] ) + + if options.quiet: + try: + subprocess.check_output( cmd, *args, stderr=subprocess.STDOUT, **kwargs ) + except subprocess.CalledProcessError as e: + print( e.output.decode( 'utf-8' ) ) + raise + else: + subprocess.check_call( cmd, *args, **kwargs ) + + +def PathToAnyWorkingPython3(): + # We can't rely on sys.executable because it's usually 'vim' (fixme, not with + # neovim?) + paths = GetPATHAsList() + + if install.GetOS() == 'windows': + candidates = [ os.path.join( sys.exec_prefix, 'python.exe' ), + 'python.exe' ] + else: + candidates = [ os.path.join( sys.exec_prefix, 'bin', 'python3' ), + 'python3', + 'python' ] + + for candidate in candidates: + try: + return FindExecutable( candidate, paths=paths ) + except MissingExecutable: + pass + raise RuntimeError( "Unable to find a working python3" ) @@ -348,7 +384,7 @@ def InstallDebugpy( name, root, gadget ): def InstallTclProDebug( name, root, gadget ): - configure = [ './configure' ] + configure = [ 'sh', './configure' ] if install.GetOS() == 'macos': # Apple removed the headers from system frameworks because they are From 9e2526c4a4fa72300e01ccab715ea04a6c5832da Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 19 Sep 2020 11:17:55 +0100 Subject: [PATCH 352/609] Fix logs in the wrong place when runnning tests withotu basedir --- .github/workflows/build.yaml | 2 +- run_tests | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 75da533..5d0315d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -115,7 +115,7 @@ jobs: if: failure() with: name: 'test-logs-${{ runner.os }}' - path: 'tests/logs/**/*' + path: 'tests/logs' - run: ./make_package macos ${{ github.run_id }} name: 'Package' diff --git a/run_tests b/run_tests index af0a598..39dc7ec 100755 --- a/run_tests +++ b/run_tests @@ -1,6 +1,14 @@ #!/usr/bin/env bash -BASEDIR=$(dirname $0) +SetBaseDir() { + BASEDIR=$1 + if [[ ! $BASEDIR = /* ]]; then + # Relative + BASEDIR=$(pwd)/${BASEDIR} + fi +} + +SetBaseDir $(dirname $0) INSTALL=0 UPDATE=0 INSTALLER_ARGS='' @@ -15,12 +23,8 @@ while [ -n "$1" ]; do case "$1" in "--basedir") shift - BASEDIR=$1 + SetBaseDir $1 shift - if [[ ! $BASEDIR = /* ]]; then - # Relative - BASEDIR=$(pwd)/${BASEDIR} - fi BASEDIR_CMD="let g:vimspector_base_dir='${BASEDIR}'" ;; "--install") @@ -131,6 +135,7 @@ echo "Testing with:" echo " * VIMSPECTOR_MIMODE=$VIMSPECTOR_MIMODE" echo " * RUN_VIM=$RUN_VIM" echo " * RUN_TEST=$RUN_TEST" +echo " * BASEDIR=$BASEDIR" echo " * BASEDIR_CMD=$BASEDIR_CMD" From 4ed915828285c067b7f4a35fecc7e0ed56fc9be8 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 19 Sep 2020 11:18:34 +0100 Subject: [PATCH 353/609] Accomodate flaky tests that I can't diagnose --- tests/lib/plugin/shared.vim | 4 ++++ tests/lib/run_test.vim | 32 ++++++++++++++++++++++++++------ tests/tabpage.test.vim | 4 ++++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/tests/lib/plugin/shared.vim b/tests/lib/plugin/shared.vim index 2d656d7..d826b1b 100644 --- a/tests/lib/plugin/shared.vim +++ b/tests/lib/plugin/shared.vim @@ -87,3 +87,7 @@ func s:WaitForCommon(expr, assert, timeout) return -1 " timed out endfunc + +function! ThisTestIsFlaky() + let g:test_is_flaky = v:true +endfunction diff --git a/tests/lib/run_test.vim b/tests/lib/run_test.vim index fca26a6..43e507d 100644 --- a/tests/lib/run_test.vim +++ b/tests/lib/run_test.vim @@ -344,12 +344,6 @@ catch \ ' @ ' . v:throwpoint) endtry -" Names of flaky tests. -let s:flaky_tests = [] - -" Pattern indicating a common flaky test failure. -let s:flaky_errors_re = '__does_not_match__' - " Locate Test_ functions and execute them. redir @q silent function /^Test_ @@ -366,7 +360,33 @@ for s:test in sort(s:tests) " Silence, please! set belloff=all + " A test can set g:test_is_flaky to retry running the test. + let g:test_is_flaky = 0 + call RunTheTest(s:test) + + " Repeat a flaky test. Give up when: + " - $TEST_NO_RETRY is not empty + " - it fails five times (with a different message) + if len(v:errors) > 0 + \ && $TEST_NO_RETRY == '' + \ && g:test_is_flaky + for retryu in range( 5 ) + call add( s:messages, 'Found errors in ' . s:test . '. Retrying.' ) + call extend( s:messages, v:errors ) + + sleep 2 + + let v:errors = [] + call RunTheTest(s:test) + + if len(v:errors) == 0 + " Test passed on rerun. + break + endif + endfor + endif + call AfterTheTest() endfor diff --git a/tests/tabpage.test.vim b/tests/tabpage.test.vim index 6ae0dcf..92b57e8 100644 --- a/tests/tabpage.test.vim +++ b/tests/tabpage.test.vim @@ -50,6 +50,8 @@ function! Test_Step_With_Different_Tabpage() endfunction function! Test_All_Buffers_Deleted_NoHidden() + call ThisTestIsFlaky() + set nohidden lcd testdata/cpp/simple edit simple.cpp @@ -79,6 +81,8 @@ function! Test_All_Buffers_Deleted_NoHidden() endfunction function! Test_All_Buffers_Deleted_Hidden() + call ThisTestIsFlaky() + set hidden lcd testdata/cpp/simple edit simple.cpp From ce4f341feb43711144625d27563515b76c5258ea Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 19 Sep 2020 11:25:53 +0100 Subject: [PATCH 354/609] Correct some typos --- tests/lib/run_test.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lib/run_test.vim b/tests/lib/run_test.vim index 43e507d..a8c7bcd 100644 --- a/tests/lib/run_test.vim +++ b/tests/lib/run_test.vim @@ -367,11 +367,11 @@ for s:test in sort(s:tests) " Repeat a flaky test. Give up when: " - $TEST_NO_RETRY is not empty - " - it fails five times (with a different message) + " - it fails five times if len(v:errors) > 0 \ && $TEST_NO_RETRY == '' \ && g:test_is_flaky - for retryu in range( 5 ) + for retry in range( 5 ) call add( s:messages, 'Found errors in ' . s:test . '. Retrying.' ) call extend( s:messages, v:errors ) From 26277cefbd87d982cd9c263ac8459613afaab3b8 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 19 Sep 2020 11:36:28 +0100 Subject: [PATCH 355/609] Another flaky test --- tests/breakpoints.test.vim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index 67bb47f..2a8c053 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -394,6 +394,8 @@ function! SetUp_Test_Conditional_Line_Breakpoint_Hit() 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 ] ) From 2ad1a3e502f18948eaad6bf262dbf21faacc01b5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 19 Sep 2020 15:11:09 +0100 Subject: [PATCH 356/609] Display a different sign when there's a breakpoint on the PC line Vim only renders a single sign-per-line. If we have the PC _and_ a breakpoint, we should make that clear. Do this using a vimspectorPCBP sign which combines both vimspectorPC and vimspectorBP (sort of). We can't (unfortuantely) render the breakpoint blob in a different colour, but it's at least obvious when we toggle on the PC line. --- README.md | 11 +-- python3/vimspector/code.py | 70 +++++++++++++++----- python3/vimspector/settings.py | 1 + tests/breakpoints.test.vim | 9 +-- tests/lib/autoload/vimspector/test/signs.vim | 2 +- 5 files changed, 67 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 251e923..ae583ab 100644 --- a/README.md +++ b/README.md @@ -1405,6 +1405,7 @@ define them in your `vimrc`. | `vimspectorBPCond` | Conditional line breakpiont | 9 | | `vimspectorBPDisabled` | Disabled breakpoint | 9 | | `vimspectorPC` | Program counter (i.e. current line) | 200 | +| `vimspectorPCBP` | Program counter and breakpoint | 200 | The default symbols are the equivalent of something like the following: @@ -1413,6 +1414,7 @@ sign define vimspectorBP text=\ ● texthl=WarningMsg sign define vimspectorBPCond text=\ ◆ texthl=WarningMsg sign define vimspectorBPDisabled text=\ ● texthl=LineNr sign define vimspectorPC text=\ ▶ texthl=MatchParen linehl=CursorLine +sign define vimspectorPCBP text=●▶ texthl=MatchParen linehl=CursorLine ``` If the signs don't display properly, your font probably doesn't contain these @@ -1420,10 +1422,11 @@ glyphs. You can easily change them by deifining the sign in your vimrc. For example, you could put this in your `vimrc` to use some simple ASCII symbols: ```viml -sign define vimspectorBP text==> texthl=WarningMsg -sign define vimspectorBPCond text=?> texthl=WarningMsg -sign define vimspectorBPDisabled text=!> texthl=LineNr -sign define vimspectorPC text=-> texthl=MatchParen +sign define vimspectorBP text=o texthl=WarningMsg +sign define vimspectorBPCond text=o? texthl=WarningMsg +sign define vimspectorBPDisabled text=o! texthl=LineNr +sign define vimspectorPC text=\ > texthl=MatchParen +sign define vimspectorPCBP text=o> texthl=MatchParen ``` ## Sign priority diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index cb55af7..c50d602 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -39,6 +39,7 @@ class CodeView( object ): 'vimspectorPC': None, 'breakpoints': [] } + self._current_frame = None with utils.LetCurrentWindow( self._window ): vim.command( 'nnoremenu WinBar.■\\ Stop :call vimspector#Stop()' ) @@ -55,6 +56,50 @@ class CodeView( object ): text = '▶', texthl = 'MatchParen', linehl = 'CursorLine' ) + if not signs.SignDefined( 'vimspectorPCBP' ): + signs.DefineSign( 'vimspectorPCBP', + text = '●▶', + texthl = 'MatchParen', + linehl = 'CursorLine' ) + + + def _UndisplayPC( self ): + if self._signs[ 'vimspectorPC' ]: + signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' ) + self._signs[ 'vimspectorPC' ] = None + + + def _DisplayPC( self ): + frame = self._current_frame + if not frame: + return + + self._UndisplayPC() + + # FIXME: Do we relly need to keep using up IDs ? + self._signs[ 'vimspectorPC' ] = self._next_sign_id + self._next_sign_id += 1 + + sign = 'vimspectorPC' + # If there's also a breakpoint on this line, use vimspectorPCBP + for bp in self._breakpoints.get( frame[ 'source' ][ 'path' ], [] ): + if 'line' not in bp: + continue + + if bp[ 'line' ] == frame[ 'line' ]: + sign = 'vimspectorPCBP' + break + + try: + signs.PlaceSign( self._signs[ 'vimspectorPC' ], + 'VimspectorCode', + sign, + frame[ 'source' ][ 'path' ], + frame[ 'line' ] ) + except vim.error as e: + # Ignore 'invalid buffer name' + if 'E158' not in str( e ): + raise def SetCurrentFrame( self, frame ): @@ -62,29 +107,16 @@ class CodeView( object ): otherwise. False means either the frame is junk, we couldn't find the file (or don't have the data) or the code window no longer exits.""" - if self._signs[ 'vimspectorPC' ]: - signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' ) - self._signs[ 'vimspectorPC' ] = None - if not frame or not frame.get( 'source' ): + self._UndisplayPC() return False if 'path' not in frame[ 'source' ]: + self._UndisplayPC() return False - self._signs[ 'vimspectorPC' ] = self._next_sign_id - self._next_sign_id += 1 - - try: - signs.PlaceSign( self._signs[ 'vimspectorPC' ], - 'VimspectorCode', - 'vimspectorPC', - frame[ 'source' ][ 'path' ], - frame[ 'line' ] ) - except vim.error as e: - # Ignore 'invalid buffer name' - if 'E158' not in str( e ): - raise + self._current_frame = frame + self._DisplayPC() if not self._window.valid: return False @@ -120,6 +152,7 @@ class CodeView( object ): signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' ) self._signs[ 'vimspectorPC' ] = None + self._current_frame = None self._UndisplaySigns() self.current_syntax = None @@ -187,6 +220,9 @@ class CodeView( object ): file_name, breakpoint[ 'line' ] ) + # We need to also check if there's a breakpoint on this PC line and chnge + # the PC + self._DisplayPC() def BreakpointsAsQuickFix( self ): qf = [] diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index 04d9834..18101f8 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -29,6 +29,7 @@ DEFAULTS = { # Signs 'sign_priority': { 'vimspectorPC': 200, + 'vimspectorPCBP': 200, 'vimspectorBP': 9, 'vimspectorBPCond': 9, 'vimspectorBPDisabled': 9, diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index 2a8c053..b380837 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -551,6 +551,7 @@ endfunction function! Test_Custom_Breakpoint_Priority() let g:vimspector_sign_priority = { \ 'vimspectorPC': 1, + \ 'vimspectorPCBP': 1, \ 'vimspectorBP': 2, \ 'vimspectorBPCond': 3, \ 'vimspectorBPDisabled': 4 @@ -597,7 +598,7 @@ function! Test_Custom_Breakpoint_Priority() call vimspector#test#signs#AssertSignAtLine( \ 'VimspectorCode', \ 15, - \ 'vimspectorPC', + \ 'vimspectorPCBP', \ 1 ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorCode', \ 17, @@ -626,7 +627,7 @@ function! Test_Custom_Breakpoint_Priority() call vimspector#test#signs#AssertSignAtLine( \ 'VimspectorCode', \ 17, - \ 'vimspectorPC', + \ 'vimspectorPCBP', \ 1 ) @@ -684,7 +685,7 @@ function! Test_Custom_Breakpoint_Priority_Partial() call vimspector#test#signs#AssertSignAtLine( \ 'VimspectorCode', \ 15, - \ 'vimspectorPC', + \ 'vimspectorPCBP', \ 200 ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorCode', \ 17, @@ -713,7 +714,7 @@ function! Test_Custom_Breakpoint_Priority_Partial() call vimspector#test#signs#AssertSignAtLine( \ 'VimspectorCode', \ 17, - \ 'vimspectorPC', + \ 'vimspectorPCBP', \ 200 ) diff --git a/tests/lib/autoload/vimspector/test/signs.vim b/tests/lib/autoload/vimspector/test/signs.vim index 926a972..66cca19 100644 --- a/tests/lib/autoload/vimspector/test/signs.vim +++ b/tests/lib/autoload/vimspector/test/signs.vim @@ -32,7 +32,7 @@ function! vimspector#test#signs#AssertPCIsAtLineInBuffer( buffer, line ) abort let index = 0 while index < len( signs[ 0 ].signs ) let s = signs[ 0 ].signs[ index ] - if s.name ==# 'vimspectorPC' + if s.name ==# 'vimspectorPC' || s.name ==# 'vimspectorPCBP' if assert_false( pc_index >= 0, 'Too many PC signs' ) return 1 endif From 6fd472418985d1599afc01e1d5db068a0003f1d7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 23 Sep 2020 12:29:40 +0100 Subject: [PATCH 357/609] 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 358/609] 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 359/609] 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 From b2f41643f4b221bf762701e63a6d8143c880b45f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 27 Sep 2020 13:13:21 +0100 Subject: [PATCH 360/609] Add double-click to expand/collapse --- README.md | 8 ++++---- python3/vimspector/stack_trace.py | 5 ++++- python3/vimspector/variables.py | 12 ++++++++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ae583ab..55019a4 100644 --- a/README.md +++ b/README.md @@ -720,7 +720,7 @@ You can configure your choices in the `.vimspector.json`. See ## Variables and scopes * Current scope shows values of locals. -* Use `` to expand/collapse (+, -). +* Use ``, or double-click with left mouse to expand/collapse (+, -). * When changing the stack frame the locals window updates. * While paused, hover to see values @@ -737,7 +737,7 @@ to add a new watch expression. typing the expression. Commit with ``. * Alternatively, use `:VimspectorWatch `. Tab-completion for expression is available in some debug adapters. -* Expand result with ``. +* Expand result with ``, or double-click with left mouse. * Delete with ``. ![watch window](https://puremourning.github.io/vimspector-web/img/vimspector-watch-window.png) @@ -762,8 +762,8 @@ let g:ycm_semantic_triggers = { ## Stack Traces -* In the threads window, use `` to expand/collapse. -* Use `` on a stack frame to jump to it. +* In the threads window, use ``, or double-click with left mouse to expand/collapse. +* Use ``, or double-click with left mouse on a stack frame to jump to it. ![stack trace](https://puremourning.github.io/vimspector-web/img/vimspector-callstack-window.png) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 6caccc8..8af6536 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -40,7 +40,10 @@ class StackTraceView( object ): utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' ) utils.SetUpUIWindow( win ) - vim.command( 'nnoremap :call vimspector#GoToFrame()' ) + vim.command( 'nnoremap ' + ':call vimspector#GoToFrame()' ) + vim.command( 'nnoremap <2-LeftMouse> ' + ':call vimspector#GoToFrame()' ) self._line_to_frame = {} self._line_to_thread = {} diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 5694e46..b50793d 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -132,13 +132,18 @@ class VariablesView( object ): self._connection = None self._current_syntax = '' + def AddExpandMappings(): + vim.command( 'nnoremap ' + ':call vimspector#ExpandVariable()' ) + vim.command( 'nnoremap <2-LeftMouse> ' + ':call vimspector#ExpandVariable()' ) + # Set up the "Variables" buffer in the variables_win self._scopes: typing.List[ Scope ] = [] self._vars = View( variables_win, {}, self._DrawScopes ) utils.SetUpHiddenBuffer( self._vars.buf, 'vimspector.Variables' ) with utils.LetCurrentWindow( variables_win ): - vim.command( - 'nnoremap :call vimspector#ExpandVariable()' ) + AddExpandMappings() # Set up the "Watches" buffer in the watches_win (and create a WinBar in # there) @@ -150,8 +155,7 @@ class VariablesView( object ): 'vimspector#AddWatchPrompt', 'vimspector#OmniFuncWatch' ) with utils.LetCurrentWindow( watches_win ): - vim.command( - 'nnoremap :call vimspector#ExpandVariable()' ) + AddExpandMappings() vim.command( 'nnoremap :call vimspector#DeleteWatch()' ) From bd09206caf5d441f22dd7362341e9c0e5644f173 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 27 Sep 2020 21:19:47 +0100 Subject: [PATCH 361/609] While debugging, use the correct path for breakpoints --- python3/vimspector/breakpoints.py | 5 +- python3/vimspector/code.py | 3 +- .../python/multiple_files/.vimspector.json | 21 ++++++ support/test/python/multiple_files/cow.py | 15 ++++ support/test/python/multiple_files/moo.py | 13 ++++ tests/breakpoints.test.vim | 73 ++++++++++++++++++- tests/breakpoints_doublewidth.test.vim | 73 ++++++++++++++++++- tests/lib/autoload/vimspector/test/signs.vim | 8 +- 8 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 support/test/python/multiple_files/.vimspector.json create mode 100644 support/test/python/multiple_files/cow.py create mode 100644 support/test/python/multiple_files/moo.py diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 617ae6c..6ab14f5 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -268,7 +268,10 @@ class ProjectBreakpoints( object ): awaiting = awaiting + 1 self._connection.DoRequest( - lambda msg: response_handler( source, msg ), + # The source=source here is critical to ensure that we capture each + # source in the iteration, rather than ending up passing the same source + # to each callback. + lambda msg, source=source: response_handler( source, msg ), { 'command': 'setBreakpoints', 'arguments': { diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index c462272..ab4fb39 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -119,7 +119,6 @@ class CodeView( object ): return False self._current_frame = frame - self._DisplayPC() if not self._window.valid: return False @@ -148,6 +147,8 @@ class CodeView( object ): self.current_syntax = utils.ToUnicode( vim.current.buffer.options[ 'syntax' ] ) + self.ShowBreakpoints() + return True def Clear( self ): diff --git a/support/test/python/multiple_files/.vimspector.json b/support/test/python/multiple_files/.vimspector.json new file mode 100644 index 0000000..aff2cb6 --- /dev/null +++ b/support/test/python/multiple_files/.vimspector.json @@ -0,0 +1,21 @@ +{ + "configurations": { + "run": { + "adapter": "debugpy", + "default": true, + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/moo.py", + "cwd": "${workspaceRoot}", + "stopOnEntry": true + }, + "breakpoints": { + "exception": { + "raised": "N", + "uncaught": "", + "userUnhandled": "" + } + } + } + } +} diff --git a/support/test/python/multiple_files/cow.py b/support/test/python/multiple_files/cow.py new file mode 100644 index 0000000..88d7082 --- /dev/null +++ b/support/test/python/multiple_files/cow.py @@ -0,0 +1,15 @@ +def Say( *args, **kwargs ): + print( *args, **kwargs ) + + + + + + + + + + + +def Quiet(): + pass diff --git a/support/test/python/multiple_files/moo.py b/support/test/python/multiple_files/moo.py new file mode 100644 index 0000000..f8da4b1 --- /dev/null +++ b/support/test/python/multiple_files/moo.py @@ -0,0 +1,13 @@ +import cow + + +def Moo(): + for i in range( 1, 100 ): + cow.Say( 'Moo' ) + + for i in range( 1, 100 ): + cow.Say( 'Ooom' ) + + +if __name__ == '__main__': + Moo() diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index b380837..f6cafa5 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -576,7 +576,7 @@ function! Test_Custom_Breakpoint_Priority() \ 'vimspectorBPDisabled', \ 4 ) call vimspector#ToggleBreakpoint() - call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 15 ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 16 ) call setpos( '.', [ 0, 17, 1 ] ) call vimspector#ToggleBreakpoint( { 'condition': '1' } ) @@ -663,7 +663,7 @@ function! Test_Custom_Breakpoint_Priority_Partial() \ 'vimspectorBPDisabled', \ 4 ) call vimspector#ToggleBreakpoint() - call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 15 ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 16 ) call setpos( '.', [ 0, 17, 1 ] ) call vimspector#ToggleBreakpoint( { 'condition': '1' } ) @@ -723,3 +723,72 @@ function! Test_Custom_Breakpoint_Priority_Partial() %bwipeout! unlet! g:vimspector_sign_priority endfunction + + +function! Test_Add_Line_BP_In_Other_File_While_Debugging() + let moo = 'moo.py' + let cow = 'cow.py' + lcd ../support/test/python/multiple_files + exe 'edit' moo + + call vimspector#Launch() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( moo, 1, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( moo, 1 ) + + call cursor( 6, 3 ) + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertPCIsAtLineInBuffer( moo, 1 ) + call WaitForAssert( {-> + \vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorCode', + \ 6, + \ 'vimspectorBP', + \ 9 ) } ) + + exe 'edit' cow + call cursor( 2, 1 ) + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', 6 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorCode', + \ 2, + \ 'vimspectorBP', + \ 9 ) } ) + + call vimspector#Continue() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( moo, 6, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( moo, 6 ) + + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', 2 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 6, + \ 'vimspectorBP', + \ 9 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 6, + \ 'vimspectorPCBP', + \ 200 ) + + call vimspector#Continue() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( cow, 2, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( cow, 2 ) + + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', 6 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 2, + \ 'vimspectorBP', + \ 9 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 2, + \ 'vimspectorPCBP', + \ 200 ) + + lcd - + call vimspector#test#setup#Reset() + %bwipe! +endfunction diff --git a/tests/breakpoints_doublewidth.test.vim b/tests/breakpoints_doublewidth.test.vim index 12756da..8ab35bb 100644 --- a/tests/breakpoints_doublewidth.test.vim +++ b/tests/breakpoints_doublewidth.test.vim @@ -577,7 +577,7 @@ function! Test_Custom_Breakpoint_Priority() \ 'vimspectorBPDisabled', \ 4 ) call vimspector#ToggleBreakpoint() - call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 15 ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 16 ) call setpos( '.', [ 0, 17, 1 ] ) call vimspector#ToggleBreakpoint( { 'condition': '1' } ) @@ -664,7 +664,7 @@ function! Test_Custom_Breakpoint_Priority_Partial() \ 'vimspectorBPDisabled', \ 4 ) call vimspector#ToggleBreakpoint() - call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 15 ) + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 16 ) call setpos( '.', [ 0, 17, 1 ] ) call vimspector#ToggleBreakpoint( { 'condition': '1' } ) @@ -724,3 +724,72 @@ function! Test_Custom_Breakpoint_Priority_Partial() %bwipeout! unlet! g:vimspector_sign_priority endfunction + + +function! Test_Add_Line_BP_In_Other_File_While_Debugging() + let moo = 'moo.py' + let cow = 'cow.py' + lcd ../support/test/python/multiple_files + exe 'edit' moo + + call vimspector#Launch() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( moo, 1, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( moo, 1 ) + + call cursor( 6, 3 ) + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertPCIsAtLineInBuffer( moo, 1 ) + call WaitForAssert( {-> + \vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorCode', + \ 6, + \ 'vimspectorBP', + \ 9 ) } ) + + exe 'edit' cow + call cursor( 2, 1 ) + call vimspector#ToggleBreakpoint() + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', 6 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorCode', + \ 2, + \ 'vimspectorBP', + \ 9 ) } ) + + call vimspector#Continue() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( moo, 6, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( moo, 6 ) + + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', 2 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 6, + \ 'vimspectorBP', + \ 9 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 6, + \ 'vimspectorPCBP', + \ 200 ) + + call vimspector#Continue() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( cow, 2, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( cow, 2 ) + + call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', 6 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 2, + \ 'vimspectorBP', + \ 9 ) + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 2, + \ 'vimspectorPCBP', + \ 200 ) + + lcd - + call vimspector#test#setup#Reset() + %bwipe! +endfunction diff --git a/tests/lib/autoload/vimspector/test/signs.vim b/tests/lib/autoload/vimspector/test/signs.vim index 66cca19..a6bb698 100644 --- a/tests/lib/autoload/vimspector/test/signs.vim +++ b/tests/lib/autoload/vimspector/test/signs.vim @@ -70,7 +70,11 @@ function! vimspector#test#signs#AssertSignGroupSingletonAtLine( group, endfunction -function! vimspector#test#signs#AssertSignAtLine( group, line, sign_name, priority ) abort +function! vimspector#test#signs#AssertSignAtLine( + \ group, + \ line, + \ sign_name, + \ priority ) abort let signs = sign_getplaced( '%', { \ 'group': a:group, @@ -106,7 +110,7 @@ endfunction function! vimspector#test#signs#AssertSignGroupEmptyAtLine( group, line ) abort let signs = sign_getplaced( '%', { \ 'group': a:group, - \ 'lnum': line( '.' ) + \ 'lnum': a:line, \ } ) return assert_equal( 1, From 3d442c978f1ec9b5f140c370d8d6c71557fca6c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ayberk=20Ayd=C4=B1n?= Date: Fri, 2 Oct 2020 00:20:50 +0300 Subject: [PATCH 362/609] Update README.md fix typo --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 55019a4..e01c98e 100644 --- a/README.md +++ b/README.md @@ -1274,7 +1274,7 @@ It allows you to debug scripts running inside chrome from within Vim. ## Java -Vimsepctor works well with the [java debug server][java-debug-server], which +Vimspector works well with the [java debug server][java-debug-server], which runs as a jdt.ls (Java Language Server) plugin, rather than a standalone debug adapter. @@ -1669,7 +1669,7 @@ hi link jsonComment Comment 7. What is the difference between a `gadget` and an `adapter`? A gadget is somethin you install with `:VimspectorInstall` or `install_gadget.py`, an - `adapter` is something that Vimspector talks to (actually it's the Vimsepctor + `adapter` is something that Vimspector talks to (actually it's the Vimspector config describing that thing). These are _usually_ one-to-one, but in theory a single gadget can supply multiple `adapter` configs. Typically this happens when a `gadget` supplies different `adapter` config @@ -1701,7 +1701,7 @@ A message from the author about the motivation for this plugin: > into the debugger, it would be faster and more enjoyable that just cerebral > code comprehension. > -> I created Vimsepctor because I find changing tools frustrating. `gdb` for c++, +> I created Vimspector because I find changing tools frustrating. `gdb` for c++, > `pdb` for python, etc. Each has its own syntax. Each its own lexicon. Each its > own foibles. > From 16f22b396fc1d6ad3a420cd7566d6b912675fe46 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 10 Oct 2020 15:32:50 +0100 Subject: [PATCH 363/609] Fix breakpoint event Few problems: - we were passing a dict instead of a list of breakpoints - if the breakpoint had a source which was {} we crashed - we didn't support the 'removed' event --- python3/vimspector/code.py | 41 ++++++++++++++++------ python3/vimspector/debug_session.py | 4 ++- tests/testdata/cpp/simple/.vimspector.json | 39 ++++++++++++++++++++ 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index ab4fb39..afd8c03 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -166,25 +166,29 @@ class CodeView( object ): def AddBreakpoints( self, source, breakpoints ): for breakpoint in breakpoints: - if 'source' not in breakpoint: - if source: - breakpoint[ 'source' ] = source - else: - self._logger.warn( 'missing source in breakpoint {0}'.format( - json.dumps( breakpoint ) ) ) - continue + source = breakpoint.get( 'source' ) or source + if not source or 'path' not in source: + self._logger.warn( 'missing source/path in breakpoint {0}'.format( + json.dumps( breakpoint ) ) ) + continue - self._breakpoints[ breakpoint[ 'source' ][ 'path' ] ].append( - breakpoint ) + breakpoint[ 'source' ] = source + self._breakpoints[ source[ 'path' ] ].append( breakpoint ) self._logger.debug( 'Breakpoints at this point: {0}'.format( json.dumps( self._breakpoints, indent = 2 ) ) ) self.ShowBreakpoints() + + def AddBreakpoint( self, breakpoint ): + self.AddBreakpoints( None, [ breakpoint ] ) + + def UpdateBreakpoint( self, bp ): if 'id' not in bp: - self.AddBreakpoints( None, [ bp ] ) + self.AddBreakpoint( bp ) + return for _, breakpoint_list in self._breakpoints.items(): for index, breakpoint in enumerate( breakpoint_list ): @@ -194,7 +198,22 @@ class CodeView( object ): return # Not found. Assume new - self.AddBreakpoints( None, [ bp ] ) + self.AddBreakpoint( bp ) + + + def RemoveBreakpoint( self, bp ): + for _, breakpoint_list in self._breakpoints.items(): + found_index = None + for index, breakpoint in enumerate( breakpoint_list ): + if 'id' in breakpoint and breakpoint[ 'id' ] == bp[ 'id' ]: + found_index = index + break + + if found_index is not None: + del breakpoint_list[ found_index ] + self.ShowBreakpoints() + return + def _UndisplaySigns( self ): for sign_id in self._signs[ 'breakpoints' ]: diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index e072a23..2c9a3c8 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -1050,7 +1050,9 @@ class DebugSession( object ): if reason == 'changed': self._codeView.UpdateBreakpoint( bp ) elif reason == 'new': - self._codeView.AddBreakpoints( None, bp ) + self._codeView.AddBreakpoint( bp ) + elif reason == 'removed': + self._codeView.RemoveBreakpoint( bp ) else: utils.UserMessage( 'Unrecognised breakpoint event (undocumented): {0}'.format( reason ), diff --git a/tests/testdata/cpp/simple/.vimspector.json b/tests/testdata/cpp/simple/.vimspector.json index 57eefad..176a44d 100644 --- a/tests/testdata/cpp/simple/.vimspector.json +++ b/tests/testdata/cpp/simple/.vimspector.json @@ -68,6 +68,45 @@ "CALCULATED_STR", "${CALCULATED_STR}" ] } + }, + "lldb-vscode": { + "adapter": "lldb-vscode", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/${fileBasenameNoExtension}", + "cwd": "${workspaceRoot}", + "externalConsole": false, + "MIMode": "lldb" + } + }, + "CodeLLDB": { + "adapter": "CodeLLDB", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/${fileBasenameNoExtension}", + "cwd": "${workspaceRoot}", + "expressions": "native" + } + } + }, + "adapters": { + "lldb-vscode": { + "variables": { + "LLVM": { + "shell": "brew --prefix llvm" + } + }, + "attach": { + "pidProperty": "pid", + "pidSelect": "ask" + }, + "command": [ + "${LLVM}/bin/lldb-vscode" + ], + "env": { + "LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY": "YES" + }, + "name": "lldb" } } } From 1b9763a4fca75152e3af4986e8b02ad67035b722 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 10 Oct 2020 16:04:55 +0100 Subject: [PATCH 364/609] Minor improvements to console usage Display failures and stop adding random text which makes the (very useful) CodeLLDB interface look messy --- autoload/vimspector.vim | 4 +-- python3/vimspector/debug_session.py | 5 +-- python3/vimspector/output.py | 14 +++++--- tests/variables.test.vim | 50 +++++++++++++++++++++++++++-- 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index c1f3640..68c49b4 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -183,7 +183,7 @@ function! vimspector#Evaluate( expr ) abort return endif py3 _vimspector_session.ShowOutput( 'Console' ) - py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) ) + py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ), True ) endfunction function! vimspector#EvaluateConsole( expr ) abort @@ -192,7 +192,7 @@ function! vimspector#EvaluateConsole( expr ) abort endif stopinsert setlocal nomodified - py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) ) + py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ), False ) endfunction function! vimspector#ShowOutput( ... ) abort diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 2c9a3c8..ccec07b 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -467,9 +467,10 @@ class DebugSession( object ): expression ) @IfConnected() - def EvaluateConsole( self, expression ): + def EvaluateConsole( self, expression, verbose ): self._outputView.Evaluate( self._stackTraceView.GetCurrentFrame(), - expression ) + expression, + verbose ) @IfConnected() def DeleteWatch( self ): diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 71c81e9..eedefc1 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -260,14 +260,18 @@ class DAPOutputView( OutputView ): # Don't clear because output is probably still useful self._connection = None - def Evaluate( self, frame, expression ): - self._Print( 'Console', [ 'Evaluating: ' + expression ] ) + def Evaluate( self, frame, expression, verbose ): + if verbose: + self._Print( 'Console', f"Evaluating: { expression }" ) def print_result( message ): result = message[ 'body' ][ 'result' ] if result is None: result = '' - self._Print( 'Console', f' Result: { result }' ) + self._Print( 'Console', result.splitlines() ) + + def print_failure( reason, msg ): + self._Print( 'Console', reason.splitlines() ) request = { 'command': 'evaluate', @@ -280,4 +284,6 @@ class DAPOutputView( OutputView ): if frame: request[ 'arguments' ][ 'frameId' ] = frame[ 'id' ] - self._connection.DoRequest( print_result, request ) + self._connection.DoRequest( print_result, + request, + print_failure ) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index 733061f..d710864 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -499,7 +499,7 @@ function Test_EvaluateConsole() call WaitForAssert( {-> \ assert_equal( \ [ - \ ' Result: 1' + \ '1' \ ], \ getbufline( bufnr( 'vimspector.Console' ), '$', '$' ) \ ) @@ -511,7 +511,7 @@ function Test_EvaluateConsole() \ assert_equal( \ [ \ 'Evaluating: t.i', - \ ' Result: 1' + \ '1' \ ], \ getbufline( bufnr( 'vimspector.Console' ), len-1, '$' ) \ ) @@ -522,3 +522,49 @@ function Test_EvaluateConsole() call vimspector#test#setup#Reset() %bwipe! endfunction + + +function Test_EvaluatePromptConsole() + 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 vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 27, 1 ) + + VimspectorShowOutput + call assert_equal( bufnr( 'vimspector.Console' ), + \ winbufnr( g:vimspector_session_windows.output ) ) + + call feedkeys( "it.i\", 'xt' ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '1' + \ ], + \ getbufline( bufnr( 'vimspector.Console' ), '$', '$' ) + \ ) + \ } ) + + let len = getbufinfo( 'vimspector.Console' )[ 0 ].linecount + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '> t.i', + \ '', + \ '1' + \ ], + \ getbufline( bufnr( 'vimspector.Console' ), len-2, '$' ) + \ ) + \ } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( + \ 'vimspector.Console', len, v:null ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction From f3c5944dd2071da06be8232dcfc381e907c3749d Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 14 Oct 2020 15:26:47 +0100 Subject: [PATCH 365/609] Java: Set cwd by default --- python3/vimspector/gadgets.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 21073f1..e3df3a7 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -157,6 +157,9 @@ GADGETS = { "vscode-java": { "name": "vscode-java", "port": "${DAPPort}", + "configuration": { + "cwd": "${workspaceRoot}" + } } }, }, From 8d175725389972e0f5ea681ba3d4d6f10c87e510 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 14 Oct 2020 15:44:52 +0100 Subject: [PATCH 366/609] brew update is failing as some taps have been removed Per https://github.com/Homebrew/homebrew-core/issues/30917, use brew update-reset to fix it --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5d0315d..1f3270b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -84,7 +84,7 @@ jobs: steps: - uses: actions/checkout@v2 - run: | - brew update + brew update-reset brew doctor || true for p in macvim tcl-tk llvm; do brew install $p From ea36b60a07f00d245d42987a77c3faa526459276 Mon Sep 17 00:00:00 2001 From: Camilo Schoeningh Date: Wed, 14 Oct 2020 13:38:33 +0200 Subject: [PATCH 367/609] vscode-go: Update version and use new repository. As of June 2020, the vscode-go repository has moved to https://github.com/golang/vscode-go. --- python3/vimspector/gadgets.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index e3df3a7..cc80e4c 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -339,22 +339,25 @@ GADGETS = { 'vscode-go': { 'language': 'go', 'download': { - 'url': 'https://github.com/microsoft/vscode-go/releases/download/' - '${version}/${file_name}' + 'url': 'https://github.com/golang/vscode-go/releases/download/' + 'v${version}/${file_name}' }, 'all': { - 'version': '0.11.4', - 'file_name': 'Go-0.11.4.vsix', + 'version': '0.17.2', + 'file_name': 'Go-0.17.2.vsix', 'checksum': - 'ff7d7b944da5448974cb3a0086f4a2fd48e2086742d9c013d6964283d416027e' + '69055b9b86387e2906f20cceb84ab2c4da86c5a5159395a7bbdb74ea3a0f982f', }, 'adapters': { 'vscode-go': { 'name': 'delve', 'command': [ 'node', - '${gadgetDir}/vscode-go/out/src/debugAdapter/goDebug.js' + '${gadgetDir}/vscode-go/dist/debugAdapter.js' ], + "configuration": { + "cwd": "${workspaceRoot}", + } }, }, }, From 78bec87f4ed058dfad6f8c33f487719db10f1bd3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 14 Oct 2020 22:33:04 +0100 Subject: [PATCH 368/609] Link to wiki for additional language support --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e01c98e..ba72b14 100644 --- a/README.md +++ b/README.md @@ -148,10 +148,10 @@ runtime dependencies). They are categorised by their level of support: ## Other languages -Vimspector should work for any debug adapter that works in Visual Studio Code, -but there are certain limitations (see FAQ). If you're trying to get vimspector -to work with a language that's not "supported", head over to Gitter and contact -the author. It should be possible to get it going. +Vimspector should work for any debug adapter that works in Visual Studio Code. + +To use Vimspector with a language that's not "built-in", see this +[wiki page](https://github.com/puremourning/vimspector/wiki/languages). # Installation From 0d112d70a0f6adccbfd9e7e60bb6e327ebab5fbc Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 13 Oct 2020 22:16:11 +0100 Subject: [PATCH 369/609] Add SetLineBreakpoint and ClaerLineBreakpoint APIs These are useful for running tests (i.e. ensure there's a breakpiont at the start of the test) and/or other programmatic usages. They will also be needed for setting temporary breakpionts. --- autoload/vimspector.vim | 25 +++++++ python3/vimspector/breakpoints.py | 108 +++++++++++++++++----------- python3/vimspector/debug_session.py | 6 ++ python3/vimspector/signs.py | 4 +- python3/vimspector/utils.py | 6 +- 5 files changed, 105 insertions(+), 44 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 68c49b4..f4d2f58 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -76,6 +76,31 @@ function! vimspector#ToggleBreakpoint( ... ) abort py3 _vimspector_session.ToggleBreakpoint( vim.eval( 'options' ) ) endfunction +function! vimspector#SetLineBreakpoint( file_name, line_num, ... ) abort + if !s:enabled + return + endif + if a:0 == 0 + let options = {} + else + let options = a:1 + endif + py3 _vimspector_session.SetLineBreakpoint( + \ vim.eval( 'a:file_name' ), + \ int( vim.eval( 'a:line_num' ) ), + \ vim.eval( 'options' ) ) +endfunction + +function! vimspector#ClearLineBreakpoint( file_name, line_num ) abort + if !s:enabled + return + endif + py3 _vimspector_session.ClearLineBreakpoint( + \ vim.eval( 'a:file_name' ), + \ int( vim.eval( 'a:line_num' ) ) ) +endfunction + + function! vimspector#AddFunctionBreakpoint( function, ... ) abort if !s:enabled return diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 6ab14f5..f5999c3 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -141,50 +141,74 @@ class ProjectBreakpoints( object ): self.UpdateUI() + def _FindLineBreakpoint( self, file_name, line ): + file_name = os.path.abspath( file_name ) + for index, bp in enumerate( self._line_breakpoints[ file_name ] ): + self._SignToLine( file_name, bp ) + if bp[ 'line' ] == line: + return bp, index + + return None, None + + + def _PutLineBreakpoint( self, file_name, line, options ): + self._line_breakpoints[ os.path.abspath( file_name ) ].append( { + 'state': 'ENABLED', + 'line': line, + 'options': options, + # 'sign_id': , + # + # Used by other breakpoint types (specified in options): + # 'condition': ..., + # 'hitCondition': ..., + # 'logMessage': ... + } ) + + + def _DeleteLineBreakpoint( self, bp, file_name, index ): + if 'sign_id' in bp: + signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) + del self._line_breakpoints[ os.path.abspath( file_name ) ][ index ] + + def ToggleBreakpoint( self, options ): - line, column = vim.current.window.cursor + line, _ = vim.current.window.cursor file_name = vim.current.buffer.name if not file_name: return - found_bp = False - action = 'New' - for index, bp in enumerate( self._line_breakpoints[ file_name ] ): - self._SignToLine( file_name, bp ) - if bp[ 'line' ] == line: - found_bp = True - if bp[ 'state' ] == 'ENABLED' and not self._connection: - bp[ 'state' ] = 'DISABLED' - action = 'Disable' - else: - if 'sign_id' in bp: - signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) - del self._line_breakpoints[ file_name ][ index ] - action = 'Delete' - break - - self._logger.debug( "Toggle found bp at {}:{} ? {} ({})".format( - file_name, - line, - found_bp, - action ) ) - - if not found_bp: - self._line_breakpoints[ file_name ].append( { - 'state': 'ENABLED', - 'line': line, - 'options': options, - # 'sign_id': , - # - # Used by other breakpoint types (specified in options): - # 'condition': ..., - # 'hitCondition': ..., - # 'logMessage': ... - } ) + bp, index = self._FindLineBreakpoint( file_name, line ) + if bp is None: + # ADD + self._PutLineBreakpoint( file_name, line, options ) + elif bp[ 'state' ] == 'ENABLED' and not self._connection: + # DISABLE + bp[ 'state' ] = 'DISABLED' + else: + # DELETE + self._DeleteLineBreakpoint( bp, file_name, index ) self.UpdateUI() + + def SetLineBreakpoint( self, file_name, line_num, options ): + bp, _ = self._FindLineBreakpoint( file_name, line_num ) + if bp is not None: + bp[ 'options' ] = options + return + self._PutLineBreakpoint( file_name, line_num, options ) + self.UpdateUI() + + + def ClearLineBreakpoint( self, file_name, line_num ): + bp, index = self._FindLineBreakpoint( file_name, line_num ) + if bp is None: + return + self._DeleteLineBreakpoint( bp, file_name, index ) + self.UpdateUI() + + def AddFunctionBreakpoint( self, function, options ): self._func_breakpoints.append( { 'state': 'ENABLED', @@ -386,17 +410,21 @@ class ProjectBreakpoints( object ): else 'vimspectorBPCond' if 'condition' in bp[ 'options' ] else 'vimspectorBP' ) - signs.PlaceSign( bp[ 'sign_id' ], - 'VimspectorBP', - sign, - file_name, - bp[ 'line' ] ) + if utils.BufferNumberForFile( file_name, False ) > 0: + signs.PlaceSign( bp[ 'sign_id' ], + 'VimspectorBP', + sign, + file_name, + bp[ 'line' ] ) def _SignToLine( self, file_name, bp ): if 'sign_id' not in bp: return bp[ 'line' ] + if utils.BufferNumberForFile( file_name, False ) <= 0: + return bp[ 'line' ] + signs = vim.eval( "sign_getplaced( '{}', {} )".format( utils.Escape( file_name ), json.dumps( { 'id': bp[ 'sign_id' ], 'group': 'VimspectorBP', } ) ) ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index ccec07b..85f32da 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -1165,6 +1165,12 @@ class DebugSession( object ): def ToggleBreakpoint( self, options ): return self._breakpoints.ToggleBreakpoint( options ) + def SetLineBreakpoint( self, file_name, line_num, options ): + return self._breakpoints.SetLineBreakpoint( file_name, line_num, options ) + + def ClearLineBreakpoint( self, file_name, line_num ): + return self._breakpoints.ClearLineBreakpoint( file_name, line_num ) + def ClearBreakpoints( self ): if self._connection: self._codeView.ClearBreakpoints() diff --git a/python3/vimspector/signs.py b/python3/vimspector/signs.py index c51bd76..39d1f03 100644 --- a/python3/vimspector/signs.py +++ b/python3/vimspector/signs.py @@ -29,7 +29,7 @@ def DefineSign( name, text, double_text, texthl, col = 'right', **kwargs ): vim.command( cmd ) -def PlaceSign( sign_id, group, name, file, line ): +def PlaceSign( sign_id, group, name, file_name, line ): priority = settings.Dict( 'sign_priority' )[ name ] cmd = ( f'sign place { sign_id } ' @@ -37,7 +37,7 @@ def PlaceSign( sign_id, group, name, file, line ): f'name={ name } ' f'priority={ priority } ' f'line={ line } ' - f'file={ file }' ) + f'file={ file_name }' ) vim.command( cmd ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 879e910..4ce387d 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -45,8 +45,10 @@ _logger = logging.getLogger( __name__ ) SetUpLogging( _logger ) -def BufferNumberForFile( file_name ): - return int( vim.eval( "bufnr( '{0}', 1 )".format( Escape( file_name ) ) ) ) +def BufferNumberForFile( file_name, create = True ): + return int( vim.eval( "bufnr( '{0}', {1} )".format( + Escape( file_name ), + int( create ) ) ) ) def BufferForFile( file_name ): From 80985148e72bb1ef6913664f0f8e73946ce49e03 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 13 Oct 2020 22:43:52 +0100 Subject: [PATCH 370/609] Add "run to cursor" support We add a 'temporary' option to line breakpionts and try and clear any temporary breakpionts on the line we end up stopping on. This might not be art, but _probably_ works in almost all cases that matter. it's a bit hacky the way we have to push the reason around, but we don't know where we stopped until we actually get the stack trace response and SetCurrentFrame Move temporary breakpionts to match server response Also delete any existing ones when adding a new one and add tests for run-to-cursor. Only continue after we successfully set the breakpoints. This makes it work in go --- README.md | 9 + autoload/vimspector.vim | 107 +++++++--- plugin/vimspector.vim | 15 +- python3/vimspector/breakpoints.py | 84 +++++++- python3/vimspector/code.py | 24 ++- python3/vimspector/debug_session.py | 43 +++- python3/vimspector/stack_trace.py | 13 +- python3/vimspector/utils.py | 4 + .../python/simple_python/.vimspector.json | 9 +- support/test/python/simple_python/make_env.sh | 9 + .../test/python/simple_python/print_env.py | 15 ++ tests/breakpoints.test.vim | 160 ++++++-------- tests/breakpoints_doublewidth.test.vim | 34 +-- tests/language_go.test.vim | 25 +++ tests/lib/autoload/vimspector/test/signs.vim | 2 + tests/mappings.test.vim | 123 +++++++++++ tests/temporary_breakpoints.test.vim | 200 ++++++++++++++++++ tests/testdata/cpp/simple/.vimspector.json | 22 ++ 18 files changed, 693 insertions(+), 205 deletions(-) create mode 100755 support/test/python/simple_python/make_env.sh create mode 100644 support/test/python/simple_python/print_env.py create mode 100644 tests/mappings.test.vim create mode 100644 tests/temporary_breakpoints.test.vim diff --git a/README.md b/README.md index ba72b14..38bd320 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ And a couple of brief demos: - breakpoints (function, line and exception breakpoints) - conditional breakpoints (function, line) - step in/out/over/up, stop, restart +- run to cursor - launch and attach - remote launch, remote attach - locals and globals display @@ -552,6 +553,7 @@ features to set your own mappings. To that end, Vimspector defines the following * `VimspectorStepOver` * `VimspectorStepInto` * `VimspectorStepOut` +* `VimspectorRunToCursor` These map roughly 1-1 with the API functions below. @@ -612,6 +614,7 @@ let g:vimspector_enable_mappings = 'HUMAN' | `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` | | `F9` | Toggle conditional line breakpoint on the current line. | `vimspector#ToggleBreakpoint( { trigger expr, hit count expr } )` | | `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '' )` | +| `F8` | Run to Cursor | `vimspector#RunToCursor()` | | `F10` | Step Over | `vimspector#StepOver()` | | `F11` | Step Into | `vimspector#StepInto()` | | `F12` | Step out of current function scope | `vimspector#StepOut()` | @@ -710,6 +713,12 @@ You can configure your choices in the `.vimspector.json`. See * Use `vimspector#ClearBreakpoints()` to clear all breakpoints including the memory of exception breakpoint choices. +### Run to Cursor + +* Use `vimspector#RunToCursor` or ``: this creates a temporary + breakpoint on the current line, then continues execution, clearing the + breakpiont when it is hit. + ## Stepping * Step in/out, finish, continue, pause etc. using the WinBar, or mappings. diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index f4d2f58..410fd2d 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -27,45 +27,57 @@ EOF endfunction -let s:enabled = vimspector#internal#state#Reset() +let s:enabled = v:null + +function! s:Initialised() abort + return s:enabled != v:null +endfunction + +function! s:Enabled() abort + if !s:Initialised() + let s:enabled = vimspector#internal#state#Reset() + endif + + return s:enabled +endfunction function! vimspector#Launch() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.Start() endfunction function! vimspector#LaunchWithSettings( settings ) abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.Start( launch_variables = vim.eval( 'a:settings' ) ) endfunction function! vimspector#Reset() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.Reset() endfunction function! vimspector#Restart() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.Restart() endfunction function! vimspector#ClearBreakpoints() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.ClearBreakpoints() endfunction function! vimspector#ToggleBreakpoint( ... ) abort - if !s:enabled + if !s:Enabled() return endif if a:0 == 0 @@ -77,7 +89,7 @@ function! vimspector#ToggleBreakpoint( ... ) abort endfunction function! vimspector#SetLineBreakpoint( file_name, line_num, ... ) abort - if !s:enabled + if !s:Enabled() return endif if a:0 == 0 @@ -92,7 +104,7 @@ function! vimspector#SetLineBreakpoint( file_name, line_num, ... ) abort endfunction function! vimspector#ClearLineBreakpoint( file_name, line_num ) abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.ClearLineBreakpoint( @@ -101,8 +113,18 @@ function! vimspector#ClearLineBreakpoint( file_name, line_num ) abort endfunction +function! vimspector#RunToCursor() abort + if !s:Enabled() + return + endif + py3 _vimspector_session.RunTo( + \ vim.eval( "expand( '%' )" ), + \ int( vim.eval( "line( '.' )" ) ) ) +endfunction + + function! vimspector#AddFunctionBreakpoint( function, ... ) abort - if !s:enabled + if !s:Enabled() return endif if a:0 == 0 @@ -115,70 +137,70 @@ function! vimspector#AddFunctionBreakpoint( function, ... ) abort endfunction function! vimspector#StepOver() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.StepOver() endfunction function! vimspector#StepInto() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.StepInto() endfunction function! vimspector#StepOut() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.StepOut() endfunction function! vimspector#Continue() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.Continue() endfunction function! vimspector#Pause() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.Pause() endfunction function! vimspector#Stop() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.Stop() endfunction function! vimspector#ExpandVariable() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.ExpandVariable() endfunction function! vimspector#DeleteWatch() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.DeleteWatch() endfunction function! vimspector#GoToFrame() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.ExpandFrameOrThread() endfunction function! vimspector#AddWatch( ... ) abort - if !s:enabled + if !s:Enabled() return endif if a:0 == 0 @@ -195,7 +217,7 @@ function! vimspector#AddWatch( ... ) abort endfunction function! vimspector#AddWatchPrompt( expr ) abort - if !s:enabled + if !s:Enabled() return endif stopinsert @@ -204,7 +226,7 @@ function! vimspector#AddWatchPrompt( expr ) abort endfunction function! vimspector#Evaluate( expr ) abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.ShowOutput( 'Console' ) @@ -212,7 +234,7 @@ function! vimspector#Evaluate( expr ) abort endfunction function! vimspector#EvaluateConsole( expr ) abort - if !s:enabled + if !s:Enabled() return endif stopinsert @@ -221,7 +243,7 @@ function! vimspector#EvaluateConsole( expr ) abort endfunction function! vimspector#ShowOutput( ... ) abort - if !s:enabled + if !s:Enabled() return endif if a:0 == 1 @@ -232,7 +254,7 @@ function! vimspector#ShowOutput( ... ) abort endfunction function! vimspector#ShowOutputInWindow( win_id, category ) abort - if !s:enabled + if !s:Enabled() return endif py3 __import__( 'vimspector', @@ -242,21 +264,21 @@ function! vimspector#ShowOutputInWindow( win_id, category ) abort endfunction function! vimspector#ToggleLog() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.ToggleLog() endfunction function! vimspector#ListBreakpoints() abort - if !s:enabled + if !s:Enabled() return endif py3 _vimspector_session.ListBreakpoints() endfunction function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort - if !s:enabled + if !s:Enabled() return endif let buffers = py3eval( '_vimspector_session.GetOutputBuffers() ' @@ -287,7 +309,7 @@ def _vimspector_GetExprCompletions( ArgLead, prev_non_keyword_char ): EOF function! vimspector#CompleteExpr( ArgLead, CmdLine, CursorPos ) abort - if !s:enabled + if !s:Enabled() return endif @@ -415,7 +437,7 @@ function! vimspector#OmniFuncConsole( find_start, query ) abort endfunction function! vimspector#Install( bang, ... ) abort - if !s:enabled + if !s:Enabled() return endif let prefix = vimspector#internal#state#GetAPIPrefix() @@ -427,7 +449,7 @@ function! vimspector#Install( bang, ... ) abort endfunction function! vimspector#CompleteInstall( ArgLead, CmdLine, CursorPos ) abort - if !s:enabled + if !s:Enabled() return endif return py3eval( '"\n".join(' @@ -437,7 +459,7 @@ function! vimspector#CompleteInstall( ArgLead, CmdLine, CursorPos ) abort endfunction function! vimspector#Update( bang, ... ) abort - if !s:enabled + if !s:Enabled() return endif @@ -450,7 +472,7 @@ function! vimspector#Update( bang, ... ) abort endfunction function! vimspector#AbortInstall() abort - if !s:enabled + if !s:Enabled() return endif @@ -459,6 +481,25 @@ function! vimspector#AbortInstall() abort endfunction +function! vimspector#OnBufferCreated( file_name ) abort + if len( a:file_name ) == 0 + return + endif + + " Don't actually load up vimsepctor python in autocommands that trigger + " regularly. We'll only create the session obkect in s:Enabled() + if !s:Initialised() + return + endif + + if !s:Enabled() + return + endif + + py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) ) +endfunction + + " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index b1e9481..2dbbd82 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -62,6 +62,9 @@ nnoremap VimspectorStepInto nnoremap VimspectorStepOut \ :call vimspector#StepOut() +nnoremap VimspectorRunToCursor + \ :call vimspector#RunToCursor() + if s:mappings ==# 'VISUAL_STUDIO' nmap VimspectorContinue nmap VimspectorStop @@ -80,6 +83,7 @@ elseif s:mappings ==# 'HUMAN' nmap VimspectorToggleBreakpoint nmap VimspectorToggleConditionalBreakpoint nmap VimspectorAddFunctionBreakpoint + nmap VimspectorRunToCursor nmap VimspectorStepOver nmap VimspectorStepInto nmap VimspectorStepOut @@ -116,9 +120,14 @@ command! -bar -nargs=0 " Dummy autocommands so that we can call this whenever augroup VimspectorUserAutoCmds - au! - au User VimspectorUICreated silent - au User VimspectorTerminalOpened silent + autocmd! + autocmd User VimspectorUICreated silent + autocmd User VimspectorTerminalOpened silent +augroup END + +augroup Vimspector + autocmd! + autocmd BufNew * call vimspector#OnBufferCreated( expand( '' ) ) augroup END " boilerplate {{{ diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index f5999c3..19075ae 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -192,13 +192,13 @@ class ProjectBreakpoints( object ): self.UpdateUI() - def SetLineBreakpoint( self, file_name, line_num, options ): + def SetLineBreakpoint( self, file_name, line_num, options, then = None ): bp, _ = self._FindLineBreakpoint( file_name, line_num ) if bp is not None: bp[ 'options' ] = options return self._PutLineBreakpoint( file_name, line_num, options ) - self.UpdateUI() + self.UpdateUI( then ) def ClearLineBreakpoint( self, file_name, line_num ): @@ -209,6 +209,51 @@ class ProjectBreakpoints( object ): self.UpdateUI() + def ClearTemporaryBreakpoint( self, file_name, line_num ): + bp, index = self._FindLineBreakpoint( file_name, line_num ) + if bp is None: + return + if bp[ 'options' ].get( 'temporary' ): + self._DeleteLineBreakpoint( bp, file_name, index ) + self.UpdateUI() + + + def ClearTemporaryBreakpoints( self ): + for file_name, breakpoints in self._line_breakpoints.items(): + self._line_breakpoints[ file_name ] = list( filter( + lambda bp: not bp[ 'options' ].get( 'temporary' ), + breakpoints ) ) + + + def _UpdateTemporaryBreakpoints( self, breakpoints, temp_idxs ): + # adjust any temporary breakpoints to match the server result + # TODO: Maybe now is the time to ditch the split breakpoints nonesense + for temp_idx, user_bp in temp_idxs: + if temp_idx >= len( breakpoints ): + # Just can't trust servers ? + self._logger.debug( "Server Error - invalid breakpoints list did not " + "contain entry for temporary breakpoint at index " + f"{ temp_idx } i.e. { user_bp }" ) + continue + + bp = breakpoints[ temp_idx ] + + if 'line' not in bp or not bp[ 'verified' ]: + utils.UserMessage( + "Unable to set temporary breakpoint at line " + f"{ user_bp[ 'line' ] } execution will continue...", + persist = True, + error = True ) + + self._logger.debug( f"Updating temporary breakpoint { user_bp } line " + f"{ user_bp[ 'line' ] } to { bp[ 'line' ] }" ) + + # if it was moved, update the user-breakpoint so that we unset it + # again properly + user_bp[ 'line' ] = bp[ 'line' ] + + + def AddFunctionBreakpoint( self, function, options ): self._func_breakpoints.append( { 'state': 'ENABLED', @@ -224,11 +269,13 @@ class ProjectBreakpoints( object ): self.UpdateUI() - def UpdateUI( self ): + def UpdateUI( self, then = None ): if self._connection: - self.SendBreakpoints() + self.SendBreakpoints( then ) else: self._ShowBreakpoints() + if then: + then() def SetBreakpointsHandler( self, handler ): @@ -254,9 +301,12 @@ class ProjectBreakpoints( object ): if awaiting == 0 and doneHandler: doneHandler() - def response_handler( source, msg ): + def response_handler( source, msg, temp_idxs = [] ): if msg: self._breakpoints_handler.AddBreakpoints( source, msg ) + + breakpoints = ( msg.get( 'body' ) or {} ).get( 'breakpoints' ) or [] + self._UpdateTemporaryBreakpoints( breakpoints, temp_idxs ) response_received() @@ -267,9 +317,9 @@ class ProjectBreakpoints( object ): # TODO: add the _configured_breakpoints to line_breakpoints - # TODO: the line numbers might have changed since pressing the F9 key! for file_name, line_breakpoints in self._line_breakpoints.items(): + temp_idxs = [] breakpoints = [] for bp in line_breakpoints: self._SignToLine( file_name, bp ) @@ -283,8 +333,15 @@ class ProjectBreakpoints( object ): dap_bp = {} dap_bp.update( bp[ 'options' ] ) dap_bp.update( { 'line': bp[ 'line' ] } ) + + dap_bp.pop( 'temporary', None ) + + if bp[ 'options' ].get( 'temporary' ): + temp_idxs.append( [ len( breakpoints ), bp ] ) + breakpoints.append( dap_bp ) + source = { 'name': os.path.basename( file_name ), 'path': file_name, @@ -295,7 +352,10 @@ class ProjectBreakpoints( object ): # The source=source here is critical to ensure that we capture each # source in the iteration, rather than ending up passing the same source # to each callback. - lambda msg, source=source: response_handler( source, msg ), + lambda msg, source=source, temp_idxs=temp_idxs: response_handler( + source, + msg, + temp_idxs = temp_idxs ), { 'command': 'setBreakpoints', 'arguments': { @@ -396,6 +456,12 @@ class ProjectBreakpoints( object ): # pay any attention to them anyway. self._exception_breakpoints[ 'exceptionOptions' ] = [] + + def Refresh( self, file_name ): + # TODO: Just this file ? + self._ShowBreakpoints() + + def _ShowBreakpoints( self ): for file_name, line_breakpoints in self._line_breakpoints.items(): for bp in line_breakpoints: @@ -410,7 +476,7 @@ class ProjectBreakpoints( object ): else 'vimspectorBPCond' if 'condition' in bp[ 'options' ] else 'vimspectorBP' ) - if utils.BufferNumberForFile( file_name, False ) > 0: + if utils.BufferExists( file_name ): signs.PlaceSign( bp[ 'sign_id' ], 'VimspectorBP', sign, @@ -422,7 +488,7 @@ class ProjectBreakpoints( object ): if 'sign_id' not in bp: return bp[ 'line' ] - if utils.BufferNumberForFile( file_name, False ) <= 0: + if not utils.BufferExists( file_name ): return bp[ 'line' ] signs = vim.eval( "sign_getplaced( '{}', {} )".format( diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index afd8c03..e1b7f59 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -93,16 +93,12 @@ class CodeView( object ): sign = 'vimspectorPCBP' break - try: + if utils.BufferExists( frame[ 'source' ][ 'path' ] ): signs.PlaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode', sign, frame[ 'source' ][ 'path' ], frame[ 'line' ] ) - except vim.error as e: - # Ignore 'invalid buffer name' - if 'E158' not in str( e ): - raise def SetCurrentFrame( self, frame ): @@ -215,6 +211,11 @@ class CodeView( object ): return + def Refresh( self, file_name ): + # TODO: jsut the file ? + self.ShowBreakpoints() + + def _UndisplaySigns( self ): for sign_id in self._signs[ 'breakpoints' ]: signs.UnplaceSign( sign_id, 'VimspectorCode' ) @@ -236,12 +237,13 @@ class CodeView( object ): sign_id = self._next_sign_id self._next_sign_id += 1 self._signs[ 'breakpoints' ].append( sign_id ) - signs.PlaceSign( sign_id, - 'VimspectorCode', - 'vimspectorBP' if breakpoint[ 'verified' ] - else 'vimspectorBPDisabled', - file_name, - breakpoint[ 'line' ] ) + if utils.BufferExists( file_name ): + signs.PlaceSign( sign_id, + 'VimspectorCode', + 'vimspectorBP' if breakpoint[ 'verified' ] + else 'vimspectorBPDisabled', + file_name, + breakpoint[ 'line' ] ) # We need to also check if there's a breakpoint on this PC line and chnge # the PC diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 85f32da..42b6340 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -77,6 +77,7 @@ class DebugSession( object ): self._launch_complete = False self._on_init_complete_handlers = [] self._server_capabilities = {} + self.ClearTemporaryBreakpoints() def Start( self, launch_variables = None ): # We mutate launch_variables, so don't mutate the default argument. @@ -560,6 +561,13 @@ class DebugSession( object ): return response[ 'body' ][ 'targets' ] + def RefreshSigns( self, file_name ): + if self._connection: + self._codeView.Refresh( file_name ) + else: + self._breakpoints.Refresh( file_name ) + + def _SetUpUI( self ): vim.command( 'tab split' ) self._uiTab = vim.current.tabpage @@ -622,7 +630,7 @@ class DebugSession( object ): self.SetCurrentFrame( None ) @RequiresUI() - def SetCurrentFrame( self, frame ): + def SetCurrentFrame( self, frame, reason = '' ): if not frame: self._stackTraceView.Clear() self._variablesView.Clear() @@ -630,11 +638,16 @@ class DebugSession( object ): if not self._codeView.SetCurrentFrame( frame ): return False - if frame: - self._variablesView.SetSyntax( self._codeView.current_syntax ) - self._stackTraceView.SetSyntax( self._codeView.current_syntax ) - self._variablesView.LoadScopes( frame ) - self._variablesView.EvaluateWatches() + # the codeView.SetCurrentFrame already checked the frame was valid and + # countained a valid source + self._variablesView.SetSyntax( self._codeView.current_syntax ) + self._stackTraceView.SetSyntax( self._codeView.current_syntax ) + self._variablesView.LoadScopes( frame ) + self._variablesView.EvaluateWatches() + + if reason == 'stopped': + self._breakpoints.ClearTemporaryBreakpoint( frame[ 'source' ][ 'path' ], + frame[ 'line' ] ) return True @@ -1165,8 +1178,22 @@ class DebugSession( object ): def ToggleBreakpoint( self, options ): return self._breakpoints.ToggleBreakpoint( options ) - def SetLineBreakpoint( self, file_name, line_num, options ): - return self._breakpoints.SetLineBreakpoint( file_name, line_num, options ) + def RunTo( self, file_name, line ): + self.ClearTemporaryBreakpoints() + self.SetLineBreakpoint( file_name, + line, + { 'temporary': True }, + lambda: self.Continue() ) + + + def ClearTemporaryBreakpoints( self ): + return self._breakpoints.ClearTemporaryBreakpoints() + + def SetLineBreakpoint( self, file_name, line_num, options, then = None ): + return self._breakpoints.SetLineBreakpoint( file_name, + line_num, + options, + then ) def ClearLineBreakpoint( self, file_name, line_num ): return self._breakpoints.ClearLineBreakpoint( file_name, line_num ) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 8af6536..584370f 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -149,12 +149,15 @@ class StackTraceView( object ): self._DrawStackTrace( thread ) - def _LoadStackTrace( self, thread, infer_current_frame ): + def _LoadStackTrace( self, + thread, + infer_current_frame, + reason = '' ): def consume_stacktrace( message ): thread[ '_frames' ] = message[ 'body' ][ 'stackFrames' ] if infer_current_frame: for frame in thread[ '_frames' ]: - if self._JumpToFrame( frame ): + if self._JumpToFrame( frame, reason ): break self._DrawThreads() @@ -183,11 +186,11 @@ class StackTraceView( object ): else: self._LoadStackTrace( thread, False ) - def _JumpToFrame( self, frame ): + def _JumpToFrame( self, frame, reason = '' ): def do_jump(): if 'line' in frame and frame[ 'line' ] > 0: self._current_frame = frame - return self._session.SetCurrentFrame( self._current_frame ) + return self._session.SetCurrentFrame( self._current_frame, reason ) return False source = frame.get( 'source' ) or {} @@ -211,7 +214,7 @@ class StackTraceView( object ): if self._current_thread is not None: for thread in self._threads: if thread[ 'id' ] == self._current_thread: - self._LoadStackTrace( thread, True ) + self._LoadStackTrace( thread, True, 'stopped' ) return self.LoadThreads( True ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 4ce387d..66f8d40 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -55,6 +55,10 @@ def BufferForFile( file_name ): return vim.buffers[ BufferNumberForFile( file_name ) ] +def BufferExists( file_name ): + return bool( int ( vim.eval( f"bufexists( '{ Escape( file_name ) }' )" ) ) ) + + def NewEmptyBuffer(): bufnr = int( vim.eval( 'bufadd("")' ) ) Call( 'bufload', bufnr ) diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 044c669..aba0578 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -52,6 +52,11 @@ }, "run - default": { "adapter": "debugpy", + "variables": { + "MAKE_ENV_OUTPUT": { + "shell": "${workspaceRoot}/make_env.sh" + } + }, "configuration": { "request": "launch", "type": "python", @@ -60,8 +65,8 @@ "stopOnEntry#json": "${StopOnEntry:true}", "console": "integratedTerminal", "args#json": "${args:[]}", - "env#json": "${env:{\\}}", - "igored#json#s": "string not json" + "igored#json#s": "string not json", + "env#json": "${MAKE_ENV_OUTPUT}" }, "breakpoints": { "exception": { diff --git a/support/test/python/simple_python/make_env.sh b/support/test/python/simple_python/make_env.sh new file mode 100755 index 0000000..a4da8ca --- /dev/null +++ b/support/test/python/simple_python/make_env.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +cat <<"EOF" +{ + "Something": "Value1", + "SomethingElse": "Value2" +} +EOF + diff --git a/support/test/python/simple_python/print_env.py b/support/test/python/simple_python/print_env.py new file mode 100644 index 0000000..4b88f2d --- /dev/null +++ b/support/test/python/simple_python/print_env.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +import os + + +def Main(): + print( os.environ.get( 'Something', 'ERROR' ) ) + print( os.environ.get( 'SomethingElse', 'ERROR' ) ) + + for k, v in os.environ: + print( f'{ k } = "{ v }"' ) + + +Main() + diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index f6cafa5..e6a1357 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -6,38 +6,6 @@ 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 @@ -78,68 +46,6 @@ function! Test_Signs_Placed_Using_API_Are_Shown() %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 @@ -200,8 +106,8 @@ function Test_DisableBreakpointWhileDebugging() \ 16 ) \ } ) - " Add the breakpoint - call feedkeys( "\", 'xt' ) + call setpos( '.', [ 0, 1, 1 ] ) + call vimspector#SetLineBreakpoint( 'simple.cpp', 16 ) call WaitForAssert( {-> \ vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorCode', @@ -211,7 +117,6 @@ function Test_DisableBreakpointWhileDebugging() \ } ) " Run to breakpoint - call setpos( '.', [ 0, 15, 1 ] ) call feedkeys( "\", 'xt' ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 ) call WaitForAssert( {-> @@ -258,6 +163,58 @@ function Test_DisableBreakpointWhileDebugging() %bwipeout! endfunction +function! Test_Add_Breakpoints_In_File_Then_Open() + lcd testdata/cpp/simple + + " Set and clear without file open + call vimspector#SetLineBreakpoint( 'simple.cpp', 16 ) + call vimspector#ClearLineBreakpoint( 'simple.cpp', 16 ) + + " Clear non-set breakpoint + call vimspector#ClearLineBreakpoint( 'simple.cpp', 1 ) + + " Re-add + call vimspector#SetLineBreakpoint( 'simple.cpp', 16 ) + + " Open and expect sign to be added + edit simple.cpp + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', + \ 16, + \ 'vimspectorBP', + \ 9 ) + call vimspector#LaunchWithSettings( { 'configuration': 'run-to-breakpoint' } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 ) + + call vimspector#test#setup#Reset() + lcd - + %bwipeout! +endfunction + +function! Test_Add_Breakpoints_In_NonOpenedFile_RunToBreak() + lcd testdata/cpp/simple + + " add + call vimspector#SetLineBreakpoint( 'simple.cpp', 16 ) + + call vimspector#LaunchWithSettings( { + \ 'configuration': 'run-to-breakpoint-specify-file', + \ 'prog': 'simple' + \ } ) + call WaitFor( {-> bufexists( 'simple.cpp' ) } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 16 ) + + call vimspector#test#signs#AssertSignAtLine( + \ 'VimspectorCode', + \ 16, + \ 'vimspectorPCBP', + \ 200 ) + + call vimspector#test#setup#Reset() + lcd - + %bwipeout! +endfunction + function! SetUp_Test_Insert_Code_Above_Breakpoint() let g:vimspector_enable_mappings = 'HUMAN' endfunction @@ -308,7 +265,6 @@ function! Test_Insert_Code_Above_Breakpoint() " Delete it call feedkeys( "\", 'xt' ) call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 26 ) - endfunction function! SetUp_Test_Conditional_Line_Breakpoint() @@ -360,8 +316,11 @@ function! Test_Conditional_Line_Breakpoint() \ 'vimspectorBP', \ 9 ) - call setpos( '.', [ 0, 17, 1 ] ) - call vimspector#ToggleBreakpoint( { 'condition': 'argc == 1' } ) + call setpos( '.', [ 0, 1, 1 ] ) + call vimspector#SetLineBreakpoint( + \ 'simple.cpp', + \ 17, + \ { 'condition': 'argc == 1' } ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 17, @@ -724,7 +683,6 @@ function! Test_Custom_Breakpoint_Priority_Partial() unlet! g:vimspector_sign_priority endfunction - function! Test_Add_Line_BP_In_Other_File_While_Debugging() let moo = 'moo.py' let cow = 'cow.py' diff --git a/tests/breakpoints_doublewidth.test.vim b/tests/breakpoints_doublewidth.test.vim index 8ab35bb..cfd4e6c 100644 --- a/tests/breakpoints_doublewidth.test.vim +++ b/tests/breakpoints_doublewidth.test.vim @@ -7,38 +7,6 @@ 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 @@ -132,7 +100,7 @@ function! Test_Use_Mappings_HUMAN() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 ) call WaitForAssert( {-> - \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cp', 16 ) + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 16 ) \ } ) call vimspector#test#setup#Reset() diff --git a/tests/language_go.test.vim b/tests/language_go.test.vim index b6a7da4..200d5d6 100644 --- a/tests/language_go.test.vim +++ b/tests/language_go.test.vim @@ -45,3 +45,28 @@ function! Test_Go_Simple() lcd - %bwipeout! endfunction + + +function! Test_Run_To_Cursor() + let fn='hello-world.go' + lcd ../support/test/go/hello_world + exe 'edit ' . fn + + call vimspector#SetLineBreakpoint( fn, 4 ) + call vimspector#Launch() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 4, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 4 ) + \ } ) + + call cursor( 5, 1 ) + call vimspector#RunToCursor() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 5, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 5 ) + \ } ) + + call vimspector#test#setup#Reset() + lcd - + %bwipeout! +endfunction diff --git a/tests/lib/autoload/vimspector/test/signs.vim b/tests/lib/autoload/vimspector/test/signs.vim index a6bb698..cf64520 100644 --- a/tests/lib/autoload/vimspector/test/signs.vim +++ b/tests/lib/autoload/vimspector/test/signs.vim @@ -1,6 +1,7 @@ function! vimspector#test#signs#AssertCursorIsAtLineInBuffer( buffer, \ line, \ column ) abort + call WaitFor( {-> bufexists( a:buffer ) } ) call WaitForAssert( {-> \ assert_equal( fnamemodify( a:buffer, ':p' ), \ fnamemodify( bufname( '%' ), ':p' ), @@ -15,6 +16,7 @@ function! vimspector#test#signs#AssertCursorIsAtLineInBuffer( buffer, endfunction function! vimspector#test#signs#AssertPCIsAtLineInBuffer( buffer, line ) abort + call WaitFor( {-> bufexists( a:buffer ) } ) let signs = sign_getplaced( a:buffer, { \ 'group': 'VimspectorCode', \ } ) diff --git a/tests/mappings.test.vim b/tests/mappings.test.vim new file mode 100644 index 0000000..fcd19fe --- /dev/null +++ b/tests/mappings.test.vim @@ -0,0 +1,123 @@ +function! SetUp() + 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()' ) ) + call assert_true( hasmapto( 'vimspector#RunToCursor()' ) ) +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_Use_Mappings_HUMAN() + let g:vimspector_enable_mappings = 'HUMAN' +endfunction + +function! Test_Use_Mappings_HUMAN() + call ThisTestIsFlaky() + 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 and clear using API + call vimspector#SetLineBreakpoint( 'simple.cpp', 15 ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', + \ 15, + \ 'vimspectorBP', + \ 9 ) + + call vimspector#ClearLineBreakpoint( 'simple.cpp', 15 ) + 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.cpp', 16 ) + \ } ) + + " Run to cursor + call cursor( 9, 1 ) + call feedkeys( "\\\", 'xt' ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 9, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 9 ) + \ } ) + + + call vimspector#test#setup#Reset() + + lcd - + %bwipeout! +endfunction + diff --git a/tests/temporary_breakpoints.test.vim b/tests/temporary_breakpoints.test.vim new file mode 100644 index 0000000..7bcad55 --- /dev/null +++ b/tests/temporary_breakpoints.test.vim @@ -0,0 +1,200 @@ +function! SetUp() + call vimspector#test#setup#SetUpWithMappings( v:none ) +endfunction + +function! ClearDown() + call vimspector#test#setup#ClearDown() +endfunction + +function s:Start() + call vimspector#SetLineBreakpoint( 'moo.py', 13 ) + call vimspector#Launch() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 1, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 1 ) + call vimspector#Continue() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 13, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 13 ) +endfunction + +function Test_Run_To_Cursor_Simple() + " Run to a position that will certainly be executed + lcd ../support/test/python/multiple_files + call vimspector#SetLineBreakpoint( 'moo.py', 13 ) + call s:Start() + + call cursor( 8, 27 ) + call vimspector#RunToCursor() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 8, 27 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 8 ) + \ } ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 9, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 9 ) + " Check there is no breakpoint set on line 8 + call WaitForAssert( {-> + \ vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', 8 ) + \ } ) + call vimspector#test#setup#Reset() + lcd - + %bwipe! +endfunction + +function Test_Run_To_Cursor_On_NonBreaking_Line() + " Run to a position that will certainly be executed, but is not a real line + lcd ../support/test/python/multiple_files + call vimspector#SetLineBreakpoint( 'moo.py', 13 ) + call s:Start() + + call cursor( 7, 1 ) + " Interestingly, debugpy moves the breakpoint to the previous line, which is + " kinda annoying + call vimspector#RunToCursor() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 6, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 6 ) + \ } ) + call vimspector#StepOver() + " It's a loop, so we go up a line + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 5, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 5 ) + + " Check there is no breakpoint set on lines 7 and 6: + " 7 - where we put the 'temporary' breakpoint + " 6 - where it got placed + " + " FIXME: This is broken, we don't _know_ that the breakpoint that was hit was + " the temporary one, and there's no way to know. + " + " I wonder if the relocated breakpoint can be matched with the _original_ + " breakpoint + call WaitForAssert( {-> + \ vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', 7 ) + \ } ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', 6 ) + \ } ) + call vimspector#test#setup#Reset() + lcd - + %bwipe! +endfunction + +function Test_Run_To_Cursor_Different_File() + " Run into a different file + " Run to a position that will certainly be executed, but is not a real line + lcd ../support/test/python/multiple_files + call vimspector#SetLineBreakpoint( 'moo.py', 13 ) + call s:Start() + + edit cow.py + call cursor( 2, 1 ) + call vimspector#RunToCursor() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'cow.py', 2, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'cow.py', 2 ) + \ } ) + + bu moo.py + call cursor( 9, 12 ) + call vimspector#RunToCursor() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 9, 12 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 9 ) + \ } ) + + call vimspector#test#setup#Reset() + lcd - + %bwipe! +endfunction + +function Test_Run_To_Cursor_Hit_Another_Breakpoint() + " Run to cursor, but hit a non-temporary breakpoint + lcd ../support/test/python/multiple_files + call vimspector#SetLineBreakpoint( 'moo.py', 13 ) + call s:Start() + + call vimspector#SetLineBreakpoint( 'moo.py', 5 ) + call cursor( 6, 1 ) + + call vimspector#RunToCursor() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 5, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 5 ) + \ } ) + + " The temporary breakpoint is still there + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorCode', + \ 6, + \ 'vimspectorBP', + \ 9 ) + + call vimspector#ClearLineBreakpoint( 'moo.py', 5 ) + + call cursor( 8, 1 ) + call vimspector#RunToCursor() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 8, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 8 ) + \ } ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', 6 ) + \ } ) + + call vimspector#test#setup#Reset() + lcd - + %bwipe! +endfunction + +function! Test_InvalidBreakpoint() + " Run to cursor, but hit a non-temporary breakpoint + lcd ../support/test/python/multiple_files + call vimspector#SetLineBreakpoint( 'moo.py', 13 ) + call s:Start() + + call vimspector#SetLineBreakpoint( 'moo.py', 9 ) + + edit .vimspector.json + call cursor( 1, 1 ) + call vimspector#RunToCursor() + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 9, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 9 ) + \ } ) + + call vimspector#test#setup#Reset() + lcd - + %bwipe! +endfunction + +function! Test_StartDebuggingWithRunToCursor() + lcd ../support/test/python/multiple_files + edit moo.py + call cursor( 9, 1 ) + call vimspector#RunToCursor() + " Stop on entry is still hit + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 1, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 1 ) + call vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorCode', + \ 9, + \ 'vimspectorBP', + \ 9 ) + + call vimspector#Continue() + " Runs to cursor + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 9, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 9 ) + + call vimspector#StepOver() + " And claers the temp breakpoint + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 8, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 8 ) + + call WaitForAssert( {-> + \ vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode', 9 ) + \ } ) + + lcd - +endfunction diff --git a/tests/testdata/cpp/simple/.vimspector.json b/tests/testdata/cpp/simple/.vimspector.json index 176a44d..0dca061 100644 --- a/tests/testdata/cpp/simple/.vimspector.json +++ b/tests/testdata/cpp/simple/.vimspector.json @@ -46,6 +46,28 @@ } } }, + "run-to-breakpoint-specify-file": { + "adapter": "vscode-cpptools", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/${prog}", + "cwd": "${workspaceRoot}", + "externalConsole": false, + "stopAtEntry": false, + "stopOnEntry": false, + "MImode": "${VIMSPECTOR_MIMODE}" + }, + "breakpoints": { + "exception": { + "cpp_catch": "", + "cpp_throw": "", + "objc_catch": "", + "objc_throw": "", + "swift_catch": "", + "swift_throw": "" + } + } + }, "calculate-some-variable": { "adapter": "vscode-cpptools", "variables": { From d5b94112566eeb381230b306f3c4c01020986edd Mon Sep 17 00:00:00 2001 From: Emmanuel Thompson Date: Thu, 5 Nov 2020 13:59:53 -0500 Subject: [PATCH 371/609] Add a GetConfigurations function --- README.md | 10 +++++++ autoload/vimspector.vim | 10 +++++++ python3/vimspector/debug_session.py | 45 ++++++++++++++++------------- tests/get_configurations.test.vim | 21 ++++++++++++++ 4 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 tests/get_configurations.test.vim diff --git a/README.md b/README.md index 38bd320..1e4e491 100644 --- a/README.md +++ b/README.md @@ -671,6 +671,16 @@ Vimspector uses the following logic to choose a configuration to launch: See [the reference guide][vimspector-ref-config-selection] for details. + +### Get configurations + +* Use `vimspector#GetConfigurations()` to get a list of configurations + +For example, to get an array of configurations and fuzzy matching on the result +```viml +:call matchfuzzy(vimspector#GetConfigurations(), "test::case_1") +``` + ## Breakpoints * Use `vimspector#ToggleBreakpoint([ { 'condition': '' } ])` diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 410fd2d..6509e14 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -277,6 +277,16 @@ function! vimspector#ListBreakpoints() abort py3 _vimspector_session.ListBreakpoints() endfunction +function! vimspector#GetConfigurations() abort + if !s:Enabled() + return + endif + let configurations = py3eval( + \ 'list( _vimspector_session.GetConfigurations()[ 1 ].keys() )' + \ . ' if _vimspector_session else []' ) + return configurations +endfunction + function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort if !s:Enabled() return diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 42b6340..4f1ef0b 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -79,6 +79,24 @@ class DebugSession( object ): self._server_capabilities = {} self.ClearTemporaryBreakpoints() + def GetConfigurations( self ): + current_file = utils.GetBufferFilepath( vim.current.buffer ) + filetypes = utils.GetBufferFiletypes( vim.current.buffer ) + configurations = {} + + for launch_config_file in PathsToAllConfigFiles( VIMSPECTOR_HOME, + current_file, + filetypes ): + self._logger.debug( f'Reading configurations from: {launch_config_file}' ) + if not launch_config_file or not os.path.exists( launch_config_file ): + continue + + with open( launch_config_file, 'r' ) as f: + database = json.loads( minify( f.read() ) ) + configurations.update( database.get( 'configurations' or {} ) ) + + return launch_config_file, configurations + def Start( self, launch_variables = None ): # We mutate launch_variables, so don't mutate the default argument. # https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments @@ -91,10 +109,15 @@ class DebugSession( object ): self._adapter = None current_file = utils.GetBufferFilepath( vim.current.buffer ) - filetypes = utils.GetBufferFiletypes( vim.current.buffer ) - configurations = {} + launch_config_file, configurations = self.GetConfigurations() adapters = {} + if not configurations: + utils.UserMessage( 'Unable to find any debug configurations. ' + 'You need to tell vimspector how to launch your ' + 'application.' ) + return + glob.glob( install.GetGadgetDir( VIMSPECTOR_HOME ) ) for gadget_config_file in PathsToAllGadgetConfigs( VIMSPECTOR_HOME, current_file ): @@ -106,24 +129,6 @@ class DebugSession( object ): a = json.loads( minify( f.read() ) ).get( 'adapters' ) or {} adapters.update( a ) - for launch_config_file in PathsToAllConfigFiles( VIMSPECTOR_HOME, - current_file, - filetypes ): - self._logger.debug( f'Reading configurations from: {launch_config_file}' ) - if not launch_config_file or not os.path.exists( launch_config_file ): - continue - - with open( launch_config_file, 'r' ) as f: - database = json.loads( minify( f.read() ) ) - adapters.update( database.get( 'adapters' ) or {} ) - configurations.update( database.get( 'configurations' or {} ) ) - - if not configurations: - utils.UserMessage( 'Unable to find any debug configurations. ' - 'You need to tell vimspector how to launch your ' - 'application.' ) - return - if 'configuration' in launch_variables: configuration_name = launch_variables.pop( 'configuration' ) elif ( len( configurations ) == 1 and diff --git a/tests/get_configurations.test.vim b/tests/get_configurations.test.vim new file mode 100644 index 0000000..4a37d01 --- /dev/null +++ b/tests/get_configurations.test.vim @@ -0,0 +1,21 @@ +function! SetUp() + call vimspector#test#setup#SetUpWithMappings( v:none ) +endfunction + +function! ClearDown() + call vimspector#test#setup#ClearDown() +endfunction + +function Test_Get_Configurations() + lcd ../support/test/csharp/ + + let configs = vimspector#GetConfigurations() + call assert_equal([ + \ 'launch - netcoredbg', + \ 'launch - mono', + \ ], configs) + + lcd - + %bwipe! +endfunction + From fb86ef924b43ccffe802b18712c9775dd0330cc1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 7 Nov 2020 18:34:38 +0000 Subject: [PATCH 372/609] Update netcoredbg --- README.md | 3 ++- python3/vimspector/debug_adapter_connection.py | 4 ++++ python3/vimspector/gadgets.py | 15 +++++++++------ .../test/csharp/obj/csharp.csproj.nuget.g.props | 7 +++++-- support/test/csharp/obj/project.assets.json | 10 ++++++++-- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1e4e491..6f12096 100644 --- a/README.md +++ b/README.md @@ -1098,7 +1098,8 @@ netcoredbg` "request": "launch", "program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll", "args": [], - "stopAtEntry": true + "stopAtEntry": true, + "cwd": "${workspaceRoot}" } } } diff --git a/python3/vimspector/debug_adapter_connection.py b/python3/vimspector/debug_adapter_connection.py index 63e0c89..283c40c 100644 --- a/python3/vimspector/debug_adapter_connection.py +++ b/python3/vimspector/debug_adapter_connection.py @@ -169,6 +169,10 @@ class DebugAdapterConnection( object ): self._headers = {} def _SendMessage( self, msg ): + if not self._Write: + # Connection was destroyed + return False + msg = json.dumps( msg ) self._logger.debug( 'Sending Message: {0}'.format( msg ) ) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index cc80e4c..5dccf94 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -228,23 +228,23 @@ GADGETS = { 'language': 'csharp', 'enabled': False, 'download': { - 'url': 'https://github.com/Samsung/netcoredbg/releases/download/latest/' - '${file_name}', + 'url': ( 'https://github.com/Samsung/netcoredbg/releases/download/' + '${version}/${file_name}' ), 'format': 'tar', }, 'all': { - 'version': 'master' + 'version': '1.2.0-635' }, 'macos': { - 'file_name': 'netcoredbg-osx-master.tar.gz', + 'file_name': 'netcoredbg-osx.tar.gz', 'checksum': '', }, 'linux': { - 'file_name': 'netcoredbg-linux-master.tar.gz', + 'file_name': 'netcoredbg-linux-bionic.tar.gz', 'checksum': '', }, 'windows': { - 'file_name': 'netcoredbg-win64-master.zip', + 'file_name': 'netcoredbg-win64.zip', 'checksum': '', }, 'do': lambda name, root, gadget: installer.MakeSymlink( @@ -261,6 +261,9 @@ GADGETS = { "pidProperty": "processId", "pidSelect": "ask" }, + "configuration": { + "cwd": "${workspaceRoot}" + } }, } }, diff --git a/support/test/csharp/obj/csharp.csproj.nuget.g.props b/support/test/csharp/obj/csharp.csproj.nuget.g.props index 4751d88..c71f0e6 100644 --- a/support/test/csharp/obj/csharp.csproj.nuget.g.props +++ b/support/test/csharp/obj/csharp.csproj.nuget.g.props @@ -3,12 +3,15 @@ True NuGet - /Users/ben/.vim/bundle/vimspector/support/test/csharp/obj/project.assets.json + $(MSBuildThisFileDirectory)project.assets.json /Users/ben/.nuget/packages/ /Users/ben/.nuget/packages/;/usr/local/share/dotnet/sdk/NuGetFallbackFolder PackageReference - 4.9.4 + 5.7.0 + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/support/test/csharp/obj/project.assets.json b/support/test/csharp/obj/project.assets.json index e6c9b94..bd6c0fc 100644 --- a/support/test/csharp/obj/project.assets.json +++ b/support/test/csharp/obj/project.assets.json @@ -732,10 +732,16 @@ } }, "imports": [ - "net461" + "net461", + "net462", + "net47", + "net471", + "net472", + "net48" ], "assetTargetFallback": true, - "warn": true + "warn": true, + "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/3.1.402/RuntimeIdentifierGraph.json" } } } From 2225735b8041331492dc1fb57de46f0236a59be9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 9 Nov 2020 22:23:37 +0000 Subject: [PATCH 373/609] Recommend CodeLLDB --- README.md | 92 +++++++++++++++++++---------------- python3/vimspector/gadgets.py | 2 +- 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 1e4e491..d779b22 100644 --- a/README.md +++ b/README.md @@ -132,20 +132,20 @@ runtime dependencies). They are categorised by their level of support: * `Experimental`: Working, but not frequently used and rarely tested * `Legacy`: No longer supported, please migrate your config -| Language | Status | Switch (for `install_gadget.py`) | Adapter (for `:VimspectorInstall`) | Dependencies | -|------------------|--------------|----------------------------------|------------------------------------|--------------------------------------------| -| C, C++, etc. | Tested | `--all` or `--enable-c` | vscode-cpptools | mono-core | -| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | -| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] | -| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | -| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | -| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | -| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | -| Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | -| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | -| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | -| Rust (CodeLLDB) | Experimental | `--force-enable-rust` | CodeLLDB | Python 3 | -| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | +| Language | Status | Switch (for `install_gadget.py`) | Adapter (for `:VimspectorInstall`) | Dependencies | +|--------------------|--------------|----------------------------------|------------------------------------|--------------------------------------------| +| C, C++, etc. | Tested | `--all` or `--enable-c` | vscode-cpptools | mono-core | +| Rust, C, C++, etc. | Supported | `--force-enable-rust` | CodeLLDB | Python 3 | +| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | +| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] | +| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | +| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | +| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | +| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | +| Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | +| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | +| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | +| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | ## Other languages @@ -869,7 +869,10 @@ Current tested with the following debug adapters. ## C, C++, Rust, etc. -* C++: [vscode-cpptools](https://github.com/Microsoft/vscode-cpptools) +* [vscode-cpptools](https://github.com/Microsoft/vscode-cpptools) +* On macOS, I *strongly* recommend using [CodeLLDB](#rust) instead for C and C++ +projects. It's really excellent, has fewer dependencies and doesn't open console +apps in another Terminal window. Example `.vimspector.json` (works with both `vscode-cpptools` and `lldb-vscode`. @@ -926,6 +929,12 @@ licensing. } ``` +* CodeLLDB (MacOS) + +CodeLLDB is superior to vscode-cpptools in a number of ways on macOS at least. + +See [Rust](#rust). + * lldb-vscode (MacOS) An alternative is to to use `lldb-vscode`, which comes with llvm. Here's how: @@ -959,6 +968,33 @@ An alternative is to to use `lldb-vscode`, which comes with llvm. Here's how: } ``` +## Rust + +Rust is supported with any gdb/lldb-based debugger. So it works fine with +`vscode-cpptools` and `lldb-vscode` above. However, support for rust is best in +[`CodeLLDB`](https://github.com/vadimcn/vscode-lldb#features). + +* `./install_gadget.py --force-enable-rust` or `:VimspectorInstall CodeLLDB` +* Example: `support/test/rust/vimspector_test` + +```json +{ + "configurations": { + "launch": { + "adapter": "CodeLLDB", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/target/debug/vimspector_test" + } + } + } +} +``` + +* Docs: https://github.com/vadimcn/vscode-lldb/blob/master/MANUAL.md + + + ### Remote debugging The cpptools documentation describes how to attach cpptools to gdbserver using @@ -1373,32 +1409,6 @@ For the launch arguments, see the See [this issue](https://github.com/puremourning/vimspector/issues/3) for more background. -## Rust - -Rust is supported with any gdb/lldb-based debugger. So it works fine with -`vscode-cpptools` and `lldb-vscode` above. However, support for rust is best in -[`CodeLLDB`](https://github.com/vadimcn/vscode-lldb#features). - -* `./install_gadget.py --force-enable-rust` or `:VimspectorInstall CodeLLDB` -* Example: `support/test/rust/vimspector_test` - -```json -{ - "configurations": { - "launch": { - "adapter": "CodeLLDB", - "configuration": { - "request": "launch", - "program": "${workspaceRoot}/target/debug/vimspector_test" - } - } - } -} -``` - -* Docs: https://github.com/vadimcn/vscode-lldb/blob/master/MANUAL.md - - ## Other servers * Java - vscode-javac. This works, but is not as functional as Java Debug diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index cc80e4c..9da9640 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -435,7 +435,7 @@ GADGETS = { }, 'CodeLLDB': { 'language': 'rust', - 'enabled': False, + 'enabled': True, 'download': { 'url': 'https://github.com/vadimcn/vscode-lldb/releases/download/' '${version}/${file_name}', From 2bdb30a45ec020a837473011e4b52c1e295d1222 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 14 Nov 2020 12:34:10 +0000 Subject: [PATCH 374/609] update docs --- docs/Gemfile.lock | 2 +- docs/README.local | 2 +- docs/configuration.md | 15 ++++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index ce0ce98..c28eebb 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -258,4 +258,4 @@ DEPENDENCIES tzinfo-data BUNDLED WITH - 2.0.2 + 2.1.4 diff --git a/docs/README.local b/docs/README.local index 7848fba..d891072 100644 --- a/docs/README.local +++ b/docs/README.local @@ -1,7 +1,7 @@ To update/install: gem install bundler -bundle install +bundle install --path vendor/bundle To run a local server/test the build diff --git a/docs/configuration.md b/docs/configuration.md index 2bb70ab..acf77b4 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -12,7 +12,8 @@ for Vimspector. * [Debug profile configuration](#debug-profile-configuration) * [Replacements and variables](#replacements-and-variables) * [The splat operator](#the-splat-operator) - * [Default values](#default-values) + * [Default values](#default-values) + * [Coercing Types](#coercing-types) * [Configuration Format](#configuration-format) * [Files and locations](#files-and-locations) * [Adapter configurations](#adapter-configurations) @@ -20,7 +21,7 @@ for Vimspector. * [Configuration selection](#configuration-selection) * [Specifying a default configuration](#specifying-a-default-configuration) * [Preventing automatic selection](#preventing-automatic-selection) - * [Exception breakpionts](#exception-breakpionts) + * [Exception Breakpoints](#exception-breakpoints) * [Predefined Variables](#predefined-variables) * [Remote Debugging Support](#remote-debugging-support) * [Python (debugpy) Example](#python-debugpy-example) @@ -29,7 +30,7 @@ for Vimspector. * [Appendix: Configuration file format](#appendix-configuration-file-format) * [Appendix: Editor configuration](#appendix-editor-configuration) - + @@ -164,7 +165,7 @@ the following variable substitutions: the shell command. Note these variables can be supplied by both the debug and adapter configurations and can be either static strings or shell commands. -### The splat operator +#### The splat operator Often we want to create a single `.vimspector.json` entry which encompasses many use cases, as it is tedious to write every use case/start up option in JSON. @@ -205,7 +206,7 @@ You can also combine with static values: This would yield the intuitive result: `[ "First", "one", "two three", "four", "Last" ]` -### Default values +#### Default values You can specify replacements with default values. In this case if the user has not specified a value, they are prompted but with the default value @@ -236,7 +237,7 @@ the closing `}`. For example, the is a common and useful case: This will prompt the user to specify `script`, but it will default to the path to the current file. -### Coercing Types +#### Coercing Types Sometimes, you want to provide an option for a boolean parameter, or want to allow the user to specify more than just strings. Vimspector allows you to do @@ -282,7 +283,7 @@ can force Vimspector to treat the value as a string by appending `#s`, as in: "unlikelyKeyName#json#s": "this is a string, not JSON data" ``` -#### Advanced usage +***Advanced usage:*** The most common usage for this is for number and bool types, but it works for objects too. If you want to be able to specify a whole object (e.g. a whole From 2819e224e7e4f26af92d8a98aa9d1b4473bf8404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Mez=C3=AAncio?= Date: Sat, 14 Nov 2020 19:34:15 -0300 Subject: [PATCH 375/609] Add lua support through local-lua-debugger-vscode Add the lua adapter to gadgets.py and installer.py, update the README.md file and create basic tests using lua, luajit and love. --- README.md | 63 ++++++++++++++++++++++-- python3/vimspector/gadgets.py | 27 ++++++++++ python3/vimspector/installer.py | 7 +++ support/test/lua/love/.vimspector.json | 17 +++++++ support/test/lua/love/main.lua | 21 ++++++++ support/test/lua/simple/.vimspector.json | 29 +++++++++++ support/test/lua/simple/simple.lua | 25 ++++++++++ 7 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 support/test/lua/love/.vimspector.json create mode 100644 support/test/lua/love/main.lua create mode 100644 support/test/lua/simple/.vimspector.json create mode 100644 support/test/lua/simple/simple.lua diff --git a/README.md b/README.md index 42b9eb0..318e8e9 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,7 @@ runtime dependencies). They are categorised by their level of support: | Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | | Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | | Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | +| Lua | Supported | `--force-enable-lua` | local-lua-debugger-vscode | Node, Npm, Lua interpreter | | C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | | C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | | Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | @@ -1309,7 +1310,7 @@ https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome It allows you to debug scripts running inside chrome from within Vim. -* `./install_gadget.py --force-enable-chrome` or `:VimspectorInstall +* `./install_gadget.py --force-enable-chrome` or `:VimspectorInstall debugger-for-chrome` * Example: `support/test/chrome` @@ -1345,7 +1346,7 @@ use it with Vimspector. * Set up [YCM for java][YcmJava]. * Get Vimspector to download the java debug plugin: `install_gadget.py --force-enable-java ` or - `:VimspectorInstall java-debug-adapter` + `:VimspectorInstall java-debug-adapter` * Configure Vimspector for your project using the `vscode-java` adapter, e.g.: ```json @@ -1410,6 +1411,60 @@ For the launch arguments, see the See [this issue](https://github.com/puremourning/vimspector/issues/3) for more background. +## Lua + +Lua is supported through +[local-lua-debugger-vscode](https://github.com/tomblind/local-lua-debugger-vscode). +This debugger uses stdio to communicate with the running process, so calls to +`io.read` will cause problems. + +* `./install_gadget.py --force-enable-lua` or `:VimspectorInstall local-lua-debugger-vscode` +* Examples: `support/test/lua/simple` and `support/test/lua/love` + +```json +{ + "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#", + "configurations": { + "lua": { + "adapter": "lua-local", + "configuration": { + "request": "launch", + "type": "lua-local", + "cwd": "${workspaceFolder}", + "program": { + "lua": "lua", + "file": "${file}" + } + } + }, + "luajit": { + "adapter": "lua-local", + "configuration": { + "request": "launch", + "type": "lua-local", + "cwd": "${workspaceFolder}", + "program": { + "lua": "luajit", + "file": "${file}" + } + } + }, + "love": { + "adapter": "lua-local", + "configuration": { + "request": "launch", + "type": "lua-local", + "cwd": "${workspaceFolder}", + "program": { + "command": "love" + }, + "args": ["${workspaceFolder}"] + } + } + } +} +``` + ## Other servers * Java - vscode-javac. This works, but is not as functional as Java Debug @@ -1685,7 +1740,7 @@ augroup END comment](https://github.com/puremourning/vimspector/issues/90#issuecomment-577857322) 4. Can I specify answers to the annoying questions about exception breakpoints in my `.vimspector.json` ? Yes, see [here][vimspector-ref-exception]. -5. Do I have to specify the file to execute in `.vimspector.json`, or could it be the current vim file? +5. Do I have to specify the file to execute in `.vimspector.json`, or could it be the current vim file? You don't need to. You can specify $file for the current active file. See [here][vimspector-ref-var] for complete list of replacements in the configuration file. 6. You allow comments in `.vimspector.json`, but Vim highlights these as errors, do you know how to make this not-an-error? Yes, put this in @@ -1733,7 +1788,7 @@ A message from the author about the motivation for this plugin: > > I created Vimspector because I find changing tools frustrating. `gdb` for c++, > `pdb` for python, etc. Each has its own syntax. Each its own lexicon. Each its -> own foibles. +> own foibles. > > I designed the configuration system in such a way that the configuration can > be committed to source control so that it _just works_ for any of your diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 6524967..c0c4890 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -495,4 +495,31 @@ GADGETS = { }, }, }, + 'local-lua-debugger-vscode': { + 'language': 'lua', + 'enabled': False, + 'repo': { + 'url': 'https://github.com/tomblind/local-lua-debugger-vscode.git', + 'ref': 'release-0.2.0' + }, + 'all': { + 'version': '0.2.0', + }, + 'do': lambda name, root, gadget: installer.InstallLuaLocal( name, + root, + gadget ), + 'adapters': { + 'lua-local': { + 'command': [ + 'node', + '${gadgetDir}/local-lua-debugger-vscode/extension/debugAdapter.js' + ], + 'name': 'lua-local', + 'configuration': { + 'interpreter': 'lua', + 'extensionPath': '${gadgetDir}/local-lua-debugger-vscode' + } + } + }, + }, } diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 36f7bec..98a2a42 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -425,6 +425,13 @@ def InstallNodeDebug( name, root, gadget ): MakeSymlink( name, root ) +def InstallLuaLocal( name, root, gadget ): + with CurrentWorkingDir( root ): + CheckCall( [ 'npm', 'install' ] ) + CheckCall( [ 'npm', 'run', 'build' ] ) + MakeSymlink( name, root ) + + def InstallGagdet( name: str, gadget: dict, manifest: Manifest, diff --git a/support/test/lua/love/.vimspector.json b/support/test/lua/love/.vimspector.json new file mode 100644 index 0000000..8864784 --- /dev/null +++ b/support/test/lua/love/.vimspector.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#", + "configurations": { + "love": { + "adapter": "lua-local", + "configuration": { + "request": "launch", + "type": "lua-local", + "cwd": "${workspaceFolder}", + "program": { + "command": "love" + }, + "args": ["${workspaceFolder}"] + } + } + } +} diff --git a/support/test/lua/love/main.lua b/support/test/lua/love/main.lua new file mode 100644 index 0000000..0e67799 --- /dev/null +++ b/support/test/lua/love/main.lua @@ -0,0 +1,21 @@ +if pcall(require, 'lldebugger') then + require('lldebugger').start() +end + +local rect = {0, 0, 64, 64} + + +function love.update(dt) + rect[1] = rect[1] + 10 * dt + rect[2] = rect[2] + 10 * dt +end + + +function love.draw() + love.graphics.rectangle('fill', rect[1], rect[2], rect[3], rect[4]) +end + + +function love.keypressed() + love.event.quit() +end diff --git a/support/test/lua/simple/.vimspector.json b/support/test/lua/simple/.vimspector.json new file mode 100644 index 0000000..ff19723 --- /dev/null +++ b/support/test/lua/simple/.vimspector.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#", + "configurations": { + "lua": { + "adapter": "lua-local", + "configuration": { + "request": "launch", + "type": "lua-local", + "cwd": "${workspaceFolder}", + "program": { + "lua": "lua", + "file": "simple.lua" + } + } + }, + "luajit": { + "adapter": "lua-local", + "configuration": { + "request": "launch", + "type": "lua-local", + "cwd": "${workspaceFolder}", + "program": { + "lua": "luajit", + "file": "simple.lua" + } + } + } + } +} diff --git a/support/test/lua/simple/simple.lua b/support/test/lua/simple/simple.lua new file mode 100644 index 0000000..8c6b1ac --- /dev/null +++ b/support/test/lua/simple/simple.lua @@ -0,0 +1,25 @@ +if pcall(require, 'lldebugger') then + require('lldebugger').start() +end + +local separator = ' ' + + +local function createMessage() + local words = {} + table.insert(words, 'Hello') + table.insert(words, 'world') + return table.concat(words, separator) +end + + +local function withEmphasis(func) + return function() + return func() .. '!' + end +end + + +createMessage = withEmphasis(createMessage) + +print(createMessage()) From 1eb2bc2199d5ea83aa64bab849da875938c653f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Mez=C3=AAncio?= Date: Sat, 14 Nov 2020 20:27:27 -0300 Subject: [PATCH 376/609] Add lua to README index. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 318e8e9..af19859 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ For a tutorial and usage overview, take a look at the * [Java](#java) * [Usage with YouCompleteMe](#usage-with-youcompleteme) * [Other LSP clients](#other-lsp-clients) + * [Lua](#lua) * [Rust](#rust) * [Other servers](#other-servers) * [Customisation](#customisation) From 69bb2737d1649b359909e60c8f560e9d63f5a73c Mon Sep 17 00:00:00 2001 From: Tama Date: Mon, 16 Nov 2020 11:37:12 +0100 Subject: [PATCH 377/609] Fixed typo 'exisitng' in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42b9eb0..b23f2d6 100644 --- a/README.md +++ b/README.md @@ -351,7 +351,7 @@ of adapters just installed, whereas `:VimspectorInstall` will _update_ it, overwriting only newly changed or installed adapters. If you want to just add a new adapter using the script without destroying the -exisitng ones, add `--update-gadget-config`, as in: +existing ones, add `--update-gadget-config`, as in: ```bash $ ./install_gadget.py --enable-tcl From 85865e00123b7bbaffff14702e444c9fac730a71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Mez=C3=AAncio?= Date: Mon, 16 Nov 2020 15:08:55 -0300 Subject: [PATCH 378/609] Add regression tests for lua support Change Dockerfile to install lua, luajit and love and also to install nodejs 12 needed to build the lua debug adapter. Create the love-headless test in support/test/lua to test love without an x server. --- .github/workflows/build.yaml | 2 +- .gitignore | 1 + README.md | 4 +- python3/vimspector/gadgets.py | 4 +- .../test/lua/love-headless/.vimspector.json | 17 ++++ support/test/lua/love-headless/conf.lua | 12 +++ support/test/lua/love-headless/main.lua | 10 +++ tests/ci/image/Dockerfile | 10 ++- tests/language_lua.test.vim | 84 +++++++++++++++++++ 9 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 support/test/lua/love-headless/.vimspector.json create mode 100644 support/test/lua/love-headless/conf.lua create mode 100644 support/test/lua/love-headless/main.lua create mode 100644 tests/language_lua.test.vim diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1f3270b..f592b21 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -86,7 +86,7 @@ jobs: - run: | brew update-reset brew doctor || true - for p in macvim tcl-tk llvm; do + for p in macvim tcl-tk llvm lua luajit love; do brew install $p brew outdated $p || brew upgrade $p done diff --git a/.gitignore b/.gitignore index 64341cf..77f22a9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ tests/*.res tests/messages tests/debuglog test.log +*.testlog gadgets/ package/ *.pyc diff --git a/README.md b/README.md index bcba8cf..bb13d1a 100644 --- a/README.md +++ b/README.md @@ -141,10 +141,10 @@ runtime dependencies). They are categorised by their level of support: | Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] | | TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | | Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | +| Lua | Supported | `--all` or `--enable-lua` | local-lua-debugger-vscode | Node >=12.13.0, Npm, Lua interpreter | | Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | | Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | | Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | -| Lua | Supported | `--force-enable-lua` | local-lua-debugger-vscode | Node, Npm, Lua interpreter | | C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | | C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | | Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | @@ -1419,7 +1419,7 @@ Lua is supported through This debugger uses stdio to communicate with the running process, so calls to `io.read` will cause problems. -* `./install_gadget.py --force-enable-lua` or `:VimspectorInstall local-lua-debugger-vscode` +* `./install_gadget.py --enable-lua` or `:VimspectorInstall local-lua-debugger-vscode` * Examples: `support/test/lua/simple` and `support/test/lua/love` ```json diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index c0c4890..363def6 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -497,10 +497,10 @@ GADGETS = { }, 'local-lua-debugger-vscode': { 'language': 'lua', - 'enabled': False, + 'enabled': True, 'repo': { 'url': 'https://github.com/tomblind/local-lua-debugger-vscode.git', - 'ref': 'release-0.2.0' + 'ref': 'release-${version}' }, 'all': { 'version': '0.2.0', diff --git a/support/test/lua/love-headless/.vimspector.json b/support/test/lua/love-headless/.vimspector.json new file mode 100644 index 0000000..8864784 --- /dev/null +++ b/support/test/lua/love-headless/.vimspector.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#", + "configurations": { + "love": { + "adapter": "lua-local", + "configuration": { + "request": "launch", + "type": "lua-local", + "cwd": "${workspaceFolder}", + "program": { + "command": "love" + }, + "args": ["${workspaceFolder}"] + } + } + } +} diff --git a/support/test/lua/love-headless/conf.lua b/support/test/lua/love-headless/conf.lua new file mode 100644 index 0000000..f74d620 --- /dev/null +++ b/support/test/lua/love-headless/conf.lua @@ -0,0 +1,12 @@ +function love.conf(t) + t.modules.audio = false + t.modules.font = false + t.modules.graphics = false + t.modules.image = false + t.modules.mouse = false + t.modules.physics = false + t.modules.sound = false + t.modules.touch = false + t.modules.video = false + t.modules.window = false +end diff --git a/support/test/lua/love-headless/main.lua b/support/test/lua/love-headless/main.lua new file mode 100644 index 0000000..899a864 --- /dev/null +++ b/support/test/lua/love-headless/main.lua @@ -0,0 +1,10 @@ +if pcall(require, 'lldebugger') then + require('lldebugger').start() +end + +local time = 0 + +function love.update(dt) + time = time + dt + love.event.quit() +end diff --git a/tests/ci/image/Dockerfile b/tests/ci/image/Dockerfile index 15d0ad0..4494cf1 100644 --- a/tests/ci/image/Dockerfile +++ b/tests/ci/image/Dockerfile @@ -4,6 +4,11 @@ ENV DEBIAN_FRONTEND=noninteractive ENV LC_ALL C.UTF-8 RUN apt-get update && \ + apt install -y curl dirmngr apt-transport-https lsb-release ca-certificates \ + software-properties-common && \ + curl -sL https://deb.nodesource.com/setup_12.x | bash - && \ + add-apt-repository ppa:bartbes/love-stable -y && \ + apt-get update && \ apt-get -y dist-upgrade && \ apt-get -y install python3-dev \ python3-pip \ @@ -17,9 +22,10 @@ RUN apt-get update && \ tcllib \ gdb \ lldb \ - curl \ nodejs \ - npm && \ + lua5.1 \ + luajit \ + love && \ apt-get -y autoremove RUN ln -fs /usr/share/zoneinfo/Europe/London /etc/localtime && \ diff --git a/tests/language_lua.test.vim b/tests/language_lua.test.vim new file mode 100644 index 0000000..0dafb10 --- /dev/null +++ b/tests/language_lua.test.vim @@ -0,0 +1,84 @@ +function! SetUp() + let g:vimspector_enable_mappings = 'HUMAN' + call vimspector#test#setup#SetUpWithMappings( v:none ) +endfunction + +function! ClearDown() + call vimspector#test#setup#ClearDown() +endfunction + + +function! BaseTest( configuration ) + let fn='simple.lua' + lcd ../support/test/lua/simple + exe 'edit ' . fn + + call vimspector#SetLineBreakpoint( fn, 9 ) + call vimspector#LaunchWithSettings( { 'configuration': a:configuration } ) + + " This debugger is ignoring stopOnEntry when not running a custom executable + " and always stopping on the first line after setting the hook. This first + " check assumes that behavior. + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 5, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 5 ) + \ } ) + " Continue + call feedkeys( "\", 'xt' ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 9, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 9 ) + \ } ) + + " Step + call feedkeys( "\", 'xt' ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 10, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 10 ) + \ } ) + + call vimspector#test#setup#Reset() + + lcd - + %bwipeout! +endfunction + + +function! Test_Lua_Simple() + call BaseTest( 'lua' ) +endfunction + + +function! Test_Lua_Luajit() + call BaseTest( 'luajit' ) +endfunction + + +function! Test_Lua_Love() + let fn='main.lua' + lcd ../support/test/lua/love-headless + exe 'edit ' . fn + + call vimspector#SetLineBreakpoint( fn, 8 ) + call vimspector#LaunchWithSettings( { 'configuration': 'love' } ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 8, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 8 ) + \ } ) + + " Step + call feedkeys( "\", 'xt' ) + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 9, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 9 ) + \ } ) + + call vimspector#test#setup#Reset() + + lcd - + %bwipeout! +endfunction From 5b8883791934194ea6822c0a631941230aa8e6e6 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 16 Nov 2020 20:14:39 +0000 Subject: [PATCH 379/609] Print the correct version for a git tag --- python3/vimspector/installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 98a2a42..b39398f 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -488,7 +488,7 @@ def InstallGagdet( name: str, url = string.Template( gadget[ 'repo' ][ 'url' ] ).substitute( spec ) ref = string.Template( gadget[ 'repo' ][ 'ref' ] ).substitute( spec ) - print( f"Installing {name}@{gadget[ 'repo' ][ 'ref' ]}..." ) + print( f"Installing {name}@{ref}..." ) spec[ 'repo' ] = gadget[ 'repo' ] if not manifest.RequiresUpdate( name, spec ): save_adapters() From cd3b5f5baa5838628901611fe4d7a6471ff7bcc8 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 16 Nov 2020 21:17:22 +0000 Subject: [PATCH 380/609] Update mono debug; even though it doesn't work --- python3/vimspector/breakpoints.py | 15 ++++++++--- python3/vimspector/gadgets.py | 13 +++++++--- python3/vimspector/terminal.py | 7 +++--- support/test/csharp/.vimspector.json | 6 ++++- support/test/csharp/Program.cs | 37 +++++++++++++++++++++++----- 5 files changed, 61 insertions(+), 17 deletions(-) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 19075ae..679cd9c 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -295,9 +295,16 @@ class ProjectBreakpoints( object ): awaiting = 0 - def response_received(): + def response_received( *failure_args ): nonlocal awaiting awaiting = awaiting - 1 + + if failure_args and self._connection: + reason, msg = failure_args + utils.UserMessage( 'Unable to set breakpoint: {0}'.format( reason ), + persist = True, + error = True ) + if awaiting == 0 and doneHandler: doneHandler() @@ -364,7 +371,7 @@ class ProjectBreakpoints( object ): }, 'sourceModified': False, # TODO: We can actually check this }, - failure_handler = lambda *_: response_received() + failure_handler = response_received ) # TODO: Add the _configured_breakpoints to function breakpoints @@ -388,7 +395,7 @@ class ProjectBreakpoints( object ): 'breakpoints': breakpoints, } }, - failure_handler = lambda *_: response_received() + failure_handler = response_received ) if self._exception_breakpoints: @@ -399,7 +406,7 @@ class ProjectBreakpoints( object ): 'command': 'setExceptionBreakpoints', 'arguments': self._exception_breakpoints }, - failure_handler = lambda *_: response_received() + failure_handler = response_received ) if awaiting == 0 and doneHandler: diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 363def6..3682ab0 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -237,7 +237,8 @@ GADGETS = { }, 'macos': { 'file_name': 'netcoredbg-osx.tar.gz', - 'checksum': '', + 'checksum': + '71c773e34d358950f25119bade7e3081c4c2f9d71847bd49027ca5792e918beb', }, 'linux': { 'file_name': 'netcoredbg-linux-bionic.tar.gz', @@ -279,9 +280,9 @@ GADGETS = { }, 'all': { 'file_name': 'vscode-mono-debug.vsix', - 'version': '0.15.8', + 'version': '0.16.2', 'checksum': - '723eb2b621b99d65a24f215cb64b45f5fe694105613a900a03c859a62a810470', + '121eca297d83daeeb1e6e1d791305d1827998dbd595c330086b3b94d33dba3b9', }, 'adapters': { 'vscode-mono-debug': { @@ -293,6 +294,12 @@ GADGETS = { "attach": { "pidSelect": "none" }, + "configuration": { + "cwd": "${workspaceRoot}", + "console": "integratedTerminal", + "args": [], + "env": {} + } }, } }, diff --git a/python3/vimspector/terminal.py b/python3/vimspector/terminal.py index a2ed264..6ffd56a 100644 --- a/python3/vimspector/terminal.py +++ b/python3/vimspector/terminal.py @@ -1,5 +1,6 @@ from vimspector import utils, settings +import os import vim @@ -17,9 +18,9 @@ def LaunchTerminal( api_prefix, else: term = existing_term - cwd = params[ 'cwd' ] - args = params[ 'args' ] - env = params.get( 'env', {} ) + cwd = params[ 'cwd' ] or os.getcwd() + args = params[ 'args' ] or [] + env = params.get( 'env' ) or {} term_options = { 'vertical': 1, diff --git a/support/test/csharp/.vimspector.json b/support/test/csharp/.vimspector.json index d5a68b6..524ae1a 100644 --- a/support/test/csharp/.vimspector.json +++ b/support/test/csharp/.vimspector.json @@ -13,7 +13,11 @@ "adapter": "vscode-mono-debug", "configuration": { "request": "launch", - "program": "${workspaceRoot}/Program.exe" + "program": "${workspaceRoot}/Program.exe", + "console": "integratedTerminal", + "cwd": "${workspaceRoot}", + "args": [], + "env": {} } } } diff --git a/support/test/csharp/Program.cs b/support/test/csharp/Program.cs index 260d2f4..ff90ed6 100644 --- a/support/test/csharp/Program.cs +++ b/support/test/csharp/Program.cs @@ -2,11 +2,36 @@ namespace csharp { - class Program - { - static void Main(string[] args) - { - Console.WriteLine("Hello World!"); - } + class Program + { + string toaster = "Making round of toast"; + static int max_bread = 100; + int bread = max_bread; + + void PrintToast( int r ) { + int this_round = ( max_bread - bread - r); + Console.WriteLine( this.toaster + ": " + this_round ); } + + void MakeToast( int rounds ) { + if (this.bread - rounds < 0) { + throw new Exception( "No moar bread!" ); + } + + this.bread -= rounds; + for (int r = 0; r < rounds; ++r) { + this.PrintToast( r ); + } + + Console.WriteLine( "Got only " + this.bread + " left" ); + } + + static void Main(string[] args) + { + Program p = new Program(); + for (int x = 1; x < 10; ++ x) { + p.MakeToast( x ); + } + } + } } From 47c2cef2a104024ca513d6b9b8c29cd18aaa0bff Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 16 Nov 2020 21:17:36 +0000 Subject: [PATCH 381/609] Use a tag for tclpro to avoid installing every time --- python3/vimspector/gadgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 3682ab0..b024c2d 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -196,7 +196,7 @@ GADGETS = { 'language': 'tcl', 'repo': { 'url': 'https://github.com/puremourning/TclProDebug', - 'ref': 'master' + 'ref': 'v1.0.0' }, 'do': lambda name, root, gadget: installer.InstallTclProDebug( name, root, From b36f9e893ae2c6971b52364032bc669be468261a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 19 Nov 2020 23:26:06 +0000 Subject: [PATCH 382/609] Work around neovim WinBar rendering bug --- python3/vimspector/code.py | 27 +++++++++++++++++++-------- python3/vimspector/output.py | 3 +++ python3/vimspector/utils.py | 6 ++++++ python3/vimspector/variables.py | 13 +++++++------ 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index e1b7f59..628fa63 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -41,14 +41,25 @@ class CodeView( object ): self._current_frame = None with utils.LetCurrentWindow( self._window ): - vim.command( 'nnoremenu WinBar.■\\ Stop :call vimspector#Stop()' ) - vim.command( 'nnoremenu WinBar.▶\\ Cont :call vimspector#Continue()' ) - vim.command( 'nnoremenu WinBar.▷\\ Pause :call vimspector#Pause()' ) - vim.command( 'nnoremenu WinBar.↷\\ Next :call vimspector#StepOver()' ) - vim.command( 'nnoremenu WinBar.→\\ Step :call vimspector#StepInto()' ) - vim.command( 'nnoremenu WinBar.←\\ Out :call vimspector#StepOut()' ) - vim.command( 'nnoremenu WinBar.⟲: :call vimspector#Restart()' ) - vim.command( 'nnoremenu WinBar.✕ :call vimspector#Reset()' ) + if utils.UseWinBar(): + # Buggy neovim doesn't render correctly when the WinBar is defined: + # https://github.com/neovim/neovim/issues/12689 + vim.command( 'nnoremenu WinBar.■\\ Stop ' + ':call vimspector#Stop()' ) + vim.command( 'nnoremenu WinBar.▶\\ Cont ' + ':call vimspector#Continue()' ) + vim.command( 'nnoremenu WinBar.▷\\ Pause ' + ':call vimspector#Pause()' ) + vim.command( 'nnoremenu WinBar.↷\\ Next ' + ':call vimspector#StepOver()' ) + vim.command( 'nnoremenu WinBar.→\\ Step ' + ':call vimspector#StepInto()' ) + vim.command( 'nnoremenu WinBar.←\\ Out ' + ':call vimspector#StepOut()' ) + vim.command( 'nnoremenu WinBar.⟲: ' + ':call vimspector#Restart()' ) + vim.command( 'nnoremenu WinBar.✕ ' + ':call vimspector#Reset()' ) if not signs.SignDefined( 'vimspectorPC' ): signs.DefineSign( 'vimspectorPC', diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index eedefc1..cf2a213 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -210,6 +210,9 @@ class OutputView( object ): utils.CleanUpHiddenBuffer( buf_to_delete ) def _RenderWinBar( self, category ): + if not utils.UseWinBar(): + return + if not self._window.valid: return diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 66f8d40..5ab9872 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -785,3 +785,9 @@ def WindowID( window, tab=None ): if tab is None: tab = window.tabpage return int( Call( 'win_getid', window.number, tab.number ) ) + + +def UseWinBar(): + # Buggy neovim doesn't render correctly when the WinBar is defined: + # https://github.com/neovim/neovim/issues/12689 + return not int( Call( 'has', 'nvim' ) ) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index b50793d..1d62c04 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -159,12 +159,13 @@ class VariablesView( object ): vim.command( 'nnoremap :call vimspector#DeleteWatch()' ) - vim.command( 'nnoremenu 1.1 WinBar.New ' - ':call vimspector#AddWatch()' ) - vim.command( 'nnoremenu 1.2 WinBar.Expand/Collapse ' - ':call vimspector#ExpandVariable()' ) - vim.command( 'nnoremenu 1.3 WinBar.Delete ' - ':call vimspector#DeleteWatch()' ) + if utils.UseWinBar(): + vim.command( 'nnoremenu 1.1 WinBar.New ' + ':call vimspector#AddWatch()' ) + vim.command( 'nnoremenu 1.2 WinBar.Expand/Collapse ' + ':call vimspector#ExpandVariable()' ) + vim.command( 'nnoremenu 1.3 WinBar.Delete ' + ':call vimspector#DeleteWatch()' ) # Set the (global!) balloon expr if supported has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) ) From 87ce1734ea394fd4fb66efeff0e7dc4b4e991ef5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 22 Nov 2020 11:55:42 +0000 Subject: [PATCH 383/609] Update vscode-go to latest version --- README.md | 3 +++ python3/vimspector/gadgets.py | 6 +++--- support/test/go/hello_world/.vimspector.json | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bb13d1a..ff2273f 100644 --- a/README.md +++ b/README.md @@ -1198,6 +1198,9 @@ Requires: } ``` +See the vscode-go docs for +[troubleshooting information](https://github.com/golang/vscode-go/blob/master/docs/debugging.md#troubleshooting) + ## PHP This uses the php-debug, see diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index b024c2d..2ea6bbc 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -353,10 +353,10 @@ GADGETS = { 'v${version}/${file_name}' }, 'all': { - 'version': '0.17.2', - 'file_name': 'Go-0.17.2.vsix', + 'version': '0.18.1', + 'file_name': 'Go-0.18.1.vsix', 'checksum': - '69055b9b86387e2906f20cceb84ab2c4da86c5a5159395a7bbdb74ea3a0f982f', + '80d4522c6cf482cfa6141997e5b458034f67d7065d92e1ce24a0456c405d6061', }, 'adapters': { 'vscode-go': { diff --git a/support/test/go/hello_world/.vimspector.json b/support/test/go/hello_world/.vimspector.json index f320b1b..8ab5091 100644 --- a/support/test/go/hello_world/.vimspector.json +++ b/support/test/go/hello_world/.vimspector.json @@ -2,6 +2,7 @@ "configurations": { "run": { "adapter": "vscode-go", + "default": true, "configuration": { "request": "launch", "program": "${workspaceRoot}/hello-world.go", @@ -10,6 +11,20 @@ "trace": true, "env": { "GO111MODULE": "off" } } + }, + "run-exec": { + // NOTE: To use this you _must_ disable optimistaion: + // go build -o hello_world -gcflags="all=-N -l" + // https://github.com/golang/vscode-go/blob/master/docs/debugging.md#troubleshooting + "adapter": "vscode-go", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/hello-world", + "mode": "exec", + "dlvToolPath": "$HOME/go/bin/dlv", + "trace": true, + "env": { "GO111MODULE": "off" } + } } } } From 3330c704d7ba3d8e254ee56b2730dfe70f1c19a0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 22 Nov 2020 12:46:15 +0000 Subject: [PATCH 384/609] Too many flakes --- tests/lib/run_test.vim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/lib/run_test.vim b/tests/lib/run_test.vim index a8c7bcd..9df6217 100644 --- a/tests/lib/run_test.vim +++ b/tests/lib/run_test.vim @@ -361,7 +361,9 @@ for s:test in sort(s:tests) set belloff=all " A test can set g:test_is_flaky to retry running the test. - let g:test_is_flaky = 0 + " + " FIXME: We assume all tests are flaky + let g:test_is_flaky = 1 call RunTheTest(s:test) @@ -371,7 +373,7 @@ for s:test in sort(s:tests) if len(v:errors) > 0 \ && $TEST_NO_RETRY == '' \ && g:test_is_flaky - for retry in range( 5 ) + for retry in range( 10 ) call add( s:messages, 'Found errors in ' . s:test . '. Retrying.' ) call extend( s:messages, v:errors ) From 07ec08e664306a077491b1938656d2d21e868857 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 22 Nov 2020 13:25:10 +0000 Subject: [PATCH 385/609] Fix clearing temp breakpoints - ensure that the isngs are undisplayed --- python3/vimspector/breakpoints.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 679cd9c..7aa50ce 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -219,10 +219,14 @@ class ProjectBreakpoints( object ): def ClearTemporaryBreakpoints( self ): + to_delete = [] for file_name, breakpoints in self._line_breakpoints.items(): - self._line_breakpoints[ file_name ] = list( filter( - lambda bp: not bp[ 'options' ].get( 'temporary' ), - breakpoints ) ) + for index, bp in enumerate( breakpoints ): + if bp[ 'options' ].get( 'temporary' ): + to_delete.append( ( bp, file_name, index ) ) + + for entry in to_delete: + self._DeleteLineBreakpoint( *entry ) def _UpdateTemporaryBreakpoints( self, breakpoints, temp_idxs ): From f87aa4aa19a55ac0fee3b6e01ae0f97549614d52 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 22 Nov 2020 13:30:14 +0000 Subject: [PATCH 386/609] Undo flakiness hack --- tests/lib/run_test.vim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/lib/run_test.vim b/tests/lib/run_test.vim index 9df6217..9006af1 100644 --- a/tests/lib/run_test.vim +++ b/tests/lib/run_test.vim @@ -361,9 +361,7 @@ for s:test in sort(s:tests) set belloff=all " A test can set g:test_is_flaky to retry running the test. - " - " FIXME: We assume all tests are flaky - let g:test_is_flaky = 1 + let g:test_is_flaky = 0 call RunTheTest(s:test) From e2ca9b53180241697b394d0046f0d16d927a9a72 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 16 Nov 2020 22:21:13 +0000 Subject: [PATCH 387/609] Threads tests WIP --- tests/stack_trace.test.vim | 49 +++++++++++++++++ tests/testdata/cpp/simple/.gitignore | 1 + tests/testdata/cpp/simple/.ycm_extra_conf.py | 1 + tests/testdata/cpp/simple/Makefile | 2 +- tests/testdata/cpp/simple/threads.cpp | 57 ++++++++++++++++++++ 5 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 tests/stack_trace.test.vim create mode 100644 tests/testdata/cpp/simple/threads.cpp diff --git a/tests/stack_trace.test.vim b/tests/stack_trace.test.vim new file mode 100644 index 0000000..675f061 --- /dev/null +++ b/tests/stack_trace.test.vim @@ -0,0 +1,49 @@ +let s:fn='testdata/cpp/simple/threads.cpp' + +function! SetUp() + call vimspector#test#setup#SetUpWithMappings( 'HUMAN' ) +endfunction + +function! ClearDown() + call vimspector#test#setup#ClearDown() +endfunction + +function! s:StartDebugging() + exe 'edit ' . s:fn + call vimspector#SetLineBreakpoint( s:fn, 13 ) + call vimspector#Launch() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 13, 1 ) +endfunction + +function! Test_Multiple_Threads() + call vimspector#SetLineBreakpoint( s:fn, 41 ) + call vimspector#SetLineBreakpoint( s:fn, 51 ) + call s:StartDebugging() + + call vimspector#Continue() + + " As we step through the thread creation we should get Thread events + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#Continue() + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#Continue() + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#Continue() + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#Continue() + + " This is the last one + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#Continue() + + " So we break out of the loop + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 51, 1 ) + + call vimspector#ClearBreakpoints() + call vimspector#test#setup#Reset() + %bwipe! +endfunction diff --git a/tests/testdata/cpp/simple/.gitignore b/tests/testdata/cpp/simple/.gitignore index d3a7590..d64075c 100644 --- a/tests/testdata/cpp/simple/.gitignore +++ b/tests/testdata/cpp/simple/.gitignore @@ -2,3 +2,4 @@ simple variables struct printer +threads diff --git a/tests/testdata/cpp/simple/.ycm_extra_conf.py b/tests/testdata/cpp/simple/.ycm_extra_conf.py index 78a71f0..c9117c6 100644 --- a/tests/testdata/cpp/simple/.ycm_extra_conf.py +++ b/tests/testdata/cpp/simple/.ycm_extra_conf.py @@ -2,6 +2,7 @@ def Settings( **kwargs ): return { 'flags': [ '-x', 'c++', + '-std=c++17', '-Wextra', '-Werror', '-Wall' ] } diff --git a/tests/testdata/cpp/simple/Makefile b/tests/testdata/cpp/simple/Makefile index 85690c5..d3487aa 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 struct printer +TARGETS=simple variables struct printer threads all: $(TARGETS) diff --git a/tests/testdata/cpp/simple/threads.cpp b/tests/testdata/cpp/simple/threads.cpp new file mode 100644 index 0000000..45f8f42 --- /dev/null +++ b/tests/testdata/cpp/simple/threads.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main( int argc, char ** argv ) +{ + int numThreads = {}; + if ( argc < 2 ) + { + numThreads = 5; + } + else + { + std::string_view numThreadArg( argv[ 1 ] ); + if ( auto [ p, ec ] = std::from_chars( numThreadArg.begin(), + numThreadArg.end(), + numThreads ); + ec != std::errc() ) + { + std::cerr << "Usage " << argv[ 0 ] << " \n"; + return 2; + } + } + + std::cout << "Creating " << numThreads << " threads" << '\n'; + + std::vector threads{}; + threads.reserve( numThreads ); + + auto eng = std::default_random_engine() ; + auto dist = std::uniform_int_distribution( 250, 1000 ); + + for ( int i = 0; i < numThreads; ++i ) + { + using namespace std::chrono_literals; + threads.emplace_back( [&,tnum=i]() { + std::cout << "Started thread " << tnum << '\n'; + std::this_thread::sleep_for( + 5s + std::chrono::milliseconds( dist( eng ) ) ); + std::cout << "Completed thread " << tnum << '\n'; + }); + } + + + for ( int i = 0; i < numThreads; ++i ) + { + threads[ i ].join(); + } + + return 0; +} From e9e0e9e5b9c37aa4fcd19c774fff6be6717480c4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 18 Nov 2020 22:44:18 +0000 Subject: [PATCH 388/609] Test for new thread creation - don't clear the stack trace on continue - track running status properly (ish) - mark threads (running) when the app is executing - indicate the "current" thread with a different icon TODO: - allow user to specify current thread? - track running status of threads individually? - allow to pause/continue specific threads? --- python3/vimspector/debug_session.py | 41 +++- python3/vimspector/stack_trace.py | 148 ++++++------ run_tests | 2 +- tests/lib/plugin/shared.vim | 28 +++ tests/stack_trace.test.vim | 322 +++++++++++++++++++++++++- tests/testdata/cpp/simple/threads.cpp | 41 +++- tests/variables.test.vim | 23 +- 7 files changed, 495 insertions(+), 110 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 4f1ef0b..683c515 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -429,6 +429,9 @@ class DebugSession( object ): }, } ) + self._stackTraceView.OnContinued() + self._codeView.SetCurrentFrame( None ) + @IfConnected() def StepInto( self ): if self._stackTraceView.GetCurrentThreadId() is None: @@ -440,6 +443,8 @@ class DebugSession( object ): 'threadId': self._stackTraceView.GetCurrentThreadId() }, } ) + self._stackTraceView.OnContinued() + self._codeView.SetCurrentFrame( None ) @IfConnected() def StepOut( self ): @@ -452,16 +457,39 @@ class DebugSession( object ): 'threadId': self._stackTraceView.GetCurrentThreadId() }, } ) + self._stackTraceView.OnContinued() + self._codeView.SetCurrentFrame( None ) def Continue( self ): - if self._connection: - self._stackTraceView.Continue() - else: + if not self._connection: self.Start() + return + + if self._stackTraceView.GetCurrentThreadId() is None: + utils.UserMessage( 'No current thread', persist = True ) + return + + self._connection.DoRequest( None, { + 'command': 'continue', + 'arguments': { + 'threadId': self._stackTraceView.GetCurrentThreadId(), + }, + } ) + self._stackTraceView.OnContinued() + self._codeView.SetCurrentFrame( None ) @IfConnected() def Pause( self ): - self._stackTraceView.Pause() + if self._stackTraceView.GetCurrentThreadId() is None: + utils.UserMessage( 'No current thread', persist = True ) + return + + self._connection.DoRequest( None, { + 'command': 'pause', + 'arguments': { + 'threadId': self._stackTraceView.GetCurrentThreadId(), + }, + } ) @IfConnected() def ExpandVariable( self ): @@ -1098,6 +1126,7 @@ class DebugSession( object ): def OnEvent_exited( self, message ): utils.UserMessage( 'The debugee exited with status code: {}'.format( message[ 'body' ][ 'exitCode' ] ) ) + self.SetCurrentFrame( None ) def OnEvent_process( self, message ): utils.UserMessage( 'The debugee was started: {}'.format( @@ -1107,7 +1136,8 @@ class DebugSession( object ): pass def OnEvent_continued( self, message ): - pass + self._stackTraceView.OnContinued() + self._codeView.SetCurrentFrame( None ) def Clear( self ): self._codeView.Clear() @@ -1142,6 +1172,7 @@ class DebugSession( object ): def OnEvent_terminated( self, message ): # We will handle this when the server actually exists utils.UserMessage( "Debugging was terminated by the server." ) + self.SetCurrentFrame( None ) def OnEvent_output( self, message ): if self._outputView: diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 584370f..1634a82 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -20,7 +20,30 @@ import logging from vimspector import utils +# TODO: Need to do something a bit like the Variables stuff +# +# class Thread: +# PAUSED = 0 +# RUNNING = 1 +# state = RUNNING +# +# thread: dict +# stacktrace: list +# +# def __init__( self, thread ): +# self.thread = thread +# self.stacktrace = None +# +# def ShouldExpand( self, current_thread_id ): +# return self.thread[ 'id' ] == current_thread_id + + class StackTraceView( object ): + class ThreadRequestState: + NO = 0 + REQUESTING = 1 + PENDING = 2 + def __init__( self, session, win ): self._logger = logging.getLogger( __name__ ) utils.SetUpLogging( self._logger ) @@ -48,14 +71,8 @@ class StackTraceView( object ): self._line_to_frame = {} self._line_to_thread = {} - # TODO: We really need a proper state model - # - # AWAIT_CONNECTION -- OnServerReady / RequestThreads --> REQUESTING_THREADS - # REQUESTING -- OnGotThreads / RequestScopes --> REQUESTING_SCOPES - # - # When we attach using gdbserver, this whole thing breaks because we request - # the threads over and over and get duff data back on later threads. - self._requesting_threads = False + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None def GetCurrentThreadId( self ): @@ -75,11 +92,12 @@ class StackTraceView( object ): def ConnectionUp( self, connection ): self._connection = connection - self._requesting_threads = False def ConnectionClosed( self ): self.Clear() self._connection = None + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None def Reset( self ): self.Clear() @@ -89,64 +107,82 @@ class StackTraceView( object ): self._scratch_buffers = [] self._buf = None + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None - def LoadThreads( self, infer_current_frame ): - pending_request = False - if self._requesting_threads: - pending_request = True + def LoadThreads( self, infer_current_frame, reason = '' ): + if self._requesting_threads != StackTraceView.ThreadRequestState.NO: + self._requesting_threads = StackTraceView.ThreadRequestState.PENDING + self._pending_thread_request = ( infer_current_frame, reason ) return def consume_threads( message ): - self._requesting_threads = False + if self._requesting_threads == StackTraceView.ThreadRequestState.PENDING: + # We may have hit a thread event, so try again. + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self.LoadThreads( *self._pending_thread_request ) + return if not message[ 'body' ][ 'threads' ]: - if pending_request: - # We may have hit a thread event, so try again. - self.LoadThreads( infer_current_frame ) - return - else: - # This is a protocol error. It is required to return at least one! - utils.UserMessage( 'Server returned no threads. Is it running?', - persist = True ) + # This is a protocol error. It is required to return at least one! + utils.UserMessage( 'Protocol error: Server returned no threads', + persist = False, + error = True ) + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None self._threads.clear() + requesting = False for thread in message[ 'body' ][ 'threads' ]: self._threads.append( thread ) if infer_current_frame and thread[ 'id' ] == self._current_thread: - self._LoadStackTrace( thread, True ) + self._LoadStackTrace( thread, True, reason ) + requesting = True elif infer_current_frame and self._current_thread is None: self._current_thread = thread[ 'id' ] - self._LoadStackTrace( thread, True ) + self._LoadStackTrace( thread, True, reason ) + requesting = True - self._DrawThreads() + if not requesting: + self._DrawThreads() def failure_handler( reason, msg ): # Make sure we request them again if the request fails - self._requesting_threads = False + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None - self._requesting_threads = True + self._requesting_threads = StackTraceView.ThreadRequestState.REQUESTING self._connection.DoRequest( consume_threads, { 'command': 'threads', }, failure_handler ) - def _DrawThreads( self ): + def _DrawThreads( self, running = False ): self._line_to_frame.clear() self._line_to_thread.clear() - with utils.ModifiableScratchBuffer( self._buf ): + with ( utils.ModifiableScratchBuffer( self._buf ), + utils.RestoreCursorPosition() ): utils.ClearBuffer( self._buf ) for thread in self._threads: - icon = '+' if '_frames' not in thread else '-' + if self._current_thread == thread[ 'id' ]: + icon = '^' if '_frames' not in thread else '>' + else: + icon = '+' if '_frames' not in thread else '-' + + # FIXME: We probably need per-thread status here + if running: + status = ' (running)' + else: + status = '' line = utils.AppendToBuffer( self._buf, - '{0} Thread: {1}'.format( icon, thread[ 'name' ] ) ) + f'{icon} Thread: {thread["name"]}{status}' ) self._line_to_thread[ line ] = thread - self._DrawStackTrace( thread ) def _LoadStackTrace( self, @@ -181,8 +217,7 @@ class StackTraceView( object ): thread = self._line_to_thread[ current_line ] if '_frames' in thread: del thread[ '_frames' ] - with utils.RestoreCursorPosition(): - self._DrawThreads() + self._DrawThreads() else: self._LoadStackTrace( thread, False ) @@ -205,51 +240,28 @@ class StackTraceView( object ): else: return do_jump() + def OnContinued( self, threadId = None ): + # FIXME: This tends to create a very flickery stack trace when steppping. + # Maybe we shouldn't remove the frames, but just update the running status? + # for thread in self._threads: + # if threadId is None or thread[ 'id' ] == threadId: + # thread.pop( '_frames', None ) + self._DrawThreads( running=True ) + def OnStopped( self, event ): if 'threadId' in event: self._current_thread = event[ 'threadId' ] elif event.get( 'allThreadsStopped', False ) and self._threads: self._current_thread = self._threads[ 0 ][ 'id' ] - if self._current_thread is not None: - for thread in self._threads: - if thread[ 'id' ] == self._current_thread: - self._LoadStackTrace( thread, True, 'stopped' ) - return - - self.LoadThreads( True ) + self.LoadThreads( True, 'stopped' ) def OnThreadEvent( self, event ): if event[ 'reason' ] == 'started' and self._current_thread is None: self._current_thread = event[ 'threadId' ] self.LoadThreads( True ) - - def Continue( self ): - if self._current_thread is None: - utils.UserMessage( 'No current thread', persist = True ) - return - - self._session._connection.DoRequest( None, { - 'command': 'continue', - 'arguments': { - 'threadId': self._current_thread, - }, - } ) - - self._session.ClearCurrentFrame() - self.LoadThreads( True ) - - def Pause( self ): - if self._current_thread is None: - utils.UserMessage( 'No current thread', persist = True ) - return - - self._session._connection.DoRequest( None, { - 'command': 'pause', - 'arguments': { - 'threadId': self._current_thread, - }, - } ) + else: + self.LoadThreads( False ) def _DrawStackTrace( self, thread ): if '_frames' not in thread: diff --git a/run_tests b/run_tests index 39dc7ec..b0b244b 100755 --- a/run_tests +++ b/run_tests @@ -142,7 +142,7 @@ echo " * BASEDIR_CMD=$BASEDIR_CMD" echo "%SETUP - Building test programs..." set -e pushd tests/testdata/cpp/simple - make clean all + make all popd set +e echo "%DONE - built test programs" diff --git a/tests/lib/plugin/shared.vim b/tests/lib/plugin/shared.vim index d826b1b..5ee640d 100644 --- a/tests/lib/plugin/shared.vim +++ b/tests/lib/plugin/shared.vim @@ -91,3 +91,31 @@ endfunc function! ThisTestIsFlaky() let g:test_is_flaky = v:true endfunction + +function! AssertMatchist( 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! GetBufLine( buf, start, end = '$' ) + if type( a:start ) != v:t_string && a:start < 0 + let start = getbufinfo( a:buf )[ 0 ].linecount + a:start + else + let start = a:start + endif + + if type( a:end ) != v:t_string && a:end < 0 + let end = getbufinfo( a:buf )[ 0 ].linecount + a:end + else + let end = a:end + endif + + return getbufline( a:buf, start, end ) +endfunction diff --git a/tests/stack_trace.test.vim b/tests/stack_trace.test.vim index 675f061..5629c2d 100644 --- a/tests/stack_trace.test.vim +++ b/tests/stack_trace.test.vim @@ -10,38 +10,338 @@ endfunction function! s:StartDebugging() exe 'edit ' . s:fn - call vimspector#SetLineBreakpoint( s:fn, 13 ) + call vimspector#SetLineBreakpoint( s:fn, 15 ) call vimspector#Launch() - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 13, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 15, 1 ) endfunction -function! Test_Multiple_Threads() - call vimspector#SetLineBreakpoint( s:fn, 41 ) - call vimspector#SetLineBreakpoint( s:fn, 51 ) +function! Test_Multiple_Threads_Continue() + + let thread_l = 67 + let notify_l = 74 + + call vimspector#SetLineBreakpoint( s:fn, thread_l ) + call vimspector#SetLineBreakpoint( s:fn, notify_l ) call s:StartDebugging() call vimspector#Continue() " As we step through the thread creation we should get Thread events - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call cursor( 1, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) call vimspector#Continue() - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call cursor( 1, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) call vimspector#Continue() - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call cursor( 1, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #3', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) call vimspector#Continue() - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call cursor( 1, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #4', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) call vimspector#Continue() " This is the last one - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call cursor( 1, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #5', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) call vimspector#Continue() " So we break out of the loop - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 51, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, notify_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( notify_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #6', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) + + call vimspector#ClearBreakpoints() + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_Multiple_Threads_Step() + let thread_l = 67 + let thread_n = thread_l + 1 + let notify_l = 74 + + call vimspector#SetLineBreakpoint( s:fn, thread_l ) + call vimspector#SetLineBreakpoint( s:fn, notify_l ) + call s:StartDebugging() + call vimspector#Continue() + + " As we step through the thread creation we should get Thread events + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) + call vimspector#Continue() + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -1, + \ '$' ) + \ ) + \ } ) + call vimspector#Continue() + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -1, + \ '$' ) + \ ) + \ } ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ '+ Thread: Thread #4', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -2, + \ '$' ) + \ ) + \ } ) + call vimspector#Continue() + + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ '+ Thread: Thread #4', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -2, + \ '$' ) + \ ) + \ } ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ '+ Thread: Thread #4', + \ '+ Thread: Thread #5', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -3, + \ '$' ) + \ ) + \ } ) + call vimspector#Continue() + + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ '+ Thread: Thread #4', + \ '+ Thread: Thread #5', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -3, + \ '$' ) + \ ) + \ } ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ '+ Thread: Thread #4', + \ '+ Thread: Thread #5', + \ '+ Thread: Thread #6', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -4, + \ '$' ) + \ ) + \ } ) + call vimspector#Continue() + + + " So we break out of the loop + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, notify_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #6', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) call vimspector#ClearBreakpoints() call vimspector#test#setup#Reset() diff --git a/tests/testdata/cpp/simple/threads.cpp b/tests/testdata/cpp/simple/threads.cpp index 45f8f42..445c09e 100644 --- a/tests/testdata/cpp/simple/threads.cpp +++ b/tests/testdata/cpp/simple/threads.cpp @@ -1,6 +1,8 @@ #include +#include #include #include +#include #include #include #include @@ -36,17 +38,40 @@ int main( int argc, char ** argv ) auto eng = std::default_random_engine() ; auto dist = std::uniform_int_distribution( 250, 1000 ); - for ( int i = 0; i < numThreads; ++i ) + std::mutex m; + std::condition_variable v; + bool ready = false; { - using namespace std::chrono_literals; - threads.emplace_back( [&,tnum=i]() { - std::cout << "Started thread " << tnum << '\n'; - std::this_thread::sleep_for( - 5s + std::chrono::milliseconds( dist( eng ) ) ); - std::cout << "Completed thread " << tnum << '\n'; - }); + std::lock_guard l(m); + + std::cout << "Preparing..." << '\n'; + + for ( int i = 0; i < numThreads; ++i ) + { + using namespace std::chrono_literals; + auto tp = [&,tnum=i]() { + // Wait for the go-ahead + { + std::unique_lock l(m); + while (!ready) { + v.wait(l); + } + } + + std::cout << "Started thread " << tnum << '\n'; + std::this_thread::sleep_for( + 5s + std::chrono::milliseconds( dist( eng ) ) ); + std::cout << "Completed thread " << tnum << '\n'; + }; + + threads.emplace_back( tp ); + } + + std::cout << "Ready to go!" << '\n'; + ready = true; } + v.notify_all(); for ( int i = 0; i < numThreads; ++i ) { diff --git a/tests/variables.test.vim b/tests/variables.test.vim index d710864..155e1d9 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -8,17 +8,6 @@ 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 = #{ @@ -222,7 +211,7 @@ function! Test_ExpandVariables() call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ '- Scope: Locals', \ ' \*- t (Test): {...}', @@ -240,7 +229,7 @@ function! Test_ExpandVariables() " Step - stays expanded call vimspector#StepOver() call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ '- Scope: Locals', \ ' - t (Test): {...}', @@ -289,7 +278,7 @@ function! Test_ExpandVariables() call setpos( '.', [ 0, 2, 1 ] ) call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ '- Scope: Locals', \ ' - t (Test): {...}', @@ -389,7 +378,7 @@ function! Test_ExpandWatch() call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ 'Watches: ----', \ 'Expression: t', @@ -408,7 +397,7 @@ function! Test_ExpandWatch() " Step - stays expanded call vimspector#StepOver() call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ 'Watches: ----', \ 'Expression: t', @@ -460,7 +449,7 @@ function! Test_ExpandWatch() call setpos( '.', [ 0, 3, 1 ] ) call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ 'Watches: ----', \ 'Expression: t', From 2399a79caed7fb4620953d758f167abb91a1dd44 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 19 Nov 2020 23:49:29 +0000 Subject: [PATCH 389/609] start to track individual thread state --- python3/vimspector/stack_trace.py | 120 ++++++++++++++++-------------- tests/stack_trace.test.vim | 2 + 2 files changed, 68 insertions(+), 54 deletions(-) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 1634a82..c9c4b2b 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -16,26 +16,22 @@ import vim import os import logging +import typing from vimspector import utils -# TODO: Need to do something a bit like the Variables stuff -# -# class Thread: -# PAUSED = 0 -# RUNNING = 1 -# state = RUNNING -# -# thread: dict -# stacktrace: list -# -# def __init__( self, thread ): -# self.thread = thread -# self.stacktrace = None -# -# def ShouldExpand( self, current_thread_id ): -# return self.thread[ 'id' ] == current_thread_id +class Thread: + PAUSED = 0 + RUNNING = 1 + state = RUNNING + + thread: typing.Dict + stacktrace: typing.List[ typing.Dict ] + + def __init__( self, thread ): + self.thread = thread + self.stacktrace = None class StackTraceView( object ): @@ -44,6 +40,9 @@ class StackTraceView( object ): REQUESTING = 1 PENDING = 2 + _threads: list[ Thread ] + _line_to_thread = dict[ int, Thread ] + def __init__( self, session, win ): self._logger = logging.getLogger( __name__ ) utils.SetUpLogging( self._logger ) @@ -63,10 +62,11 @@ class StackTraceView( object ): utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' ) utils.SetUpUIWindow( win ) - vim.command( 'nnoremap ' - ':call vimspector#GoToFrame()' ) - vim.command( 'nnoremap <2-LeftMouse> ' - ':call vimspector#GoToFrame()' ) + with utils.LetCurrentWindow( win ): + vim.command( 'nnoremap ' + ':call vimspector#GoToFrame()' ) + vim.command( 'nnoremap <2-LeftMouse> ' + ':call vimspector#GoToFrame()' ) self._line_to_frame = {} self._line_to_thread = {} @@ -85,7 +85,7 @@ class StackTraceView( object ): self._current_frame = None self._current_thread = None self._current_syntax = "" - self._threads = [] + self._threads.clear() self._sources = {} with utils.ModifiableScratchBuffer( self._buf ): utils.ClearBuffer( self._buf ) @@ -134,16 +134,18 @@ class StackTraceView( object ): self._threads.clear() requesting = False - for thread in message[ 'body' ][ 'threads' ]: + for t in message[ 'body' ][ 'threads' ]: + thread = Thread( t ) self._threads.append( thread ) - if infer_current_frame and thread[ 'id' ] == self._current_thread: - self._LoadStackTrace( thread, True, reason ) - requesting = True - elif infer_current_frame and self._current_thread is None: - self._current_thread = thread[ 'id' ] - self._LoadStackTrace( thread, True, reason ) - requesting = True + if infer_current_frame: + if thread.thread[ 'id' ] == self._current_thread: + self._LoadStackTrace( thread, True, reason ) + requesting = True + elif self._current_thread is None: + self._current_thread = thread.thread[ 'id' ] + self._LoadStackTrace( thread, True, reason ) + requesting = True if not requesting: self._DrawThreads() @@ -158,7 +160,7 @@ class StackTraceView( object ): 'command': 'threads', }, failure_handler ) - def _DrawThreads( self, running = False ): + def _DrawThreads( self ): self._line_to_frame.clear() self._line_to_thread.clear() @@ -167,32 +169,33 @@ class StackTraceView( object ): utils.ClearBuffer( self._buf ) for thread in self._threads: - if self._current_thread == thread[ 'id' ]: - icon = '^' if '_frames' not in thread else '>' + if self._current_thread == thread.thread[ 'id' ]: + icon = '^' if thread.stacktrace is None else '>' else: - icon = '+' if '_frames' not in thread else '-' + icon = '+' if thread.stacktrace is None else '-' # FIXME: We probably need per-thread status here - if running: + if thread.state == Thread.RUNNING: status = ' (running)' else: status = '' line = utils.AppendToBuffer( self._buf, - f'{icon} Thread: {thread["name"]}{status}' ) + f'{icon} Thread: {thread.thread["name"]}{status}' ) self._line_to_thread[ line ] = thread self._DrawStackTrace( thread ) def _LoadStackTrace( self, - thread, + thread: Thread, infer_current_frame, reason = '' ): + def consume_stacktrace( message ): - thread[ '_frames' ] = message[ 'body' ][ 'stackFrames' ] + thread.stacktrace = message[ 'body' ][ 'stackFrames' ] if infer_current_frame: - for frame in thread[ '_frames' ]: + for frame in thread.stacktrace: if self._JumpToFrame( frame, reason ): break @@ -201,7 +204,7 @@ class StackTraceView( object ): self._connection.DoRequest( consume_stacktrace, { 'command': 'stackTrace', 'arguments': { - 'threadId': thread[ 'id' ], + 'threadId': thread.thread[ 'id' ], } } ) @@ -215,8 +218,8 @@ class StackTraceView( object ): self._JumpToFrame( self._line_to_frame[ current_line ] ) elif current_line in self._line_to_thread: thread = self._line_to_thread[ current_line ] - if '_frames' in thread: - del thread[ '_frames' ] + if thread.stacktrace is not None: + thread.stacktrace = None self._DrawThreads() else: self._LoadStackTrace( thread, False ) @@ -241,18 +244,29 @@ class StackTraceView( object ): return do_jump() def OnContinued( self, threadId = None ): - # FIXME: This tends to create a very flickery stack trace when steppping. - # Maybe we shouldn't remove the frames, but just update the running status? - # for thread in self._threads: - # if threadId is None or thread[ 'id' ] == threadId: - # thread.pop( '_frames', None ) - self._DrawThreads( running=True ) + for thread in self._threads: + if threadId is None: + thread.state = Thread.RUNNING + elif thread.thread[ 'id' ] == threadId: + thread.state = Thread.RUNNING + break + + self._DrawThreads() def OnStopped( self, event ): if 'threadId' in event: self._current_thread = event[ 'threadId' ] - elif event.get( 'allThreadsStopped', False ) and self._threads: - self._current_thread = self._threads[ 0 ][ 'id' ] + + for thread in self._threads: + if thread.thread[ 'id' ] == event[ 'threadId' ]: + thread.state = Thread.PAUSED + break + elif event.get( 'allThreadsStopped', False ): + if self._threads: + self._current_thread = self._threads[ 0 ].thread[ 'id' ] + + for thread in self._threads: + thread.state = Thread.PAUSED self.LoadThreads( True, 'stopped' ) @@ -263,13 +277,11 @@ class StackTraceView( object ): else: self.LoadThreads( False ) - def _DrawStackTrace( self, thread ): - if '_frames' not in thread: + def _DrawStackTrace( self, thread: Thread ): + if thread.stacktrace is None: return - stackFrames = thread[ '_frames' ] - - for frame in stackFrames: + for frame in thread.stacktrace: if frame.get( 'source' ): source = frame[ 'source' ] else: diff --git a/tests/stack_trace.test.vim b/tests/stack_trace.test.vim index 5629c2d..f0e4a1a 100644 --- a/tests/stack_trace.test.vim +++ b/tests/stack_trace.test.vim @@ -347,3 +347,5 @@ function! Test_Multiple_Threads_Step() call vimspector#test#setup#Reset() %bwipe! endfunction + +" TODO: Set current frame while thread is running sets the PC From a5d66a7477bbc5176b34d60cb3f3a65c9774277f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 20 Nov 2020 23:16:47 +0000 Subject: [PATCH 390/609] Correctly track and now actually report running/paused status It's quirky, we have to pass the stopped event to LoadThreads so that it can correctly work out the state of any _newly_ added threads. We now also correctly apply the allThreadsStopped=False behaviour where you must not allow expansion of such threads (in theory, that's untested). --- python3/vimspector/debug_session.py | 1 + python3/vimspector/stack_trace.py | 153 ++++++++++++++++++++-------- tests/stack_trace.test.vim | 74 +++++++------- 3 files changed, 147 insertions(+), 81 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 683c515..85240df 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -1136,6 +1136,7 @@ class DebugSession( object ): pass def OnEvent_continued( self, message ): + # FIXME: allThreadsContinued ? self._stackTraceView.OnContinued() self._codeView.SetCurrentFrame( None ) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index c9c4b2b..e5620ee 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -22,17 +22,57 @@ from vimspector import utils class Thread: + """The state of a single thread.""" PAUSED = 0 RUNNING = 1 + TERMINATED = 3 state = RUNNING + stopped_event: typing.Dict thread: typing.Dict stacktrace: typing.List[ typing.Dict ] + id: str def __init__( self, thread ): + self.id = thread[ 'id' ] + self.stopped_event = None + self.Update( thread ) + + def Update( self, thread ): self.thread = thread self.stacktrace = None + def Paused( self, event ): + self.state = Thread.PAUSED + self.stopped_event = event + + def Continued( self ): + self.state = Thread.RUNNING + self.stopped_event = None + + def Exited( self ): + self.state = Thread.TERMINATED + self.stopped_event = None + + def State( self ): + if self.state == Thread.PAUSED: + return self.stopped_event.get( 'description', 'paused' ) + elif self.state == Thread.RUNNING: + return 'running' + return 'terminated' + + def Expand( self, stack_trace ): + self.stacktrace = stack_trace + + def Collapse( self ): + self.stacktrace = None + + def IsExpanded( self ): + return self.stacktrace is not None + + def CanExpand( self ): + return self.state == Thread.PAUSED + class StackTraceView( object ): class ThreadRequestState: @@ -40,6 +80,7 @@ class StackTraceView( object ): REQUESTING = 1 PENDING = 2 + # FIXME: Make into a dict by id ? _threads: list[ Thread ] _line_to_thread = dict[ int, Thread ] @@ -87,6 +128,8 @@ class StackTraceView( object ): self._current_syntax = "" self._threads.clear() self._sources = {} + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None with utils.ModifiableScratchBuffer( self._buf ): utils.ClearBuffer( self._buf ) @@ -96,24 +139,24 @@ class StackTraceView( object ): def ConnectionClosed( self ): self.Clear() self._connection = None - self._requesting_threads = StackTraceView.ThreadRequestState.NO - self._pending_thread_request = None def Reset( self ): self.Clear() utils.CleanUpHiddenBuffer( self._buf ) for b in self._scratch_buffers: utils.CleanUpHiddenBuffer( b ) - self._scratch_buffers = [] self._buf = None - self._requesting_threads = StackTraceView.ThreadRequestState.NO - self._pending_thread_request = None - def LoadThreads( self, infer_current_frame, reason = '' ): + def LoadThreads( self, + infer_current_frame, + reason = '', + stopEvent = None ): if self._requesting_threads != StackTraceView.ThreadRequestState.NO: self._requesting_threads = StackTraceView.ThreadRequestState.PENDING - self._pending_thread_request = ( infer_current_frame, reason ) + self._pending_thread_request = ( infer_current_frame, + reason, + stopEvent ) return def consume_threads( message ): @@ -131,19 +174,42 @@ class StackTraceView( object ): self._requesting_threads = StackTraceView.ThreadRequestState.NO self._pending_thread_request = None + + existing_threads = self._threads[:] self._threads.clear() + if stopEvent is not None: + stoppedThreadId = stopEvent.get( 'threadId' ) + allThreadsStopped = stopEvent.get( 'allThreadsStopped', False ) + requesting = False + + # FIXME: This is horribly inefficient for t in message[ 'body' ][ 'threads' ]: - thread = Thread( t ) + thread = None + for existing_thread in existing_threads: + if existing_thread.id == t[ 'id' ]: + thread = existing_thread + thread.Update( t ) + break + + if not thread: + thread = Thread( t ) + self._threads.append( thread ) + if stopEvent: + if allThreadsStopped: + thread.Paused( stopEvent ) + elif stoppedThreadId is not None and thread.id == stoppedThreadId: + thread.Paused( stopEvent ) + if infer_current_frame: - if thread.thread[ 'id' ] == self._current_thread: + if thread.id == self._current_thread: self._LoadStackTrace( thread, True, reason ) requesting = True elif self._current_thread is None: - self._current_thread = thread.thread[ 'id' ] + self._current_thread = thread.id self._LoadStackTrace( thread, True, reason ) requesting = True @@ -169,20 +235,14 @@ class StackTraceView( object ): utils.ClearBuffer( self._buf ) for thread in self._threads: - if self._current_thread == thread.thread[ 'id' ]: - icon = '^' if thread.stacktrace is None else '>' + if self._current_thread == thread.id: + icon = '^' if not thread.IsExpanded() else '>' else: - icon = '+' if thread.stacktrace is None else '-' - - # FIXME: We probably need per-thread status here - if thread.state == Thread.RUNNING: - status = ' (running)' - else: - status = '' + icon = '+' if not thread.IsExpanded() else '-' line = utils.AppendToBuffer( self._buf, - f'{icon} Thread: {thread.thread["name"]}{status}' ) + f'{icon} Thread: {thread.thread["name"]} ({thread.State()})' ) self._line_to_thread[ line ] = thread self._DrawStackTrace( thread ) @@ -193,7 +253,7 @@ class StackTraceView( object ): reason = '' ): def consume_stacktrace( message ): - thread.stacktrace = message[ 'body' ][ 'stackFrames' ] + thread.Expand( message[ 'body' ][ 'stackFrames' ] ) if infer_current_frame: for frame in thread.stacktrace: if self._JumpToFrame( frame, reason ): @@ -218,11 +278,14 @@ class StackTraceView( object ): self._JumpToFrame( self._line_to_frame[ current_line ] ) elif current_line in self._line_to_thread: thread = self._line_to_thread[ current_line ] - if thread.stacktrace is not None: - thread.stacktrace = None + if thread.IsExpanded(): + thread.Collapse() self._DrawThreads() - else: + elif thread.CanExpand(): self._LoadStackTrace( thread, False ) + else: + utils.UserMessage( "Thread is not stopped" ) + def _JumpToFrame( self, frame, reason = '' ): def do_jump(): @@ -246,39 +309,41 @@ class StackTraceView( object ): def OnContinued( self, threadId = None ): for thread in self._threads: if threadId is None: - thread.state = Thread.RUNNING - elif thread.thread[ 'id' ] == threadId: - thread.state = Thread.RUNNING + thread.Continued() + elif thread.id == threadId: + thread.Continued() break self._DrawThreads() def OnStopped( self, event ): - if 'threadId' in event: - self._current_thread = event[ 'threadId' ] + threadId = event.get( 'threadId' ) + allThreadsStopped = event.get( 'allThreadsStopped', False ) - for thread in self._threads: - if thread.thread[ 'id' ] == event[ 'threadId' ]: - thread.state = Thread.PAUSED - break - elif event.get( 'allThreadsStopped', False ): - if self._threads: - self._current_thread = self._threads[ 0 ].thread[ 'id' ] + # Work out if we should change the current thread + if threadId is not None: + self._current_thread = threadId + elif self._current_thread is None and allThreadsStopped and self._threads: + self._current_thread = self._threads[ 0 ].id - for thread in self._threads: - thread.state = Thread.PAUSED - - self.LoadThreads( True, 'stopped' ) + self.LoadThreads( True, 'stopped', event ) def OnThreadEvent( self, event ): + infer_current_frame = False if event[ 'reason' ] == 'started' and self._current_thread is None: self._current_thread = event[ 'threadId' ] - self.LoadThreads( True ) - else: - self.LoadThreads( False ) + infer_current_frame = True + + if event[ 'reason' ] == 'exited': + for thread in self._threads: + if thread.id == event[ 'threadId' ]: + thread.Exited() + break + + self.LoadThreads( infer_current_frame ) def _DrawStackTrace( self, thread: Thread ): - if thread.stacktrace is None: + if not thread.IsExpanded(): return for frame in thread.stacktrace: diff --git a/tests/stack_trace.test.vim b/tests/stack_trace.test.vim index f0e4a1a..1475874 100644 --- a/tests/stack_trace.test.vim +++ b/tests/stack_trace.test.vim @@ -33,7 +33,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1', + \ '> Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -48,7 +48,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1', + \ '> Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -59,7 +59,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2', + \ '+ Thread: Thread #2 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -73,7 +73,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1', + \ '> Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -84,7 +84,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #3', + \ '+ Thread: Thread #3 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -98,7 +98,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1', + \ '> Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -109,7 +109,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #4', + \ '+ Thread: Thread #4 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -124,7 +124,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1', + \ '> Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -135,7 +135,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #5', + \ '+ Thread: Thread #5 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -149,7 +149,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1', + \ '> Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( notify_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -160,7 +160,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #6', + \ '+ Thread: Thread #6 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -189,7 +189,7 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1', + \ '> Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -202,7 +202,7 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2', + \ '+ Thread: Thread #2 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -215,7 +215,7 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2', + \ '+ Thread: Thread #2 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -227,8 +227,8 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2', - \ '+ Thread: Thread #3', + \ '+ Thread: Thread #2 (paused)', + \ '+ Thread: Thread #3 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -1, @@ -241,8 +241,8 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2', - \ '+ Thread: Thread #3', + \ '+ Thread: Thread #2 (paused)', + \ '+ Thread: Thread #3 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -1, @@ -254,9 +254,9 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2', - \ '+ Thread: Thread #3', - \ '+ Thread: Thread #4', + \ '+ Thread: Thread #2 (paused)', + \ '+ Thread: Thread #3 (paused)', + \ '+ Thread: Thread #4 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -2, @@ -270,9 +270,9 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2', - \ '+ Thread: Thread #3', - \ '+ Thread: Thread #4', + \ '+ Thread: Thread #2 (paused)', + \ '+ Thread: Thread #3 (paused)', + \ '+ Thread: Thread #4 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -2, @@ -284,10 +284,10 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2', - \ '+ Thread: Thread #3', - \ '+ Thread: Thread #4', - \ '+ Thread: Thread #5', + \ '+ Thread: Thread #2 (paused)', + \ '+ Thread: Thread #3 (paused)', + \ '+ Thread: Thread #4 (paused)', + \ '+ Thread: Thread #5 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -3, @@ -301,10 +301,10 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2', - \ '+ Thread: Thread #3', - \ '+ Thread: Thread #4', - \ '+ Thread: Thread #5', + \ '+ Thread: Thread #2 (paused)', + \ '+ Thread: Thread #3 (paused)', + \ '+ Thread: Thread #4 (paused)', + \ '+ Thread: Thread #5 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -3, @@ -316,11 +316,11 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2', - \ '+ Thread: Thread #3', - \ '+ Thread: Thread #4', - \ '+ Thread: Thread #5', - \ '+ Thread: Thread #6', + \ '+ Thread: Thread #2 (paused)', + \ '+ Thread: Thread #3 (paused)', + \ '+ Thread: Thread #4 (paused)', + \ '+ Thread: Thread #5 (paused)', + \ '+ Thread: Thread #6 (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -4, From f0785c11f22ca997497c050dbf1cdb1be20e5be2 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 20 Nov 2020 23:18:20 +0000 Subject: [PATCH 391/609] Allow pausing individual threads (in theory) --- autoload/vimspector.vim | 7 +++++++ python3/vimspector/debug_session.py | 14 ++++++++++++++ python3/vimspector/stack_trace.py | 21 ++++++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 6509e14..468421f 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -171,6 +171,13 @@ function! vimspector#Pause() abort py3 _vimspector_session.Pause() endfunction +function! vimspector#PauseThread() abort + if !s:Enabled() + return + endif + py3 _vimspector_session.PauseThread() +endfunction + function! vimspector#Stop() abort if !s:Enabled() return diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 85240df..fc2837e 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -491,6 +491,20 @@ class DebugSession( object ): }, } ) + @IfConnected() + def PauseThread( self ): + threadId = self._stackTraceView.GetSelectedThreadId() + if threadId is None: + utils.UserMessage( 'No thread selected' ) + return + + self._connection.DoRequest( None, { + 'command': 'pause', + 'arguments': { + 'threadId': threadId, + }, + } ) + @IfConnected() def ExpandVariable( self ): self._variablesView.ExpandVariable() diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index e5620ee..2fd761b 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -109,6 +109,13 @@ class StackTraceView( object ): vim.command( 'nnoremap <2-LeftMouse> ' ':call vimspector#GoToFrame()' ) + if utils.UseWinBar(): + vim.command( 'nnoremenu 1.2 WinBar.Open ' + ':call vimspector#GoToFrame()' ) + vim.command( 'nnoremenu 1.1 WinBar.Pause ' + ':call vimspector#PauseThread()' ) + + self._line_to_frame = {} self._line_to_thread = {} @@ -264,10 +271,22 @@ class StackTraceView( object ): self._connection.DoRequest( consume_stacktrace, { 'command': 'stackTrace', 'arguments': { - 'threadId': thread.thread[ 'id' ], + 'threadId': thread.id, } } ) + + def GetSelectedThreadId( self ): + if vim.current.buffer != self._buf: + return None + + thread = self._line_to_thread.get( vim.current.window.cursor[ 0 ] ) + if not thread: + return None + + return thread.id + + def ExpandFrameOrThread( self ): if vim.current.buffer != self._buf: return From 42cdff043a8492869154ad085cbe81824605a3aa Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 20 Nov 2020 23:18:56 +0000 Subject: [PATCH 392/609] Redraw the screen each 20 retries This allows us to eyeball why something is failing --- tests/lib/plugin/shared.vim | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/lib/plugin/shared.vim b/tests/lib/plugin/shared.vim index 5ee640d..70e297e 100644 --- a/tests/lib/plugin/shared.vim +++ b/tests/lib/plugin/shared.vim @@ -51,7 +51,10 @@ func s:WaitForCommon(expr, assert, timeout) let start = reltime() endif + let iters = 0 + while 1 + let iters += 1 let errors_before = len( v:errors ) if type(a:expr) == v:t_func let success = a:expr() @@ -65,6 +68,10 @@ func s:WaitForCommon(expr, assert, timeout) return slept endif + if iters % 20 == 0 + redraw! + endif + if slept >= a:timeout break endif From 0f0d684e9298ba39fa116c1e879a1d66870d8b55 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 20 Nov 2020 23:27:18 +0000 Subject: [PATCH 393/609] FixUp: comment about why we apply thread state in consume_threads --- python3/vimspector/stack_trace.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 2fd761b..da4f54d 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -205,6 +205,10 @@ class StackTraceView( object ): self._threads.append( thread ) + # If the threads were requested due to a stopped event, update any + # stopped thread state. Note we have to do this here (rather than in the + # stopped event handler) because we must apply this event to any new + # threads that are received here. if stopEvent: if allThreadsStopped: thread.Paused( stopEvent ) From 8e3a7341414fb9c42ab1f76839c575c84c7c4ea3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 20 Nov 2020 23:42:08 +0000 Subject: [PATCH 394/609] Support continued event properly --- python3/vimspector/debug_session.py | 3 +-- python3/vimspector/stack_trace.py | 11 +++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index fc2837e..3a24f9d 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -1150,8 +1150,7 @@ class DebugSession( object ): pass def OnEvent_continued( self, message ): - # FIXME: allThreadsContinued ? - self._stackTraceView.OnContinued() + self._stackTraceView.OnContinued( message[ 'body' ] ) self._codeView.SetCurrentFrame( None ) def Clear( self ): diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index da4f54d..55e9bd9 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -329,9 +329,16 @@ class StackTraceView( object ): else: return do_jump() - def OnContinued( self, threadId = None ): + def OnContinued( self, event = None ): + threadId = None + allThreadsContinued = True + + if event is not None: + threadId = event[ 'threadId' ] + allThreadsContinued = event.get( 'allThreadsContinued', False ) + for thread in self._threads: - if threadId is None: + if allThreadsContinued: thread.Continued() elif thread.id == threadId: thread.Continued() From 53b1d124475324bdf4778a5e0dfb0bb9a4e7b047 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 21 Nov 2020 14:34:56 +0000 Subject: [PATCH 395/609] Allow setting the current thread, use a sign to highlight the line with the current thread --- autoload/vimspector.vim | 11 ++- python3/vimspector/code.py | 1 + python3/vimspector/debug_session.py | 16 ++--- python3/vimspector/settings.py | 11 +-- python3/vimspector/stack_trace.py | 104 ++++++++++++++++++++++------ tests/stack_trace.test.vim | 17 ++--- 6 files changed, 110 insertions(+), 50 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 468421f..3886924 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -171,11 +171,18 @@ function! vimspector#Pause() abort py3 _vimspector_session.Pause() endfunction -function! vimspector#PauseThread() abort +function! vimspector#PauseContinueThread() abort if !s:Enabled() return endif - py3 _vimspector_session.PauseThread() + py3 _vimspector_session.PauseContinueThread() +endfunction + +function! vimspector#SetCurrentThread() abort + if !s:Enabled() + return + endif + py3 _vimspector_session.SetCurrentThread() endfunction function! vimspector#Stop() abort diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 628fa63..d3c6324 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -32,6 +32,7 @@ class CodeView( object ): self._logger = logging.getLogger( __name__ ) utils.SetUpLogging( self._logger ) + # FIXME: This ID is by group, so should be module scope self._next_sign_id = 1 self._breakpoints = defaultdict( list ) self._signs = { diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 3a24f9d..67ec6a1 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -492,18 +492,12 @@ class DebugSession( object ): } ) @IfConnected() - def PauseThread( self ): - threadId = self._stackTraceView.GetSelectedThreadId() - if threadId is None: - utils.UserMessage( 'No thread selected' ) - return + def PauseContinueThread( self ): + self._stackTraceView.PauseContinueThread() - self._connection.DoRequest( None, { - 'command': 'pause', - 'arguments': { - 'threadId': threadId, - }, - } ) + @IfConnected() + def SetCurrentThread( self ): + self._stackTraceView.SetCurrentThread() @IfConnected() def ExpandVariable( self ): diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index 18101f8..5ff729b 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -28,11 +28,12 @@ DEFAULTS = { # Signs 'sign_priority': { - 'vimspectorPC': 200, - 'vimspectorPCBP': 200, - 'vimspectorBP': 9, - 'vimspectorBPCond': 9, - 'vimspectorBPDisabled': 9, + 'vimspectorPC': 200, + 'vimspectorPCBP': 200, + 'vimspectorBP': 9, + 'vimspectorBPCond': 9, + 'vimspectorBPDisabled': 9, + 'vimspectorCurrentThread': 200 }, # Installer diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 55e9bd9..f039dde 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -18,7 +18,7 @@ import os import logging import typing -from vimspector import utils +from vimspector import utils, signs class Thread: @@ -100,6 +100,9 @@ class StackTraceView( object ): self._sources = {} self._scratch_buffers = [] + # FIXME: This ID is by group, so should be module scope + self._next_sign_id = 1 + utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' ) utils.SetUpUIWindow( win ) @@ -110,11 +113,22 @@ class StackTraceView( object ): ':call vimspector#GoToFrame()' ) if utils.UseWinBar(): - vim.command( 'nnoremenu 1.2 WinBar.Open ' + vim.command( 'nnoremenu 1.1 WinBar.Pause/Continue ' + ':call vimspector#PauseContinueThread()' ) + vim.command( 'nnoremenu 1.2 WinBar.Expand/Collapse ' ':call vimspector#GoToFrame()' ) - vim.command( 'nnoremenu 1.1 WinBar.Pause ' - ':call vimspector#PauseThread()' ) + vim.command( 'nnoremenu 1.3 WinBar.Focus ' + ':call vimspector#SetCurrentThread()' ) + win.options[ 'cursorline' ] = False + + + if not signs.SignDefined( 'vimspectorCurrentThread' ): + signs.DefineSign( 'vimspectorCurrentThread', + text = '▶ ', + double_text = '▶', + texthl = 'MatchParen', + linehl = 'CursorLine' ) self._line_to_frame = {} self._line_to_thread = {} @@ -137,6 +151,10 @@ class StackTraceView( object ): self._sources = {} self._requesting_threads = StackTraceView.ThreadRequestState.NO self._pending_thread_request = None + if self._next_sign_id: + signs.UnplaceSign( self._next_sign_id, 'VimspectorStackTrace' ) + self._next_sign_id = 0 + with utils.ModifiableScratchBuffer( self._buf ): utils.ClearBuffer( self._buf ) @@ -241,20 +259,28 @@ class StackTraceView( object ): self._line_to_frame.clear() self._line_to_thread.clear() + if self._next_sign_id: + signs.UnplaceSign( self._next_sign_id, 'VimspectorStackTrace' ) + else: + self._next_sign_id = 1 + with ( utils.ModifiableScratchBuffer( self._buf ), utils.RestoreCursorPosition() ): utils.ClearBuffer( self._buf ) for thread in self._threads: - if self._current_thread == thread.id: - icon = '^' if not thread.IsExpanded() else '>' - else: - icon = '+' if not thread.IsExpanded() else '-' - + icon = '+' if not thread.IsExpanded() else '-' line = utils.AppendToBuffer( self._buf, f'{icon} Thread: {thread.thread["name"]} ({thread.State()})' ) + if self._current_thread == thread.id: + signs.PlaceSign( self._next_sign_id, + 'VimspectorStackTrace', + 'vimspectorCurrentThread', + self._buf.name, + line ) + self._line_to_thread[ line ] = thread self._DrawStackTrace( thread ) @@ -280,27 +306,32 @@ class StackTraceView( object ): } ) - def GetSelectedThreadId( self ): + def _GetSelectedThread( self ) -> Thread: if vim.current.buffer != self._buf: return None - thread = self._line_to_thread.get( vim.current.window.cursor[ 0 ] ) - if not thread: - return None + return self._line_to_thread.get( vim.current.window.cursor[ 0 ] ) - return thread.id + + def GetSelectedThreadId( self ): + thread = self._GetSelectedThread() + return thread.id if thread else thread + + + def SetCurrentThread( self ): + thread = self._GetSelectedThread() + if not thread: + utils.UserMessage( "No thread selected" ) + else: + self._current_thread = thread.id + + self._DrawThreads() def ExpandFrameOrThread( self ): - if vim.current.buffer != self._buf: - return + thread = self._GetSelectedThread() - current_line = vim.current.window.cursor[ 0 ] - - if current_line in self._line_to_frame: - self._JumpToFrame( self._line_to_frame[ current_line ] ) - elif current_line in self._line_to_thread: - thread = self._line_to_thread[ current_line ] + if thread: if thread.IsExpanded(): thread.Collapse() self._DrawThreads() @@ -308,6 +339,10 @@ class StackTraceView( object ): self._LoadStackTrace( thread, False ) else: utils.UserMessage( "Thread is not stopped" ) + elif vim.current.buffer != self._buf: + return + elif vim.current.window.cursor[ 0 ] in self._line_to_frame: + self._JumpToFrame( self._line_to_frame[ vim.current.window.cursor[ 0 ] ] ) def _JumpToFrame( self, frame, reason = '' ): @@ -329,6 +364,31 @@ class StackTraceView( object ): else: return do_jump() + + + def PauseContinueThread( self ): + thread = self._GetSelectedThread() + if thread is None: + utils.UserMessage( 'No thread selected' ) + elif thread.state == Thread.PAUSED: + self._session._connection.DoRequest( None, { + 'command': 'continue', + 'arguments': { + 'threadId': thread.id, + }, + } ) + elif thread.state == Thread.RUNNING: + self._session._connection.DoRequest( None, { + 'command': 'pause', + 'arguments': { + 'threadId': thread.id, + }, + } ) + else: + utils.UserMessage( + f'Thread cannot be modified in state {thread.State()}' ) + + def OnContinued( self, event = None ): threadId = None allThreadsContinued = True diff --git a/tests/stack_trace.test.vim b/tests/stack_trace.test.vim index 1475874..d88b081 100644 --- a/tests/stack_trace.test.vim +++ b/tests/stack_trace.test.vim @@ -16,7 +16,6 @@ function! s:StartDebugging() endfunction function! Test_Multiple_Threads_Continue() - let thread_l = 67 let notify_l = 74 @@ -33,7 +32,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1 (paused)', + \ '- Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -48,7 +47,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1 (paused)', + \ '- Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -73,7 +72,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1 (paused)', + \ '- Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -98,7 +97,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1 (paused)', + \ '- Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -124,7 +123,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1 (paused)', + \ '- Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -149,7 +148,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1 (paused)', + \ '- Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( notify_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -189,7 +188,7 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '> Thread: Thread #1 (paused)', + \ '- Thread: Thread #1 (paused)', \ ' .*: threads!main@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), @@ -347,5 +346,3 @@ function! Test_Multiple_Threads_Step() call vimspector#test#setup#Reset() %bwipe! endfunction - -" TODO: Set current frame while thread is running sets the PC From c769e8a4796fa791e4dea0393a3d7e973da4b3e7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 21 Nov 2020 16:42:26 +0000 Subject: [PATCH 396/609] Fix up the tests to work in linux container too --- autoload/vimspector/internal/state.vim | 1 + python3/vimspector/stack_trace.py | 37 ++++----- run_tests | 2 +- tests/breakpoints_doublewidth.test.vim | 1 + tests/ci/image/Dockerfile | 15 +++- tests/stack_trace.test.vim | 106 ++++++++++++++----------- tests/testdata/cpp/simple/Makefile | 1 + 7 files changed, 93 insertions(+), 70 deletions(-) diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index b4f18aa..f1e690a 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -34,6 +34,7 @@ function! vimspector#internal#state#Reset() abort catch /.*/ echohl WarningMsg echom 'Exception while loading vimspector:' v:exception + echom 'From:' v:throwpoint echom 'Vimspector unavailable: Requires Vim compiled with Python 3.6' echohl None return v:false diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index f039dde..e394df9 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -81,8 +81,8 @@ class StackTraceView( object ): PENDING = 2 # FIXME: Make into a dict by id ? - _threads: list[ Thread ] - _line_to_thread = dict[ int, Thread ] + _threads: typing.List[ Thread ] + _line_to_thread = typing.Dict[ int, Thread ] def __init__( self, session, win ): self._logger = logging.getLogger( __name__ ) @@ -264,25 +264,26 @@ class StackTraceView( object ): else: self._next_sign_id = 1 - with ( utils.ModifiableScratchBuffer( self._buf ), - utils.RestoreCursorPosition() ): - utils.ClearBuffer( self._buf ) + with utils.ModifiableScratchBuffer( self._buf ): + with utils.RestoreCursorPosition(): + utils.ClearBuffer( self._buf ) - for thread in self._threads: - icon = '+' if not thread.IsExpanded() else '-' - line = utils.AppendToBuffer( - self._buf, - f'{icon} Thread: {thread.thread["name"]} ({thread.State()})' ) + for thread in self._threads: + icon = '+' if not thread.IsExpanded() else '-' + line = utils.AppendToBuffer( + self._buf, + f'{icon} Thread {thread.id}: {thread.thread["name"]} ' + f'({thread.State()})' ) - if self._current_thread == thread.id: - signs.PlaceSign( self._next_sign_id, - 'VimspectorStackTrace', - 'vimspectorCurrentThread', - self._buf.name, - line ) + if self._current_thread == thread.id: + signs.PlaceSign( self._next_sign_id, + 'VimspectorStackTrace', + 'vimspectorCurrentThread', + self._buf.name, + line ) - self._line_to_thread[ line ] = thread - self._DrawStackTrace( thread ) + self._line_to_thread[ line ] = thread + self._DrawStackTrace( thread ) def _LoadStackTrace( self, thread: Thread, diff --git a/run_tests b/run_tests index b0b244b..39dc7ec 100755 --- a/run_tests +++ b/run_tests @@ -142,7 +142,7 @@ echo " * BASEDIR_CMD=$BASEDIR_CMD" echo "%SETUP - Building test programs..." set -e pushd tests/testdata/cpp/simple - make all + make clean all popd set +e echo "%DONE - built test programs" diff --git a/tests/breakpoints_doublewidth.test.vim b/tests/breakpoints_doublewidth.test.vim index cfd4e6c..73f1f01 100644 --- a/tests/breakpoints_doublewidth.test.vim +++ b/tests/breakpoints_doublewidth.test.vim @@ -695,6 +695,7 @@ endfunction function! Test_Add_Line_BP_In_Other_File_While_Debugging() + call ThisTestIsFlaky() let moo = 'moo.py' let cow = 'cow.py' lcd ../support/test/python/multiple_files diff --git a/tests/ci/image/Dockerfile b/tests/ci/image/Dockerfile index 4494cf1..a25febc 100644 --- a/tests/ci/image/Dockerfile +++ b/tests/ci/image/Dockerfile @@ -4,13 +4,19 @@ ENV DEBIAN_FRONTEND=noninteractive ENV LC_ALL C.UTF-8 RUN apt-get update && \ - apt install -y curl dirmngr apt-transport-https lsb-release ca-certificates \ - software-properties-common && \ + apt-get install -y curl \ + dirmngr \ + apt-transport-https \ + lsb-release \ + ca-certificates \ + software-properties-common && \ curl -sL https://deb.nodesource.com/setup_12.x | bash - && \ add-apt-repository ppa:bartbes/love-stable -y && \ apt-get update && \ apt-get -y dist-upgrade && \ - apt-get -y install python3-dev \ + apt-get -y install gcc-8 \ + g++-8 \ + python3-dev \ python3-pip \ python3-venv \ ca-cacert \ @@ -31,6 +37,9 @@ RUN apt-get update && \ RUN ln -fs /usr/share/zoneinfo/Europe/London /etc/localtime && \ dpkg-reconfigure --frontend noninteractive tzdata +RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 1 \ + --slave /usr/bin/g++ g++ /usr/bin/g++-8 + ## cleanup of files from setup RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* diff --git a/tests/stack_trace.test.vim b/tests/stack_trace.test.vim index d88b081..569376c 100644 --- a/tests/stack_trace.test.vim +++ b/tests/stack_trace.test.vim @@ -11,7 +11,7 @@ endfunction function! s:StartDebugging() exe 'edit ' . s:fn call vimspector#SetLineBreakpoint( s:fn, 15 ) - call vimspector#Launch() + call vimspector#LaunchWithSettings( #{ configuration: 'run-to-breakpoint' } ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 15, 1 ) endfunction @@ -32,8 +32,8 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '- Thread: Thread #1 (paused)', - \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ '- Thread [0-9]\+: .* (paused)', + \ ' .*: .*@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ 1, @@ -47,8 +47,8 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '- Thread: Thread #1 (paused)', - \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ '- Thread [0-9]\+: .* (paused)', + \ ' .*: .*@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ 1, @@ -58,7 +58,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -72,8 +72,8 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '- Thread: Thread #1 (paused)', - \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ '- Thread [0-9]\+: .* (paused)', + \ ' .*: .*@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ 1, @@ -83,7 +83,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #3 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -97,8 +97,8 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '- Thread: Thread #1 (paused)', - \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ '- Thread [0-9]\+: .* (paused)', + \ ' .*: .*@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ 1, @@ -108,7 +108,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #4 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -123,8 +123,8 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '- Thread: Thread #1 (paused)', - \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ '- Thread [0-9]\+: .* (paused)', + \ ' .*: .*@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ 1, @@ -134,7 +134,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #5 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -148,8 +148,8 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '- Thread: Thread #1 (paused)', - \ ' .*: threads!main@threads.cpp:' . string( notify_l ) + \ '- Thread [0-9]\+: .* (paused)', + \ ' .*: .*@threads.cpp:' . string( notify_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ 1, @@ -159,7 +159,7 @@ function! Test_Multiple_Threads_Continue() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #6 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -174,7 +174,13 @@ endfunction function! Test_Multiple_Threads_Step() let thread_l = 67 - let thread_n = thread_l + 1 + if $VIMSPECTOR_MIMODE ==# 'lldb' + " } + let thread_n = thread_l + 1 + else + " for .... + let thread_n = 49 + endif let notify_l = 74 call vimspector#SetLineBreakpoint( s:fn, thread_l ) @@ -188,8 +194,8 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '- Thread: Thread #1 (paused)', - \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ '- Thread [0-9]\+: .* (paused)', + \ ' .*: .*@threads.cpp:' . string( thread_l ) \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ 1, @@ -201,7 +207,7 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -214,7 +220,7 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ '$', @@ -226,8 +232,8 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2 (paused)', - \ '+ Thread: Thread #3 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -1, @@ -240,8 +246,8 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2 (paused)', - \ '+ Thread: Thread #3 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -1, @@ -253,9 +259,9 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2 (paused)', - \ '+ Thread: Thread #3 (paused)', - \ '+ Thread: Thread #4 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -2, @@ -269,9 +275,9 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2 (paused)', - \ '+ Thread: Thread #3 (paused)', - \ '+ Thread: Thread #4 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -2, @@ -283,10 +289,10 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2 (paused)', - \ '+ Thread: Thread #3 (paused)', - \ '+ Thread: Thread #4 (paused)', - \ '+ Thread: Thread #5 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -3, @@ -300,10 +306,10 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2 (paused)', - \ '+ Thread: Thread #3 (paused)', - \ '+ Thread: Thread #4 (paused)', - \ '+ Thread: Thread #5 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -3, @@ -315,11 +321,11 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #2 (paused)', - \ '+ Thread: Thread #3 (paused)', - \ '+ Thread: Thread #4 (paused)', - \ '+ Thread: Thread #5 (paused)', - \ '+ Thread: Thread #6 (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), \ -4, @@ -334,10 +340,14 @@ function! Test_Multiple_Threads_Step() call WaitForAssert( {-> \ AssertMatchist( \ [ - \ '+ Thread: Thread #6', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', + \ '+ Thread [0-9]\+: .* (paused)', \ ], \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), - \ '$', + \ -4, \ '$' ) \ ) \ } ) diff --git a/tests/testdata/cpp/simple/Makefile b/tests/testdata/cpp/simple/Makefile index d3487aa..471edae 100644 --- a/tests/testdata/cpp/simple/Makefile +++ b/tests/testdata/cpp/simple/Makefile @@ -3,6 +3,7 @@ CXXFLAGS=-g -O0 -std=c++17 .PHONY: all TARGETS=simple variables struct printer threads +LDLIBS=-lpthread all: $(TARGETS) From e1c1347bdd336e9de953b19e90b3ceccfc55d613 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 21 Nov 2020 16:52:38 +0000 Subject: [PATCH 397/609] Fix lint --- python3/vimspector/stack_trace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index e394df9..dd2662c 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -200,7 +200,7 @@ class StackTraceView( object ): self._requesting_threads = StackTraceView.ThreadRequestState.NO self._pending_thread_request = None - existing_threads = self._threads[:] + existing_threads = self._threads[ : ] self._threads.clear() if stopEvent is not None: From 8801c2dac4b97a2b5100d4d222a095b4c64b3daf Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 21 Nov 2020 17:41:20 +0000 Subject: [PATCH 398/609] Fix pause/continue of individual threads work around buggy java server sending invalid threads response. java server supports this separate threads running/paused as a test case. --- python3/vimspector/debug_session.py | 40 ++++++++++++++++++----------- python3/vimspector/stack_trace.py | 19 ++++++++------ 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 67ec6a1..c6a1f72 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -434,49 +434,59 @@ class DebugSession( object ): @IfConnected() def StepInto( self ): - if self._stackTraceView.GetCurrentThreadId() is None: + threadId = self._stackTraceView.GetCurrentThreadId() + if threadId is None: return - self._connection.DoRequest( None, { + def handler( *_ ): + self._stackTraceView.OnContinued( { 'threadId': threadId } ) + self._codeView.SetCurrentFrame( None ) + + self._connection.DoRequest( handler, { 'command': 'stepIn', 'arguments': { - 'threadId': self._stackTraceView.GetCurrentThreadId() + 'threadId': threadId }, } ) - self._stackTraceView.OnContinued() - self._codeView.SetCurrentFrame( None ) @IfConnected() def StepOut( self ): - if self._stackTraceView.GetCurrentThreadId() is None: + threadId = self._stackTraceView.GetCurrentThreadId() + if threadId is None: return - self._connection.DoRequest( None, { + def handler( *_ ): + self._stackTraceView.OnContinued( { 'threadId': threadId } ) + self._codeView.SetCurrentFrame( None ) + + self._connection.DoRequest( handler, { 'command': 'stepOut', 'arguments': { - 'threadId': self._stackTraceView.GetCurrentThreadId() + 'threadId': threadId }, } ) - self._stackTraceView.OnContinued() - self._codeView.SetCurrentFrame( None ) + def Continue( self ): if not self._connection: self.Start() return - if self._stackTraceView.GetCurrentThreadId() is None: + threadId = self._stackTraceView.GetCurrentThreadId() + if threadId is None: utils.UserMessage( 'No current thread', persist = True ) return - self._connection.DoRequest( None, { + def handler( *_ ): + self._stackTraceView.OnContinued( { 'threadId': threadId } ) + self._codeView.SetCurrentFrame( None ) + + self._connection.DoRequest( handler, { 'command': 'continue', 'arguments': { - 'threadId': self._stackTraceView.GetCurrentThreadId(), + 'threadId': threadId, }, } ) - self._stackTraceView.OnContinued() - self._codeView.SetCurrentFrame( None ) @IfConnected() def Pause( self ): diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index dd2662c..a0b4734 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -49,6 +49,7 @@ class Thread: def Continued( self ): self.state = Thread.RUNNING self.stopped_event = None + self.Collapse() def Exited( self ): self.state = Thread.TERMINATED @@ -56,7 +57,7 @@ class Thread: def State( self ): if self.state == Thread.PAUSED: - return self.stopped_event.get( 'description', 'paused' ) + return self.stopped_event.get( 'description' ) or 'paused' elif self.state == Thread.RUNNING: return 'running' return 'terminated' @@ -191,7 +192,7 @@ class StackTraceView( object ): self.LoadThreads( *self._pending_thread_request ) return - if not message[ 'body' ][ 'threads' ]: + if not ( message.get( 'body' ) or {} ).get( 'threads' ): # This is a protocol error. It is required to return at least one! utils.UserMessage( 'Protocol error: Server returned no threads', persist = False, @@ -372,12 +373,14 @@ class StackTraceView( object ): if thread is None: utils.UserMessage( 'No thread selected' ) elif thread.state == Thread.PAUSED: - self._session._connection.DoRequest( None, { - 'command': 'continue', - 'arguments': { - 'threadId': thread.id, - }, - } ) + self._session._connection.DoRequest( + lambda *_: self.OnContinued( { 'threadId': thread.id } ), + { + 'command': 'continue', + 'arguments': { + 'threadId': thread.id, + }, + } ) elif thread.state == Thread.RUNNING: self._session._connection.DoRequest( None, { 'command': 'pause', From 7d5ad3ffa101d34585a519f3aaa06a4b97a35bbf Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 21 Nov 2020 17:53:41 +0000 Subject: [PATCH 399/609] Another flaky test --- tests/breakpoints.test.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index e6a1357..9ae1907 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -684,6 +684,7 @@ function! Test_Custom_Breakpoint_Priority_Partial() endfunction function! Test_Add_Line_BP_In_Other_File_While_Debugging() + call ThisTestIsFlaky() let moo = 'moo.py' let cow = 'cow.py' lcd ../support/test/python/multiple_files From 82307ff1ba78b54997a4101d4e3553edba947e11 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 21 Nov 2020 18:12:34 +0000 Subject: [PATCH 400/609] Correctly handle the 'continue' response when continuing a specific thread --- python3/vimspector/debug_session.py | 9 +++++++-- python3/vimspector/stack_trace.py | 15 ++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index c6a1f72..b5387ee 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -477,8 +477,13 @@ class DebugSession( object ): utils.UserMessage( 'No current thread', persist = True ) return - def handler( *_ ): - self._stackTraceView.OnContinued( { 'threadId': threadId } ) + def handler( msg ): + self._stackTraceView.OnContinued( { + 'threadId': threadId, + 'allThreadsContinued': ( msg.get( 'body' ) or {} ).get( + 'allThreadsContinued', + True ) + } ) self._codeView.SetCurrentFrame( None ) self._connection.DoRequest( handler, { diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index a0b4734..23ddb0d 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -192,14 +192,15 @@ class StackTraceView( object ): self.LoadThreads( *self._pending_thread_request ) return + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None + if not ( message.get( 'body' ) or {} ).get( 'threads' ): # This is a protocol error. It is required to return at least one! utils.UserMessage( 'Protocol error: Server returned no threads', persist = False, error = True ) - - self._requesting_threads = StackTraceView.ThreadRequestState.NO - self._pending_thread_request = None + return existing_threads = self._threads[ : ] self._threads.clear() @@ -367,14 +368,18 @@ class StackTraceView( object ): return do_jump() - def PauseContinueThread( self ): thread = self._GetSelectedThread() if thread is None: utils.UserMessage( 'No thread selected' ) elif thread.state == Thread.PAUSED: self._session._connection.DoRequest( - lambda *_: self.OnContinued( { 'threadId': thread.id } ), + lambda msg: self.OnContinued( { + 'threadId': thread.id, + 'allThreadsContinued': ( msg.get( 'body' ) or {} ).get( + 'allThreadsContinued', + True ) + } ), { 'command': 'continue', 'arguments': { From a9d0ebde0b2816ae19f532ccfc5be743c5ca06c1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 21 Nov 2020 20:37:35 +0000 Subject: [PATCH 401/609] Add mapping for Focus, and make focusing a stack frame focus the thread --- README.md | 27 +++++++++++++++++++++++++-- python3/vimspector/stack_trace.py | 27 +++++++++++++++++++-------- support/test/node/simple/simple.js | 2 +- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ff2273f..bd26efe 100644 --- a/README.md +++ b/README.md @@ -751,6 +751,9 @@ Scopes and variables are represented by the buffer `vimspector.Variables`. ## Watches +The watch window is used to inspect variables and expressions. Expressions are +evaluated in the selected stack frame which is "focussed" + The watches window is a prompt buffer, where that's available. Enter insert mode to add a new watch expression. @@ -767,7 +770,7 @@ The watches are represented by the buffer `vimspector.StackTrace`. ### Watch autocompletion -The watch prompt buffer has its `omnifunc` set to a function that will +The watch prompt buffer has its `omnifunc` set to a function that will calcualte completion for the current expression. This is trivailly used with `` (see `:help ins-completion`), or integrated with your favourite completion system. The filetype in the buffer is set to @@ -783,8 +786,28 @@ let g:ycm_semantic_triggers = { ## Stack Traces -* In the threads window, use ``, or double-click with left mouse to expand/collapse. +The stack trace window shows the state of each progream thread. Threads which +are stopped can be expanded to show the strack trace of that thread. + +Often, but not always, all threads are stopped when a breakpoint is hit. The +status of a thread is show in parentheses after the thread's name. Where +supported by the underlying debugger, threads can be paused and continued +individually from within the Stack Trace window. + +A particular thread, highlighted with the `CursorLine` highlight group is the +"focussed" thread. This is the thread that receives commands like "Stop In", +"Stop Out", "Continue" and "Pause" in the code window. The focussed thread can +be changed manually to "switch to" that thread. + +* Use ``, or double-click with left mouse to expand/collapse a thread stack + trace, or use the WinBar button. * Use ``, or double-click with left mouse on a stack frame to jump to it. +* Use the WinBar or `vimspector#PauseContinueThread()` to individually pause or + continue the selected thread. +* Use the "Focus" WinBar button, `` or `vimspector#SetCurrentThread()` + to set the "focussed" thread to the currently selected one. If the selected + line is a stack frame, set the focussed thread to the thread of that frame and + jump to that frame in the code window. ![stack trace](https://puremourning.github.io/vimspector-web/img/vimspector-callstack-window.png) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 23ddb0d..b84ac2e 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -110,6 +110,8 @@ class StackTraceView( object ): with utils.LetCurrentWindow( win ): vim.command( 'nnoremap ' ':call vimspector#GoToFrame()' ) + vim.command( 'nnoremap ' + ':call vimspector#SetCurrentThread()' ) vim.command( 'nnoremap <2-LeftMouse> ' ':call vimspector#GoToFrame()' ) @@ -320,16 +322,22 @@ class StackTraceView( object ): thread = self._GetSelectedThread() return thread.id if thread else thread + def _SetCurrentThread( self, thread: Thread ): + self._current_thread = thread.id + self._DrawThreads() def SetCurrentThread( self ): thread = self._GetSelectedThread() - if not thread: - utils.UserMessage( "No thread selected" ) + if thread: + self._SetCurrentThread( thread ) + elif vim.current.buffer != self._buf: + return + elif vim.current.window.cursor[ 0 ] in self._line_to_frame: + thread, frame = self._line_to_frame[ vim.current.window.cursor[ 0 ] ] + self._SetCurrentThread( thread ) + self._JumpToFrame( frame ) else: - self._current_thread = thread.id - - self._DrawThreads() - + utils.UserMessage( "No thread selected" ) def ExpandFrameOrThread( self ): thread = self._GetSelectedThread() @@ -345,12 +353,15 @@ class StackTraceView( object ): elif vim.current.buffer != self._buf: return elif vim.current.window.cursor[ 0 ] in self._line_to_frame: - self._JumpToFrame( self._line_to_frame[ vim.current.window.cursor[ 0 ] ] ) + thread, frame = self._line_to_frame[ vim.current.window.cursor[ 0 ] ] + self._JumpToFrame( frame ) def _JumpToFrame( self, frame, reason = '' ): def do_jump(): if 'line' in frame and frame[ 'line' ] > 0: + # Should this set the current _Thread_ too ? If i jump to a frame in + # Thread 2, should that become the focussed thread ? self._current_frame = frame return self._session.SetCurrentFrame( self._current_frame, reason ) return False @@ -469,7 +480,7 @@ class StackTraceView( object ): source[ 'name' ], frame[ 'line' ] ) ) - self._line_to_frame[ line ] = frame + self._line_to_frame[ line ] = ( thread, frame ) def _ResolveSource( self, source, and_then ): source_reference = int( source[ 'sourceReference' ] ) diff --git a/support/test/node/simple/simple.js b/support/test/node/simple/simple.js index 77b8e32..f5c51f4 100644 --- a/support/test/node/simple/simple.js +++ b/support/test/node/simple/simple.js @@ -3,7 +3,7 @@ var msg = 'Hello, world!' var obj = { test: 'testing', toast: function() { - return 'toasty' . this.test; + return 'toasty' + this.test; } } From 979c1e8779b9842ec38bc6b6d3c5be79fe645bc7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 22 Nov 2020 14:35:30 +0000 Subject: [PATCH 402/609] Reshuffle README --- README.md | 118 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index bd26efe..2ae945c 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,17 @@ For a tutorial and usage overview, take a look at the * [Supported languages](#supported-languages) * [Other languages](#other-languages) * [Installation](#installation) + * [Quick Start](#quick-start) * [Dependencies](#dependencies) * [Neovim differences](#neovim-differences) * [Windows differences](#windows-differences) - * [Clone the plugin](#clone-the-plugin) + * [Trying it out](#trying-it-out) + * [Cloning the plugin](#cloning-the-plugin) * [Install some gadgets](#install-some-gadgets) * [VimspectorInstall and VimspectorUpdate commands](#vimspectorinstall-and-vimspectorupdate-commands) * [install_gadget.py](#install_gadgetpy) * [Manual gadget installation](#manual-gadget-installation) * [The gadget directory](#the-gadget-directory) - * [Trying it out](#trying-it-out) * [Upgrade](#upgrade) * [About](#about) * [Background](#background) @@ -33,9 +34,11 @@ For a tutorial and usage overview, take a look at the * [Launch and attach by PID:](#launch-and-attach-by-pid) * [Launch with options](#launch-with-options) * [Debug configuration selection](#debug-configuration-selection) + * [Get configurations](#get-configurations) * [Breakpoints](#breakpoints) * [Exception breakpoints](#exception-breakpoints) * [Clear breakpoints](#clear-breakpoints) + * [Run to Cursor](#run-to-cursor) * [Stepping](#stepping) * [Variables and scopes](#variables-and-scopes) * [Watches](#watches) @@ -48,6 +51,7 @@ For a tutorial and usage overview, take a look at the * [Closing debugger](#closing-debugger) * [Debug adapter configuration](#debug-adapter-configuration) * [C, C , Rust, etc.](#c-c-rust-etc) + * [Rust](#rust) * [Remote debugging](#remote-debugging) * [Remote launch and attach](#remote-launch-and-attach) * [Python](#python) @@ -65,7 +69,6 @@ For a tutorial and usage overview, take a look at the * [Usage with YouCompleteMe](#usage-with-youcompleteme) * [Other LSP clients](#other-lsp-clients) * [Lua](#lua) - * [Rust](#rust) * [Other servers](#other-servers) * [Customisation](#customisation) * [Changing the default signs](#changing-the-default-signs) @@ -80,7 +83,7 @@ For a tutorial and usage overview, take a look at the * [License](#license) * [Sponsorship](#sponsorship) - + @@ -121,8 +124,6 @@ And a couple of brief demos: - logging/stdout display - simple stable API for custom tooling (e.g. integrate with language server) -For other languages, you'll need some other way to install the gadget. - ## Supported languages The following table lists the languages that are "built-in" (along with their @@ -158,10 +159,12 @@ To use Vimspector with a language that's not "built-in", see this # Installation +## Quick Start + There are 2 installation methods: -* Using a release tarball, or -* Manually +* Using a release tarball and vim packages +* Using a clone of the repo (e.g. package manager) Release tarballs come with debug adapters for the default languages pre-packaged. To use a release tarball: @@ -174,15 +177,27 @@ $ mkdir -p $HOME/.vim/pack $ curl -L | tar -C $HOME/.vim/pack zxvf - ``` +3. Add `packadd! vimspector` to you `.vimrc` + 3. Configure your project's debug profiles (create `.vimspector.json`) Alternatively, you can clone the repo and select which gadgets are installed: 1. Check the dependencies 1. Install the plugin as a Vim package. See `:help packages`. -2. Install some 'gadgets' (debug adapters) +2. Add `packadd! vimspector` to you `.vimrc` +2. Install some 'gadgets' (debug adapters) - see `:VimspectorInstall ...` 3. Configure your project's debug profiles (create `.vimspector.json`) +If you prefer to use a plugin manager, see the plugin manager's docs. For +Vundle, use: + +```vim +Plugin 'puremourning/vimspector' +``` + +The following sections expand on the above brief overview. + ## Dependencies Vimspector requires: @@ -239,20 +254,54 @@ The following features are not implemented for Windows: * Tailing the vimspector log in the Output Window. -## Clone the plugin +## Trying it out + +If you just want to try out vimspector without changing your vim config, there +are example projects for a number of languages in `support/test`, including: + +* Python (`support/test/python/simple_python`) +* Go (`support/test/go/hello_world`) +* Nodejs (`support/test/node/simple`) +* Chrome (`support/test/chrome/`) +* etc. + +To test one of these out, cd to the directory and run: + +``` +vim -Nu /path/to/vimspector/tests/vimrc --cmd "let g:vimspector_enable_mappings='HUMAN'" +``` + +Then press ``. + +There's also a C++ project in `tests/testdata/cpp/simple/` with a `Makefile` +which can be used to check everything is working. This is used by the regression +tests in CI so should always work, and is a good way to check if the problem is +your configuration rather than a bug. + +## Cloning the plugin + +If you're not using a release tarball, you'll need to clone this repo to the +appropriate place. + +1. Clone the plugin There are many Vim plugin managers, and I'm not going to state a particular -preference, so if you choose to use one, you're on your own with installation -issues. +preference, so if you choose to use one, follow the plugin manager's +documentation. For example, for Vundle, use: -Install vimspector as a Vim package, either by cloning this repository into your -package path, like this: +```viml +Plugin 'puremourning/vimspector' +``` + +If you don't use a plugin manager already, install vimspector as a Vim package +by cloning this repository into your package path, like this: ``` $ git clone https://github.com/puremourning/vimspector ~/.vim/pack/vimspector/opt/vimspector ``` -2. Configure vimspector in your `.vimrc`: +2. Configure vimspector in your `.vimrc`, for example to enable the standard + mapings: ```viml let g:vimspector_enable_mappings = 'HUMAN' @@ -265,7 +314,7 @@ let g:vimspector_enable_mappings = 'HUMAN' packadd! vimspector ``` -See support/doc/example_vimrc.vim. +See support/doc/example_vimrc.vim for a minimal example. ## Install some gadgets @@ -281,11 +330,10 @@ There are a few ways to do this: installed for you. * Using `:VimspectorInstall ` (use TAB `wildmenu` to see the options, also accepts any `install_gadget.py` option) -* Alternatively, using `python3 install_gadget.py ` (use `--help` to see - all options) -* When attempting to launch a debug configuration, if the configured adapter - can't be found, vimspector might suggest installing one. -* Use `:VimspectorUpdate` to install the latest supported versions of the +* Using `python3 install_gadget.py ` (use `--help` to see all options) +* Attempting to launch a debug configuration; if the configured adapter + can't be found, vimspector will suggest installing one. +* Using `:VimspectorUpdate` to install the latest supported versions of the gadgets. Here's a demo of doing somee installs and an upgrade: @@ -293,7 +341,7 @@ Here's a demo of doing somee installs and an upgrade: [![asciicast](https://asciinema.org/a/Hfu4ZvuyTZun8THNen9FQbTay.svg)](https://asciinema.org/a/Hfu4ZvuyTZun8THNen9FQbTay) Both `install_gadget.py` and `:VimspectorInstall` do the same set of things, -though the default behaviours are slightly different. For supported languages, +though the default behaviours are slightly different. For supported languages, they will: * Download the relevant debug adapter at a version that's been tested from the @@ -306,7 +354,7 @@ they will: broken in this regard. * Set up the `gadgetDir` symlinks for the platform. -To install the tested debug adapter for a language, run: +For example, to install the tested debug adapter for a language, run: | To install | Script | Command | | --- | --- | --- | @@ -468,30 +516,6 @@ Vimspector will also load any fies matching: format as `.gadgets.json` but are not overwritten when running `install_gadget.py`. -## Trying it out - -If you just want to try out vimspector without changing your vim config, there -are example projects for a number of languages in `support/test`, including: - -* Python (`support/test/python/simple_python`) -* Go (`support/test/go/hello_world`) -* Nodejs (`support/test/node/simple`) -* Chrome (`support/test/chrome/`) -* etc. - -To test one of these out, cd to the directory and run: - -``` -vim -Nu /path/to/vimspector/tests/vimrc --cmd "let g:vimspector_enable_mappings='HUMAN'" -``` - -Then press ``. - -There's also a C++ project in `tests/testdata/cpp/simple/` with a `Makefile` -which can be used to check everything is working. This is used by the regression -tests in CI so should always work, and is a good way to check if the problem is -your configuration rather than a bug. - ## Upgrade After updating the Vimspector code (either via `git pull` or whatever package From 5ab92a7e6793a447be4afcb563c15275220b54b3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 22 Nov 2020 14:43:52 +0000 Subject: [PATCH 403/609] Breakpont tests too flaky --- tests/breakpoints.test.vim | 1 + tests/breakpoints_doublewidth.test.vim | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index 9ae1907..2e3eef8 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -1,5 +1,6 @@ function! SetUp() call vimspector#test#setup#SetUpWithMappings( v:none ) + call ThisTestIsFlaky() endfunction function! ClearDown() diff --git a/tests/breakpoints_doublewidth.test.vim b/tests/breakpoints_doublewidth.test.vim index 73f1f01..c646c3e 100644 --- a/tests/breakpoints_doublewidth.test.vim +++ b/tests/breakpoints_doublewidth.test.vim @@ -1,6 +1,7 @@ function! SetUp() set ambiwidth=double call vimspector#test#setup#SetUpWithMappings( v:none ) + call ThisTestIsFlaky() endfunction function! ClearDown() From 4f03e4f65a67a780bf9a60f0377dd9ee3d812e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Mez=C3=AAncio?= Date: Sun, 22 Nov 2020 18:38:40 -0300 Subject: [PATCH 404/609] Improve lua configuration and test files Change lua test files to call `require 'lldebugger'` only when using love, because it's not needed with lua or luajit. Also add `stopOnEntry` key to test `.vimspector.json` because it works correctly with this change. --- support/test/lua/love-headless/.vimspector.json | 3 ++- support/test/lua/love/.vimspector.json | 3 ++- support/test/lua/simple/.vimspector.json | 6 ++++-- support/test/lua/simple/simple.lua | 4 ---- tests/language_lua.test.vim | 16 +++------------- 5 files changed, 11 insertions(+), 21 deletions(-) diff --git a/support/test/lua/love-headless/.vimspector.json b/support/test/lua/love-headless/.vimspector.json index 8864784..5e2cb99 100644 --- a/support/test/lua/love-headless/.vimspector.json +++ b/support/test/lua/love-headless/.vimspector.json @@ -10,7 +10,8 @@ "program": { "command": "love" }, - "args": ["${workspaceFolder}"] + "args": ["${workspaceFolder}"], + "stopOnEntry": false } } } diff --git a/support/test/lua/love/.vimspector.json b/support/test/lua/love/.vimspector.json index 8864784..5e2cb99 100644 --- a/support/test/lua/love/.vimspector.json +++ b/support/test/lua/love/.vimspector.json @@ -10,7 +10,8 @@ "program": { "command": "love" }, - "args": ["${workspaceFolder}"] + "args": ["${workspaceFolder}"], + "stopOnEntry": false } } } diff --git a/support/test/lua/simple/.vimspector.json b/support/test/lua/simple/.vimspector.json index ff19723..10d39dc 100644 --- a/support/test/lua/simple/.vimspector.json +++ b/support/test/lua/simple/.vimspector.json @@ -9,7 +9,8 @@ "cwd": "${workspaceFolder}", "program": { "lua": "lua", - "file": "simple.lua" + "file": "simple.lua", + "stopOnEntry": false } } }, @@ -21,7 +22,8 @@ "cwd": "${workspaceFolder}", "program": { "lua": "luajit", - "file": "simple.lua" + "file": "simple.lua", + "stopOnEntry": false } } } diff --git a/support/test/lua/simple/simple.lua b/support/test/lua/simple/simple.lua index 8c6b1ac..ed74655 100644 --- a/support/test/lua/simple/simple.lua +++ b/support/test/lua/simple/simple.lua @@ -1,7 +1,3 @@ -if pcall(require, 'lldebugger') then - require('lldebugger').start() -end - local separator = ' ' diff --git a/tests/language_lua.test.vim b/tests/language_lua.test.vim index 0dafb10..dc296bb 100644 --- a/tests/language_lua.test.vim +++ b/tests/language_lua.test.vim @@ -13,30 +13,20 @@ function! BaseTest( configuration ) lcd ../support/test/lua/simple exe 'edit ' . fn - call vimspector#SetLineBreakpoint( fn, 9 ) + call vimspector#SetLineBreakpoint( fn, 5 ) call vimspector#LaunchWithSettings( { 'configuration': a:configuration } ) - " This debugger is ignoring stopOnEntry when not running a custom executable - " and always stopping on the first line after setting the hook. This first - " check assumes that behavior. call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 5, 1 ) call WaitForAssert( {-> \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 5 ) \ } ) - " Continue - call feedkeys( "\", 'xt' ) - - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 9, 1 ) - call WaitForAssert( {-> - \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 9 ) - \ } ) " Step call feedkeys( "\", 'xt' ) - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 10, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 6, 1 ) call WaitForAssert( {-> - \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 10 ) + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 6 ) \ } ) call vimspector#test#setup#Reset() From e6500d39d8a5296d7f6cd91131115f92bea72fce Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 26 Nov 2020 12:11:23 +0000 Subject: [PATCH 405/609] Neovim has prompt buffers in nightly --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ae945c..4a36912 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,8 @@ neovim doesn't implement some features Vimspector relies on: * WinBar - used for the buttons at the top of the code window and for changing the output window's current output. -* Prompt Buffers - used to send commands in the Console and add Watches +* Prompt Buffers - used to send commands in the Console and add Watches. + (*Note*: prompt buffers are available in neovim nightly) * Balloons - used to display the values of variables when debugging. Workarounds are in place as follows: From 4c2b3bd8860f9c4bf2684ba1618c104a40505abb Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 26 Nov 2020 12:38:34 +0000 Subject: [PATCH 406/609] Make it clear that the mappings have to be enabled --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 4a36912..6aa95c3 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,12 @@ $ curl -L | tar -C $HOME/.vim/pack zxvf - 3. Add `packadd! vimspector` to you `.vimrc` +4. (optionally) Enable the default set of mappings: + +``` +let g:vimspector_enable_mappings = 'HUMAN' +``` + 3. Configure your project's debug profiles (create `.vimspector.json`) Alternatively, you can clone the repo and select which gadgets are installed: From 4db4880b6d4fcc2cf4f1586fd6fcbf318862f61d Mon Sep 17 00:00:00 2001 From: felixfeit <55563921+felixfeit@users.noreply.github.com> Date: Thu, 26 Nov 2020 19:31:46 +0100 Subject: [PATCH 407/609] Fix typos. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6aa95c3..0d9fb5d 100644 --- a/README.md +++ b/README.md @@ -1108,7 +1108,7 @@ to: [the debugpy documentation](https://github.com/microsoft/debugpy#debugpy-cli-usage) for details. -* use the built-in "multi-session" adapter. This just asks for the host/port to +* Use the built-in "multi-session" adapter. This just asks for the host/port to connect to. For example: ```json @@ -1127,11 +1127,11 @@ to: } ``` -See [deatils of the launch +See [details of the launch configuration](https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings) for explanation of things like `pathMappings`. -Additional documenation, including how to do this when the remote machine can +Additional documentation, including how to do this when the remote machine can only be contacted via SSH [are provided by debugpy](https://github.com/microsoft/debugpy/wiki/Debugging-over-SSH). From e15c50a4f48b8f299abaf5a5fd532d1bf90b9a13 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 28 Nov 2020 00:17:42 +0000 Subject: [PATCH 408/609] Don't request strack trace if the thread isn't stopped --- python3/vimspector/stack_trace.py | 13 +++++++++---- support/test/cpp/simple_c_program/.vimspector.json | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index b84ac2e..df650b9 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -237,14 +237,19 @@ class StackTraceView( object ): elif stoppedThreadId is not None and thread.id == stoppedThreadId: thread.Paused( stopEvent ) + # If this is a stopped event, load the stack trace for the "current" + # thread. Don't do this on other thrads requests because some servers + # just break when that happens. if infer_current_frame: if thread.id == self._current_thread: - self._LoadStackTrace( thread, True, reason ) - requesting = True + if thread.CanExpand(): + self._LoadStackTrace( thread, True, reason ) + requesting = True elif self._current_thread is None: self._current_thread = thread.id - self._LoadStackTrace( thread, True, reason ) - requesting = True + if thread.CanExpand(): + self._LoadStackTrace( thread, True, reason ) + requesting = True if not requesting: self._DrawThreads() diff --git a/support/test/cpp/simple_c_program/.vimspector.json b/support/test/cpp/simple_c_program/.vimspector.json index 6623908..ae61ee8 100644 --- a/support/test/cpp/simple_c_program/.vimspector.json +++ b/support/test/cpp/simple_c_program/.vimspector.json @@ -1,7 +1,7 @@ { "configurations": { "simple_c_program - Launch": { - "adapter": "vscode-cpptools", + "adapter": "CodeLLDB", "configuration": { "request": "launch", "program": "${workspaceRoot}/test", From 4a45753a4c66329fcfe3d71c74d6ca619e0e9bfb Mon Sep 17 00:00:00 2001 From: Drew Willey Date: Fri, 27 Nov 2020 21:16:51 -0700 Subject: [PATCH 409/609] Add detail about potential troubleshooting. Recommend jsdls_extension_path be set in vimrc. --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0d9fb5d..d964445 100644 --- a/README.md +++ b/README.md @@ -1426,16 +1426,20 @@ use it with Vimspector. } ``` -* Tell YCM to load the debugger plugin and create a mapping, such as - `` to start the debug server and launch vimspector, e.g. in - `~/.vim/ftplugin/java.vim`: +* Tell YCM to load the debugger plugin. This should be the `gadgets/` + directory, not any specific adapter. e.g. in `.vimrc` ```viml " Tell YCM where to find the plugin. Add to any existing values. let g:ycm_java_jdtls_extension_path = [ \ '' \ ] +``` +* Create a mapping, such as `` to start the debug server and launch + vimspector, e.g. in `~/.vim/ftplugin/java.vim`: + +```viml let s:jdt_ls_debugger_port = 0 function! s:StartDebugging() if s:jdt_ls_debugger_port <= 0 @@ -1461,6 +1465,18 @@ nnoremap :call StartDebugging() You can then use `` to start debugging rather than just ``. +If you see "Unable to get DAP port - is JDT.LS initialized?", try running +`:YcmCompleter ExecuteCommand vscode.java.startDebugSession` and note the +output. If you see an error like `ResponseFailedException: Request failed: +-32601: No delegateCommandHandler for vscode.java.startDebugSession`, make sure +that: +* Your YCM jdt.ls is actually working, see the + [YCM docs](https://github.com/ycm-core/YouCompleteMe#troubleshooting) for + troubleshooting +* The YCM jdt.ls has had time to initialize before you start the debugger +* That `g:ycm_java_jdtls_extension_path` is set in `.vimrc` or prior to YCM + starting + For the launch arguments, see the [vscode document](https://code.visualstudio.com/docs/java/java-debugging). From 8e2d352eb8958693ea2021f7f43ae0418dcb083b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 30 Nov 2020 10:19:02 +0000 Subject: [PATCH 410/609] Fix local adapter specs Somehow we lost the ability to define adapters in the local .vimspector.json, I think when GetConfigurations was added. Put that feature back. --- autoload/vimspector.vim | 4 ++-- python3/vimspector/debug_session.py | 7 ++++--- support/test/python/simple_python/.vimspector.json | 12 ++++++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 3886924..35ecd03 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -296,8 +296,8 @@ function! vimspector#GetConfigurations() abort return endif let configurations = py3eval( - \ 'list( _vimspector_session.GetConfigurations()[ 1 ].keys() )' - \ . ' if _vimspector_session else []' ) + \ 'list( _vimspector_session.GetConfigurations( {} )[ 1 ].keys() )' + \ . ' if _vimspector_session else []' ) return configurations endfunction diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index b5387ee..63cd140 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -79,7 +79,7 @@ class DebugSession( object ): self._server_capabilities = {} self.ClearTemporaryBreakpoints() - def GetConfigurations( self ): + def GetConfigurations( self, adapters ): current_file = utils.GetBufferFilepath( vim.current.buffer ) filetypes = utils.GetBufferFiletypes( vim.current.buffer ) configurations = {} @@ -93,7 +93,8 @@ class DebugSession( object ): with open( launch_config_file, 'r' ) as f: database = json.loads( minify( f.read() ) ) - configurations.update( database.get( 'configurations' or {} ) ) + configurations.update( database.get( 'configurations' ) or {} ) + adapters.update( database.get( 'adapters' ) or {} ) return launch_config_file, configurations @@ -109,8 +110,8 @@ class DebugSession( object ): self._adapter = None current_file = utils.GetBufferFilepath( vim.current.buffer ) - launch_config_file, configurations = self.GetConfigurations() adapters = {} + launch_config_file, configurations = self.GetConfigurations( adapters ) if not configurations: utils.UserMessage( 'Unable to find any debug configurations. ' diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index aba0578..5eadb0c 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -1,5 +1,17 @@ { + "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json", + "adapters": { + "test_custom": { + "command": "This is a test" + } + }, "configurations": { + "Use custom gadget": { + "adapter": "test_custom", + "configuration": { + "request": "launch" + } + }, // This is a comment. "run legacy vscode-python": { "adapter": "vscode-python", /* coment goes here too */ From 2eac9ddff864f0d7ded6d32bf03c04e2f3198706 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 25 Nov 2020 15:49:50 +0000 Subject: [PATCH 411/609] Print failure when watch expression errors --- python3/vimspector/variables.py | 27 +++++++++++++++++++++----- tests/variables.test.vim | 34 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 1d62c04..4028722 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -84,6 +84,12 @@ class WatchResult( Expandable ): self.result = result +class WatchFailure( WatchResult ): + def __init__( self, reason ): + super().__init__( { 'result': reason } ) + self.changed = True + + class Variable( Expandable ): """Holds one level of an expanded value tree. Also itself expandable.""" def __init__( self, variable: dict ): @@ -296,11 +302,14 @@ class VariablesView( object ): def EvaluateWatches( self ): for watch in self._watches: - self._connection.DoRequest( partial( self._UpdateWatchExpression, - watch ), { - 'command': 'evaluate', - 'arguments': watch.expression, - } ) + self._connection.DoRequest( + partial( self._UpdateWatchExpression, watch ), + { + 'command': 'evaluate', + 'arguments': watch.expression, + }, + failure_handler = lambda reason, msg, watch=watch: + self._WatchExpressionFailed( reason, watch ) ) def _UpdateWatchExpression( self, watch: Watch, message: dict ): if watch.result is not None: @@ -321,6 +330,14 @@ class VariablesView( object ): self._DrawWatches() + def _WatchExpressionFailed( self, reason: str, watch: Watch ): + if watch.result is not None: + # We already have a result for this watch. Wut ? + return + + watch.result = WatchFailure( reason ) + self._DrawWatches() + def ExpandVariable( self ): if vim.current.buffer == self._vars.buf: view = self._vars diff --git a/tests/variables.test.vim b/tests/variables.test.vim index 155e1d9..9152875 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -557,3 +557,37 @@ function Test_EvaluatePromptConsole() call vimspector#test#setup#Reset() %bwipe! endfunction + +function! Test_EvaluateFailure() + call s:StartDebugging() + + " Add a wtch + call vimspector#AddWatch( 'test' ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ 'Watches: ----', + \ 'Expression: test', + \ " *- Result: NameError: name 'test' is not defined", + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + VimspectorEval test + call assert_equal( bufnr( 'vimspector.Console' ), + \ winbufnr( g:vimspector_session_windows.output ) ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ "NameError: name 'test' is not defined" + \ ], + \ getbufline( bufnr( 'vimspector.Console' ), '$', '$' ) + \ ) + \ } ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction From c531686d39ccb79fe70e54c94c7782069ddcc67e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 2 Dec 2020 16:28:44 +0000 Subject: [PATCH 412/609] Use the cask of macvim because apparently the formula requires building gcc from source --- .github/workflows/build.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f592b21..040a325 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -86,10 +86,11 @@ jobs: - run: | brew update-reset brew doctor || true - for p in macvim tcl-tk llvm lua luajit love; do - brew install $p - brew outdated $p || brew upgrade $p + for p in python@3.8 tcl-tk llvm lua luajit love; do + brew install $p || brew outdated $p || brew upgrade $p done + brew install --cask macvim + brew link --overwrite python@3.8 name: 'Install vim and deps' - run: go get -u github.com/go-delve/delve/cmd/dlv From 632e6696efb5831067ea19b9d049fbc30402f059 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 2 Dec 2020 17:56:34 +0000 Subject: [PATCH 413/609] Add SSH login for failures --- .github/workflows/build.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 040a325..17f9601 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -38,6 +38,7 @@ jobs: options: --cap-add=SYS_PTRACE --security-opt seccomp=unconfined steps: - uses: actions/checkout@v2 + - run: | eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) go get -u github.com/go-delve/delve/cmd/dlv @@ -79,10 +80,18 @@ jobs: name: 'package-linux' path: 'package/linux-${{ github.run_id }}.tar.gz' + # - name: Start SSH session if failed + # uses: luchihoratiu/debug-via-ssh@main + # if: failure() + # with: + # NGROK_AUTH_TOKEN: ${{ secrets.NGROK_AUTH_TOKEN }} + # SSH_PASS: ${{ secrets.SSH_PASS }} + MacOS: runs-on: 'macos-10.15' steps: - uses: actions/checkout@v2 + - run: | brew update-reset brew doctor || true @@ -129,6 +138,13 @@ jobs: name: 'package-macos' path: 'package/macos-${{ github.run_id }}.tar.gz' + # - name: Start SSH session if failed + # uses: luchihoratiu/debug-via-ssh@main + # if: failure() + # with: + # NGROK_AUTH_TOKEN: ${{ secrets.NGROK_AUTH_TOKEN }} + # SSH_PASS: ${{ secrets.SSH_PASS }} + PublishRelease: runs-on: 'ubuntu-16.04' needs: From 8261cde3c993b20211e79bd16aa72b840b3e0308 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 5 Dec 2020 16:38:27 +0000 Subject: [PATCH 414/609] Don't copy the environment to update it This causes problems on windows, and is wasteful anyway. The subprocess will pick up the environment from its parent. --- python3/vimspector/debug_session.py | 6 +----- .../cpp/simple_c_program/.vimspector.json | 10 ++++++++- support/test/cpp/simple_c_program/test_c.cpp | 4 ++++ .../python/simple_python/.vimspector.json | 21 +++++++++++++++++-- .../python/simple_python/run_with_debugpy | 11 +++++----- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 63cd140..4e912ac 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -740,11 +740,7 @@ class DebugSession( object ): self._connection_type = self._api_prefix + self._connection_type - # TODO: Do we actually need to copy and update or does Vim do that? - env = os.environ.copy() - if 'env' in self._adapter: - env.update( self._adapter[ 'env' ] ) - self._adapter[ 'env' ] = env + self._adapter[ 'env' ] = self._adapter.get( 'env', {} ) if 'cwd' not in self._adapter: self._adapter[ 'cwd' ] = os.getcwd() diff --git a/support/test/cpp/simple_c_program/.vimspector.json b/support/test/cpp/simple_c_program/.vimspector.json index ae61ee8..7b8c53a 100644 --- a/support/test/cpp/simple_c_program/.vimspector.json +++ b/support/test/cpp/simple_c_program/.vimspector.json @@ -1,12 +1,20 @@ { "configurations": { - "simple_c_program - Launch": { + "CodeLLDB": { "adapter": "CodeLLDB", "configuration": { "request": "launch", "program": "${workspaceRoot}/test", "stopAtEntry": true } + }, + "lldb-vscode": { + "adapter": "lldb-vscode", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/test", + "stopAtEntry": true + } } } } diff --git a/support/test/cpp/simple_c_program/test_c.cpp b/support/test/cpp/simple_c_program/test_c.cpp index 4a95936..8dcc2dc 100644 --- a/support/test/cpp/simple_c_program/test_c.cpp +++ b/support/test/cpp/simple_c_program/test_c.cpp @@ -1,3 +1,5 @@ +#include +#include #include namespace Test @@ -33,6 +35,8 @@ int main ( int argc, char ** argv ) { int x{ 10 }; + printf( "HOME: %s\n", getenv( "HOME" ) ); + Test::TestStruct t{ true, {99} }; foo( t ); } diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 5eadb0c..2f63bc4 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -1,8 +1,12 @@ { "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json", "adapters": { - "test_custom": { - "command": "This is a test" + "run_with_debugpy": { + "command": [ "${workspaceRoot}/run_with_debugpy" ], + "port": 9876, + "env": { + "DEBUG_PORT": "9876" + } } }, "configurations": { @@ -44,6 +48,19 @@ } } }, + "attach-run": { + "adapter": "run_with_debugpy", + "configuration": { + "request": "attach" + }, + "breakpoints": { + "exception": { + "raised": "N", + "uncaught": "", + "userUnhandled": "" + } + } + }, "run": { "adapter": "debugpy", "configuration": { diff --git a/support/test/python/simple_python/run_with_debugpy b/support/test/python/simple_python/run_with_debugpy index 03da11c..950ce83 100755 --- a/support/test/python/simple_python/run_with_debugpy +++ b/support/test/python/simple_python/run_with_debugpy @@ -1,9 +1,7 @@ #!/usr/bin/env bash -PYTHON=python3 - -if [ -n "$1" ]; then - PYTHON=$1 +if [ -z "$DEBUG_PORT" ]; then + DEBUG_PORT=5678 fi pushd $(dirname $0) @@ -11,10 +9,11 @@ pushd $(dirname $0) rm -rf env fi - $PYTHON -m venv env + python3 -m venv env . env/bin/activate python -m pip install -r requirements.txt + echo "using port $DEBUG_PORT" echo "*** Launching ***" - python -m debugpy --wait-for-client --listen 0.0.0.0:5678 main.py + python -m debugpy --wait-for-client --listen 0.0.0.0:${DEBUG_PORT} main.py popd From 28c5534704ea4b8ec9207220d0bb6b3932581824 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Mon, 14 Dec 2020 21:37:54 +0800 Subject: [PATCH 415/609] language list --- install_gadget.py | 68 +++++++++++++++++++-------------- python3/vimspector/gadgets.py | 4 +- python3/vimspector/installer.py | 7 +++- 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 52621c3..aaa07e6 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -114,32 +114,34 @@ parser.add_argument( '--sudo', done_languages = set() for name, gadget in gadgets.GADGETS.items(): - lang = gadget[ 'language' ] - if lang in done_languages: - continue + langs = gadget[ 'language' ] + if not isinstance(langs, list): langs = [langs] + for lang in langs: + if lang in done_languages: + continue + + done_languages.add( lang ) + if not gadget.get( 'enabled', True ): + parser.add_argument( + '--force-enable-' + lang, + action = 'store_true', + help = 'Install the unsupported {} debug adapter for {} support'.format( + name, + lang ) ) + continue - done_languages.add( lang ) - if not gadget.get( 'enabled', True ): parser.add_argument( - '--force-enable-' + lang, + '--enable-' + lang, action = 'store_true', - help = 'Install the unsupported {} debug adapter for {} support'.format( + help = 'Install the {} debug adapter for {} support'.format( name, lang ) ) - continue - parser.add_argument( - '--enable-' + lang, - action = 'store_true', - help = 'Install the {} debug adapter for {} support'.format( - name, - lang ) ) - - parser.add_argument( - '--disable-' + lang, - action = 'store_true', - help = "Don't install the {} debug adapter for {} support " - '(when supplying --all)'.format( name, lang ) ) + parser.add_argument( + '--disable-' + lang, + action = 'store_true', + help = "Don't install the {} debug adapter for {} support " + '(when supplying --all)'.format( name, lang ) ) parser.add_argument( "--no-check-certificate", @@ -182,15 +184,23 @@ all_adapters = installer.ReadAdapters( manifest = installer.Manifest() for name, gadget in gadgets.GADGETS.items(): - if not gadget.get( 'enabled', True ): - if ( not args.force_all - and not getattr( args, 'force_enable_' + gadget[ 'language' ] ) ): - continue - else: - if not args.all and not getattr( args, 'enable_' + gadget[ 'language' ] ): - continue - if getattr( args, 'disable_' + gadget[ 'language' ] ): - continue + langs = gadget[ 'language' ] + if not isinstance(langs, list): langs = [langs] + skip = 0 + for lang in langs: + if not gadget.get( 'enabled', True ): + if ( not args.force_all + and not getattr( args, 'force_enable_' + lang) ): + skip = skip + 1 + continue + else: + if not args.all and not getattr( args, 'enable_' + lang ): + skip = skip + 1 + continue + if getattr( args, 'disable_' + lang ): + skip = skip + 1 + continue + if skip == len(langs): continue if not args.upgrade: manifest.Clear( name ) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 2ea6bbc..182b8c3 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -21,7 +21,7 @@ import os GADGETS = { 'vscode-cpptools': { - 'language': 'c', + 'language': ['c','cpp'], 'download': { 'url': 'https://github.com/Microsoft/vscode-cpptools/releases/download/' '${version}/${file_name}', @@ -225,7 +225,7 @@ GADGETS = { }, }, 'netcoredbg': { - 'language': 'csharp', + 'language': ['csharp', 'fsharp', 'vbnet'], 'enabled': False, 'download': { 'url': ( 'https://github.com/Samsung/netcoredbg/releases/download/' diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index b39398f..286cdd3 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -229,10 +229,13 @@ def GadgetListToInstallerArgs( *gadget_list ): except KeyError: continue + lang = gadget[ "language" ] + if isinstance(lang, list): lang = lang[0] + if not gadget.get( 'enabled', True ): - installer_args.append( f'--force-enable-{ gadget[ "language" ] }' ) + installer_args.append( f'--force-enable-{lang}' ) else: - installer_args.append( f'--enable-{ gadget[ "language" ] }' ) + installer_args.append( f'--enable-{lang}' ) return installer_args From b65d9536ad8a0cf79fd8bf11fff4514884ddba19 Mon Sep 17 00:00:00 2001 From: cposture Date: Tue, 15 Dec 2020 00:22:35 +0800 Subject: [PATCH 416/609] update gadgets vscode-go version --- python3/vimspector/gadgets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 2ea6bbc..0271069 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -353,10 +353,10 @@ GADGETS = { 'v${version}/${file_name}' }, 'all': { - 'version': '0.18.1', - 'file_name': 'Go-0.18.1.vsix', + 'version': '0.19.1', + 'file_name': 'go-0.19.1.vsix', 'checksum': - '80d4522c6cf482cfa6141997e5b458034f67d7065d92e1ce24a0456c405d6061', + '7f9dc014245b030d9f562b28f3ea9b1fd6e2708fac996c53ff6a707f8204ec64', }, 'adapters': { 'vscode-go': { From f161ce1e8cc62a956083d5b1d80c3499d614c5f5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 14 Dec 2020 16:14:02 +0000 Subject: [PATCH 417/609] Remove redundant comments --- plugin/vimspector.vim | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 2dbbd82..05245cd 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -28,11 +28,6 @@ if exists( 'g:loaded_vimpector' ) endif "}}} -" TODO: -" - Check Vim version (for jobs) -" - Check python support -" - Add commands/mappings/menus? - let g:loaded_vimpector = 1 let g:vimspector_home = expand( ':p:h:h' ) From 61179b7670491a70b40cb34ddeefe793bee7fcb8 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 14 Dec 2020 20:54:58 +0000 Subject: [PATCH 418/609] Update CONTRIBUTING.md for PRs --- CONTRIBUTING.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cfa6c9d..067f7dc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,6 +88,42 @@ vim -Nu /path/to/vimspector/support/minimal_vimrc ## Pull Requests +Vimspector is open to all contributors with ideas great and small! However, +there is a limit to the intended scope of the plugin and the amount of time the +maintainer has to support and... well... maintain features. It's probably well +understood that the contributor's input typically ends when a PR is megred, but +the maintainers have to keep it working forever. + +### Small changes + +For bug fixes, documentation changes, gadget versin updates, etc. please just +send a PR, I'm super happy to merge these! + +If you are unsure, or looking for some pointers, feel free to ask in Gitter, or +mention is in the PR. + +### Larger changes + +For larger features that might be in any way controvertial, or increase the +complexity of the overall plugin, please come to Gitter and talk to the +maintainer(s) first. This saves a lot of potential back-and-forth and makes sure +that we're "on the same page" about the idea and the ongoing maintenance. + +In addition, if you like hacking, feel free to raise a PR tagged with `[RFC]` in +the title and we can discuss the idea. I still prefer to discuss these things on +Gitter rather than back-and-forth on GitHub, though. + +Please don't be offended if the maintainer(s) request significant rework for (or +perhaps even dismiss) a PR that's not gone through this process. + +Please also don't be offended if the maintainer(s) ask if you're willing to +provide ongoing support for the feature. As an OSS project manned entirely in +what little spare time the maintainer(s) have, we're always looking for +contributions and contributors who will help with support and maintenance of +larger new features. + +### PR Guidelines + When contributing pull requests, I ask that: * You provide a clear and complete summary of the change, the use case and how @@ -95,6 +131,8 @@ When contributing pull requests, I ask that: * You avoid using APIs that are not available in the versions listed in the dependencies on README.md * You add tests for your PR. +* You test your changes in both Vim and Neovim at the supported versions (and + state that in the PR). * You follow the style of the code as-is; the python code is YCM-stye, it is *not* PEP8, nor should it be. From 66130389c521cad34400267efe58f6ad9aec3641 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 15 Dec 2020 16:25:26 +0800 Subject: [PATCH 419/609] code format fixes, update docs --- README.md | 31 ++++++++++++++++--------------- install_gadget.py | 9 ++++++--- python3/vimspector/gadgets.py | 2 +- python3/vimspector/installer.py | 3 ++- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index d964445..75a1da1 100644 --- a/README.md +++ b/README.md @@ -134,21 +134,22 @@ runtime dependencies). They are categorised by their level of support: * `Experimental`: Working, but not frequently used and rarely tested * `Legacy`: No longer supported, please migrate your config -| Language | Status | Switch (for `install_gadget.py`) | Adapter (for `:VimspectorInstall`) | Dependencies | -|--------------------|--------------|----------------------------------|------------------------------------|--------------------------------------------| -| C, C++, etc. | Tested | `--all` or `--enable-c` | vscode-cpptools | mono-core | -| Rust, C, C++, etc. | Supported | `--force-enable-rust` | CodeLLDB | Python 3 | -| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | -| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] | -| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | -| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | -| Lua | Supported | `--all` or `--enable-lua` | local-lua-debugger-vscode | Node >=12.13.0, Npm, Lua interpreter | -| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | -| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | -| Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | -| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | -| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | -| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | +| Language | Status | Switch (for `install_gadget.py`) | Adapter (for `:VimspectorInstall`) | Dependencies | +|--------------------|--------------|------------------------------------|------------------------------------|--------------------------------------------| +| C, C++, Rust etc. | Tested | `--all` or `--enable-c` (or cpp) | vscode-cpptools | mono-core | +| Rust, C, C++, etc. | Supported | `--force-enable-rust` | CodeLLDB | Python 3 | +| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | +| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] | +| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | +| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | +| Lua | Supported | `--all` or `--enable-lua` | local-lua-debugger-vscode | Node >=12.13.0, Npm, Lua interpreter | +| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | +| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | +| Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | +| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | +| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | +| F#, VB, etc. | Experimental | `--force-enable-fsharp` (or vbnet) | netcoredbg | DotNet core | +| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | ## Other languages diff --git a/install_gadget.py b/install_gadget.py index aaa07e6..91ad887 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -115,7 +115,8 @@ parser.add_argument( '--sudo', done_languages = set() for name, gadget in gadgets.GADGETS.items(): langs = gadget[ 'language' ] - if not isinstance(langs, list): langs = [langs] + if not isinstance(langs, list): + langs = [langs] for lang in langs: if lang in done_languages: continue @@ -185,7 +186,8 @@ manifest = installer.Manifest() for name, gadget in gadgets.GADGETS.items(): langs = gadget[ 'language' ] - if not isinstance(langs, list): langs = [langs] + if not isinstance(langs, list): + langs = [langs] skip = 0 for lang in langs: if not gadget.get( 'enabled', True ): @@ -200,7 +202,8 @@ for name, gadget in gadgets.GADGETS.items(): if getattr( args, 'disable_' + lang ): skip = skip + 1 continue - if skip == len(langs): continue + if skip == len(langs): + continue if not args.upgrade: manifest.Clear( name ) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 182b8c3..99ed55e 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -21,7 +21,7 @@ import os GADGETS = { 'vscode-cpptools': { - 'language': ['c','cpp'], + 'language': ['c', 'cpp', 'rust'], 'download': { 'url': 'https://github.com/Microsoft/vscode-cpptools/releases/download/' '${version}/${file_name}', diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index 286cdd3..cc8c3f2 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -230,7 +230,8 @@ def GadgetListToInstallerArgs( *gadget_list ): continue lang = gadget[ "language" ] - if isinstance(lang, list): lang = lang[0] + if isinstance(lang, list): + lang = lang[0] if not gadget.get( 'enabled', True ): installer_args.append( f'--force-enable-{lang}' ) From f8d1e484f95e742ea6dac3cdcaeb94a64c501e78 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 18 Dec 2020 17:10:29 +0000 Subject: [PATCH 420/609] Update CONTRIBUTING.md Minor fix and correct CI path --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 067f7dc..3ba53bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -167,10 +167,10 @@ Vimspector creator. ### Code Style The code style of the Python code is "YCM" style, because that's how I like it. -[`flake8`][] is used to check for certain errors and code style. +`flake8` is used to check for certain errors and code style. The code style of the Vimscript is largely the same, and it is linted by -[`vint`][]. +`vint`. To run them: @@ -182,7 +182,7 @@ To run them: They're also run by CI, so please check for lint failures. The canonical definition of the command to run is the command run in CI, i.e. in -`azure-pipelines.yml`. +`.git/workflows/build.yml`. # Code of conduct From f20c4c9725d0fcfd195cefbe95f84bf7c3d18d2a Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 19 Dec 2020 22:35:12 +0800 Subject: [PATCH 421/609] fix linter errors --- install_gadget.py | 12 ++++++------ python3/vimspector/gadgets.py | 4 ++-- python3/vimspector/installer.py | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/install_gadget.py b/install_gadget.py index 91ad887..b6e2fd7 100755 --- a/install_gadget.py +++ b/install_gadget.py @@ -115,8 +115,8 @@ parser.add_argument( '--sudo', done_languages = set() for name, gadget in gadgets.GADGETS.items(): langs = gadget[ 'language' ] - if not isinstance(langs, list): - langs = [langs] + if not isinstance( langs, list ): + langs = [ langs ] for lang in langs: if lang in done_languages: continue @@ -186,13 +186,13 @@ manifest = installer.Manifest() for name, gadget in gadgets.GADGETS.items(): langs = gadget[ 'language' ] - if not isinstance(langs, list): - langs = [langs] + if not isinstance( langs, list ): + langs = [ langs ] skip = 0 for lang in langs: if not gadget.get( 'enabled', True ): if ( not args.force_all - and not getattr( args, 'force_enable_' + lang) ): + and not getattr( args, 'force_enable_' + lang ) ): skip = skip + 1 continue else: @@ -202,7 +202,7 @@ for name, gadget in gadgets.GADGETS.items(): if getattr( args, 'disable_' + lang ): skip = skip + 1 continue - if skip == len(langs): + if skip == len( langs ): continue if not args.upgrade: diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 99ed55e..58104b3 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -21,7 +21,7 @@ import os GADGETS = { 'vscode-cpptools': { - 'language': ['c', 'cpp', 'rust'], + 'language': [ 'c', 'cpp', 'rust' ], 'download': { 'url': 'https://github.com/Microsoft/vscode-cpptools/releases/download/' '${version}/${file_name}', @@ -225,7 +225,7 @@ GADGETS = { }, }, 'netcoredbg': { - 'language': ['csharp', 'fsharp', 'vbnet'], + 'language': [ 'csharp', 'fsharp', 'vbnet' ], 'enabled': False, 'download': { 'url': ( 'https://github.com/Samsung/netcoredbg/releases/download/' diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index cc8c3f2..f27f46e 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -230,8 +230,8 @@ def GadgetListToInstallerArgs( *gadget_list ): continue lang = gadget[ "language" ] - if isinstance(lang, list): - lang = lang[0] + if isinstance( lang, list ): + lang = lang[ 0 ] if not gadget.get( 'enabled', True ): installer_args.append( f'--force-enable-{lang}' ) From 0942aa4523d2a3bf75c6ea213ef2b0c577662a07 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 14 Dec 2020 17:53:06 +0000 Subject: [PATCH 422/609] Fix docker example for python The example was was using 'launchCommand' which is not valid according to the schema; it should be 'runCommand'. But also, it never really worked. Vimspector would start the "adapter" (in this case, try and connect to the TCP port) before running the "prepare" commands, wich in this case would actually start debugpy listening. So to solve that we run the prepare commands earlier. Hopefully this won't cause a regression for Java and C++ remote attach, which we don't really have tests for. Finally, due to the way docker works, when you forward a port and nothing is listening on it, docker _accepts_ the connection then immediately drops it. This is _super_ annoying meaning that it looks to vimspector liek the server instantly dies if it takes nonzero time for the remote commands to open the port. So to solve this we add loaunch and attach delays which can be configured in the adapter config. This actually solves a prolem where the java debugger just takes agest to attach on remote launch too. (Finally, finally...) updated the vimspector schema to represent the real launch/attach remote configuration, which was incorrectly spec'd at the adapter level, but it's actually per launch/attach block. --- docs/configuration.md | 32 +- docs/schema/vimspector.schema.json | 283 ++++++++++-------- python3/vimspector/debug_session.py | 61 ++-- .../python/simple_python/.vimspector.json | 31 ++ support/test/python/simple_python/Dockerfile | 22 ++ .../test/python/simple_python/build-container | 11 + 6 files changed, 282 insertions(+), 158 deletions(-) create mode 100644 support/test/python/simple_python/Dockerfile create mode 100755 support/test/python/simple_python/build-container diff --git a/docs/configuration.md b/docs/configuration.md index acf77b4..f3d0814 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -633,19 +633,24 @@ Vimspector then orchestrates the various tools to set you up. // Command to launch the debugee and attach the debugger; // %CMD% replaced with the remote-cmdLine configured in the launch // configuration. (mandatory) - "launchCommmand": [ + "runCommand": [ "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}", "%CMD%" ] - // Optional alternative to launchCommmand (if you need to run multiple + // Optional alternative to runCommand (if you need to run multiple // commands) - // "launchCommmands": [ + // "runCommands": [ // [ /* first command */ ], // [ /* second command */ ] // ] } + + // optional delay to wait after running runCommand(s). This is often + // needed because of the way docker handles TCP, or if you're using some + // wrapper (e.g. to start the JVM) + // "delay": "1000m" // format as per :help sleep }, "attach": { "remote": { @@ -685,6 +690,10 @@ Vimspector then orchestrates the various tools to set you up. // "args": [ "-o", "StrictHostKeyChecking=no" ] // }, } + // optional delay to wait after running runCommand(s). This is often + // needed because of the way docker handles TCP, or if you're using some + // wrapper (e.g. to start the JVM) + // "delay": "1000m" // format as per :help sleep } } }, @@ -754,7 +763,7 @@ and have to tell cpptools a few more options. "remote": { "host": "${host}", "account": "${account}", - "launchCommmand": [ + "runCommand": [ "gdbserver", "--once", "--no-startup-with-shell", @@ -838,19 +847,23 @@ port. // Command to launch the debugee and attach the debugger; // %CMD% replaced with the remote-cmdLine configured in the launch // configuration. (mandatory) - "launchCommmand": [ + "runCommand": [ "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}", "%CMD%" ] - // Optional alternative to launchCommmand (if you need to run multiple + // Optional alternative to runCommand (if you need to run multiple // commands) - // "launchCommmands": [ + // "runCommands": [ // [ /* first command */ ], // [ /* second command */ ] // ] } + + // optional delay to wait after running runCommand(s). This is often + // needed because of the way docker handles TCP + "delay": "1000m" // format as per :help sleep }, "attach": { "remote": { @@ -886,6 +899,11 @@ port. // ] } + + // optional delay to wait after running runCommand(s). This is often + // needed because of the way docker handles TCP, or if you're using some + // wrapper (e.g. to start the JVM) + "delay": "1000m" // format as per :help sleep } } }, diff --git a/docs/schema/vimspector.schema.json b/docs/schema/vimspector.schema.json index c8af196..97fea75 100644 --- a/docs/schema/vimspector.schema.json +++ b/docs/schema/vimspector.schema.json @@ -38,11 +38,144 @@ ] } }, - "adapter-common": { + "adapter-launchattach": { + "properties": { + "launch": { + "allOf": [ + { "$ref": "#/definitions/adapter-remote" }, + { + "properties": { + "delay": { + "type": "string", + "description": "A time in the format understood by :help :sleep to wait after running the attachCommand(s)" + } + } + } + ] + }, + "attach": { + "allOf": [ + { "$ref": "#/definitions/adapter-remote" }, + { + "type": "object", + "required": [ "pidSelect" ], + "properties": { + "pidSelect": { + "enum": [ "ask", "none" ] + }, + "pidProperty": { + "type": "string", + "description": "The launch config property which the PID should be injected into. Required when 'pidSelect' is 'ask'." + }, + "delay": { + "type": "string", + "description": "A time in the format understood by :help :sleep to wait after running the attachCommand(s)" + } + } + } + ] + } + } + }, + "adapter-remote": { + "type": "object", + "properties": { + "remote": { + "type": "object", + "description": "Configures how Vimspector will marshal remote debugging requests. When remote debugging, Vimspector will either ssh to 'account'@'host' or docker exec -it to 'container' and run 'pidCommand', 'attachCommands', 'runCommands', etc. based on the 'remote-command' option in the debug configuration. If 'remote-command' is 'launch', it runs 'runCommand(s)', otherwise (it's 'attach') vimspector runs 'pidCommand', followed by 'attachCommand(s)'.Then it starts up the debug adapter with the debug configuration as normal. Usually this is configured with an 'attach' request (whether we remotely 'launched' or not). Once the initialization exchange is complete, Vimspector runs the optional 'initCompleteCommand' which can be used to force the application to break, e.g. by sending it SIGINT. This is required on some platforms which have buggy gdbservers (for example)", + "allOf": [ + { + "oneOf": [ + { "required": [ "host" ] }, + { "required": [ "container" ] } + ] + }, + { + "properties": { + "account": { + "type": "string", + "description": "Remote account name used when ssh'ing. Defaults to the current user account." + }, + "host": { + "type": "string", + "description": "Name of the remote host to connect to (via passwordless SSH)." + }, + "container": { + "type": "string", + "description": "Name or container id of the docker run container to connect to (via docker exec). Note the container must already be running (Vimspector will not start it) and it must have the port forwarded to the host if subsequently connecting via a port (for example docker run -p 8765:8765 -it simple_python)." + } + } + }, + { + "oneOf": [ + { + "allOf": [ + { + "oneOf": [ + { "required": [ "attachCommand" ] }, + { "required": [ "attachCommands" ] } + ] + }, + { + "properties": { + "initCompleteCommand": { + "type": "array", + "items": { "type": "string" }, + "description": "For remote-attach. Remote command to execute after initialization of the debug adapter. Can be used to work around buggy attach behaviour on certain platforms (advanced usage). Can contain the special token %PID% which is replaced with the PID returned by 'pidCommand'" + }, + "pidCommand": { + "type": "array", + "items": { "type": "string" }, + "description": "Required for remote-attach. Remote command to execute to return the PID to attach to." + }, + "attachCommands": { + "type": [ "array" ], + "items": { "type": "array", "items": { "type": "string" } }, + "description": "For remote-attach. List of commands to execute remotely to set up the attach. Can contain the special token %PID% which is replaced with the PID returned by the remote 'pidCommand'." + }, + "attachCommand": { + "type": "array", + "items": { "type": "string" }, + "description": "A single command to execute for remote-attach. Like attachCommands but for a single command. If attachCommands is supplied, this is not used." + } + } + } + ] + }, + { + "allOf": [ + { + "oneOf": [ + { "required": [ "runCommand" ] }, + { "required": [ "runCommands" ] } + ] + }, + { + "properties": { + "runCommands": { + "type": [ "array" ], + "items": { "type": "array", "items": { "type": "string" } }, + "description": "For remote-launch. List of commands to execute remotely to set up the launch. An entry in the array can be the special token '%CMD%' which is replaced with the evaluated 'remote-cmdLine' value in the debug configuration. This is useful to parameterize launcging remotely under something like gdbserver." + }, + "runCommand": { + "type": "array", + "items": { "type": "string" }, + "description": "A single command to execute for remote-launch. Like runCommands but for a single command." + } + } + } + ] + } + ] + } + ] + } + } + }, + "adapter": { "allOf": [ + { "type": "object" }, { "$ref": "#/definitions/variables" }, - { "$ref": "#/definitions/adapter-remote" }, - { "$ref": "#/definitions/adapter-attach" }, { "properties": { "name": { @@ -54,136 +187,30 @@ "description": "Base debug configuration. Can be used to set default values for all debug configurations. When reading individual debug configurations from 'configurations', those configurations are merged with this object. Definitions in the debug configuration override anything in this object. Typical usage for this is to set the 'type' parameter, which some debug adapters are very picky about, or to set e.g. the path to an underlying debugger." } } - } - ] - }, - "adapter-attach": { - "properties": { - "attach": { - "type": "object", - "required": [ "pidSelect" ], - "properties": { - "pidSelect": { - "enum": [ "ask", "none" ] - }, - "pidProperty": { - "type": "string", - "description": "The launch config property which the PID should be injected into. Required when 'pidSelect' is 'ask'." - } - } - } - } - }, - "adapter-remote": { - "properties": { - "remote": { - "type": "object", - "oneOf": [ - { - "required": [ - "host" - ] - }, - { - "required": [ - "container" - ] - } - ], - "description": "Configures how Vimspector will marshal remote debugging requests. When remote debugging, Vimspector will either ssh to 'account'@'host' or docker exec -it to 'container' and run 'pidCommand', 'attachCommands', 'runCommands', etc. based on the 'remote-command' option in the debug configuration. If 'remote-command' is 'launch', it runs 'runCommand(s)', otherwise (it's 'attach') vimspector runs 'pidCommand', followed by 'attachCommand(s)'.Then it starts up the debug adapter with the debug configuration as normal. Usually this is configured with an 'attach' request (whether we remotely 'launched' or not). Once the initialization exchange is complete, Vimspector runs the optional 'initCompleteCommand' which can be used to force the application to break, e.g. by sending it SIGINT. This is required on some platforms which have buggy gdbservers (for example)", - "properties": { - "account": { - "type": "string", - "description": "Remote account name used when ssh'ing. Defaults to the current user account." - }, - "host": { - "type": "string", - "description": "Name of the remote host to connect to (via passwordless SSH)." - }, - "container": { - "type": "string", - "description": "Name or container id of the docker run container to connect to (via docker exec -it)." - }, - "pidCommand": { - "type": "array", - "items": { "type": "string" }, - "description": "Required for remote-attach. Remote command to execute to return the PID to attach to." - }, - "initCompleteCommand": { - "type": "array", - "items": { "type": "string" }, - "description": "For remote-attach. Remote command to execute after initialization of the debug adapter. Can be used to work around buggy attach behaviour on certain platforms (advanced usage). Can contain the special token %PID% which is replaced with the PID returned by 'pidCommand'" - }, - "attachCommands": { - "type": [ "array" ], - "items": { "type": "array", "items": { "type": "string" } }, - "description": "For remote-attach. List of commands to execute remotely to set up the attach. Can contain the special token %PID% which is replaced with the PID returned by the remote 'pidCommand'." - }, - "attachCommand": { - "type": "array", - "items": { "type": "string" }, - "description": "A single command to execute for remote-attach. Like attachCommands but for a single command. If attachCommands is supplied, this is not used." - }, - "runCommands": { - "type": [ "array" ], - "items": { "type": "array", "items": { "type": "string" } }, - "description": "For remote-launch. List of commands to execute remotely to set up the launch. An entry in the array can be the special token '%CMD%' which is replaced with the evaluated 'remote-cmdLine' value in the debug configuration. This is useful to parameterize launcging remotely under something like gdbserver." - }, - "runCommand": { - "type": "array", - "items": { "type": "string" }, - "description": "A single command to execute for remote-launch. Like runCommands but for a single command." - } - } - } - } - }, - "adapter": { - "oneOf": [ + }, + { "$ref": "#/definitions/adapter-launchattach" }, { - "allOf": [ - { "$ref": "#/definitions/adapter-common" }, - { - "required": [ "port" ], - "properties": { - "port": { - "oneOf": [ - { - "type": "string", - "enum": [ "ask" ] - }, - { - "type": "integer" - } - ], - "description": "If supplied, indicates that a socket connection should be made to this port on 'localhost'. If the value is 'ask', then the user is asked to enter the port number to connect to." - } - } - } + "anyOf": [ + { "required": [ "command" ] }, + { "required": [ "port" ] }, + { "required": [ "command", "port" ] } ] }, { - "allOf": [ - { "$ref": "#/definitions/adapter-common" }, - { - "required": [ "command" ], - "properties": { - "command": { - "type": [ "string", "array" ], - "description": "Command line to execute the debug adapter.", - "items": { "type": "string" } - }, - "env": { - "type": "object", - "description": "Name/value pairs to set in the environment when starting the adapter." - }, - "cwd": { - "type": "string", - "description": "Directory from which to start the adapter. Defaults to the working directory of the window on launch" - } - } + "properties": { + "host": { + "type": "string", + "default": "localhost", + "description": "Connect to this host in multi-session mode" + }, + "port": { + "oneOf": [ + { "type": "string" }, + { "type": "integer" } + ], + "description": "If supplied, indicates that a socket connection should be made to this port on 'host'. If the value is 'ask', then the user is asked to enter the port number to connect to." } - ] + } } ] } diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 4e912ac..ea72651 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -68,6 +68,7 @@ class DebugSession( object ): self._configuration = None self._adapter = None + self._launch_config = None self._ResetServerState() @@ -108,6 +109,7 @@ class DebugSession( object ): launch_variables ) self._configuration = None self._adapter = None + self._launch_config = None current_file = utils.GetBufferFilepath( vim.current.buffer ) adapters = {} @@ -280,6 +282,7 @@ class DebugSession( object ): def start(): self._configuration = configuration self._adapter = adapter + self._launch_config = None self._logger.info( 'Configuration: %s', json.dumps( self._configuration ) ) @@ -291,6 +294,7 @@ class DebugSession( object ): else: vim.current.tabpage = self._uiTab + self._Prepare() self._StartDebugAdapter() self._Initialise() @@ -723,7 +727,6 @@ class DebugSession( object ): json.dumps( self._adapter ) ) self._init_complete = False - self._on_init_complete_handlers = [] self._launch_complete = False self._run_on_server_exit = None @@ -739,6 +742,7 @@ class DebugSession( object ): self._adapter[ 'port' ] = port self._connection_type = self._api_prefix + self._connection_type + self._logger.debug( f"Connection Type: { self._connection_type }" ) self._adapter[ 'env' ] = self._adapter.get( 'env', {} ) @@ -794,16 +798,16 @@ class DebugSession( object ): def _PrepareAttach( self, adapter_config, launch_config ): - atttach_config = adapter_config.get( 'attach' ) + attach_config = adapter_config.get( 'attach' ) - if not atttach_config: + if not attach_config: return - if 'remote' in atttach_config: + if 'remote' in attach_config: # FIXME: We almost want this to feed-back variables to be expanded later, # e.g. expand variables when we use them, not all at once. This would # remove the whole %PID% hack. - remote = atttach_config[ 'remote' ] + remote = attach_config[ 'remote' ] remote_exec_cmd = self._GetRemoteExecCommand( remote ) # FIXME: Why does this not use self._GetCommands ? @@ -844,20 +848,23 @@ class DebugSession( object ): self._codeView._window, self._remote_term ) else: - if atttach_config[ 'pidSelect' ] == 'ask': - prop = atttach_config[ 'pidProperty' ] + if attach_config[ 'pidSelect' ] == 'ask': + prop = attach_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': + elif attach_config[ 'pidSelect' ] == 'none': return raise ValueError( 'Unrecognised pidSelect {0}'.format( - atttach_config[ 'pidSelect' ] ) ) + attach_config[ 'pidSelect' ] ) ) + if 'delay' in attach_config: + utils.UserMessage( f"Waiting ( { attach_config[ 'delay' ] } )..." ) + vim.command( f'sleep { attach_config[ "delay" ] }' ) def _PrepareLaunch( self, command_line, adapter_config, launch_config ): @@ -890,6 +897,11 @@ class DebugSession( object ): self._codeView._window, self._remote_term ) + if 'delay' in run_config: + utils.UserMessage( f"Waiting ( {run_config[ 'delay' ]} )..." ) + vim.command( f'sleep { run_config[ "delay" ] }' ) + + def _GetSSHCommand( self, remote ): ssh = [ 'ssh' ] + remote.get( 'ssh', {} ).get( 'args', [] ) @@ -988,16 +1000,18 @@ class DebugSession( object ): message ) self._outputView.Print( 'server', msg ) - def _Launch( self ): + + def _Prepare( self ): + self._on_init_complete_handlers = [] + self._logger.debug( "LAUNCH!" ) - adapter_config = self._adapter - launch_config = {} - launch_config.update( self._adapter.get( 'configuration', {} ) ) - launch_config.update( self._configuration[ 'configuration' ] ) + self._launch_config = {} + self._launch_config.update( self._adapter.get( 'configuration', {} ) ) + self._launch_config.update( self._configuration[ 'configuration' ] ) request = self._configuration.get( 'remote-request', - launch_config.get( 'request', 'launch' ) ) + self._launch_config.get( 'request', 'launch' ) ) if request == "attach": self._splash_screen = utils.DisplaySplash( @@ -1005,7 +1019,7 @@ class DebugSession( object ): self._splash_screen, "Attaching to debugee..." ) - self._PrepareAttach( adapter_config, launch_config ) + self._PrepareAttach( self._adapter, self._launch_config ) elif request == "launch": self._splash_screen = utils.DisplaySplash( self._api_prefix, @@ -1014,14 +1028,16 @@ class DebugSession( object ): # FIXME: This cmdLine hack is not fun. self._PrepareLaunch( self._configuration.get( 'remote-cmdLine', [] ), - adapter_config, - launch_config ) + self._adapter, + self._launch_config ) # FIXME: name is mandatory. Forcefully add it (we should really use the # _actual_ name, but that isn't actually remembered at this point) - if 'name' not in launch_config: - launch_config[ 'name' ] = 'test' + if 'name' not in self._launch_config: + self._launch_config[ 'name' ] = 'test' + + def _Launch( self ): def failure_handler( reason, msg ): text = [ 'Launch Failed', @@ -1034,12 +1050,11 @@ class DebugSession( object ): self._splash_screen, text ) - self._connection.DoRequest( lambda msg: self._OnLaunchComplete(), { - 'command': launch_config[ 'request' ], - 'arguments': launch_config + 'command': self._launch_config[ 'request' ], + 'arguments': self._launch_config }, failure_handler ) diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 2f63bc4..29d7d43 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -7,6 +7,23 @@ "env": { "DEBUG_PORT": "9876" } + }, + "python-remote-docker": { + "variables": { + "port": "8765" + }, + "port": "${port}", + "launch": { + "remote": { + "container": "${ContainerID}", + "runCommand": [ + "python3", "-m", "debugpy", "--listen", "0.0.0.0:${port}", + "--wait-for-client", + "%CMD%" + ] + }, + "delay": "5000m" + } } }, "configurations": { @@ -61,6 +78,20 @@ } } }, + "docker-attach": { + "adapter": "python-remote-docker", + "remote-cmdLine": [ "/root/main.py" ], + "remote-request": "launch", + "configuration": { + "request": "attach", + "pathMappings": [ + { + "localRoot": "${workspaceRoot}", + "remoteRoot": "/root" + } + ] + } + }, "run": { "adapter": "debugpy", "configuration": { diff --git a/support/test/python/simple_python/Dockerfile b/support/test/python/simple_python/Dockerfile new file mode 100644 index 0000000..7748fef --- /dev/null +++ b/support/test/python/simple_python/Dockerfile @@ -0,0 +1,22 @@ +FROM ubuntu:18.04 + +RUN apt-get update && \ + apt-get -y dist-upgrade && \ + apt-get -y install sudo \ + lsb-release \ + ca-certificates \ + python3-dev \ + python3-pip \ + ca-cacert \ + locales \ + language-pack-en \ + libncurses5-dev libncursesw5-dev \ + git && \ + apt-get -y autoremove + +## cleanup of files from setup +RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN pip3 install debugpy + +ADD main.py /root/main.py diff --git a/support/test/python/simple_python/build-container b/support/test/python/simple_python/build-container new file mode 100755 index 0000000..bc6450a --- /dev/null +++ b/support/test/python/simple_python/build-container @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -e + +if [ "$1" = "--continue" ]; then + OPTS="" +else + OPTS="--no-cache" +fi + +docker build ${OPTS} -t puremourning/vimspector:simple_python . From 523ea29faa1237e6536aa3ff6035a9a8b87e45ea Mon Sep 17 00:00:00 2001 From: Alex Weigl Date: Tue, 22 Dec 2020 23:49:25 +0200 Subject: [PATCH 423/609] Fix Typo in PHP section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d964445..994081e 100644 --- a/README.md +++ b/README.md @@ -1284,7 +1284,7 @@ xdebug.remote_connect_back=true xdebug.remote_port=9000 ``` -* .vimspectory.json +* .vimspector.json ```json { "configurations": { From 47c404f823b124391ebf1475362185fe367f6bdc Mon Sep 17 00:00:00 2001 From: Allan Guigou Date: Thu, 24 Dec 2020 12:57:49 -0500 Subject: [PATCH 424/609] Allow a gadget to specify a custom extension path When installing a custom gadget not officially supported by vimspector the default extension path is 'extension', this works for vscode extensions but does not support some debug adapters. This commit gives the ability to change that default extension path by specifying 'extension_path' within the gadget installer file. Installing 'cust_adapter' would use the extension path 'adapter' rather than 'extension' (ie ${gadgetDir}/cust_adapter/${version}/root/adapter) { "cust_adapter": { "download": { ... }, "all": { "extension_path": "adapter", "adapters": { ... } } } } --- python3/vimspector/installer.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index b39398f..46a9e69 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -340,11 +340,12 @@ def WriteAdapters( all_adapters, to_file=None ): def InstallGeneric( name, root, gadget ): - extension = os.path.join( root, 'extension' ) + extension_path = gadget.get( 'extension_path', 'extension' ) + extension = os.path.join( root, extension_path ) for f in gadget.get( 'make_executable', [] ): MakeExecutable( os.path.join( extension, f ) ) - MakeExtensionSymlink( name, root ) + MakeExtensionSymlink( name, root, extension_path ) def InstallCppTools( name, root, gadget ): @@ -699,8 +700,8 @@ def ExtractZipTo( file_path, destination, format ): CheckCall( [ 'tar', 'zxvf', file_path ] ) -def MakeExtensionSymlink( name, root ): - MakeSymlink( name, os.path.join( root, 'extension' ) ), +def MakeExtensionSymlink( name, root, extension_path = 'extension' ): + MakeSymlink( name, os.path.join( root, extension_path ) ), def MakeSymlink( link, pointing_to, in_folder = None ): From ec68811c8a598fda979e6d21bc933bb6a31d50c1 Mon Sep 17 00:00:00 2001 From: Allan Guigou Date: Thu, 24 Dec 2020 14:59:03 -0500 Subject: [PATCH 425/609] Add support test for kotlin --- support/test/kotlin/.gitignore | 2 + support/test/kotlin/.vimspector.json | 21 ++ support/test/kotlin/build.gradle.kts | 25 +++ .../kotlin/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58910 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + support/test/kotlin/gradlew | 185 ++++++++++++++++++ support/test/kotlin/gradlew.bat | 104 ++++++++++ support/test/kotlin/settings.gradle.kts | 1 + .../kotlin/vimspector/test/Application.kt | 5 + 9 files changed, 348 insertions(+) create mode 100644 support/test/kotlin/.gitignore create mode 100644 support/test/kotlin/.vimspector.json create mode 100644 support/test/kotlin/build.gradle.kts create mode 100644 support/test/kotlin/gradle/wrapper/gradle-wrapper.jar create mode 100644 support/test/kotlin/gradle/wrapper/gradle-wrapper.properties create mode 100755 support/test/kotlin/gradlew create mode 100644 support/test/kotlin/gradlew.bat create mode 100644 support/test/kotlin/settings.gradle.kts create mode 100644 support/test/kotlin/src/main/kotlin/vimspector/test/Application.kt diff --git a/support/test/kotlin/.gitignore b/support/test/kotlin/.gitignore new file mode 100644 index 0000000..f8b92c3 --- /dev/null +++ b/support/test/kotlin/.gitignore @@ -0,0 +1,2 @@ +.gradle +build diff --git a/support/test/kotlin/.vimspector.json b/support/test/kotlin/.vimspector.json new file mode 100644 index 0000000..d2c2a63 --- /dev/null +++ b/support/test/kotlin/.vimspector.json @@ -0,0 +1,21 @@ +{ + "configurations": { + "kotlin-debug-adapter launch": { + "adapter": "cust_kotlin-debug-adapter", + "configuration": { + "request": "launch", + "projectRoot": "${workspaceFolder}", + "mainClass": "vimspector/test/ApplicationKt" + } + }, + "kotlin-debug-adapter attach": { + "adapter": "cust_kotlin-debug-adapter", + "configuration": { + "request": "attach", + "projectRoot": "${workspaceFolder}", + "hostName": "${hostName}", + "port": "${port}" + } + } + } +} diff --git a/support/test/kotlin/build.gradle.kts b/support/test/kotlin/build.gradle.kts new file mode 100644 index 0000000..e93c004 --- /dev/null +++ b/support/test/kotlin/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + kotlin("jvm") version "1.4.0" + + application +} + +repositories { + // Use jcenter for resolving dependencies. + // You can declare any Maven/Ivy/file repository here. + jcenter() +} + +dependencies { + // Align versions of all Kotlin components + implementation(platform("org.jetbrains.kotlin:kotlin-bom")) + + // Use the Kotlin JDK 8 standard library. + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") +} + +application { + // Define the main class for the application. + mainClassName = "vimspector.test.ApplicationKt" +} + diff --git a/support/test/kotlin/gradle/wrapper/gradle-wrapper.jar b/support/test/kotlin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..62d4c053550b91381bbd28b1afc82d634bf73a8a GIT binary patch literal 58910 zcma&ObC74zk}X`WF59+k+qTVL*+!RbS9RI8Z5v&-ZFK4Nn|tqzcjwK__x+Iv5xL`> zj94dg?X`0sMHx^qXds{;KY)OMg#H>35XgTVfq6#vc9ww|9) z@UMfwUqk)B9p!}NrNqTlRO#i!ALOPcWo78-=iy}NsAr~T8T0X0%G{DhX~u-yEwc29WQ4D zuv2j{a&j?qB4wgCu`zOXj!~YpTNFg)TWoV>DhYlR^Gp^rkOEluvxkGLB?!{fD!T@( z%3cy>OkhbIKz*R%uoKqrg1%A?)uTZD&~ssOCUBlvZhx7XHQ4b7@`&sPdT475?*zWy z>xq*iK=5G&N6!HiZaD{NSNhWL;+>Quw_#ZqZbyglna!Fqn3N!$L`=;TFPrhodD-Q` z1l*=DP2gKJP@)cwI@-M}?M$$$%u~=vkeC%>cwR$~?y6cXx-M{=wdT4|3X(@)a|KkZ z`w$6CNS@5gWS7s7P86L<=vg$Mxv$?)vMj3`o*7W4U~*Nden}wz=y+QtuMmZ{(Ir1D zGp)ZsNiy{mS}Au5;(fYf93rs^xvi(H;|H8ECYdC`CiC&G`zw?@)#DjMc7j~daL_A$ z7e3nF2$TKlTi=mOftyFBt8*Xju-OY@2k@f3YBM)-v8+5_o}M?7pxlNn)C0Mcd@87?+AA4{Ti2ptnYYKGp`^FhcJLlT%RwP4k$ad!ho}-^vW;s{6hnjD0*c39k zrm@PkI8_p}mnT&5I@=O1^m?g}PN^8O8rB`;t`6H+?Su0IR?;8txBqwK1Au8O3BZAX zNdJB{bpQWR@J|e=Z>XSXV1DB{uhr3pGf_tb)(cAkp)fS7*Qv))&Vkbb+cvG!j}ukd zxt*C8&RN}5ck{jkw0=Q7ldUp0FQ&Pb_$M7a@^nf`8F%$ftu^jEz36d#^M8Ia{VaTy z5(h$I)*l3i!VpPMW+XGgzL~fcN?{~1QWu9!Gu0jOWWE zNW%&&by0DbXL&^)r-A*7R@;T$P}@3eOj#gqJ!uvTqBL5bupU91UK#d|IdxBUZAeh1 z>rAI#*Y4jv>uhOh7`S@mnsl0g@1C;k$Z%!d*n8#_$)l}-1&z2kr@M+xWoKR z!KySy-7h&Bf}02%JeXmQGjO3ntu={K$jy$rFwfSV8!zqAL_*&e2|CJ06`4&0+ceI026REfNT>JzAdwmIlKLEr2? zaZ#d*XFUN*gpzOxq)cysr&#6zNdDDPH% zd8_>3B}uA7;bP4fKVdd~Og@}dW#74ceETOE- zlZgQqQfEc?-5ly(Z5`L_CCM!&Uxk5#wgo=OLs-kFHFG*cTZ)$VE?c_gQUW&*!2@W2 z7Lq&_Kf88OCo?BHCtwe*&fu&8PQ(R5&lnYo8%+U73U)Ec2&|A)Y~m7(^bh299REPe zn#gyaJ4%o4>diN3z%P5&_aFUmlKytY$t21WGwx;3?UC}vlxi-vdEQgsKQ;=#sJ#ll zZeytjOad$kyON4XxC}frS|Ybh`Yq!<(IrlOXP3*q86ImyV*mJyBn$m~?#xp;EplcM z+6sez%+K}Xj3$YN6{}VL;BZ7Fi|iJj-ywlR+AP8lq~mnt5p_%VmN{Sq$L^z!otu_u znVCl@FgcVXo510e@5(wnko%Pv+^r^)GRh;>#Z(|#cLnu_Y$#_xG&nvuT+~gzJsoSi zBvX`|IS~xaold!`P!h(v|=>!5gk)Q+!0R1Ge7!WpRP{*Ajz$oGG$_?Ajvz6F0X?809o`L8prsJ*+LjlGfSziO;+ zv>fyRBVx#oC0jGK8$%$>Z;0+dfn8x;kHFQ?Rpi7(Rc{Uq{63Kgs{IwLV>pDK7yX-2 zls;?`h!I9YQVVbAj7Ok1%Y+F?CJa-Jl>1x#UVL(lpzBBH4(6v0^4 z3Tf`INjml5`F_kZc5M#^J|f%7Hgxg3#o}Zwx%4l9yYG!WaYUA>+dqpRE3nw#YXIX%= ziH3iYO~jr0nP5xp*VIa#-aa;H&%>{mfAPPlh5Fc!N7^{!z$;p-p38aW{gGx z)dFS62;V;%%fKp&i@+5x=Cn7Q>H`NofJGXmNeh{sOL+Nk>bQJJBw3K*H_$}%*xJM=Kh;s#$@RBR z|75|g85da@#qT=pD777m$wI!Q8SC4Yw3(PVU53bzzGq$IdGQoFb-c_(iA_~qD|eAy z@J+2!tc{|!8fF;%6rY9`Q!Kr>MFwEH%TY0y>Q(D}xGVJM{J{aGN0drG&|1xO!Ttdw z-1^gQ&y~KS5SeslMmoA$Wv$ly={f}f9<{Gm!8ycp*D9m*5Ef{ymIq!MU01*)#J1_! zM_i4{LYButqlQ>Q#o{~W!E_#(S=hR}kIrea_67Z5{W>8PD>g$f;dTvlD=X@T$8D0;BWkle@{VTd&D5^)U>(>g(jFt4lRV6A2(Te->ooI{nk-bZ(gwgh zaH4GT^wXPBq^Gcu%xW#S#p_&x)pNla5%S5;*OG_T^PhIIw1gXP&u5c;{^S(AC*+$> z)GuVq(FT@zq9;i{*9lEsNJZ)??BbSc5vF+Kdh-kL@`(`l5tB4P!9Okin2!-T?}(w% zEpbEU67|lU#@>DppToestmu8Ce=gz=e#V+o)v)#e=N`{$MI5P0O)_fHt1@aIC_QCv=FO`Qf=Ga%^_NhqGI)xtN*^1n{ z&vgl|TrKZ3Vam@wE0p{c3xCCAl+RqFEse@r*a<3}wmJl-hoJoN<|O2zcvMRl<#BtZ z#}-bPCv&OTw`GMp&n4tutf|er`@#d~7X+);##YFSJ)BitGALu}-N*DJdCzs(cQ?I- z6u(WAKH^NUCcOtpt5QTsQRJ$}jN28ZsYx+4CrJUQ%egH zo#tMoywhR*oeIkS%}%WUAIbM`D)R6Ya&@sZvvUEM7`fR0Ga03*=qaEGq4G7-+30Ck zRkje{6A{`ebq?2BTFFYnMM$xcQbz0nEGe!s%}O)m={`075R0N9KTZ>vbv2^eml>@}722%!r#6Wto}?vNst? zs`IasBtcROZG9+%rYaZe^=5y3chDzBf>;|5sP0!sP(t^= z^~go8msT@|rp8LJ8km?4l?Hb%o10h7(ixqV65~5Y>n_zG3AMqM3UxUNj6K-FUgMT7 z*Dy2Y8Ws+%`Z*~m9P zCWQ8L^kA2$rf-S@qHow$J86t)hoU#XZ2YK~9GXVR|*`f6`0&8j|ss_Ai-x=_;Df^*&=bW$1nc{Gplm zF}VF`w)`5A;W@KM`@<9Bw_7~?_@b{Z`n_A6c1AG#h#>Z$K>gX6reEZ*bZRjCup|0# zQ{XAb`n^}2cIwLTN%5Ix`PB*H^(|5S{j?BwItu+MS`1)VW=TnUtt6{3J!WR`4b`LW z?AD#ZmoyYpL=903q3LSM=&5eNP^dwTDRD~iP=}FXgZ@2WqfdyPYl$9do?wX{RU*$S zgQ{OqXK-Yuf4+}x6P#A*la&^G2c2TC;aNNZEYuB(f25|5eYi|rd$;i0qk7^3Ri8of ziP~PVT_|4$n!~F-B1_Et<0OJZ*e+MN;5FFH`iec(lHR+O%O%_RQhvbk-NBQ+$)w{D+dlA0jxI;z|P zEKW`!X)${xzi}Ww5G&@g0akBb_F`ziv$u^hs0W&FXuz=Ap>SUMw9=M?X$`lgPRq11 zqq+n44qL;pgGO+*DEc+Euv*j(#%;>p)yqdl`dT+Og zZH?FXXt`<0XL2@PWYp|7DWzFqxLK)yDXae&3P*#+f+E{I&h=$UPj;ey9b`H?qe*Oj zV|-qgI~v%&oh7rzICXfZmg$8$B|zkjliQ=e4jFgYCLR%yi!9gc7>N z&5G#KG&Hr+UEfB;M(M>$Eh}P$)<_IqC_WKOhO4(cY@Gn4XF(#aENkp&D{sMQgrhDT zXClOHrr9|POHqlmm+*L6CK=OENXbZ+kb}t>oRHE2xVW<;VKR@ykYq04LM9L-b;eo& zl!QQo!Sw{_$-qosixZJWhciN>Gbe8|vEVV2l)`#5vKyrXc6E`zmH(76nGRdL)pqLb@j<&&b!qJRLf>d`rdz}^ZSm7E;+XUJ ziy;xY&>LM?MA^v0Fu8{7hvh_ynOls6CI;kQkS2g^OZr70A}PU;i^~b_hUYN1*j-DD zn$lHQG9(lh&sDii)ip*{;Sb_-Anluh`=l~qhqbI+;=ZzpFrRp&T+UICO!OoqX@Xr_ z32iJ`xSpx=lDDB_IG}k+GTYG@K8{rhTS)aoN8D~Xfe?ul&;jv^E;w$nhu-ICs&Q)% zZ=~kPNZP0-A$pB8)!`TEqE`tY3Mx^`%O`?EDiWsZpoP`e-iQ#E>fIyUx8XN0L z@S-NQwc;0HjSZKWDL}Au_Zkbh!juuB&mGL0=nO5)tUd_4scpPy&O7SNS^aRxUy0^< zX}j*jPrLP4Pa0|PL+nrbd4G;YCxCK-=G7TG?dby~``AIHwxqFu^OJhyIUJkO0O<>_ zcpvg5Fk$Wpj}YE3;GxRK67P_Z@1V#+pu>pRj0!mFf(m_WR3w3*oQy$s39~U7Cb}p(N&8SEwt+)@%o-kW9Ck=^?tvC2$b9% ze9(Jn+H`;uAJE|;$Flha?!*lJ0@lKfZM>B|c)3lIAHb;5OEOT(2453m!LgH2AX=jK zQ93An1-#l@I@mwB#pLc;M7=u6V5IgLl>E%gvE|}Hvd4-bE1>gs(P^C}gTv*&t>W#+ zASLRX$y^DD3Jrht zwyt`yuA1j(TcP*0p*Xkv>gh+YTLrcN_HuaRMso~0AJg`^nL#52dGBzY+_7i)Ud#X) zVwg;6$WV20U2uyKt8<)jN#^1>PLg`I`@Mmut*Zy!c!zshSA!e^tWVoKJD%jN&ml#{ z@}B$j=U5J_#rc%T7(DGKF+WwIblEZ;Vq;CsG~OKxhWYGJx#g7fxb-_ya*D0=_Ys#f zhXktl=Vnw#Z_neW>Xe#EXT(4sT^3p6srKby4Ma5LLfh6XrHGFGgM;5Z}jv-T!f~=jT&n>Rk z4U0RT-#2fsYCQhwtW&wNp6T(im4dq>363H^ivz#>Sj;TEKY<)dOQU=g=XsLZhnR>e zd}@p1B;hMsL~QH2Wq>9Zb; zK`0`09fzuYg9MLJe~cdMS6oxoAD{kW3sFAqDxvFM#{GpP^NU@9$d5;w^WgLYknCTN z0)N425mjsJTI@#2kG-kB!({*+S(WZ-{SckG5^OiyP%(6DpRsx60$H8M$V65a_>oME z^T~>oG7r!ew>Y)&^MOBrgc-3PezgTZ2xIhXv%ExMFgSf5dQbD=Kj*!J4k^Xx!Z>AW ziZfvqJvtm|EXYsD%A|;>m1Md}j5f2>kt*gngL=enh<>#5iud0dS1P%u2o+>VQ{U%(nQ_WTySY(s#~~> zrTsvp{lTSup_7*Xq@qgjY@1#bisPCRMMHnOL48qi*jQ0xg~TSW%KMG9zN1(tjXix()2$N}}K$AJ@GUth+AyIhH6Aeh7qDgt#t*`iF5#A&g4+ zWr0$h9Zx6&Uo2!Ztcok($F>4NA<`dS&Js%L+67FT@WmI)z#fF~S75TUut%V($oUHw z$IJsL0X$KfGPZYjB9jaj-LaoDD$OMY4QxuQ&vOGo?-*9@O!Nj>QBSA6n$Lx|^ zky)4+sy{#6)FRqRt6nM9j2Lzba!U;aL%ZcG&ki1=3gFx6(&A3J-oo|S2_`*w9zT)W z4MBOVCp}?4nY)1))SOX#6Zu0fQQ7V{RJq{H)S#;sElY)S)lXTVyUXTepu4N)n85Xo zIpWPT&rgnw$D2Fsut#Xf-hO&6uA0n~a;a3!=_!Tq^TdGE&<*c?1b|PovU}3tfiIUu z){4W|@PY}zJOXkGviCw^x27%K_Fm9GuKVpd{P2>NJlnk^I|h2XW0IO~LTMj>2<;S* zZh2uRNSdJM$U$@=`zz}%;ucRx{aKVxxF7?0hdKh6&GxO6f`l2kFncS3xu0Ly{ew0& zeEP*#lk-8-B$LD(5yj>YFJ{yf5zb41PlW7S{D9zC4Aa4nVdkDNH{UsFJp)q-`9OYt zbOKkigbmm5hF?tttn;S4g^142AF^`kiLUC?e7=*JH%Qe>uW=dB24NQa`;lm5yL>Dyh@HbHy-f%6Vz^ zh&MgwYsh(z#_fhhqY$3*f>Ha}*^cU-r4uTHaT?)~LUj5``FcS46oyoI5F3ZRizVD% zPFY(_S&5GN8$Nl2=+YO6j4d|M6O7CmUyS&}m4LSn6}J`$M0ZzT&Ome)ZbJDFvM&}A zZdhDn(*viM-JHf84$!I(8eakl#zRjJH4qfw8=60 z11Ely^FyXjVvtv48-Fae7p=adlt9_F^j5#ZDf7)n!#j?{W?@j$Pi=k`>Ii>XxrJ?$ z^bhh|X6qC8d{NS4rX5P!%jXy=>(P+r9?W(2)|(=a^s^l~x*^$Enw$~u%WRuRHHFan{X|S;FD(Mr z@r@h^@Bs#C3G;~IJMrERd+D!o?HmFX&#i|~q(7QR3f8QDip?ms6|GV_$86aDb|5pc?_-jo6vmWqYi{P#?{m_AesA4xX zi&ki&lh0yvf*Yw~@jt|r-=zpj!bw<6zI3Aa^Wq{|*WEC}I=O!Re!l~&8|Vu<$yZ1p zs-SlwJD8K!$(WWyhZ+sOqa8cciwvyh%zd`r$u;;fsHn!hub0VU)bUv^QH?x30#;tH zTc_VbZj|prj7)d%ORU;Vs{#ERb>K8>GOLSImnF7JhR|g$7FQTU{(a7RHQ*ii-{U3X z^7+vM0R$8b3k1aSU&kxvVPfOz3~)0O2iTYinV9_5{pF18j4b{o`=@AZIOAwwedB2@ ztXI1F04mg{<>a-gdFoRjq$6#FaevDn$^06L)k%wYq03&ysdXE+LL1#w$rRS1Y;BoS zH1x}{ms>LHWmdtP(ydD!aRdAa(d@csEo z0EF9L>%tppp`CZ2)jVb8AuoYyu;d^wfje6^n6`A?6$&%$p>HcE_De-Zh)%3o5)LDa zskQ}%o7?bg$xUj|n8gN9YB)z!N&-K&!_hVQ?#SFj+MpQA4@4oq!UQ$Vm3B`W_Pq3J z=ngFP4h_y=`Iar<`EESF9){%YZVyJqLPGq07TP7&fSDmnYs2NZQKiR%>){imTBJth zPHr@p>8b+N@~%43rSeNuOz;rgEm?14hNtI|KC6Xz1d?|2J`QS#`OW7gTF_;TPPxu@ z)9J9>3Lx*bc>Ielg|F3cou$O0+<b34_*ZJhpS&$8DP>s%47a)4ZLw`|>s=P_J4u z?I_%AvR_z8of@UYWJV?~c4Yb|A!9n!LEUE6{sn@9+D=0w_-`szJ_T++x3MN$v-)0d zy`?1QG}C^KiNlnJBRZBLr4G~15V3$QqC%1G5b#CEB0VTr#z?Ug%Jyv@a`QqAYUV~^ zw)d|%0g&kl{j#FMdf$cn(~L@8s~6eQ)6{`ik(RI(o9s0g30Li{4YoxcVoYd+LpeLz zai?~r)UcbYr@lv*Z>E%BsvTNd`Sc?}*}>mzJ|cr0Y(6rA7H_6&t>F{{mJ^xovc2a@ zFGGDUcGgI-z6H#o@Gj29C=Uy{wv zQHY2`HZu8+sBQK*_~I-_>fOTKEAQ8_Q~YE$c?cSCxI;vs-JGO`RS464Ft06rpjn+a zqRS0Y3oN(9HCP@{J4mOWqIyD8PirA!pgU^Ne{LHBG;S*bZpx3|JyQDGO&(;Im8!ed zNdpE&?3U?E@O~>`@B;oY>#?gXEDl3pE@J30R1;?QNNxZ?YePc)3=NS>!STCrXu*lM z69WkLB_RBwb1^-zEm*tkcHz3H;?v z;q+x0Jg$|?5;e1-kbJnuT+^$bWnYc~1qnyVTKh*cvM+8yJT-HBs1X@cD;L$su65;i z2c1MxyL~NuZ9+)hF=^-#;dS#lFy^Idcb>AEDXu1!G4Kd8YPy~0lZz$2gbv?su}Zn} zGtIbeYz3X8OA9{sT(aleold_?UEV{hWRl(@)NH6GFH@$<8hUt=dNte%e#Jc>7u9xi zuqv!CRE@!fmZZ}3&@$D>p0z=*dfQ_=IE4bG0hLmT@OP>x$e`qaqf_=#baJ8XPtOpWi%$ep1Y)o2(sR=v)M zt(z*pGS$Z#j_xq_lnCr+x9fwiT?h{NEn#iK(o)G&Xw-#DK?=Ms6T;%&EE${Gq_%99 z6(;P~jPKq9llc+cmI(MKQ6*7PcL)BmoI}MYFO)b3-{j>9FhNdXLR<^mnMP`I7z0v` zj3wxcXAqi4Z0kpeSf>?V_+D}NULgU$DBvZ^=0G8Bypd7P2>;u`yW9`%4~&tzNJpgp zqB+iLIM~IkB;ts!)exn643mAJ8-WlgFE%Rpq!UMYtB?$5QAMm)%PT0$$2{>Yu7&U@ zh}gD^Qdgu){y3ANdB5{75P;lRxSJPSpQPMJOiwmpMdT|?=q;&$aTt|dl~kvS z+*i;6cEQJ1V`R4Fd>-Uzsc=DPQ7A7#VPCIf!R!KK%LM&G%MoZ0{-8&99H!|UW$Ejv zhDLX3ESS6CgWTm#1ZeS2HJb`=UM^gsQ84dQpX(ESWSkjn>O zVxg%`@mh(X9&&wN$lDIc*@>rf?C0AD_mge3f2KkT6kGySOhXqZjtA?5z`vKl_{(5g z&%Y~9p?_DL{+q@siT~*3Q*$nWXQfNN;%s_eHP_A;O`N`SaoB z6xYR;z_;HQ2xAa9xKgx~2f2xEKiEDpGPH1d@||v#f#_Ty6_gY>^oZ#xac?pc-F`@ z*}8sPV@xiz?efDMcmmezYVw~qw=vT;G1xh+xRVBkmN66!u(mRG3G6P#v|;w@anEh7 zCf94arw%YB*=&3=RTqX?z4mID$W*^+&d6qI*LA-yGme;F9+wTsNXNaX~zl2+qIK&D-aeN4lr0+yP;W>|Dh?ms_ogT{DT+ ztXFy*R7j4IX;w@@R9Oct5k2M%&j=c_rWvoul+` z<18FH5D@i$P38W9VU2(EnEvlJ(SHCqTNBa)brkIjGP|jCnK&Qi%97tikU}Y#3L?s! z2ujL%YiHO-#!|g5066V01hgT#>fzls7P>+%D~ogOT&!Whb4iF=CnCto82Yb#b`YoVsj zS2q^W0Rj!RrM@=_GuPQy5*_X@Zmu`TKSbqEOP@;Ga&Rrr>#H@L41@ZX)LAkbo{G8+ z;!5EH6vv-ip0`tLB)xUuOX(*YEDSWf?PIxXe`+_B8=KH#HFCfthu}QJylPMTNmoV; zC63g%?57(&osaH^sxCyI-+gwVB|Xs2TOf=mgUAq?V~N_5!4A=b{AXbDae+yABuuu3B_XSa4~c z1s-OW>!cIkjwJf4ZhvT|*IKaRTU)WAK=G|H#B5#NB9<{*kt?7`+G*-^<)7$Iup@Um z7u*ABkG3F*Foj)W9-I&@BrN8(#$7Hdi`BU#SR1Uz4rh&=Ey!b76Qo?RqBJ!U+rh(1 znw@xw5$)4D8OWtB_^pJO*d~2Mb-f~>I!U#*=Eh*xa6$LX?4Evp4%;ENQR!mF4`f7F zpG!NX=qnCwE8@NAbQV`*?!v0;NJ(| zBip8}VgFVsXFqslXUV>_Z>1gmD(7p#=WACXaB|Y`=Kxa=p@_ALsL&yAJ`*QW^`2@% zW7~Yp(Q@ihmkf{vMF?kqkY%SwG^t&CtfRWZ{syK@W$#DzegcQ1>~r7foTw3^V1)f2Tq_5f$igmfch;8 zT-<)?RKcCdQh6x^mMEOS;4IpQ@F2q-4IC4%*dU@jfHR4UdG>Usw4;7ESpORL|2^#jd+@zxz{(|RV*1WKrw-)ln*8LnxVkKDfGDHA%7`HaiuvhMu%*mY9*Ya{Ti#{DW?i0 zXXsp+Bb(_~wv(3t70QU3a$*<$1&zm1t++x#wDLCRI4K)kU?Vm9n2c0m@TyUV&&l9%}fulj!Z9)&@yIcQ3gX}l0b1LbIh4S z5C*IDrYxR%qm4LVzSk{0;*npO_SocYWbkAjA6(^IAwUnoAzw_Uo}xYFo?Y<-4Zqec z&k7HtVlFGyt_pA&kX%P8PaRD8y!Wsnv}NMLNLy-CHZf(ObmzV|t-iC#@Z9*d-zUsx zxcYWw{H)nYXVdnJu5o-U+fn~W z-$h1ax>h{NlWLA7;;6TcQHA>UJB$KNk74T1xNWh9)kwK~wX0m|Jo_Z;g;>^E4-k4R zRj#pQb-Hg&dAh}*=2;JY*aiNZzT=IU&v|lQY%Q|=^V5pvTR7^t9+@+ST&sr!J1Y9a z514dYZn5rg6@4Cy6P`-?!3Y& z?B*5zw!mTiD2)>f@3XYrW^9V-@%YFkE_;PCyCJ7*?_3cR%tHng9%ZpIU}LJM=a+0s z(SDDLvcVa~b9O!cVL8)Q{d^R^(bbG=Ia$)dVN_tGMee3PMssZ7Z;c^Vg_1CjZYTnq z)wnF8?=-MmqVOMX!iE?YDvHCN?%TQtKJMFHp$~kX4}jZ;EDqP$?jqJZjoa2PM@$uZ zF4}iab1b5ep)L;jdegC3{K4VnCH#OV;pRcSa(&Nm50ze-yZ8*cGv;@+N+A?ncc^2z9~|(xFhwOHmPW@ zR5&)E^YKQj@`g=;zJ_+CLamsPuvppUr$G1#9urUj+p-mPW_QSSHkPMS!52t>Hqy|g z_@Yu3z%|wE=uYq8G>4`Q!4zivS}+}{m5Zjr7kMRGn_p&hNf|pc&f9iQ`^%78rl#~8 z;os@rpMA{ZioY~(Rm!Wf#Wx##A0PthOI341QiJ=G*#}pDAkDm+{0kz&*NB?rC0-)glB{0_Tq*^o zVS1>3REsv*Qb;qg!G^9;VoK)P*?f<*H&4Su1=}bP^Y<2PwFpoqw#up4IgX3L z`w~8jsFCI3k~Y9g(Y9Km`y$0FS5vHb)kb)Jb6q-9MbO{Hbb zxg?IWQ1ZIGgE}wKm{axO6CCh~4DyoFU+i1xn#oyfe+<{>=^B5tm!!*1M?AW8c=6g+%2Ft97_Hq&ZmOGvqGQ!Bn<_Vw`0DRuDoB6q8ME<;oL4kocr8E$NGoLI zXWmI7Af-DR|KJw!vKp2SI4W*x%A%5BgDu%8%Iato+pWo5`vH@!XqC!yK}KLzvfS(q z{!y(S-PKbk!qHsgVyxKsQWk_8HUSSmslUA9nWOjkKn0%cwn%yxnkfxn?Y2rysXKS=t-TeI%DN$sQ{lcD!(s>(4y#CSxZ4R} zFDI^HPC_l?uh_)-^ppeYRkPTPu~V^0Mt}#jrTL1Q(M;qVt4zb(L|J~sxx7Lva9`mh zz!#A9tA*6?q)xThc7(gB2Ryam$YG4qlh00c}r&$y6u zIN#Qxn{7RKJ+_r|1G1KEv!&uKfXpOVZ8tK{M775ws%nDyoZ?bi3NufNbZs)zqXiqc zqOsK@^OnlFMAT&mO3`@3nZP$3lLF;ds|;Z{W(Q-STa2>;)tjhR17OD|G>Q#zJHb*> zMO<{WIgB%_4MG0SQi2;%f0J8l_FH)Lfaa>*GLobD#AeMttYh4Yfg22@q4|Itq};NB z8;o*+@APqy@fPgrc&PTbGEwdEK=(x5K!If@R$NiO^7{#j9{~w=RBG)ZkbOw@$7Nhl zyp{*&QoVBd5lo{iwl2gfyip@}IirZK;ia(&ozNl!-EEYc=QpYH_= zJkv7gA{!n4up6$CrzDJIBAdC7D5D<_VLH*;OYN>_Dx3AT`K4Wyx8Tm{I+xplKP6k7 z2sb!i7)~%R#J0$|hK?~=u~rnH7HCUpsQJujDDE*GD`qrWWog+C+E~GGy|Hp_t4--} zrxtrgnPh}r=9o}P6jpAQuDN}I*GI`8&%Lp-C0IOJt#op)}XSr!ova@w{jG2V=?GXl3zEJJFXg)U3N>BQP z*Lb@%Mx|Tu;|u>$-K(q^-HG!EQ3o93%w(A7@ngGU)HRWoO&&^}U$5x+T&#zri>6ct zXOB#EF-;z3j311K`jrYyv6pOPF=*`SOz!ack=DuEi({UnAkL5H)@R?YbRKAeP|06U z?-Ns0ZxD0h9D8)P66Sq$w-yF+1hEVTaul%&=kKDrQtF<$RnQPZ)ezm1`aHIjAY=!S z`%vboP`?7mItgEo4w50C*}Ycqp9_3ZEr^F1;cEhkb`BNhbc6PvnXu@wi=AoezF4~K zkxx%ps<8zb=wJ+9I8o#do)&{(=yAlNdduaDn!=xGSiuo~fLw~Edw$6;l-qaq#Z7?# zGrdU(Cf-V@$x>O%yRc6!C1Vf`b19ly;=mEu8u9|zitcG^O`lbNh}k=$%a)UHhDwTEKis2yc4rBGR>l*(B$AC7ung&ssaZGkY-h(fpwcPyJSx*9EIJMRKbMP9}$nVrh6$g-Q^5Cw)BeWqb-qi#37ZXKL!GR;ql)~ z@PP*-oP?T|ThqlGKR84zi^CN z4TZ1A)7vL>ivoL2EU_~xl-P{p+sE}9CRwGJDKy{>0KP+gj`H9C+4fUMPnIB1_D`A- z$1`G}g0lQmqMN{Y&8R*$xYUB*V}dQPxGVZQ+rH!DVohIoTbh%#z#Tru%Px@C<=|og zGDDwGq7yz`%^?r~6t&>x*^We^tZ4!E4dhwsht#Pb1kCY{q#Kv;z%Dp#Dq;$vH$-(9 z8S5tutZ}&JM2Iw&Y-7KY4h5BBvS=Ove0#+H2qPdR)WyI zYcj)vB=MA{7T|3Ij_PN@FM@w(C9ANBq&|NoW30ccr~i#)EcH)T^3St~rJ0HKKd4wr z@_+132;Bj+>UC@h)Ap*8B4r5A1lZ!Dh%H7&&hBnlFj@eayk=VD*i5AQc z$uN8YG#PL;cuQa)Hyt-}R?&NAE1QT>svJDKt*)AQOZAJ@ zyxJoBebiobHeFlcLwu_iI&NEZuipnOR;Tn;PbT1Mt-#5v5b*8ULo7m)L-eti=UcGf zRZXidmxeFgY!y80-*PH-*=(-W+fK%KyUKpg$X@tuv``tXj^*4qq@UkW$ZrAo%+hay zU@a?z&2_@y)o@D!_g>NVxFBO!EyB&6Z!nd4=KyDP^hl!*(k{dEF6@NkXztO7gIh zQ&PC+p-8WBv;N(rpfKdF^@Z~|E6pa)M1NBUrCZvLRW$%N%xIbv^uv?=C!=dDVq3%* zgvbEBnG*JB*@vXx8>)7XL*!{1Jh=#2UrByF7U?Rj_}VYw88BwqefT_cCTv8aTrRVjnn z1HNCF=44?*&gs2`vCGJVHX@kO z240eo#z+FhI0=yy6NHQwZs}a+J~4U-6X`@ zZ7j+tb##m`x%J66$a9qXDHG&^kp|GkFFMmjD(Y-k_ClY~N$H|n@NkSDz=gg?*2ga5 z)+f)MEY>2Lp15;~o`t`qj;S>BaE;%dv@Ux11yq}I(k|o&`5UZFUHn}1kE^gIK@qV& z!S2IhyU;->VfA4Qb}m7YnkIa9%z{l~iPWo2YPk-`hy2-Eg=6E$21plQA5W2qMZDFU z-a-@Dndf%#on6chT`dOKnU9}BJo|kJwgGC<^nfo34zOKH96LbWY7@Wc%EoFF=}`VU zksP@wd%@W;-p!e^&-)N7#oR331Q)@9cx=mOoU?_Kih2!Le*8fhsZ8Qvo6t2vt+UOZ zw|mCB*t2%z21YqL>whu!j?s~}-L`OS+jdg1(XnmYw$rg~r(?5Y+qTg`$F}q3J?GtL z@BN&8#`u2RqkdG4yGGTus@7U_%{6C{XAhFE!2SelH?KtMtX@B1GBhEIDL-Bj#~{4! zd}p7!#XE9Lt;sy@p5#Wj*jf8zGv6tTotCR2X$EVOOup;GnRPRVU5A6N@Lh8?eA7k? zn~hz&gY;B0ybSpF?qwQ|sv_yO=8}zeg2$0n3A8KpE@q26)?707pPw?H76lCpjp=5r z6jjp|auXJDnW}uLb6d7rsxekbET9(=zdTqC8(F5@NNqII2+~yB;X5iJNQSiv`#ozm zf&p!;>8xAlwoxUC3DQ#!31ylK%VrcwS<$WeCY4V63V!|221oj+5#r}fGFQ}|uwC0) zNl8(CF}PD`&Sj+p{d!B&&JtC+VuH z#>US`)YQrhb6lIAYb08H22y(?)&L8MIQsA{26X`R5Km{YU)s!x(&gIsjDvq63@X`{ z=7{SiH*_ZsPME#t2m|bS76Uz*z{cpp1m|s}HIX}Ntx#v7Eo!1%G9__4dGSGl`p+xi zZ!VK#Qe;Re=9bqXuW+0DSP{uZ5-QXrNn-7qW19K0qU}OhVru7}3vqsG?#D67 zb}crN;QwsH*vymw(maZr_o|w&@sQki(X+D)gc5Bt&@iXisFG;eH@5d43~Wxq|HO(@ zV-rip4n#PEkHCWCa5d?@cQp^B;I-PzOfag|t-cuvTapQ@MWLmh*41NH`<+A+JGyKX zyYL6Ba7qqa5j@3lOk~`OMO7f0!@FaOeZxkbG@vXP(t3#U*fq8=GAPqUAS>vW2uxMk{a(<0=IxB;# zMW;M+owrHaZBp`3{e@7gJCHP!I(EeyGFF;pdFPdeP+KphrulPSVidmg#!@W`GpD&d z9p6R`dpjaR2E1Eg)Ws{BVCBU9-aCgN57N~uLvQZH`@T+2eOBD%73rr&sV~m#2~IZx zY_8f8O;XLu2~E3JDXnGhFvsyb^>*!D>5EtlKPe%kOLv6*@=Jpci`8h0z?+fbBUg_7 zu6DjqO=$SjAv{|Om5)nz41ZkS4E_|fk%NDY509VV5yNeo%O|sb>7C#wj8mL9cEOFh z>nDz%?vb!h*!0dHdnxDA>97~EoT~!N40>+)G2CeYdOvJr5^VnkGz)et&T9hrD(VAgCAJjQ7V$O?csICB*HFd^k@$M5*v$PZJD-OVL?Ze(U=XGqZPVG8JQ z<~ukO%&%nNXYaaRibq#B1KfW4+XMliC*Tng2G(T1VvP;2K~;b$EAqthc${gjn_P!b zs62UT(->A>!ot}cJXMZHuy)^qfqW~xO-In2);e>Ta{LD6VG2u&UT&a@>r-;4<)cJ9 zjpQThb4^CY)Ev0KR7TBuT#-v}W?Xzj{c7$S5_zJA57Qf=$4^npEjl9clH0=jWO8sX z3Fuu0@S!WY>0XX7arjH`?)I<%2|8HfL!~#c+&!ZVmhbh`wbzy0Ux|Jpy9A{_7GGB0 zadZ48dW0oUwUAHl%|E-Q{gA{z6TXsvU#Hj09<7i)d}wa+Iya)S$CVwG{4LqtB>w%S zKZx(QbV7J9pYt`W4+0~f{hoo5ZG<0O&&5L57oF%hc0xGJ@Zrg_D&lNO=-I^0y#3mxCSZFxN2-tN_mU@7<@PnWG?L5OSqkm8TR!`| zRcTeWH~0z1JY^%!N<(TtxSP5^G9*Vw1wub`tC-F`=U)&sJVfvmh#Pi`*44kSdG};1 zJbHOmy4Ot|%_?@$N?RA9fF?|CywR8Sf(SCN_luM8>(u0NSEbKUy7C(Sk&OuWffj)f za`+mo+kM_8OLuCUiA*CNE|?jra$M=$F3t+h-)?pXz&r^F!ck;r##`)i)t?AWq-9A9 zSY{m~TC1w>HdEaiR*%j)L);H{IULw)uxDO>#+WcBUe^HU)~L|9#0D<*Ld459xTyew zbh5vCg$a>`RCVk)#~ByCv@Ce!nm<#EW|9j><#jQ8JfTmK#~jJ&o0Fs9jz0Ux{svdM4__<1 zrb>H(qBO;v(pXPf5_?XDq!*3KW^4>(XTo=6O2MJdM^N4IIcYn1sZZpnmMAEdt}4SU zPO54j2d|(xJtQ9EX-YrlXU1}6*h{zjn`in-N!Ls}IJsG@X&lfycsoCemt_Ym(PXhv zc*QTnkNIV=Ia%tg%pwJtT^+`v8ng>;2~ps~wdqZSNI7+}-3r+#r6p`8*G;~bVFzg= z!S3&y)#iNSUF6z;%o)%h!ORhE?CUs%g(k2a-d576uOP2@QwG-6LT*G!I$JQLpd`cz z-2=Brr_+z96a0*aIhY2%0(Sz=|D`_v_7h%Yqbw2)8@1DwH4s*A82krEk{ zoa`LbCdS)R?egRWNeHV8KJG0Ypy!#}kslun?67}^+J&02!D??lN~t@;h?GS8#WX`)6yC**~5YNhN_Hj}YG<%2ao^bpD8RpgV|V|GQwlL27B zEuah|)%m1s8C6>FLY0DFe9Ob66fo&b8%iUN=y_Qj;t3WGlNqP9^d#75ftCPA*R4E8 z)SWKBKkEzTr4JqRMEs`)0;x8C35yRAV++n(Cm5++?WB@ya=l8pFL`N0ag`lWhrYo3 zJJ$< zQ*_YAqIGR*;`VzAEx1Pd4b3_oWtdcs7LU2#1#Ls>Ynvd8k^M{Ef?8`RxA3!Th-?ui{_WJvhzY4FiPxA?E4+NFmaC-Uh*a zeLKkkECqy>Qx&1xxEhh8SzMML=8VP}?b*sgT9ypBLF)Zh#w&JzP>ymrM?nnvt!@$2 zh>N$Q>mbPAC2kNd&ab;FkBJ}39s*TYY0=@e?N7GX>wqaM>P=Y12lciUmve_jMF0lY zBfI3U2{33vWo(DiSOc}!5##TDr|dgX1Uojq9!vW3$m#zM_83EGsP6&O`@v-PDdO3P z>#!BEbqpOXd5s?QNnN!p+92SHy{sdpePXHL{d@c6UilT<#~I!tH$S(~o}c#(j<2%! zQvm}MvAj-95Ekx3D4+|e%!?lO(F+DFw9bxb-}rsWQl)b44###eUg4N?N-P(sFH2hF z`{zu?LmAxn2=2wCE8?;%ZDi#Y;Fzp+RnY8fWlzVz_*PDO6?Je&aEmuS>=uCXgdP6r zoc_JB^TA~rU5*geh{G*gl%_HnISMS~^@{@KVC;(aL^ZA-De+1zwUSXgT>OY)W?d6~ z72znET0m`53q%AVUcGraYxIcAB?OZA8AT!uK8jU+=t;WneL~|IeQ>$*dWa#x%rB(+ z5?xEkZ&b{HsZ4Ju9TQ|)c_SIp`7r2qMJgaglfSBHhl)QO1aNtkGr0LUn{@mvAt=}nd7#>7ru}&I)FNsa*x?Oe3-4G`HcaR zJ}c%iKlwh`x)yX1vBB;-Nr=7>$~(u=AuPX2#&Eh~IeFw%afU+U)td0KC!pHd zyn+X$L|(H3uNit-bpn7%G%{&LsAaEfEsD?yM<;U2}WtD4KuVKuX=ec9X zIe*ibp1?$gPL7<0uj*vmj2lWKe`U(f9E{KVbr&q*RsO;O>K{i-7W)8KG5~~uS++56 zm@XGrX@x+lGEjDQJp~XCkEyJG5Y57omJhGN{^2z5lj-()PVR&wWnDk2M?n_TYR(gM zw4kQ|+i}3z6YZq8gVUN}KiYre^sL{ynS}o{z$s&I z{(rWaLXxcQ=MB(Cz7W$??Tn*$1y(7XX)tv;I-{7F$fPB%6YC7>-Dk#=Y8o1=&|>t5 zV_VVts>Eb@)&4%m}!K*WfLoLl|3FW)V~E1Z!yu`Sn+bAP5sRDyu7NEbLt?khAyz-ZyL-}MYb&nQ zU16f@q7E1rh!)d%f^tTHE3cVoa%Xs%rKFc|temN1sa)aSlT*)*4k?Z>b3NP(IRXfq zlB^#G6BDA1%t9^Nw1BD>lBV(0XW5c?l%vyB3)q*;Z5V~SU;HkN;1kA3Nx!$!9wti= zB8>n`gt;VlBt%5xmDxjfl0>`K$fTU-C6_Z;!A_liu0@Os5reMLNk;jrlVF^FbLETI zW+Z_5m|ozNBn7AaQ<&7zk}(jmEdCsPgmo%^GXo>YYt82n&7I-uQ%A;k{nS~VYGDTn zlr3}HbWQG6xu8+bFu^9%%^PYCbkLf=*J|hr>Sw+#l(Y#ZGKDufa#f-f0k-{-XOb4i zwVG1Oa0L2+&(u$S7TvedS<1m45*>a~5tuOZ;3x%!f``{=2QQlJk|b4>NpD4&L+xI+ z+}S(m3}|8|Vv(KYAGyZK5x*sgwOOJklN0jsq|BomM>OuRDVFf_?cMq%B*iQ*&|vS9 zVH7Kh)SjrCBv+FYAE=$0V&NIW=xP>d-s7@wM*sdfjVx6-Y@=~>rz%2L*rKp|*WXIz z*vR^4tV&7MQpS9%{9b*>E9d_ls|toL7J|;srnW{l-}1gP_Qr-bBHt=}PL@WlE|&KH zCUmDLZb%J$ZzNii-5VeygOM?K8e$EcK=z-hIk63o4y63^_*RdaitO^THC{boKstphXZ2Z+&3ToeLQUG(0Frs?b zCxB+65h7R$+LsbmL51Kc)pz_`YpGEzFEclzb=?FJ=>rJwgcp0QH-UuKRS1*yCHsO) z-8t?Zw|6t($Eh&4K+u$I7HqVJBOOFCRcmMMH};RX_b?;rnk`rz@vxT_&|6V@q0~Uk z9ax|!pA@Lwn8h7syrEtDluZ6G!;@=GL> zse#PRQrdDs=qa_v@{Wv(3YjYD0|qocDC;-F~&{oaTP?@pi$n z1L6SlmFU2~%)M^$@C(^cD!y)-2SeHo3t?u3JiN7UBa7E2 z;<+_A$V084@>&u)*C<4h7jw9joHuSpVsy8GZVT;(>lZ(RAr!;)bwM~o__Gm~exd`K zKEgh2)w?ReH&syI`~;Uo4`x4$&X+dYKI{e`dS~bQuS|p zA`P_{QLV3r$*~lb=9vR^H0AxK9_+dmHX}Y} zIV*#65%jRWem5Z($ji{!6ug$En4O*=^CiG=K zp4S?+xE|6!cn$A%XutqNEgUqYY3fw&N(Z6=@W6*bxdp~i_yz5VcgSj=lf-6X1Nz75 z^DabwZ4*70$$8NsEy@U^W67tcy7^lNbu;|kOLcJ40A%J#pZe0d#n zC{)}+p+?8*ftUlxJE*!%$`h~|KZSaCb=jpK3byAcuHk7wk@?YxkT1!|r({P*KY^`u z!hw#`5$JJZGt@nkBK_nwWA31_Q9UGvv9r-{NU<&7HHMQsq=sn@O?e~fwl20tnSBG* zO%4?Ew6`aX=I5lqmy&OkmtU}bH-+zvJ_CFy z_nw#!8Rap5Wcex#5}Ldtqhr_Z$}@jPuYljTosS1+WG+TxZ>dGeT)?ZP3#3>sf#KOG z0)s%{cEHBkS)019}-1A2kd*it>y65-C zh7J9zogM74?PU)0c0YavY7g~%j%yiWEGDb+;Ew5g5Gq@MpVFFBNOpu0x)>Yn>G6uo zKE%z1EhkG_N5$a8f6SRm(25iH#FMeaJ1^TBcBy<04ID47(1(D)q}g=_6#^V@yI?Y&@HUf z`;ojGDdsvRCoTmasXndENqfWkOw=#cV-9*QClpI03)FWcx(m5(P1DW+2-{Hr-`5M{v##Zu-i-9Cvt;V|n)1pR^y ztp3IXzHjYWqabuPqnCY9^^;adc!a%Z35VN~TzwAxq{NU&Kp35m?fw_^D{wzB}4FVXX5Zk@#={6jRh%wx|!eu@Xp;%x+{2;}!&J4X*_SvtkqE#KDIPPn@ z5BE$3uRlb>N<2A$g_cuRQM1T#5ra9u2x9pQuqF1l2#N{Q!jVJ<>HlLeVW|fN|#vqSnRr<0 zTVs=)7d`=EsJXkZLJgv~9JB&ay16xDG6v(J2eZy;U%a@EbAB-=C?PpA9@}?_Yfb&) zBpsih5m1U9Px<+2$TBJ@7s9HW>W){i&XKLZ_{1Wzh-o!l5_S+f$j^RNYo85}uVhN# zq}_mN-d=n{>fZD2Lx$Twd2)}X2ceasu91}n&BS+4U9=Y{aZCgV5# z?z_Hq-knIbgIpnkGzJz-NW*=p?3l(}y3(aPCW=A({g9CpjJfYuZ%#Tz81Y)al?!S~ z9AS5#&nzm*NF?2tCR#|D-EjBWifFR=da6hW^PHTl&km-WI9*F4o>5J{LBSieVk`KO z2(^9R(zC$@g|i3}`mK-qFZ33PD34jd_qOAFj29687wCUy>;(Hwo%Me&c=~)V$ua)V zsaM(aThQ3{TiM~;gTckp)LFvN?%TlO-;$y+YX4i`SU0hbm<})t0zZ!t1=wY&j#N>q zONEHIB^RW6D5N*cq6^+?T}$3m|L{Fe+L!rxJ=KRjlJS~|z-&CC{#CU8`}2|lo~)<| zk?Wi1;Cr;`?02-C_3^gD{|Ryhw!8i?yx5i0v5?p)9wZxSkwn z3C;pz25KR&7{|rc4H)V~y8%+6lX&KN&=^$Wqu+}}n{Y~K4XpI-#O?L=(2qncYNePX zTsB6_3`7q&e0K67=Kg7G=j#?r!j0S^w7;0?CJbB3_C4_8X*Q%F1%cmB{g%XE&|IA7 z(#?AeG{l)s_orNJp!$Q~qGrj*YnuKlV`nVdg4vkTNS~w$4d^Oc3(dxi(W5jq0e>x} z(GN1?u2%Sy;GA|B%Sk)ukr#v*UJU%(BE9X54!&KL9A^&rR%v zIdYt0&D59ggM}CKWyxGS@ z>T#})2Bk8sZMGJYFJtc>D#k0+Rrrs)2DG;(u(DB_v-sVg=GFMlSCx<&RL;BH}d6AG3VqP!JpC0Gv6f8d|+7YRC@g|=N=C2 zo>^0CE0*RW?W))S(N)}NKA)aSwsR{1*rs$(cZIs?nF9)G*bSr%%SZo^YQ|TSz={jX z4Z+(~v_>RH0(|IZ-_D_h@~p_i%k^XEi+CJVC~B zsPir zA0Jm2yIdo4`&I`hd%$Bv=Rq#-#bh{Mxb_{PN%trcf(#J3S1UKDfC1QjH2E;>wUf5= ze8tY9QSYx0J;$JUR-0ar6fuiQTCQP#P|WEq;Ez|*@d?JHu-(?*tTpGHC+=Q%H>&I> z*jC7%nJIy+HeoURWN%3X47UUusY2h7nckRxh8-)J61Zvn@j-uPA@99|y48pO)0XcW zX^d&kW^p7xsvdX?2QZ8cEUbMZ7`&n{%Bo*xgFr4&fd#tHOEboQos~xm8q&W;fqrj} z%KYnnE%R`=`+?lu-O+J9r@+$%YnqYq!SVs>xp;%Q8p^$wA~oynhnvIFp^)Z2CvcyC zIN-_3EUHW}1^VQ0;Oj>q?mkPx$Wj-i7QoXgQ!HyRh6Gj8p~gH22k&nmEqUR^)9qni{%uNeV{&0-H60C zibHZtbV=8=aX!xFvkO}T@lJ_4&ki$d+0ns3FXb+iP-VAVN`B7f-hO)jyh#4#_$XG%Txk6M<+q6D~ zi*UcgRBOoP$7P6RmaPZ2%MG}CMfs=>*~(b97V4+2qdwvwA@>U3QQAA$hiN9zi%Mq{ z*#fH57zUmi)GEefh7@`Uy7?@@=BL7cXbd{O9)*lJh*v!@ z-6}p9u0AreiGauxn7JBEa-2w&d=!*TLJ49`U@D7%2ppIh)ynMaAE2Q4dl@47cNu{9 z&3vT#pG$#%hrXzXsj=&Ss*0;W`Jo^mcy4*L8b^sSi;H{*`zW9xX2HAtQ*sO|x$c6UbRA(7*9=;D~(%wfo(Z6#s$S zuFk`dr%DfVX5KC|Af8@AIr8@OAVj=6iX!~8D_P>p7>s!Hj+X0_t}Y*T4L5V->A@Zx zcm1wN;TNq=h`5W&>z5cNA99U1lY6+!!u$ib|41VMcJk8`+kP{PEOUvc@2@fW(bh5pp6>C3T55@XlpsAd#vn~__3H;Dz2w=t9v&{v*)1m4)vX;4 zX4YAjM66?Z7kD@XX{e`f1t_ZvYyi*puSNhVPq%jeyBteaOHo7vOr8!qqp7wV;)%jtD5>}-a?xavZ;i|2P3~7c)vP2O#Fb`Y&Kce zQNr7%fr4#S)OOV-1piOf7NgQvR{lcvZ*SNbLMq(olrdDC6su;ubp5un!&oT=jVTC3uTw7|r;@&y*s)a<{J zkzG(PApmMCpMmuh6GkM_`AsBE@t~)EDcq1AJ~N@7bqyW_i!mtHGnVgBA`Dxi^P93i z5R;}AQ60wy=Q2GUnSwz+W6C^}qn`S-lY7=J(3#BlOK%pCl=|RVWhC|IDj1E#+|M{TV0vE;vMZLy7KpD1$Yk zi0!9%qy8>CyrcRK`juQ)I};r)5|_<<9x)32b3DT1M`>v^ld!yabX6@ihf`3ZVTgME zfy(l-ocFuZ(L&OM4=1N#Mrrm_<>1DZpoWTO70U8+x4r3BpqH6z@(4~sqv!A9_L}@7 z7o~;|?~s-b?ud&Wx6==9{4uTcS|0-p@dKi0y#tPm2`A!^o3fZ8Uidxq|uz2vxf;wr zM^%#9)h^R&T;}cxVI(XX7kKPEVb);AQO?cFT-ub=%lZPwxefymBk+!H!W(o(>I{jW z$h;xuNUr#^0ivvSB-YEbUqe$GLSGrU$B3q28&oA55l)ChKOrwiTyI~e*uN;^V@g-Dm4d|MK!ol8hoaSB%iOQ#i_@`EYK_9ZEjFZ8Ho7P^er z^2U6ZNQ{*hcEm?R-lK)pD_r(e=Jfe?5VkJ$2~Oq^7YjE^5(6a6Il--j@6dBHx2Ulq z!%hz{d-S~i9Eo~WvQYDt7O7*G9CP#nrKE#DtIEbe_uxptcCSmYZMqT2F}7Kw0AWWC zPjwo0IYZ6klc(h9uL|NY$;{SGm4R8Bt^^q{e#foMxfCSY^-c&IVPl|A_ru!ebwR#7 z3<4+nZL(mEsU}O9e`^XB4^*m)73hd04HH%6ok^!;4|JAENnEr~%s6W~8KWD)3MD*+ zRc46yo<}8|!|yW-+KulE86aB_T4pDgL$XyiRW(OOcnP4|2;v!m2fB7Hw-IkY#wYfF zP4w;k-RInWr4fbz=X$J;z2E8pvAuy9kLJUSl8_USi;rW`kZGF?*Ur%%(t$^{Rg!=v zg;h3@!Q$eTa7S0#APEDHLvK%RCn^o0u!xC1Y0Jg!Baht*a4mmKHy~88md{YmN#x) zBOAp_i-z2h#V~*oO-9k(BizR^l#Vm%uSa^~3337d;f=AhVp?heJ)nlZGm`}D(U^2w z#vC}o1g1h?RAV^90N|Jd@M00PoNUPyA?@HeX0P7`TKSA=*4s@R;Ulo4Ih{W^CD{c8 ze(ipN{CAXP(KHJ7UvpOc@9SUAS^wKo3h-}BDZu}-qjdNlVtp^Z{|CxKOEo?tB}-4; zEXyDzGbXttJ3V$lLo-D?HYwZm7vvwdRo}P#KVF>F|M&eJ44n*ZO~0)#0e0Vy&j00I z{%IrnUvKp70P?>~J^$^0Wo%>le>re2ZSvRfes@dC-*e=DD1-j%<$^~4^4>Id5w^Fr z{RWL>EbUCcyC%1980kOYqZAcgdz5cS8c^7%vvrc@CSPIx;X=RuodO2dxk17|am?HJ@d~Mp_l8H?T;5l0&WGFoTKM{eP!L-a0O8?w zgBPhY78tqf^+xv4#OK2I#0L-cSbEUWH2z+sDur85*!hjEhFfD!i0Eyr-RRLFEm5(n z-RV6Zf_qMxN5S6#8fr9vDL01PxzHr7wgOn%0Htmvk9*gP^Um=n^+7GLs#GmU&a#U^4jr)BkIubQO7oUG!4CneO2Ixa`e~+Jp9m{l6apL8SOqA^ zvrfEUPwnHQ8;yBt!&(hAwASmL?Axitiqvx%KZRRP?tj2521wyxN3ZD9buj4e;2y6U zw=TKh$4%tt(eh|y#*{flUJ5t4VyP*@3af`hyY^YU3LCE3Z|22iRK7M7E;1SZVHbXF zKVw!L?2bS|kl7rN4(*4h2qxyLjWG0vR@`M~QFPsf^KParmCX;Gh4OX6Uy9#4e_%oK zv1DRnfvd$pu(kUoV(MmAc09ckDiuqS$a%!AQ1Z>@DM#}-yAP$l`oV`BDYpkqpk(I|+qk!yoo$TwWr6dRzLy(c zi+qbVlYGz0XUq@;Fm3r~_p%by)S&SVWS+wS0rC9bk^3K^_@6N5|2rtF)wI>WJ=;Fz zn8$h<|Dr%kN|nciMwJAv;_%3XG9sDnO@i&pKVNEfziH_gxKy{l zo`2m4rnUT(qenuq9B0<#Iy(RPxP8R)=5~9wBku=%&EBoZ82x1GlV<>R=hIqf0PK!V zw?{z9e^B`bGyg2nH!^x}06oE%J_JLk)^QyHLipoCs2MWIqc>vaxsJj(=gg1ZSa=u{ zt}od#V;e7sA4S(V9^<^TZ#InyVBFT(V#$fvI7Q+pgsr_2X`N~8)IOZtX}e(Bn(;eF zsNj#qOF_bHl$nw5!ULY{lNx@93Fj}%R@lewUuJ*X*1$K`DNAFpE z7_lPE+!}uZ6c?+6NY1!QREg#iFy=Z!OEW}CXBd~wW|r_9%zkUPR0A3m+@Nk%4p>)F zXVut7$aOZ6`w}%+WV$te6-IX7g2yms@aLygaTlIv3=Jl#Nr}nN zp|vH-3L03#%-1-!mY`1z?+K1E>8K09G~JcxfS)%DZbteGQnQhaCGE2Y<{ut#(k-DL zh&5PLpi9x3$HM82dS!M?(Z zEsqW?dx-K_GMQu5K54pYJD=5+Rn&@bGjB?3$xgYl-|`FElp}?zP&RAd<522c$Rv6} zcM%rYClU%JB#GuS>FNb{P2q*oHy}UcQ-pZ2UlT~zXt5*k-ZalE(`p7<`0n7i(r2k{ zb84&^LA7+aW1Gx5!wK!xTbw0slM?6-i32CaOcLC2B>ZRI16d{&-$QBEu1fKF0dVU>GTP05x2>Tmdy`75Qx! z^IG;HB9V1-D5&&)zjJ&~G}VU1-x7EUlT3QgNT<&eIDUPYey$M|RD6%mVkoDe|;2`8Z+_{0&scCq>Mh3hj|E*|W3;y@{$qhu77D)QJ` znD9C1AHCKSAHQqdWBiP`-cAjq7`V%~JFES1=i-s5h6xVT<50kiAH_dn0KQB4t*=ua zz}F@mcKjhB;^7ka@WbSJFZRPeYI&JFkpJ-!B z!ju#!6IzJ;D@$Qhvz9IGY5!%TD&(db3<*sCpZ?U#1^9RWQ zs*O-)j!E85SMKtoZzE^8{w%E0R0b2lwwSJ%@E}Lou)iLmPQyO=eirG8h#o&E4~eew z;h><=|4m0$`ANTOixHQOGpksXlF0yy17E&JksB4_(vKR5s$Ve+i;gco2}^RRJI+~R zWJ82WGigLIUwP!uSELh3AAs9HmY-kz=_EL-w|9}noKE#(a;QBpEx9 z4BT-zY=6dJT>72Hkz=9J1E=}*MC;zzzUWb@x(Ho8cU_aRZ?fxse5_Ru2YOvcr?kg&pt@v;{ai7G--k$LQtoYj+Wjk+nnZty;XzANsrhoH#7=xVqfPIW(p zX5{YF+5=k4_LBnhLUZxX*O?29olfPS?u*ybhM_y z*XHUqM6OLB#lyTB`v<BZ&YRs$N)S@5Kn_b3;gjz6>fh@^j%y2-ya({>Hd@kv{CZZ2e)tva7gxLLp z`HoGW);eRtov~Ro5tetU2y72~ zQh>D`@dt@s^csdfN-*U&o*)i3c4oBufCa0e|BwT2y%Y~=U7A^ny}tx zHwA>Wm|!SCko~UN?hporyQHRUWl3djIc722EKbTIXQ6>>iC!x+cq^sUxVSj~u)dsY zW8QgfZlE*2Os%=K;_vy3wx{0u!2%A)qEG-$R^`($%AOfnA^LpkB_}Dd7AymC)zSQr z>C&N8V57)aeX8ap!|7vWaK6=-3~ko9meugAlBKYGOjc#36+KJwQKRNa_`W@7;a>ot zdRiJkz?+QgC$b}-Owzuaw3zBVLEugOp6UeMHAKo2$m4w zpw?i%Lft^UtuLI}wd4(-9Z^*lVoa}11~+0|Hs6zAgJ01`dEA&^>Ai=mr0nC%eBd_B zzgv2G_~1c1wr*q@QqVW*Wi1zn=}KCtSwLjwT>ndXE_Xa22HHL_xCDhkM( zhbw+j4uZM|r&3h=Z#YrxGo}GX`)AZyv@7#7+nd-D?BZV>thtc|3jt30j$9{aIw9)v zDY)*fsSLPQTNa&>UL^RWH(vpNXT7HBv@9=*=(Q?3#H*crA2>KYx7Ab?-(HU~a275)MBp~`P)hhzSsbj|d`aBe(L*(;zif{iFJu**ZR zkL-tPyh!#*r-JVQJq>5b0?cCy!uSKef+R=$s3iA7*k*_l&*e!$F zYwGI;=S^0)b`mP8&Ry@{R(dPfykD&?H)na^ihVS7KXkxb36TbGm%X1!QSmbV9^#>A z-%X>wljnTMU0#d;tpw?O1W@{X-k*>aOImeG z#N^x?ehaaQd}ReQykp>i;92q@%$a!y1PNyPYDIvMm& zyYVwn;+0({W@3h(r&i#FuCDE)AC(y&Vu>4?1@j0|CWnhHUx4|zL7cdaA32RSk?wl% zMK^n42@i5AU>f70(huWfOwaucbaToxj%+)7hnG^CjH|O`A}+GHZyQ-X57(WuiyRXV zPf>0N3GJ<2Myg!sE4XJY?Z7@K3ZgHy8f7CS5ton0Eq)Cp`iLROAglnsiEXpnI+S8; zZn>g2VqLxi^p8#F#Laf3<00AcT}Qh&kQnd^28u!9l1m^`lfh9+5$VNv=?(~Gl2wAl zx(w$Z2!_oESg_3Kk0hUsBJ<;OTPyL(?z6xj6LG5|Ic4II*P+_=ac7KRJZ`(k2R$L# zv|oWM@116K7r3^EL*j2ktjEEOY9c!IhnyqD&oy7+645^+@z5Y|;0+dyR2X6^%7GD* zXrbPqTO}O={ z4cGaI#DdpP;5u?lcNb($V`l>H7k7otl_jQFu1hh>=(?CTPN#IPO%O_rlVX}_Nq;L< z@YNiY>-W~&E@=EC5%o_z<^3YEw)i_c|NXxHF{=7U7Ev&C`c^0Z4-LGKXu*Hkk&Av= zG&RAv{cR7o4${k~f{F~J48Ks&o(D@j-PQ2`LL@I~b=ifx3q!p6`d>~Y!<-^mMk3)e zhi1;(YLU5KH}zzZNhl^`0HT(r`5FfmDEzxa zk&J7WQ|!v~TyDWdXQ)!AN_Y%xM*!jv^`s)A`|F%;eGg27KYsrCE2H}7*r)zvum6B{ z$k5Har9pv!dcG%f|3hE(#hFH+12RZPycVi?2y`-9I7JHryMn3 z9Y8?==_(vOAJ7PnT<0&85`_jMD0#ipta~Q3M!q5H1D@Nj-YXI$W%OQplM(GWZ5Lpq z-He6ul|3<;ZQsqs!{Y7x`FV@pOQc4|N;)qgtRe(Uf?|YqZv^$k8On7DJ5>f2%M=TV zw~x}9o=mh$JVF{v4H5Su1pq66+mhTG6?F>Do}x{V(TgFwuLfvNP^ijkrp5#s4UT!~ zEU7pr8aA)2z1zb|X9IpmJykQcqI#(rS|A4&=TtWu@g^;JCN`2kL}%+K!KlgC z>P)v+uCeI{1KZpewf>C=?N7%1e10Y3pQCZST1GT5fVyB1`q)JqCLXM zSN0qlreH1=%Zg-5`(dlfSHI&2?^SQdbEE&W4#%Eve2-EnX>NfboD<2l((>>34lE%) zS6PWibEvuBG7)KQo_`?KHSPk+2P;`}#xEs}0!;yPaTrR#j(2H|#-CbVnTt_?9aG`o z(4IPU*n>`cw2V~HM#O`Z^bv|cK|K};buJ|#{reT8R)f+P2<3$0YGh!lqx3&a_wi2Q zN^U|U$w4NP!Z>5|O)>$GjS5wqL3T8jTn%Vfg3_KnyUM{M`?bm)9oqZP&1w1)o=@+(5eUF@=P~ zk2B5AKxQ96n-6lyjh&xD!gHCzD$}OOdKQQk7LXS-fk2uy#h{ktqDo{o&>O!6%B|)` zg?|JgcH{P*5SoE3(}QyGc=@hqlB5w;bnmF#pL4iH`TSuft$dE5j^qP2S)?)@pjRQZ zBfo6g>c!|bN-Y|(Wah2o61Vd|OtXS?1`Fu&mFZ^yzUd4lgu7V|MRdGj3e#V`=mnk- zZ@LHn?@dDi=I^}R?}mZwduik!hC%=Hcl56u{Wrk1|1SxlgnzG&e7Vzh*wNM(6Y!~m z`cm8Ygc1$@z9u9=m5vs1(XXvH;q16fxyX4&e5dP-{!Kd555FD6G^sOXHyaCLka|8j zKKW^E>}>URx736WWNf?U6Dbd37Va3wQkiE;5F!quSnVKnmaIRl)b5rM_ICu4txs+w zj}nsd0I_VG^<%DMR8Zf}vh}kk;heOQTbl ziEoE;9@FBIfR7OO9y4Pwyz02OeA$n)mESpj zdd=xPwA`nO06uGGsXr4n>Cjot7m^~2X~V4yH&- zv2llS{|und45}Pm1-_W@)a-`vFBpD~>eVP(-rVHIIA|HD@%7>k8JPI-O*<7X{L*Ik zh^K`aEN!BteiRaY82FVo6<^8_22=aDIa8P&2A3V<(BQ;;x8Zs-1WuLRWjQvKv1rd2 zt%+fZ!L|ISVKT?$3iCK#7whp|1ivz1rV*R>yc5dS3kIKy_0`)n*%bfNyw%e7Uo}Mnnf>QwDgeH$X5eg_)!pI4EJjh6?kkG2oc6Af0py z(txE}$ukD|Zn=c+R`Oq;m~CSY{ebu9?!is}01sOK_mB?{lSY33E=!KkKtMeI*FO2b z%95awv9;Z|UDp3xm+aP*5I!R-_M2;GxeCRx3ATS0iF<_Do2Mi)Hk2 zjBF35VB>(oamIYjunu?g0O-?LuOvtfs5F(iiIicbu$HMPPF%F>pE@hIRjzT)>aa=m zwe;H9&+2|S!m74!E3xfO{l3E_ab`Q^tZ4yH9=~o2DUEtEMDqG=&D*8!>?2uao%w`&)THr z^>=L3HJquY>6)>dW4pCWbzrIB+>rdr{s}}cL_?#!sOPztRwPm1B=!jP7lQG|Iy6rP zVqZDNA;xaUx&xUt?Ox|;`9?oz`C0#}mc<1Urs#vTW4wd{1_r`eX=BeSV z_9WV*9mz>PH6b^z{VYQJ1nSTSqOFHE9u>cY)m`Q>=w1NzUShxcHsAxasnF2BG;NQ; zqL1tjLjImz_`q=|bAOr_i5_NEijqYZ^;d5y3ZFj6kCYakJh**N_wbfH;ICXq?-p#r z{{ljNDPSytOaG#7=yPmA&5gyYI%^7pLnMOw-RK}#*dk=@usL;|4US?{@K%7esmc&n z5$D*+l&C9)Bo@$d;Nwipd!68&+NnOj^<~vRcKLX>e03E|;to;$ndgR;9~&S-ly5gf z{rzj+j-g$;O|u?;wwxrEpD=8iFzUHQfl{B>bLHqH(9P zI59SS2PEBE;{zJUlcmf(T4DrcO?XRWR}?fekN<($1&AJTRDyW+D*2(Gyi?Qx-i}gy z&BpIO!NeVdLReO!YgdUfnT}7?5Z#~t5rMWqG+$N2n%5o#Np6ccNly}#IZQsW4?|NV zR9hrcyP(l#A+U4XcQvT;4{#i)dU>HK>aS!k1<3s2LyAhm2(!Nu%vRC9T`_yn9D+r} z1i&U~IcQ?4xhZYyH6WL-f%}qIhZkc&}n2N0PM| z6|XA9d-y;!`D{p;xu*gv7a|zaZ*MiQ)}zPzW4GB0mr)}N-DmB&hl1&x`2@sxN572_ zS)RdJyR%<7kW0v3Q_|57JKy&9tUdbqz}|hwn84}U*0r^jt6Ssrp+#1y=JBcZ+F`f(N?O0XL1OFGN`1-r?S<#t4*C9|y~e)!UYZ zRQ3M8m%~M)VriIvn~XzoP;5qeu(ZI>Y#r zAd)J)G9)*BeE%gmm&M@Olg3DI_zokjh9NvdGbT z+u4(Y&uC6tBBefIg~e=J#8i1Zxr>RT)#rGaB2C71usdsT=}mm`<#WY^6V{L*J6v&l z1^Tkr6-+^PA)yC;s1O^3Q!)Reb=fxs)P~I*?i&j{Vbb(Juc?La;cA5(H7#FKIj0Or zgV0BO{DUs`I9HgQ{-!g@5P^Vr|C4}~w6b=#`Zx0XcVSd?(04HUHwK(gJNafgQNB9Z zCi3TgNXAeJ+x|X|b@27$RxuYYuNSUBqo#uyiH6H(b~K*#!@g__4i%HP5wb<+Q7GSb zTZjJw96htUaGZ89$K_iBo4xEOJ#DT#KRu9ozu!GH0cqR>hP$nk=KXM%Y!(%vWQ#}s zy=O#BZ>xjUejMH^F39Bf0}>D}yiAh^toa-ts#gt6Mk9h1D<9_mGMBhLT0Ce2O3d_U znaTkBaxd-8XgwSp5)x-pqX5=+{cSuk6kyl@k|5DQ!5zLUVV%1X9vjY0gerbuG6nwZu5KDMdq(&UMLZ zy?jW#F6joUtVyz`Y?-#Yc0=i*htOFwQ3`hk$8oq35D}0m$FAOp#UFTV3|U3F>@N?d zeXLZCZjRC($%?dz(41e~)CN10qjh^1CdAcY(<=GMGk@`b1ptA&L*{L@_M{%Vd5b*x#b1(qh=7((<_l%ZUaHtmgq} zjchBdiis{Afxf@3CjPR09E*2#X(`W#-n`~6PcbaL_(^3tfDLk?Nb6CkW9v!v#&pWJ3iV-9hz zngp#Q`w`r~2wt&cQ9#S7z0CA^>Mzm7fpt72g<0y-KT{G~l-@L#edmjZQ}7{*$mLgSdJfS$Ge{hrD=mr;GD)uYq8}xS zT>(w_;}894Kb}(P5~FOpFIEjadhmxD(PsZbKwa-qxVa7Oc7~ebPKMeN(pCRzq8s@l z`|l^*X1eK1+Spz--WkSW_nK`Cs@JmkY4+p=U91nJoy{tSH;TzuIyS)Q_(S@;Iakua zpuDo5W54Mo;jY@Ly1dY)j|+M%$FJ0`C=FW#%UvOd&?p}0QqL20Xt!#pr8ujy6CA-2 zFz6Ex5H1i)c9&HUNwG{8K%FRK7HL$RJwvGakleLLo}tsb>t_nBCIuABNo$G--_j!gV&t8L^4N6wC|aLC)l&w04CD6Vc#h^(YH@Zs4nwUGkhc_-yt{dK zMZ<%$swLmUl8`E~RLihGt@J5v;r;vT&*Q!Cx zZ55-zpb;W7_Q{tf$mQvF61(K>kwTq0x{#Din||)B{+6O#ArLi)kiHWVC4`fOT&B(h zw&YV`J1|^FLx~9Q%r-SFhYl4PywI7sF2Q$>4o50~dfp5nn}XHv-_DM?RGs#+4gM;% znU>k=81G~f6u%^Z{bcX&sUv*h|L+|mNq=W43y@{~C zpL-TW3hYPs0^*OqS#KQwA^CGG_A-6#`_{1LBCD&*3nY0UHWJj1D|VP%oQlFxLllaA zVI@2^)HZ%E*=RbQcFOKIP7?+|_xVK+2oG(t_EGl2y;Ovox zZb^qVpe!4^reKvpIBFzx;Ji=PmrV>uu-Hb>`s?k?YZQ?>av45>i(w0V!|n?AP|v5H zm`e&Tgli#lqGEt?=(?~fy<(%#nDU`O@}Vjib6^rfE2xn;qgU6{u36j_+Km%v*2RLnGpsvS+THbZ>p(B zgb{QvqE?~50pkLP^0(`~K& zjT=2Pt2nSnwmnDFi2>;*C|OM1dY|CAZ5R|%SAuU|5KkjRM!LW_)LC*A zf{f>XaD+;rl6Y>Umr>M8y>lF+=nSxZX_-Z7lkTXyuZ(O6?UHw^q; z&$Zsm4U~}KLWz8>_{p*WQ!OgxT1JC&B&>|+LE3Z2mFNTUho<0u?@r^d=2 z-av!n8r#5M|F%l;=D=S1mGLjgFsiYAOODAR}#e^a8 zfVt$k=_o}kt3PTz?EpLkt54dY}kyd$rU zVqc9SN>0c z753j-gdN~UiW*FUDMOpYEkVzP)}{Ds*3_)ZBi)4v26MQr140|QRqhFoP=a|;C{#KS zD^9b-9HM11W+cb1Y)HAuk<^GUUo(ut!5kILBzAe)Vaxwu4Up!7Ql*#DDu z>EB84&xSrh>0jT!*X81jJQq$CRHqNj29!V3FN9DCx)~bvZbLwSlo3l^zPb1sqBnp) zfZpo|amY^H*I==3#8D%x3>zh#_SBf?r2QrD(Y@El!wa;Ja6G9Y1947P*DC|{9~nO& z*vDnnU!8(cV%HevsraF%Y%2{Z>CL0?64eu9r^t#WjW4~3uw8d}WHzsV%oq-T)Y z0-c!FWX5j1{1##?{aTeCW2b$PEnwe;t`VPCm@sQ`+$$L2=3kBR%2XU1{_|__XJ$xt zibjY2QlDVs)RgHH*kl&+jn*JqquF)k_Ypibo00lcc<2RYqsi-G%}k0r(N97H7JEn7@E3ZTH0JK>d8)E~A-D z!B&z9zJw0Bi^fgQZI%LirYaBKnWBXgc`An*qvO^*$xymqKOp(+3}IsnVhu?YnN7qz zNJxDN-JWd7-vIiv2M9ih>x3gNVY%DzzY~dCnA}76IRl!`VM=6=TYQ=o&uuE8kHqZT zoUNod0v+s9D)7aLJ|hVqL0li1hg)%&MAciI(4YJ=%D4H$fGQ&Lu-?@>>@pEgC;ERrL= zI^cS&3q8fvEGTJZgZwL5j&jp%j9U^Of6pR{wA^u=tVt#yCQepXNIbynGnuWbsC_EE zRyMFq{5DK692-*kyGy~An>AdVR9u___fzmmJ4;^s0yAGgO^h{YFmqJ%ZJ_^0BgCET zE6(B*SzeZ4pAxear^B-YW<%BK->X&Cr`g9_;qH~pCle# zdY|UB5cS<}DFRMO;&czbmV(?vzikf)Ks`d$LL801@HTP5@r><}$xp}+Ip`u_AZ~!K zT}{+R9Wkj}DtC=4QIqJok5(~0Ll&_6PPVQ`hZ+2iX1H{YjI8axG_Bw#QJy`6T>1Nn z%u^l`>XJ{^vX`L0 z1%w-ie!dE|!SP<>#c%ma9)8K4gm=!inHn2U+GR+~ zqZVoa!#aS0SP(|**WfQSe?cA=1|Jwk`UDsny%_y{@AV??N>xWekf>_IZLUEK3{Ksi zWWW$if&Go~@Oz)`#=6t_bNtD$d9FMBN#&97+XKa+K2C@I9xWgTE{?Xnhc9_KKPcujj@NprM@e|KtV_SR+ zSpeJ!1FGJ=Te6={;;+;a46-*DW*FjTnBfeuzI_=I1yk8M(}IwEIGWV0Y~wia;}^dg z{BK#G7^J`SE10z4(_Me=kF&4ld*}wpNs91%2Ute>Om`byv9qgK4VfwPj$`axsiZ)wxS4k4KTLb-d~!7I@^Jq`>?TrixHk|9 zqCX7@sWcVfNP8N;(T>>PJgsklQ#GF>F;fz_Rogh3r!dy*0qMr#>hvSua;$d z3TCZ4tlkyWPTD<=5&*bUck~J;oaIzSQ0E03_2x{?weax^jL3o`ZP#uvK{Z5^%H4b6 z%Kbp6K?>{;8>BnQy64Jy$~DN?l(ufkcs6TpaO&i~dC>0fvi-I^7YT#h?m;TVG|nba%CKRG%}3P*wejg) zI(ow&(5X3HR_xk{jrnkA-hbwxEQh|$CET9Qv6UpM+-bY?E!XVorBvHoU59;q<9$hK z%w5K-SK zWT#1OX__$ceoq0cRt>9|)v}$7{PlfwN}%Wh3rwSl;%JD|k~@IBMd5}JD#TOvp=S57 zae=J#0%+oH`-Av}a(Jqhd4h5~eG5ASOD)DfuqujI6p!;xF_GFcc;hZ9k^a7c%%h(J zhY;n&SyJWxju<+r`;pmAAWJmHDs{)V-x7(0-;E?I9FWK@Z6G+?7Py8uLc2~Fh1^0K zzC*V#P88(6U$XBjLmnahi2C!a+|4a)5Ho5>owQw$jaBm<)H2fR=-B*AI8G@@P-8I8 zHios92Q6Nk-n0;;c|WV$Q);Hu4;+y%C@3alP`cJ2{z~*m-@de%OKVgiWp;4Q)qf9n zJ!vmx(C=_>{+??w{U^Bh|LFJ<6t}Er<-Tu{C{dv8eb(kVQ4!fOuopTo!^x1OrG}0D zR{A#SrmN`=7T29bzQ}bwX8OUufW9d9T4>WY2n15=k3_rfGOp6sK0oj7(0xGaEe+-C zVuWa;hS*MB{^$=0`bWF(h|{}?53{5Wf!1M%YxVw}io4u-G2AYN|FdmhI13HvnoK zNS2fStm=?8ZpKt}v1@Dmz0FD(9pu}N@aDG3BY8y`O*xFsSz9f+Y({hFx;P_h>ER_& z`~{z?_vCNS>agYZI?ry*V96_uh;|EFc0*-x*`$f4A$*==p`TUVG;YDO+I4{gJGrj^ zn?ud(B4BlQr;NN?vaz_7{&(D9mfd z8esj=a4tR-ybJjCMtqV8>zn`r{0g$hwoWRUI3}X5=dofN){;vNoftEwX>2t@nUJro z#%7rpie2eH1sRa9i6TbBA4hLE8SBK@blOs=ouBvk{zFCYn4xY;v3QSM%y6?_+FGDn z4A;m)W?JL!gw^*tRx$gqmBXk&VU=Nh$gYp+Swu!h!+e(26(6*3Q!(!MsrMiLri`S= zKItik^R9g!0q7y$lh+L4zBc-?Fsm8`CX1+f>4GK7^X2#*H|oK}reQnT{Mm|0ar<+S zRc_dM%M?a3bC2ILD`|;6vKA`a3*N~(cjw~Xy`zhuY2s{(7KLB{S>QtR3NBQ3>vd+= z#}Q)AJr7Y_-eV(sMN#x!uGX08oE*g=grB*|bBs}%^3!RVA4f%m3=1f0K=T^}iI&2K zuM2GG5_%+#v-&V>?x4W9wQ|jE2Q7Be8mOyJtZrqn#gXy-1fF1P$C8+We&B*-pi#q5 zETp%H6g+%#sH+L4=ww?-h;MRCd2J9zwQUe4gHAbCbH08gDJY;F6F)HtWCRW1fLR;)ysGZanlz*a+|V&@(ipWdB!tz=m_0 z6F}`d$r%33bw?G*azn*}Z;UMr{z4d9j~s`0*foZkUPwpJsGgoR0aF>&@DC;$A&(av z?b|oo;`_jd>_5nye`DVOcMLr-*Nw&nA z82E8Dw^$Lpso)gEMh?N|Uc^X*NIhg=U%enuzZOGi-xcZRUZmkmq~(cP{S|*+A6P;Q zprIkJkIl51@ng)8cR6QSXJtoa$AzT@*(zN3M+6`BTO~ZMo0`9$s;pg0HE3C;&;D@q zd^0zcpT+jC%&=cYJF+j&uzX87d(gP9&kB9|-zN=69ymQS9_K@h3ph&wD5_!4q@qI@ zBMbd`2JJ2%yNX?`3(u&+nUUJLZ=|{t7^Rpw#v-pqD2_3}UEz!QazhRty%|Q~WCo7$ z+sIugHA%Lmm{lBP#bnu_>G}Ja<*6YOvSC;89z67M%iG0dagOt1HDpDn$<&H0DWxMU zxOYaaks6%R@{`l~zlZ*~2}n53mn2|O&gE+j*^ypbrtBv{xd~G(NF?Z%F3>S6+qcry z?ZdF9R*a;3lqX_!rI(Cov8ER_mOqSn6g&ZU(I|DHo7Jj`GJ}mF;T(vax`2+B8)H_D zD0I;%I?*oGD616DsC#j0x*p+ZpBfd=9gR|TvB)832CRhsW_7g&WI@zp@r7dhg}{+4f=(cO2s+)jg0x(*6|^+6W_=YIfSH0lTcK* z%)LyaOL6em@*-_u)}Swe8rU)~#zT-vNiW(D*~?Zp3NWl1y#fo!3sK-5Ek6F$F5l3| zrFFD~WHz1}WHmzzZ!n&O8rTgfytJG*7iE~0`0;HGXgWTgx@2fD`oodipOM*MOWN-} zJY-^>VMEi8v23ZlOn0NXp{7!QV3F1FY_URZjRKMcY(2PV_ms}EIC^x z=EYB5UUQ{@R~$2Mwiw$_JAcF+szKB*n(`MYpDCl>~ss54uDQ%Xf-8|dgO zY)B_qju=IaShS|XsQo=nSYxV$_vQR@hd~;qW)TEfU|BA0&-JSwO}-a*T;^}l;MgLM zz}CjPlJX|W2vCzm3oHw3vqsRc3RY=2()}iw_k2#eKf&VEP7TQ;(DDzEAUgj!z_h2Br;Z3u=K~LqM6YOrlh)v9`!n|6M-s z?XvA~y<5?WJ{+yM~uPh7uVM&g-(;IC3>uA}ud?B3F zelSyc)Nx>(?F=H88O&_70%{ATsLVTAp88F-`+|egQ7C4rpIgOf;1tU1au+D3 zlz?k$jJtTOrl&B2%}D}8d=+$NINOZjY$lb{O<;oT<zXoAp01KYG$Y4*=)!&4g|FL(!54OhR-?)DXC&VS5E|1HGk8LY;)FRJqnz zb_rV2F7=BGwHgDK&4J3{%&IK~rQx<&Kea|qEre;%A~5YD6x`mo>mdR)l?Nd%T2(5U z_ciT02-zt_*C|vn?BYDuqSFrk3R(4B0M@CRFmG{5sovIq4%8AhjXA5UwRGo)MxZlI zI%vz`v8B+#ff*XtGnciczFG}l(I}{YuCco#2E6|+5WJ|>BSDfz0oT+F z%QI^ixD|^(AN`MS6J$ zXlKNTFhb>KDkJp*4*LaZ2WWA5YR~{`={F^hwXGG*rJYQA7kx|nwnC58!eogSIvy{F zm1C#9@$LhK^Tl>&iM0wsnbG7Y^MnQ=q))MgApj4)DQt!Q5S`h+5a%c7M!m%)?+h65 z0NHDiEM^`W+M4)=q^#sk(g!GTpB}edwIe>FJQ+jAbCo#b zXmtd3raGJNH8vnqMtjem<_)9`gU_-RF&ZK!aIenv7B2Y0rZhon=2yh&VsHzM|`y|0x$Zez$bUg5Nqj?@~^ zPN43MB}q0kF&^=#3C;2T*bDBTyO(+#nZnULkVy0JcGJ36or7yl1wt7HI_>V7>mdud zv2II9P61FyEXZuF$=69dn%Z6F;SOwyGL4D5mKfW)q4l$8yUhv7|>>h_-4T*_CwAyu7;DW}_H zo>N_7Gm6eed=UaiEp_7aZko@CC61@(E1be&5I9TUq%AOJW>s^9w%pR5g2{7HW9qyF zh+ZvX;5}PN0!B4q2FUy+C#w5J?0Tkd&S#~94(AP4%fRb^742pgH7Tb1))siXWXHUT z1Wn5CG&!mGtr#jq6(P#!ck@K+FNprcWP?^wA2>mHA03W?kj>5b|P0ErXS) zg2qDTjQ|grCgYhrH-RapWCvMq5vCaF?{R%*mu}1)UDll~6;}3Q*^QOfj!dlt02lSzK z?+P)02Rrq``NbU3j&s*;<%i4Y>y9NK&=&KsYwvEmf5jwTG6?+Pu1q9M8lLlx)uZZ7 zizhr~e0ktGs-=$li-2jz^_48-jk**y&5u0`B2gc#i$T1~t+AS*kEfR*b{^Ec>2-F~ zKYRl&uQ5yO@EtAZX8ZSqx;8+AKf+CqhlUSpp*VfyBMv+%wxN5GukZEi^_to%MFRc0 zdXqJ*jk?#uYT6EJe446@(f6G4vhnxQP|pGeJ?-#|Ksq?g*ky=}x+Qnx+!<>Y(XStN zQIND`{KU}&l)E*ntI^}kJ=ly8DML{!(58Xk4_bzIc@v~e;>wKl_`7G%pGz~4KH*CTp;_|52)d!+ximd$|8v@zzEq%j68QXkgf$7eM~xdM5q5i z{?qFx_W|eq@L03bWJfjy^z@()-iCjzjREuf zb_a(yTz)ZKWCF%Lp>^2-%Q?*t{06}x#DLN3cO=i>h6#-a`z;<5rBGGM6GA(WqvRcX%Pn?Uvs1#e|ePSNJEC%+X(YI$x)`s$%>O#%}D9dgqWfq4yfVz^%FglokdFR}uJQhx|}_w`9Ulx38Ha>ZslKs58c-@IFI&f;?xM zbK>rKNfPFsf>%+k6%(A6=7Aac^_qrOCNqb3ZVJ;8pt!?1DR*ynJb#@II9h?)xB)A~ zm9Kk)Hy}!Z+W}i6ZJDy+?yY_=#kWrzgV)2eZAx_E=}Nh7*#<&mQz`Umfe$+l^P(xd zN}PA2qII4}ddCU+PN+yxkH%y!Qe(;iH3W%bwM3NKbU_saBo<8x9fGNtTAc_SizU=o zC3n2;c%LoU^j90Sz>B_p--Fzqv7x7*?|~-x{haH8RP)p|^u$}S9pD-}5;88pu0J~9 zj}EC`Q^Fw}`^pvAs4qOIuxKvGN@DUdRQ8p-RXh=3S#<`3{+Qv6&nEm)uV|kRVnu6f zco{(rJaWw(T0PWim?kkj9pJ)ZsUk9)dSNLDHf`y&@wbd;_ita>6RXFJ+8XC*-wsiN z(HR|9IF283fn=DI#3Ze&#y3yS5;!yoIBAH(v}3p5_Zr+F99*%+)cp!Sy8e+lG?dOc zuEz<;3X9Z5kkpL_ZYQa`sioR_@_cG z8tT~GOSTWnO~#?$u)AcaBSaV7P~RT?Nn8(OSL1RmzPWRWQ$K2`6*)+&7^zZBeWzud z*xb3|Fc~|R9eH+lQ#4wF#c;)Gka6lL(63C;>(bZob!i8F-3EhYU3|6-JBC0*5`y0| zBs!Frs=s!Sy0qmQNgIH|F`6(SrD1js2prni_QbG9Sv@^Pu2szR9NZl8GU89gWWvVg z2^-b*t+F{Nt>v?js7hnlC`tRU(an0qQG7;h6T~ z-`vf#R-AE$pzk`M{gCaia}F`->O2)60AuGFAJg> z*O2IZqTx=AzDvC49?A92>bQLdb&32_4>0Bgp0ESXXnd4B)!$t$g{*FG%HYdt3b3a^J9#so%BJMyr2 z{y?rzW!>lr097b9(75#&4&@lkB1vT*w&0E>!dS+a|ZOu6t^zro2tiP)bhcNNxn zbJs3_Fz+?t;4bkd8GfDI7ccJ5zU`Bs~ zN~bci`c`a%DoCMel<-KUCBdZRmew`MbZEPYE|R#|*hhvhyhOL#9Yt7$g_)!X?fK^F z8UDz)(zpsvriJ5aro5>qy`Fnz%;IR$@Kg3Z3EE!fv9CAdrAym6QU82=_$_N5*({_1 z7!-=zy(R{xg9S519S6W{HpJZ8Is|kQ!0?`!vxDggmslD59)>iQ15f z7J8NqdR`9f8H|~iFGNsPV!N)(CC9JRmzL9S}7U-K@`X893f3f<8|8Ls!^eA^#(O6nA+ByFIXcz_WLbfeG|nHJ5_sJJ^gNJ%SI9#XEfNRbzV+!RkI zXS$MOVYb2!0vU}Gt7oUy*|WpF^*orBot~b2J@^be?Gq;U%#am8`PmH-UCFZ&uTJlnetYij0z{K1mmivk$bdPbLodu;-R@@#gAV!=d%(caz$E?r zURX0pqAn7UuF6dULnoF1dZ$WM)tHAM{eZK6DbU1J`V5Dw<;xk}Nl`h+nfMO_Rdv z3SyOMzAbYaD;mkxA7_I_DOs#Bk;e5D%gsS3q)hlmi1w{FsjKNJE22`AjmNiAPRnIc zcIkN25;rOn3FipAFd(PnlK9{03w6Q<(68#1Jw`{axEGQE{Ac>^U$h);h2ADICmaNxrfpb`Jdr*)Y1SicpYKCFv$3vf~;5aW>n^7QGa63MJ z;B1+Z>WQ615R2D8JmmT`T{QcgZ+Kz1hTu{9FOL}Q8+iFx-Vyi}ZVVcGjTe>QfA`7W zFoS__+;E_rQIQxd(Bq4$egKeKsk#-9=&A!)(|hBvydsr5ts0Zjp*%*C0lM2sIOx1s zg$xz?Fh?x!P^!vWa|}^+SY8oZHub7f;E!S&Q;F?dZmvBxuFEISC}$^B_x*N-xRRJh zn4W*ThEWaPD*$KBr8_?}XRhHY7h^U1aN6>m=n~?YJQd8+!Uyq_3^)~4>XjelM&!c9 zCo|0KsGq7!KsZ~9@%G?i>LaU7#uSTMpypocm*oqJHR|wOgVWc7_8PVuuw>x{kEG4T z$p^DV`}jUK39zqFc(d5;N+M!Zd3zhZN&?Ww(<@AV-&f!v$uV>%z+dg9((35o@4rqLvTC-se@hkn^6k7+xHiK-vTRvM8{bCejbU;1@U=*r}GTI?Oc$!b6NRcj83-zF; z=TB#ESDB`F`jf4)z=OS76Se}tQDDHh{VKJk#Ad6FDB_=afpK#pyRkGrk~OuzmQG)} z*$t!nZu$KN&B;|O-aD=H<|n6aGGJZ=K9QFLG0y=Jye_ElJFNZJT;fU8P8CZcLBERjioAOC0Vz_pIXIc};)8HjfPwNy zE!g|lkRv3qpmU?shz(BBt5%TbpJC3HzP9!t7k*Fh48!-HlJ4TTgdCr3rCU!iF}kgu z4Qs;K@XOY~4f~N}Jl8V_mGbwzvNLbl&0e9UG4W;kvjTK|5`-Ld+eQ6YRF`N0ct%u% z^3J_{7r#_W1zm|>IPN!yWCRrN)N!7v`~ptNkIXKipQ6ogFvcnI5ugxdoa{d;uD67g zgo^}QuZRkB540Vc!@c80(wFG=$ct}oHq(#W0+-XX(;Rrt`x=<45X}ficNtI2(&}=~ zb(!}tNz?s`wm{gK?2tdf+OEF;tzx<(3fMd7_tM@Ghs$Z(Os-H(kYq#qB|J-aC9Ku?fsWwJhB36c)A zu|a7ZF?V8X7l2g5~xqZf>2=6Dsi5lfo zKIRL&@MLJyaBE)V_9=pJYu%U2wxR*-(0MI5_|yqP`?h@cks(5LR@XUKLMI_xuVtiu zRvpDS8MyUMRFM6`P+Sjc!A_e^H38Qu7b{b7QZ>NHyA6k-YYygQuW&C_OGO(7V7?}r)zedSVpBI zuk29Z4GW3C0GpfozbZQya454sjt@ndQmsp=DA&@sWw&xmOlDk1JIcMNp~-ES$&A~k zG#W(6hBj?!Fu8Q4WYexoSBa8_5=v20xnx6H?e;$t)5|f&{7=vOye^&3_c-Ug?|a@e z=X`&qT_5B7N9vZoPBhXOTEDV;4&x2Je4}T(UB~O-$D#CjX77$R?RZ*`ed~$G;$4YS z4n*|Pop(!NN79Hk2}U#cfEEwdxM)xQm}$~rV03xc=#U@@Y*}qEmot5KvDb=8{!E-n zl4p?}&g2h^sUGyTcGh=0aQzQb*k;K;dvbeZUgmwEv>%#(EPtj=gHKdi|E8@w+|>KC zxEU>b>P+9Xf}pEyQK(}#QrBG4Jaf!iE!qpMbTu>gb!gtdq<`@xO+roQl+S_7)!G(% zdy)$iGmJ1cwP?F=IyyV1-$|kf|EKM3B@I&lZ%NI@VV;*mQdLWjc#t|Vbk_Q~>&O03 zIcSr$(qLAINj7a z;!||v&1D5SX#X@5jNd}jUsi-CH_Scjyht&}q2p*CJCC-`&NyXf)vD5{e!HO629D-O z%bZelTcq=DoRX>zeWCa^RmR3*{x9;3lZ75M#S)!W0bRIFH#P6b%{|HRSZ5!!I#s)W z_|XXZQ<0_`>b^^0Z>LU64Yg1w)8}#M^9se(OZ9~baZ7fsKFc;EtnB>kesci#>=icG zuHdjax2^=!_(9?0l7;G7^-}9>Y#M zm;9*GT~dBuYWdk49%mZM0=H#FY1)}7NE5DE_vsqrA0`?0R0q535qHjWXcl|gz9Fq$ zMKxgL;68l!gm3y0durIr3LHv~y*ABm` zYhQG0UW#hg@*A{&G!;$FS43}rIF$e6yRdGJWVR<}uuJ_5_8qa3xaHH^!VzUteVp;> z<0`M>3tnY$ZFb$(`0sg93TwGyP;`9UYUWxO&CvAnSzei&ap))NcW;R`tA=y^?mBmG+M*&bqW5kL$V(O;(p)aEk`^ci?2Jwxu>0sy>a7+Wa9t z5#I2o;+gr^9^&km^z7>xJWbN&Ft>Vna34E zI@BBzwX)R}K3SL?)enrDJ45QLt;-7CFJk{`cF3L4Z^CtG_r5)0)HV>BOYPIUh#D%| zYQAu31f{bm-D*`_k7DTTr?Nkw_gY%J1cb2&TdtibY?V=|SSIOlA;|5C!2@?YQ z-$?G0jj^mG|MP>DmbF7}T~C$H6=CpZ~hd zZ1C|xV@=h#^~`3LSCnmI(vZ|5r3>eq5*UB)dhdy``*gKY3Eg%jSK8I-`G+OWWlD)T zt$wSQ=||lSkiKy}YF-k}@W9EiS?)z`hK{R!dd-$BCJvBtAN-yXn3njU$MisEtp!?Q z%Vk-*(wy9dd15(-WFw_&^tT;;IpF?ox1`Qq3-0zVTk+$W_?q}GfAQlPcrB^?&tWSI z2BB!K=sH7FUYmXa_dcV^Z3>5z8}~W{S!$jVR_3hu_|wl2|gmRH8ftn^z@fW75*;-`;wU+fY+BR_yx6BZnE5_Hna({jrPiubRp$jZ=T=t$hx&NeCV1!vuCcl4PJ0p0Fjp>6K} zHkoD1gQk=P2hYcT%)cJ2Q5WuA|5_x+dX0%hnozfTF>$#Wz~X!MY>){H4#fB#7^ID* z1*o2Hzp}?WVs&gbS?Uq(CT0sP+F)u9{xfgg6o_{8J#m;|NeJqDHhb(Q8%z8aM_qeM zn83>d`uDd47WIuKp78JBYo2SYupGcNXIzeou^eMY`@%Bv8elZ>q~3uq#~IX)g%g;h zoUXymEd>|kVsMkyb&1l~lrE-`w(0PObapYa35DJ4Y03Jv_!DKp}0HTbOgZRM=;PSsuAJJJ1 zItc+tu9;ANG;qHaCI|T85!euhFK~VK^G2LZV1+cbzS?>ar@>emg;JTI5VAn1g5U~| zU=p&k0OlSzc$U=s#9_uL3&n|6A1X$XvrE9vFV@`A4G#!D1QcFCeE`F2N(deJx>)*A z$XIW0P~-NbAd=5i6`s<~(vAQX9t$dbVqc5|E|CHRtb$1(l&KSNh_t2#k_l95KnP86 z)ns_DGspv-M0z0#h2a+*oH|{5~j{ zXGD=}cLrBSESQ0u$XmQlFfWMCAWaS;wKK%#aSSYK=qljBiY(s zT$v;We24&$w=avIILsMt0%1fDyah|AlLNg#WL$Lu)tf}YfqO%+pH~QC*bZO4aM*i9 zrPFf|5!hv@XY8CzaFh*Dy9vH|2fKKr(@x}`L#9^*vOae|lk`adG#oZZAyk|TOV8`9L zc-sQu%y1MQes&J?)a1}Zc*>-P!6j-T#75V$lLC!TuMB(!G-+D2;XptUxymSPFI-K&0x}B1?h$ z3-9**-9!);fwyiWB5gS$i;P~c=^}5-6G@{4TWDBRDc6(M|%qa-mS`z`u9kWo{Xl_uc;hXOkRd literal 0 HcmV?d00001 diff --git a/support/test/kotlin/gradle/wrapper/gradle-wrapper.properties b/support/test/kotlin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..622ab64 --- /dev/null +++ b/support/test/kotlin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/support/test/kotlin/gradlew b/support/test/kotlin/gradlew new file mode 100755 index 0000000..fbd7c51 --- /dev/null +++ b/support/test/kotlin/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/support/test/kotlin/gradlew.bat b/support/test/kotlin/gradlew.bat new file mode 100644 index 0000000..a9f778a --- /dev/null +++ b/support/test/kotlin/gradlew.bat @@ -0,0 +1,104 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/support/test/kotlin/settings.gradle.kts b/support/test/kotlin/settings.gradle.kts new file mode 100644 index 0000000..8cbc5be --- /dev/null +++ b/support/test/kotlin/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "vimspector-test" diff --git a/support/test/kotlin/src/main/kotlin/vimspector/test/Application.kt b/support/test/kotlin/src/main/kotlin/vimspector/test/Application.kt new file mode 100644 index 0000000..99655da --- /dev/null +++ b/support/test/kotlin/src/main/kotlin/vimspector/test/Application.kt @@ -0,0 +1,5 @@ +package vimspector.test + +fun main(args: Array) { + println("Hello World!") +} From ce8dd9113cf94d958461be96321bf7eb1958900b Mon Sep 17 00:00:00 2001 From: Allan Guigou Date: Thu, 31 Dec 2020 10:39:01 -0500 Subject: [PATCH 426/609] Remove gradle wrapper The gradle wrapper is too large of a dependency to include for every download of vimspector. Users can use a local installation of gradle to run the sample kotlin application if necessary. --- .../kotlin/gradle/wrapper/gradle-wrapper.jar | Bin 58910 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - support/test/kotlin/gradlew | 185 ------------------ support/test/kotlin/gradlew.bat | 104 ---------- 4 files changed, 294 deletions(-) delete mode 100644 support/test/kotlin/gradle/wrapper/gradle-wrapper.jar delete mode 100644 support/test/kotlin/gradle/wrapper/gradle-wrapper.properties delete mode 100755 support/test/kotlin/gradlew delete mode 100644 support/test/kotlin/gradlew.bat diff --git a/support/test/kotlin/gradle/wrapper/gradle-wrapper.jar b/support/test/kotlin/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 62d4c053550b91381bbd28b1afc82d634bf73a8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58910 zcma&ObC74zk}X`WF59+k+qTVL*+!RbS9RI8Z5v&-ZFK4Nn|tqzcjwK__x+Iv5xL`> zj94dg?X`0sMHx^qXds{;KY)OMg#H>35XgTVfq6#vc9ww|9) z@UMfwUqk)B9p!}NrNqTlRO#i!ALOPcWo78-=iy}NsAr~T8T0X0%G{DhX~u-yEwc29WQ4D zuv2j{a&j?qB4wgCu`zOXj!~YpTNFg)TWoV>DhYlR^Gp^rkOEluvxkGLB?!{fD!T@( z%3cy>OkhbIKz*R%uoKqrg1%A?)uTZD&~ssOCUBlvZhx7XHQ4b7@`&sPdT475?*zWy z>xq*iK=5G&N6!HiZaD{NSNhWL;+>Quw_#ZqZbyglna!Fqn3N!$L`=;TFPrhodD-Q` z1l*=DP2gKJP@)cwI@-M}?M$$$%u~=vkeC%>cwR$~?y6cXx-M{=wdT4|3X(@)a|KkZ z`w$6CNS@5gWS7s7P86L<=vg$Mxv$?)vMj3`o*7W4U~*Nden}wz=y+QtuMmZ{(Ir1D zGp)ZsNiy{mS}Au5;(fYf93rs^xvi(H;|H8ECYdC`CiC&G`zw?@)#DjMc7j~daL_A$ z7e3nF2$TKlTi=mOftyFBt8*Xju-OY@2k@f3YBM)-v8+5_o}M?7pxlNn)C0Mcd@87?+AA4{Ti2ptnYYKGp`^FhcJLlT%RwP4k$ad!ho}-^vW;s{6hnjD0*c39k zrm@PkI8_p}mnT&5I@=O1^m?g}PN^8O8rB`;t`6H+?Su0IR?;8txBqwK1Au8O3BZAX zNdJB{bpQWR@J|e=Z>XSXV1DB{uhr3pGf_tb)(cAkp)fS7*Qv))&Vkbb+cvG!j}ukd zxt*C8&RN}5ck{jkw0=Q7ldUp0FQ&Pb_$M7a@^nf`8F%$ftu^jEz36d#^M8Ia{VaTy z5(h$I)*l3i!VpPMW+XGgzL~fcN?{~1QWu9!Gu0jOWWE zNW%&&by0DbXL&^)r-A*7R@;T$P}@3eOj#gqJ!uvTqBL5bupU91UK#d|IdxBUZAeh1 z>rAI#*Y4jv>uhOh7`S@mnsl0g@1C;k$Z%!d*n8#_$)l}-1&z2kr@M+xWoKR z!KySy-7h&Bf}02%JeXmQGjO3ntu={K$jy$rFwfSV8!zqAL_*&e2|CJ06`4&0+ceI026REfNT>JzAdwmIlKLEr2? zaZ#d*XFUN*gpzOxq)cysr&#6zNdDDPH% zd8_>3B}uA7;bP4fKVdd~Og@}dW#74ceETOE- zlZgQqQfEc?-5ly(Z5`L_CCM!&Uxk5#wgo=OLs-kFHFG*cTZ)$VE?c_gQUW&*!2@W2 z7Lq&_Kf88OCo?BHCtwe*&fu&8PQ(R5&lnYo8%+U73U)Ec2&|A)Y~m7(^bh299REPe zn#gyaJ4%o4>diN3z%P5&_aFUmlKytY$t21WGwx;3?UC}vlxi-vdEQgsKQ;=#sJ#ll zZeytjOad$kyON4XxC}frS|Ybh`Yq!<(IrlOXP3*q86ImyV*mJyBn$m~?#xp;EplcM z+6sez%+K}Xj3$YN6{}VL;BZ7Fi|iJj-ywlR+AP8lq~mnt5p_%VmN{Sq$L^z!otu_u znVCl@FgcVXo510e@5(wnko%Pv+^r^)GRh;>#Z(|#cLnu_Y$#_xG&nvuT+~gzJsoSi zBvX`|IS~xaold!`P!h(v|=>!5gk)Q+!0R1Ge7!WpRP{*Ajz$oGG$_?Ajvz6F0X?809o`L8prsJ*+LjlGfSziO;+ zv>fyRBVx#oC0jGK8$%$>Z;0+dfn8x;kHFQ?Rpi7(Rc{Uq{63Kgs{IwLV>pDK7yX-2 zls;?`h!I9YQVVbAj7Ok1%Y+F?CJa-Jl>1x#UVL(lpzBBH4(6v0^4 z3Tf`INjml5`F_kZc5M#^J|f%7Hgxg3#o}Zwx%4l9yYG!WaYUA>+dqpRE3nw#YXIX%= ziH3iYO~jr0nP5xp*VIa#-aa;H&%>{mfAPPlh5Fc!N7^{!z$;p-p38aW{gGx z)dFS62;V;%%fKp&i@+5x=Cn7Q>H`NofJGXmNeh{sOL+Nk>bQJJBw3K*H_$}%*xJM=Kh;s#$@RBR z|75|g85da@#qT=pD777m$wI!Q8SC4Yw3(PVU53bzzGq$IdGQoFb-c_(iA_~qD|eAy z@J+2!tc{|!8fF;%6rY9`Q!Kr>MFwEH%TY0y>Q(D}xGVJM{J{aGN0drG&|1xO!Ttdw z-1^gQ&y~KS5SeslMmoA$Wv$ly={f}f9<{Gm!8ycp*D9m*5Ef{ymIq!MU01*)#J1_! zM_i4{LYButqlQ>Q#o{~W!E_#(S=hR}kIrea_67Z5{W>8PD>g$f;dTvlD=X@T$8D0;BWkle@{VTd&D5^)U>(>g(jFt4lRV6A2(Te->ooI{nk-bZ(gwgh zaH4GT^wXPBq^Gcu%xW#S#p_&x)pNla5%S5;*OG_T^PhIIw1gXP&u5c;{^S(AC*+$> z)GuVq(FT@zq9;i{*9lEsNJZ)??BbSc5vF+Kdh-kL@`(`l5tB4P!9Okin2!-T?}(w% zEpbEU67|lU#@>DppToestmu8Ce=gz=e#V+o)v)#e=N`{$MI5P0O)_fHt1@aIC_QCv=FO`Qf=Ga%^_NhqGI)xtN*^1n{ z&vgl|TrKZ3Vam@wE0p{c3xCCAl+RqFEse@r*a<3}wmJl-hoJoN<|O2zcvMRl<#BtZ z#}-bPCv&OTw`GMp&n4tutf|er`@#d~7X+);##YFSJ)BitGALu}-N*DJdCzs(cQ?I- z6u(WAKH^NUCcOtpt5QTsQRJ$}jN28ZsYx+4CrJUQ%egH zo#tMoywhR*oeIkS%}%WUAIbM`D)R6Ya&@sZvvUEM7`fR0Ga03*=qaEGq4G7-+30Ck zRkje{6A{`ebq?2BTFFYnMM$xcQbz0nEGe!s%}O)m={`075R0N9KTZ>vbv2^eml>@}722%!r#6Wto}?vNst? zs`IasBtcROZG9+%rYaZe^=5y3chDzBf>;|5sP0!sP(t^= z^~go8msT@|rp8LJ8km?4l?Hb%o10h7(ixqV65~5Y>n_zG3AMqM3UxUNj6K-FUgMT7 z*Dy2Y8Ws+%`Z*~m9P zCWQ8L^kA2$rf-S@qHow$J86t)hoU#XZ2YK~9GXVR|*`f6`0&8j|ss_Ai-x=_;Df^*&=bW$1nc{Gplm zF}VF`w)`5A;W@KM`@<9Bw_7~?_@b{Z`n_A6c1AG#h#>Z$K>gX6reEZ*bZRjCup|0# zQ{XAb`n^}2cIwLTN%5Ix`PB*H^(|5S{j?BwItu+MS`1)VW=TnUtt6{3J!WR`4b`LW z?AD#ZmoyYpL=903q3LSM=&5eNP^dwTDRD~iP=}FXgZ@2WqfdyPYl$9do?wX{RU*$S zgQ{OqXK-Yuf4+}x6P#A*la&^G2c2TC;aNNZEYuB(f25|5eYi|rd$;i0qk7^3Ri8of ziP~PVT_|4$n!~F-B1_Et<0OJZ*e+MN;5FFH`iec(lHR+O%O%_RQhvbk-NBQ+$)w{D+dlA0jxI;z|P zEKW`!X)${xzi}Ww5G&@g0akBb_F`ziv$u^hs0W&FXuz=Ap>SUMw9=M?X$`lgPRq11 zqq+n44qL;pgGO+*DEc+Euv*j(#%;>p)yqdl`dT+Og zZH?FXXt`<0XL2@PWYp|7DWzFqxLK)yDXae&3P*#+f+E{I&h=$UPj;ey9b`H?qe*Oj zV|-qgI~v%&oh7rzICXfZmg$8$B|zkjliQ=e4jFgYCLR%yi!9gc7>N z&5G#KG&Hr+UEfB;M(M>$Eh}P$)<_IqC_WKOhO4(cY@Gn4XF(#aENkp&D{sMQgrhDT zXClOHrr9|POHqlmm+*L6CK=OENXbZ+kb}t>oRHE2xVW<;VKR@ykYq04LM9L-b;eo& zl!QQo!Sw{_$-qosixZJWhciN>Gbe8|vEVV2l)`#5vKyrXc6E`zmH(76nGRdL)pqLb@j<&&b!qJRLf>d`rdz}^ZSm7E;+XUJ ziy;xY&>LM?MA^v0Fu8{7hvh_ynOls6CI;kQkS2g^OZr70A}PU;i^~b_hUYN1*j-DD zn$lHQG9(lh&sDii)ip*{;Sb_-Anluh`=l~qhqbI+;=ZzpFrRp&T+UICO!OoqX@Xr_ z32iJ`xSpx=lDDB_IG}k+GTYG@K8{rhTS)aoN8D~Xfe?ul&;jv^E;w$nhu-ICs&Q)% zZ=~kPNZP0-A$pB8)!`TEqE`tY3Mx^`%O`?EDiWsZpoP`e-iQ#E>fIyUx8XN0L z@S-NQwc;0HjSZKWDL}Au_Zkbh!juuB&mGL0=nO5)tUd_4scpPy&O7SNS^aRxUy0^< zX}j*jPrLP4Pa0|PL+nrbd4G;YCxCK-=G7TG?dby~``AIHwxqFu^OJhyIUJkO0O<>_ zcpvg5Fk$Wpj}YE3;GxRK67P_Z@1V#+pu>pRj0!mFf(m_WR3w3*oQy$s39~U7Cb}p(N&8SEwt+)@%o-kW9Ck=^?tvC2$b9% ze9(Jn+H`;uAJE|;$Flha?!*lJ0@lKfZM>B|c)3lIAHb;5OEOT(2453m!LgH2AX=jK zQ93An1-#l@I@mwB#pLc;M7=u6V5IgLl>E%gvE|}Hvd4-bE1>gs(P^C}gTv*&t>W#+ zASLRX$y^DD3Jrht zwyt`yuA1j(TcP*0p*Xkv>gh+YTLrcN_HuaRMso~0AJg`^nL#52dGBzY+_7i)Ud#X) zVwg;6$WV20U2uyKt8<)jN#^1>PLg`I`@Mmut*Zy!c!zshSA!e^tWVoKJD%jN&ml#{ z@}B$j=U5J_#rc%T7(DGKF+WwIblEZ;Vq;CsG~OKxhWYGJx#g7fxb-_ya*D0=_Ys#f zhXktl=Vnw#Z_neW>Xe#EXT(4sT^3p6srKby4Ma5LLfh6XrHGFGgM;5Z}jv-T!f~=jT&n>Rk z4U0RT-#2fsYCQhwtW&wNp6T(im4dq>363H^ivz#>Sj;TEKY<)dOQU=g=XsLZhnR>e zd}@p1B;hMsL~QH2Wq>9Zb; zK`0`09fzuYg9MLJe~cdMS6oxoAD{kW3sFAqDxvFM#{GpP^NU@9$d5;w^WgLYknCTN z0)N425mjsJTI@#2kG-kB!({*+S(WZ-{SckG5^OiyP%(6DpRsx60$H8M$V65a_>oME z^T~>oG7r!ew>Y)&^MOBrgc-3PezgTZ2xIhXv%ExMFgSf5dQbD=Kj*!J4k^Xx!Z>AW ziZfvqJvtm|EXYsD%A|;>m1Md}j5f2>kt*gngL=enh<>#5iud0dS1P%u2o+>VQ{U%(nQ_WTySY(s#~~> zrTsvp{lTSup_7*Xq@qgjY@1#bisPCRMMHnOL48qi*jQ0xg~TSW%KMG9zN1(tjXix()2$N}}K$AJ@GUth+AyIhH6Aeh7qDgt#t*`iF5#A&g4+ zWr0$h9Zx6&Uo2!Ztcok($F>4NA<`dS&Js%L+67FT@WmI)z#fF~S75TUut%V($oUHw z$IJsL0X$KfGPZYjB9jaj-LaoDD$OMY4QxuQ&vOGo?-*9@O!Nj>QBSA6n$Lx|^ zky)4+sy{#6)FRqRt6nM9j2Lzba!U;aL%ZcG&ki1=3gFx6(&A3J-oo|S2_`*w9zT)W z4MBOVCp}?4nY)1))SOX#6Zu0fQQ7V{RJq{H)S#;sElY)S)lXTVyUXTepu4N)n85Xo zIpWPT&rgnw$D2Fsut#Xf-hO&6uA0n~a;a3!=_!Tq^TdGE&<*c?1b|PovU}3tfiIUu z){4W|@PY}zJOXkGviCw^x27%K_Fm9GuKVpd{P2>NJlnk^I|h2XW0IO~LTMj>2<;S* zZh2uRNSdJM$U$@=`zz}%;ucRx{aKVxxF7?0hdKh6&GxO6f`l2kFncS3xu0Ly{ew0& zeEP*#lk-8-B$LD(5yj>YFJ{yf5zb41PlW7S{D9zC4Aa4nVdkDNH{UsFJp)q-`9OYt zbOKkigbmm5hF?tttn;S4g^142AF^`kiLUC?e7=*JH%Qe>uW=dB24NQa`;lm5yL>Dyh@HbHy-f%6Vz^ zh&MgwYsh(z#_fhhqY$3*f>Ha}*^cU-r4uTHaT?)~LUj5``FcS46oyoI5F3ZRizVD% zPFY(_S&5GN8$Nl2=+YO6j4d|M6O7CmUyS&}m4LSn6}J`$M0ZzT&Ome)ZbJDFvM&}A zZdhDn(*viM-JHf84$!I(8eakl#zRjJH4qfw8=60 z11Ely^FyXjVvtv48-Fae7p=adlt9_F^j5#ZDf7)n!#j?{W?@j$Pi=k`>Ii>XxrJ?$ z^bhh|X6qC8d{NS4rX5P!%jXy=>(P+r9?W(2)|(=a^s^l~x*^$Enw$~u%WRuRHHFan{X|S;FD(Mr z@r@h^@Bs#C3G;~IJMrERd+D!o?HmFX&#i|~q(7QR3f8QDip?ms6|GV_$86aDb|5pc?_-jo6vmWqYi{P#?{m_AesA4xX zi&ki&lh0yvf*Yw~@jt|r-=zpj!bw<6zI3Aa^Wq{|*WEC}I=O!Re!l~&8|Vu<$yZ1p zs-SlwJD8K!$(WWyhZ+sOqa8cciwvyh%zd`r$u;;fsHn!hub0VU)bUv^QH?x30#;tH zTc_VbZj|prj7)d%ORU;Vs{#ERb>K8>GOLSImnF7JhR|g$7FQTU{(a7RHQ*ii-{U3X z^7+vM0R$8b3k1aSU&kxvVPfOz3~)0O2iTYinV9_5{pF18j4b{o`=@AZIOAwwedB2@ ztXI1F04mg{<>a-gdFoRjq$6#FaevDn$^06L)k%wYq03&ysdXE+LL1#w$rRS1Y;BoS zH1x}{ms>LHWmdtP(ydD!aRdAa(d@csEo z0EF9L>%tppp`CZ2)jVb8AuoYyu;d^wfje6^n6`A?6$&%$p>HcE_De-Zh)%3o5)LDa zskQ}%o7?bg$xUj|n8gN9YB)z!N&-K&!_hVQ?#SFj+MpQA4@4oq!UQ$Vm3B`W_Pq3J z=ngFP4h_y=`Iar<`EESF9){%YZVyJqLPGq07TP7&fSDmnYs2NZQKiR%>){imTBJth zPHr@p>8b+N@~%43rSeNuOz;rgEm?14hNtI|KC6Xz1d?|2J`QS#`OW7gTF_;TPPxu@ z)9J9>3Lx*bc>Ielg|F3cou$O0+<b34_*ZJhpS&$8DP>s%47a)4ZLw`|>s=P_J4u z?I_%AvR_z8of@UYWJV?~c4Yb|A!9n!LEUE6{sn@9+D=0w_-`szJ_T++x3MN$v-)0d zy`?1QG}C^KiNlnJBRZBLr4G~15V3$QqC%1G5b#CEB0VTr#z?Ug%Jyv@a`QqAYUV~^ zw)d|%0g&kl{j#FMdf$cn(~L@8s~6eQ)6{`ik(RI(o9s0g30Li{4YoxcVoYd+LpeLz zai?~r)UcbYr@lv*Z>E%BsvTNd`Sc?}*}>mzJ|cr0Y(6rA7H_6&t>F{{mJ^xovc2a@ zFGGDUcGgI-z6H#o@Gj29C=Uy{wv zQHY2`HZu8+sBQK*_~I-_>fOTKEAQ8_Q~YE$c?cSCxI;vs-JGO`RS464Ft06rpjn+a zqRS0Y3oN(9HCP@{J4mOWqIyD8PirA!pgU^Ne{LHBG;S*bZpx3|JyQDGO&(;Im8!ed zNdpE&?3U?E@O~>`@B;oY>#?gXEDl3pE@J30R1;?QNNxZ?YePc)3=NS>!STCrXu*lM z69WkLB_RBwb1^-zEm*tkcHz3H;?v z;q+x0Jg$|?5;e1-kbJnuT+^$bWnYc~1qnyVTKh*cvM+8yJT-HBs1X@cD;L$su65;i z2c1MxyL~NuZ9+)hF=^-#;dS#lFy^Idcb>AEDXu1!G4Kd8YPy~0lZz$2gbv?su}Zn} zGtIbeYz3X8OA9{sT(aleold_?UEV{hWRl(@)NH6GFH@$<8hUt=dNte%e#Jc>7u9xi zuqv!CRE@!fmZZ}3&@$D>p0z=*dfQ_=IE4bG0hLmT@OP>x$e`qaqf_=#baJ8XPtOpWi%$ep1Y)o2(sR=v)M zt(z*pGS$Z#j_xq_lnCr+x9fwiT?h{NEn#iK(o)G&Xw-#DK?=Ms6T;%&EE${Gq_%99 z6(;P~jPKq9llc+cmI(MKQ6*7PcL)BmoI}MYFO)b3-{j>9FhNdXLR<^mnMP`I7z0v` zj3wxcXAqi4Z0kpeSf>?V_+D}NULgU$DBvZ^=0G8Bypd7P2>;u`yW9`%4~&tzNJpgp zqB+iLIM~IkB;ts!)exn643mAJ8-WlgFE%Rpq!UMYtB?$5QAMm)%PT0$$2{>Yu7&U@ zh}gD^Qdgu){y3ANdB5{75P;lRxSJPSpQPMJOiwmpMdT|?=q;&$aTt|dl~kvS z+*i;6cEQJ1V`R4Fd>-Uzsc=DPQ7A7#VPCIf!R!KK%LM&G%MoZ0{-8&99H!|UW$Ejv zhDLX3ESS6CgWTm#1ZeS2HJb`=UM^gsQ84dQpX(ESWSkjn>O zVxg%`@mh(X9&&wN$lDIc*@>rf?C0AD_mge3f2KkT6kGySOhXqZjtA?5z`vKl_{(5g z&%Y~9p?_DL{+q@siT~*3Q*$nWXQfNN;%s_eHP_A;O`N`SaoB z6xYR;z_;HQ2xAa9xKgx~2f2xEKiEDpGPH1d@||v#f#_Ty6_gY>^oZ#xac?pc-F`@ z*}8sPV@xiz?efDMcmmezYVw~qw=vT;G1xh+xRVBkmN66!u(mRG3G6P#v|;w@anEh7 zCf94arw%YB*=&3=RTqX?z4mID$W*^+&d6qI*LA-yGme;F9+wTsNXNaX~zl2+qIK&D-aeN4lr0+yP;W>|Dh?ms_ogT{DT+ ztXFy*R7j4IX;w@@R9Oct5k2M%&j=c_rWvoul+` z<18FH5D@i$P38W9VU2(EnEvlJ(SHCqTNBa)brkIjGP|jCnK&Qi%97tikU}Y#3L?s! z2ujL%YiHO-#!|g5066V01hgT#>fzls7P>+%D~ogOT&!Whb4iF=CnCto82Yb#b`YoVsj zS2q^W0Rj!RrM@=_GuPQy5*_X@Zmu`TKSbqEOP@;Ga&Rrr>#H@L41@ZX)LAkbo{G8+ z;!5EH6vv-ip0`tLB)xUuOX(*YEDSWf?PIxXe`+_B8=KH#HFCfthu}QJylPMTNmoV; zC63g%?57(&osaH^sxCyI-+gwVB|Xs2TOf=mgUAq?V~N_5!4A=b{AXbDae+yABuuu3B_XSa4~c z1s-OW>!cIkjwJf4ZhvT|*IKaRTU)WAK=G|H#B5#NB9<{*kt?7`+G*-^<)7$Iup@Um z7u*ABkG3F*Foj)W9-I&@BrN8(#$7Hdi`BU#SR1Uz4rh&=Ey!b76Qo?RqBJ!U+rh(1 znw@xw5$)4D8OWtB_^pJO*d~2Mb-f~>I!U#*=Eh*xa6$LX?4Evp4%;ENQR!mF4`f7F zpG!NX=qnCwE8@NAbQV`*?!v0;NJ(| zBip8}VgFVsXFqslXUV>_Z>1gmD(7p#=WACXaB|Y`=Kxa=p@_ALsL&yAJ`*QW^`2@% zW7~Yp(Q@ihmkf{vMF?kqkY%SwG^t&CtfRWZ{syK@W$#DzegcQ1>~r7foTw3^V1)f2Tq_5f$igmfch;8 zT-<)?RKcCdQh6x^mMEOS;4IpQ@F2q-4IC4%*dU@jfHR4UdG>Usw4;7ESpORL|2^#jd+@zxz{(|RV*1WKrw-)ln*8LnxVkKDfGDHA%7`HaiuvhMu%*mY9*Ya{Ti#{DW?i0 zXXsp+Bb(_~wv(3t70QU3a$*<$1&zm1t++x#wDLCRI4K)kU?Vm9n2c0m@TyUV&&l9%}fulj!Z9)&@yIcQ3gX}l0b1LbIh4S z5C*IDrYxR%qm4LVzSk{0;*npO_SocYWbkAjA6(^IAwUnoAzw_Uo}xYFo?Y<-4Zqec z&k7HtVlFGyt_pA&kX%P8PaRD8y!Wsnv}NMLNLy-CHZf(ObmzV|t-iC#@Z9*d-zUsx zxcYWw{H)nYXVdnJu5o-U+fn~W z-$h1ax>h{NlWLA7;;6TcQHA>UJB$KNk74T1xNWh9)kwK~wX0m|Jo_Z;g;>^E4-k4R zRj#pQb-Hg&dAh}*=2;JY*aiNZzT=IU&v|lQY%Q|=^V5pvTR7^t9+@+ST&sr!J1Y9a z514dYZn5rg6@4Cy6P`-?!3Y& z?B*5zw!mTiD2)>f@3XYrW^9V-@%YFkE_;PCyCJ7*?_3cR%tHng9%ZpIU}LJM=a+0s z(SDDLvcVa~b9O!cVL8)Q{d^R^(bbG=Ia$)dVN_tGMee3PMssZ7Z;c^Vg_1CjZYTnq z)wnF8?=-MmqVOMX!iE?YDvHCN?%TQtKJMFHp$~kX4}jZ;EDqP$?jqJZjoa2PM@$uZ zF4}iab1b5ep)L;jdegC3{K4VnCH#OV;pRcSa(&Nm50ze-yZ8*cGv;@+N+A?ncc^2z9~|(xFhwOHmPW@ zR5&)E^YKQj@`g=;zJ_+CLamsPuvppUr$G1#9urUj+p-mPW_QSSHkPMS!52t>Hqy|g z_@Yu3z%|wE=uYq8G>4`Q!4zivS}+}{m5Zjr7kMRGn_p&hNf|pc&f9iQ`^%78rl#~8 z;os@rpMA{ZioY~(Rm!Wf#Wx##A0PthOI341QiJ=G*#}pDAkDm+{0kz&*NB?rC0-)glB{0_Tq*^o zVS1>3REsv*Qb;qg!G^9;VoK)P*?f<*H&4Su1=}bP^Y<2PwFpoqw#up4IgX3L z`w~8jsFCI3k~Y9g(Y9Km`y$0FS5vHb)kb)Jb6q-9MbO{Hbb zxg?IWQ1ZIGgE}wKm{axO6CCh~4DyoFU+i1xn#oyfe+<{>=^B5tm!!*1M?AW8c=6g+%2Ft97_Hq&ZmOGvqGQ!Bn<_Vw`0DRuDoB6q8ME<;oL4kocr8E$NGoLI zXWmI7Af-DR|KJw!vKp2SI4W*x%A%5BgDu%8%Iato+pWo5`vH@!XqC!yK}KLzvfS(q z{!y(S-PKbk!qHsgVyxKsQWk_8HUSSmslUA9nWOjkKn0%cwn%yxnkfxn?Y2rysXKS=t-TeI%DN$sQ{lcD!(s>(4y#CSxZ4R} zFDI^HPC_l?uh_)-^ppeYRkPTPu~V^0Mt}#jrTL1Q(M;qVt4zb(L|J~sxx7Lva9`mh zz!#A9tA*6?q)xThc7(gB2Ryam$YG4qlh00c}r&$y6u zIN#Qxn{7RKJ+_r|1G1KEv!&uKfXpOVZ8tK{M775ws%nDyoZ?bi3NufNbZs)zqXiqc zqOsK@^OnlFMAT&mO3`@3nZP$3lLF;ds|;Z{W(Q-STa2>;)tjhR17OD|G>Q#zJHb*> zMO<{WIgB%_4MG0SQi2;%f0J8l_FH)Lfaa>*GLobD#AeMttYh4Yfg22@q4|Itq};NB z8;o*+@APqy@fPgrc&PTbGEwdEK=(x5K!If@R$NiO^7{#j9{~w=RBG)ZkbOw@$7Nhl zyp{*&QoVBd5lo{iwl2gfyip@}IirZK;ia(&ozNl!-EEYc=QpYH_= zJkv7gA{!n4up6$CrzDJIBAdC7D5D<_VLH*;OYN>_Dx3AT`K4Wyx8Tm{I+xplKP6k7 z2sb!i7)~%R#J0$|hK?~=u~rnH7HCUpsQJujDDE*GD`qrWWog+C+E~GGy|Hp_t4--} zrxtrgnPh}r=9o}P6jpAQuDN}I*GI`8&%Lp-C0IOJt#op)}XSr!ova@w{jG2V=?GXl3zEJJFXg)U3N>BQP z*Lb@%Mx|Tu;|u>$-K(q^-HG!EQ3o93%w(A7@ngGU)HRWoO&&^}U$5x+T&#zri>6ct zXOB#EF-;z3j311K`jrYyv6pOPF=*`SOz!ack=DuEi({UnAkL5H)@R?YbRKAeP|06U z?-Ns0ZxD0h9D8)P66Sq$w-yF+1hEVTaul%&=kKDrQtF<$RnQPZ)ezm1`aHIjAY=!S z`%vboP`?7mItgEo4w50C*}Ycqp9_3ZEr^F1;cEhkb`BNhbc6PvnXu@wi=AoezF4~K zkxx%ps<8zb=wJ+9I8o#do)&{(=yAlNdduaDn!=xGSiuo~fLw~Edw$6;l-qaq#Z7?# zGrdU(Cf-V@$x>O%yRc6!C1Vf`b19ly;=mEu8u9|zitcG^O`lbNh}k=$%a)UHhDwTEKis2yc4rBGR>l*(B$AC7ung&ssaZGkY-h(fpwcPyJSx*9EIJMRKbMP9}$nVrh6$g-Q^5Cw)BeWqb-qi#37ZXKL!GR;ql)~ z@PP*-oP?T|ThqlGKR84zi^CN z4TZ1A)7vL>ivoL2EU_~xl-P{p+sE}9CRwGJDKy{>0KP+gj`H9C+4fUMPnIB1_D`A- z$1`G}g0lQmqMN{Y&8R*$xYUB*V}dQPxGVZQ+rH!DVohIoTbh%#z#Tru%Px@C<=|og zGDDwGq7yz`%^?r~6t&>x*^We^tZ4!E4dhwsht#Pb1kCY{q#Kv;z%Dp#Dq;$vH$-(9 z8S5tutZ}&JM2Iw&Y-7KY4h5BBvS=Ove0#+H2qPdR)WyI zYcj)vB=MA{7T|3Ij_PN@FM@w(C9ANBq&|NoW30ccr~i#)EcH)T^3St~rJ0HKKd4wr z@_+132;Bj+>UC@h)Ap*8B4r5A1lZ!Dh%H7&&hBnlFj@eayk=VD*i5AQc z$uN8YG#PL;cuQa)Hyt-}R?&NAE1QT>svJDKt*)AQOZAJ@ zyxJoBebiobHeFlcLwu_iI&NEZuipnOR;Tn;PbT1Mt-#5v5b*8ULo7m)L-eti=UcGf zRZXidmxeFgY!y80-*PH-*=(-W+fK%KyUKpg$X@tuv``tXj^*4qq@UkW$ZrAo%+hay zU@a?z&2_@y)o@D!_g>NVxFBO!EyB&6Z!nd4=KyDP^hl!*(k{dEF6@NkXztO7gIh zQ&PC+p-8WBv;N(rpfKdF^@Z~|E6pa)M1NBUrCZvLRW$%N%xIbv^uv?=C!=dDVq3%* zgvbEBnG*JB*@vXx8>)7XL*!{1Jh=#2UrByF7U?Rj_}VYw88BwqefT_cCTv8aTrRVjnn z1HNCF=44?*&gs2`vCGJVHX@kO z240eo#z+FhI0=yy6NHQwZs}a+J~4U-6X`@ zZ7j+tb##m`x%J66$a9qXDHG&^kp|GkFFMmjD(Y-k_ClY~N$H|n@NkSDz=gg?*2ga5 z)+f)MEY>2Lp15;~o`t`qj;S>BaE;%dv@Ux11yq}I(k|o&`5UZFUHn}1kE^gIK@qV& z!S2IhyU;->VfA4Qb}m7YnkIa9%z{l~iPWo2YPk-`hy2-Eg=6E$21plQA5W2qMZDFU z-a-@Dndf%#on6chT`dOKnU9}BJo|kJwgGC<^nfo34zOKH96LbWY7@Wc%EoFF=}`VU zksP@wd%@W;-p!e^&-)N7#oR331Q)@9cx=mOoU?_Kih2!Le*8fhsZ8Qvo6t2vt+UOZ zw|mCB*t2%z21YqL>whu!j?s~}-L`OS+jdg1(XnmYw$rg~r(?5Y+qTg`$F}q3J?GtL z@BN&8#`u2RqkdG4yGGTus@7U_%{6C{XAhFE!2SelH?KtMtX@B1GBhEIDL-Bj#~{4! zd}p7!#XE9Lt;sy@p5#Wj*jf8zGv6tTotCR2X$EVOOup;GnRPRVU5A6N@Lh8?eA7k? zn~hz&gY;B0ybSpF?qwQ|sv_yO=8}zeg2$0n3A8KpE@q26)?707pPw?H76lCpjp=5r z6jjp|auXJDnW}uLb6d7rsxekbET9(=zdTqC8(F5@NNqII2+~yB;X5iJNQSiv`#ozm zf&p!;>8xAlwoxUC3DQ#!31ylK%VrcwS<$WeCY4V63V!|221oj+5#r}fGFQ}|uwC0) zNl8(CF}PD`&Sj+p{d!B&&JtC+VuH z#>US`)YQrhb6lIAYb08H22y(?)&L8MIQsA{26X`R5Km{YU)s!x(&gIsjDvq63@X`{ z=7{SiH*_ZsPME#t2m|bS76Uz*z{cpp1m|s}HIX}Ntx#v7Eo!1%G9__4dGSGl`p+xi zZ!VK#Qe;Re=9bqXuW+0DSP{uZ5-QXrNn-7qW19K0qU}OhVru7}3vqsG?#D67 zb}crN;QwsH*vymw(maZr_o|w&@sQki(X+D)gc5Bt&@iXisFG;eH@5d43~Wxq|HO(@ zV-rip4n#PEkHCWCa5d?@cQp^B;I-PzOfag|t-cuvTapQ@MWLmh*41NH`<+A+JGyKX zyYL6Ba7qqa5j@3lOk~`OMO7f0!@FaOeZxkbG@vXP(t3#U*fq8=GAPqUAS>vW2uxMk{a(<0=IxB;# zMW;M+owrHaZBp`3{e@7gJCHP!I(EeyGFF;pdFPdeP+KphrulPSVidmg#!@W`GpD&d z9p6R`dpjaR2E1Eg)Ws{BVCBU9-aCgN57N~uLvQZH`@T+2eOBD%73rr&sV~m#2~IZx zY_8f8O;XLu2~E3JDXnGhFvsyb^>*!D>5EtlKPe%kOLv6*@=Jpci`8h0z?+fbBUg_7 zu6DjqO=$SjAv{|Om5)nz41ZkS4E_|fk%NDY509VV5yNeo%O|sb>7C#wj8mL9cEOFh z>nDz%?vb!h*!0dHdnxDA>97~EoT~!N40>+)G2CeYdOvJr5^VnkGz)et&T9hrD(VAgCAJjQ7V$O?csICB*HFd^k@$M5*v$PZJD-OVL?Ze(U=XGqZPVG8JQ z<~ukO%&%nNXYaaRibq#B1KfW4+XMliC*Tng2G(T1VvP;2K~;b$EAqthc${gjn_P!b zs62UT(->A>!ot}cJXMZHuy)^qfqW~xO-In2);e>Ta{LD6VG2u&UT&a@>r-;4<)cJ9 zjpQThb4^CY)Ev0KR7TBuT#-v}W?Xzj{c7$S5_zJA57Qf=$4^npEjl9clH0=jWO8sX z3Fuu0@S!WY>0XX7arjH`?)I<%2|8HfL!~#c+&!ZVmhbh`wbzy0Ux|Jpy9A{_7GGB0 zadZ48dW0oUwUAHl%|E-Q{gA{z6TXsvU#Hj09<7i)d}wa+Iya)S$CVwG{4LqtB>w%S zKZx(QbV7J9pYt`W4+0~f{hoo5ZG<0O&&5L57oF%hc0xGJ@Zrg_D&lNO=-I^0y#3mxCSZFxN2-tN_mU@7<@PnWG?L5OSqkm8TR!`| zRcTeWH~0z1JY^%!N<(TtxSP5^G9*Vw1wub`tC-F`=U)&sJVfvmh#Pi`*44kSdG};1 zJbHOmy4Ot|%_?@$N?RA9fF?|CywR8Sf(SCN_luM8>(u0NSEbKUy7C(Sk&OuWffj)f za`+mo+kM_8OLuCUiA*CNE|?jra$M=$F3t+h-)?pXz&r^F!ck;r##`)i)t?AWq-9A9 zSY{m~TC1w>HdEaiR*%j)L);H{IULw)uxDO>#+WcBUe^HU)~L|9#0D<*Ld459xTyew zbh5vCg$a>`RCVk)#~ByCv@Ce!nm<#EW|9j><#jQ8JfTmK#~jJ&o0Fs9jz0Ux{svdM4__<1 zrb>H(qBO;v(pXPf5_?XDq!*3KW^4>(XTo=6O2MJdM^N4IIcYn1sZZpnmMAEdt}4SU zPO54j2d|(xJtQ9EX-YrlXU1}6*h{zjn`in-N!Ls}IJsG@X&lfycsoCemt_Ym(PXhv zc*QTnkNIV=Ia%tg%pwJtT^+`v8ng>;2~ps~wdqZSNI7+}-3r+#r6p`8*G;~bVFzg= z!S3&y)#iNSUF6z;%o)%h!ORhE?CUs%g(k2a-d576uOP2@QwG-6LT*G!I$JQLpd`cz z-2=Brr_+z96a0*aIhY2%0(Sz=|D`_v_7h%Yqbw2)8@1DwH4s*A82krEk{ zoa`LbCdS)R?egRWNeHV8KJG0Ypy!#}kslun?67}^+J&02!D??lN~t@;h?GS8#WX`)6yC**~5YNhN_Hj}YG<%2ao^bpD8RpgV|V|GQwlL27B zEuah|)%m1s8C6>FLY0DFe9Ob66fo&b8%iUN=y_Qj;t3WGlNqP9^d#75ftCPA*R4E8 z)SWKBKkEzTr4JqRMEs`)0;x8C35yRAV++n(Cm5++?WB@ya=l8pFL`N0ag`lWhrYo3 zJJ$< zQ*_YAqIGR*;`VzAEx1Pd4b3_oWtdcs7LU2#1#Ls>Ynvd8k^M{Ef?8`RxA3!Th-?ui{_WJvhzY4FiPxA?E4+NFmaC-Uh*a zeLKkkECqy>Qx&1xxEhh8SzMML=8VP}?b*sgT9ypBLF)Zh#w&JzP>ymrM?nnvt!@$2 zh>N$Q>mbPAC2kNd&ab;FkBJ}39s*TYY0=@e?N7GX>wqaM>P=Y12lciUmve_jMF0lY zBfI3U2{33vWo(DiSOc}!5##TDr|dgX1Uojq9!vW3$m#zM_83EGsP6&O`@v-PDdO3P z>#!BEbqpOXd5s?QNnN!p+92SHy{sdpePXHL{d@c6UilT<#~I!tH$S(~o}c#(j<2%! zQvm}MvAj-95Ekx3D4+|e%!?lO(F+DFw9bxb-}rsWQl)b44###eUg4N?N-P(sFH2hF z`{zu?LmAxn2=2wCE8?;%ZDi#Y;Fzp+RnY8fWlzVz_*PDO6?Je&aEmuS>=uCXgdP6r zoc_JB^TA~rU5*geh{G*gl%_HnISMS~^@{@KVC;(aL^ZA-De+1zwUSXgT>OY)W?d6~ z72znET0m`53q%AVUcGraYxIcAB?OZA8AT!uK8jU+=t;WneL~|IeQ>$*dWa#x%rB(+ z5?xEkZ&b{HsZ4Ju9TQ|)c_SIp`7r2qMJgaglfSBHhl)QO1aNtkGr0LUn{@mvAt=}nd7#>7ru}&I)FNsa*x?Oe3-4G`HcaR zJ}c%iKlwh`x)yX1vBB;-Nr=7>$~(u=AuPX2#&Eh~IeFw%afU+U)td0KC!pHd zyn+X$L|(H3uNit-bpn7%G%{&LsAaEfEsD?yM<;U2}WtD4KuVKuX=ec9X zIe*ibp1?$gPL7<0uj*vmj2lWKe`U(f9E{KVbr&q*RsO;O>K{i-7W)8KG5~~uS++56 zm@XGrX@x+lGEjDQJp~XCkEyJG5Y57omJhGN{^2z5lj-()PVR&wWnDk2M?n_TYR(gM zw4kQ|+i}3z6YZq8gVUN}KiYre^sL{ynS}o{z$s&I z{(rWaLXxcQ=MB(Cz7W$??Tn*$1y(7XX)tv;I-{7F$fPB%6YC7>-Dk#=Y8o1=&|>t5 zV_VVts>Eb@)&4%m}!K*WfLoLl|3FW)V~E1Z!yu`Sn+bAP5sRDyu7NEbLt?khAyz-ZyL-}MYb&nQ zU16f@q7E1rh!)d%f^tTHE3cVoa%Xs%rKFc|temN1sa)aSlT*)*4k?Z>b3NP(IRXfq zlB^#G6BDA1%t9^Nw1BD>lBV(0XW5c?l%vyB3)q*;Z5V~SU;HkN;1kA3Nx!$!9wti= zB8>n`gt;VlBt%5xmDxjfl0>`K$fTU-C6_Z;!A_liu0@Os5reMLNk;jrlVF^FbLETI zW+Z_5m|ozNBn7AaQ<&7zk}(jmEdCsPgmo%^GXo>YYt82n&7I-uQ%A;k{nS~VYGDTn zlr3}HbWQG6xu8+bFu^9%%^PYCbkLf=*J|hr>Sw+#l(Y#ZGKDufa#f-f0k-{-XOb4i zwVG1Oa0L2+&(u$S7TvedS<1m45*>a~5tuOZ;3x%!f``{=2QQlJk|b4>NpD4&L+xI+ z+}S(m3}|8|Vv(KYAGyZK5x*sgwOOJklN0jsq|BomM>OuRDVFf_?cMq%B*iQ*&|vS9 zVH7Kh)SjrCBv+FYAE=$0V&NIW=xP>d-s7@wM*sdfjVx6-Y@=~>rz%2L*rKp|*WXIz z*vR^4tV&7MQpS9%{9b*>E9d_ls|toL7J|;srnW{l-}1gP_Qr-bBHt=}PL@WlE|&KH zCUmDLZb%J$ZzNii-5VeygOM?K8e$EcK=z-hIk63o4y63^_*RdaitO^THC{boKstphXZ2Z+&3ToeLQUG(0Frs?b zCxB+65h7R$+LsbmL51Kc)pz_`YpGEzFEclzb=?FJ=>rJwgcp0QH-UuKRS1*yCHsO) z-8t?Zw|6t($Eh&4K+u$I7HqVJBOOFCRcmMMH};RX_b?;rnk`rz@vxT_&|6V@q0~Uk z9ax|!pA@Lwn8h7syrEtDluZ6G!;@=GL> zse#PRQrdDs=qa_v@{Wv(3YjYD0|qocDC;-F~&{oaTP?@pi$n z1L6SlmFU2~%)M^$@C(^cD!y)-2SeHo3t?u3JiN7UBa7E2 z;<+_A$V084@>&u)*C<4h7jw9joHuSpVsy8GZVT;(>lZ(RAr!;)bwM~o__Gm~exd`K zKEgh2)w?ReH&syI`~;Uo4`x4$&X+dYKI{e`dS~bQuS|p zA`P_{QLV3r$*~lb=9vR^H0AxK9_+dmHX}Y} zIV*#65%jRWem5Z($ji{!6ug$En4O*=^CiG=K zp4S?+xE|6!cn$A%XutqNEgUqYY3fw&N(Z6=@W6*bxdp~i_yz5VcgSj=lf-6X1Nz75 z^DabwZ4*70$$8NsEy@U^W67tcy7^lNbu;|kOLcJ40A%J#pZe0d#n zC{)}+p+?8*ftUlxJE*!%$`h~|KZSaCb=jpK3byAcuHk7wk@?YxkT1!|r({P*KY^`u z!hw#`5$JJZGt@nkBK_nwWA31_Q9UGvv9r-{NU<&7HHMQsq=sn@O?e~fwl20tnSBG* zO%4?Ew6`aX=I5lqmy&OkmtU}bH-+zvJ_CFy z_nw#!8Rap5Wcex#5}Ldtqhr_Z$}@jPuYljTosS1+WG+TxZ>dGeT)?ZP3#3>sf#KOG z0)s%{cEHBkS)019}-1A2kd*it>y65-C zh7J9zogM74?PU)0c0YavY7g~%j%yiWEGDb+;Ew5g5Gq@MpVFFBNOpu0x)>Yn>G6uo zKE%z1EhkG_N5$a8f6SRm(25iH#FMeaJ1^TBcBy<04ID47(1(D)q}g=_6#^V@yI?Y&@HUf z`;ojGDdsvRCoTmasXndENqfWkOw=#cV-9*QClpI03)FWcx(m5(P1DW+2-{Hr-`5M{v##Zu-i-9Cvt;V|n)1pR^y ztp3IXzHjYWqabuPqnCY9^^;adc!a%Z35VN~TzwAxq{NU&Kp35m?fw_^D{wzB}4FVXX5Zk@#={6jRh%wx|!eu@Xp;%x+{2;}!&J4X*_SvtkqE#KDIPPn@ z5BE$3uRlb>N<2A$g_cuRQM1T#5ra9u2x9pQuqF1l2#N{Q!jVJ<>HlLeVW|fN|#vqSnRr<0 zTVs=)7d`=EsJXkZLJgv~9JB&ay16xDG6v(J2eZy;U%a@EbAB-=C?PpA9@}?_Yfb&) zBpsih5m1U9Px<+2$TBJ@7s9HW>W){i&XKLZ_{1Wzh-o!l5_S+f$j^RNYo85}uVhN# zq}_mN-d=n{>fZD2Lx$Twd2)}X2ceasu91}n&BS+4U9=Y{aZCgV5# z?z_Hq-knIbgIpnkGzJz-NW*=p?3l(}y3(aPCW=A({g9CpjJfYuZ%#Tz81Y)al?!S~ z9AS5#&nzm*NF?2tCR#|D-EjBWifFR=da6hW^PHTl&km-WI9*F4o>5J{LBSieVk`KO z2(^9R(zC$@g|i3}`mK-qFZ33PD34jd_qOAFj29687wCUy>;(Hwo%Me&c=~)V$ua)V zsaM(aThQ3{TiM~;gTckp)LFvN?%TlO-;$y+YX4i`SU0hbm<})t0zZ!t1=wY&j#N>q zONEHIB^RW6D5N*cq6^+?T}$3m|L{Fe+L!rxJ=KRjlJS~|z-&CC{#CU8`}2|lo~)<| zk?Wi1;Cr;`?02-C_3^gD{|Ryhw!8i?yx5i0v5?p)9wZxSkwn z3C;pz25KR&7{|rc4H)V~y8%+6lX&KN&=^$Wqu+}}n{Y~K4XpI-#O?L=(2qncYNePX zTsB6_3`7q&e0K67=Kg7G=j#?r!j0S^w7;0?CJbB3_C4_8X*Q%F1%cmB{g%XE&|IA7 z(#?AeG{l)s_orNJp!$Q~qGrj*YnuKlV`nVdg4vkTNS~w$4d^Oc3(dxi(W5jq0e>x} z(GN1?u2%Sy;GA|B%Sk)ukr#v*UJU%(BE9X54!&KL9A^&rR%v zIdYt0&D59ggM}CKWyxGS@ z>T#})2Bk8sZMGJYFJtc>D#k0+Rrrs)2DG;(u(DB_v-sVg=GFMlSCx<&RL;BH}d6AG3VqP!JpC0Gv6f8d|+7YRC@g|=N=C2 zo>^0CE0*RW?W))S(N)}NKA)aSwsR{1*rs$(cZIs?nF9)G*bSr%%SZo^YQ|TSz={jX z4Z+(~v_>RH0(|IZ-_D_h@~p_i%k^XEi+CJVC~B zsPir zA0Jm2yIdo4`&I`hd%$Bv=Rq#-#bh{Mxb_{PN%trcf(#J3S1UKDfC1QjH2E;>wUf5= ze8tY9QSYx0J;$JUR-0ar6fuiQTCQP#P|WEq;Ez|*@d?JHu-(?*tTpGHC+=Q%H>&I> z*jC7%nJIy+HeoURWN%3X47UUusY2h7nckRxh8-)J61Zvn@j-uPA@99|y48pO)0XcW zX^d&kW^p7xsvdX?2QZ8cEUbMZ7`&n{%Bo*xgFr4&fd#tHOEboQos~xm8q&W;fqrj} z%KYnnE%R`=`+?lu-O+J9r@+$%YnqYq!SVs>xp;%Q8p^$wA~oynhnvIFp^)Z2CvcyC zIN-_3EUHW}1^VQ0;Oj>q?mkPx$Wj-i7QoXgQ!HyRh6Gj8p~gH22k&nmEqUR^)9qni{%uNeV{&0-H60C zibHZtbV=8=aX!xFvkO}T@lJ_4&ki$d+0ns3FXb+iP-VAVN`B7f-hO)jyh#4#_$XG%Txk6M<+q6D~ zi*UcgRBOoP$7P6RmaPZ2%MG}CMfs=>*~(b97V4+2qdwvwA@>U3QQAA$hiN9zi%Mq{ z*#fH57zUmi)GEefh7@`Uy7?@@=BL7cXbd{O9)*lJh*v!@ z-6}p9u0AreiGauxn7JBEa-2w&d=!*TLJ49`U@D7%2ppIh)ynMaAE2Q4dl@47cNu{9 z&3vT#pG$#%hrXzXsj=&Ss*0;W`Jo^mcy4*L8b^sSi;H{*`zW9xX2HAtQ*sO|x$c6UbRA(7*9=;D~(%wfo(Z6#s$S zuFk`dr%DfVX5KC|Af8@AIr8@OAVj=6iX!~8D_P>p7>s!Hj+X0_t}Y*T4L5V->A@Zx zcm1wN;TNq=h`5W&>z5cNA99U1lY6+!!u$ib|41VMcJk8`+kP{PEOUvc@2@fW(bh5pp6>C3T55@XlpsAd#vn~__3H;Dz2w=t9v&{v*)1m4)vX;4 zX4YAjM66?Z7kD@XX{e`f1t_ZvYyi*puSNhVPq%jeyBteaOHo7vOr8!qqp7wV;)%jtD5>}-a?xavZ;i|2P3~7c)vP2O#Fb`Y&Kce zQNr7%fr4#S)OOV-1piOf7NgQvR{lcvZ*SNbLMq(olrdDC6su;ubp5un!&oT=jVTC3uTw7|r;@&y*s)a<{J zkzG(PApmMCpMmuh6GkM_`AsBE@t~)EDcq1AJ~N@7bqyW_i!mtHGnVgBA`Dxi^P93i z5R;}AQ60wy=Q2GUnSwz+W6C^}qn`S-lY7=J(3#BlOK%pCl=|RVWhC|IDj1E#+|M{TV0vE;vMZLy7KpD1$Yk zi0!9%qy8>CyrcRK`juQ)I};r)5|_<<9x)32b3DT1M`>v^ld!yabX6@ihf`3ZVTgME zfy(l-ocFuZ(L&OM4=1N#Mrrm_<>1DZpoWTO70U8+x4r3BpqH6z@(4~sqv!A9_L}@7 z7o~;|?~s-b?ud&Wx6==9{4uTcS|0-p@dKi0y#tPm2`A!^o3fZ8Uidxq|uz2vxf;wr zM^%#9)h^R&T;}cxVI(XX7kKPEVb);AQO?cFT-ub=%lZPwxefymBk+!H!W(o(>I{jW z$h;xuNUr#^0ivvSB-YEbUqe$GLSGrU$B3q28&oA55l)ChKOrwiTyI~e*uN;^V@g-Dm4d|MK!ol8hoaSB%iOQ#i_@`EYK_9ZEjFZ8Ho7P^er z^2U6ZNQ{*hcEm?R-lK)pD_r(e=Jfe?5VkJ$2~Oq^7YjE^5(6a6Il--j@6dBHx2Ulq z!%hz{d-S~i9Eo~WvQYDt7O7*G9CP#nrKE#DtIEbe_uxptcCSmYZMqT2F}7Kw0AWWC zPjwo0IYZ6klc(h9uL|NY$;{SGm4R8Bt^^q{e#foMxfCSY^-c&IVPl|A_ru!ebwR#7 z3<4+nZL(mEsU}O9e`^XB4^*m)73hd04HH%6ok^!;4|JAENnEr~%s6W~8KWD)3MD*+ zRc46yo<}8|!|yW-+KulE86aB_T4pDgL$XyiRW(OOcnP4|2;v!m2fB7Hw-IkY#wYfF zP4w;k-RInWr4fbz=X$J;z2E8pvAuy9kLJUSl8_USi;rW`kZGF?*Ur%%(t$^{Rg!=v zg;h3@!Q$eTa7S0#APEDHLvK%RCn^o0u!xC1Y0Jg!Baht*a4mmKHy~88md{YmN#x) zBOAp_i-z2h#V~*oO-9k(BizR^l#Vm%uSa^~3337d;f=AhVp?heJ)nlZGm`}D(U^2w z#vC}o1g1h?RAV^90N|Jd@M00PoNUPyA?@HeX0P7`TKSA=*4s@R;Ulo4Ih{W^CD{c8 ze(ipN{CAXP(KHJ7UvpOc@9SUAS^wKo3h-}BDZu}-qjdNlVtp^Z{|CxKOEo?tB}-4; zEXyDzGbXttJ3V$lLo-D?HYwZm7vvwdRo}P#KVF>F|M&eJ44n*ZO~0)#0e0Vy&j00I z{%IrnUvKp70P?>~J^$^0Wo%>le>re2ZSvRfes@dC-*e=DD1-j%<$^~4^4>Id5w^Fr z{RWL>EbUCcyC%1980kOYqZAcgdz5cS8c^7%vvrc@CSPIx;X=RuodO2dxk17|am?HJ@d~Mp_l8H?T;5l0&WGFoTKM{eP!L-a0O8?w zgBPhY78tqf^+xv4#OK2I#0L-cSbEUWH2z+sDur85*!hjEhFfD!i0Eyr-RRLFEm5(n z-RV6Zf_qMxN5S6#8fr9vDL01PxzHr7wgOn%0Htmvk9*gP^Um=n^+7GLs#GmU&a#U^4jr)BkIubQO7oUG!4CneO2Ixa`e~+Jp9m{l6apL8SOqA^ zvrfEUPwnHQ8;yBt!&(hAwASmL?Axitiqvx%KZRRP?tj2521wyxN3ZD9buj4e;2y6U zw=TKh$4%tt(eh|y#*{flUJ5t4VyP*@3af`hyY^YU3LCE3Z|22iRK7M7E;1SZVHbXF zKVw!L?2bS|kl7rN4(*4h2qxyLjWG0vR@`M~QFPsf^KParmCX;Gh4OX6Uy9#4e_%oK zv1DRnfvd$pu(kUoV(MmAc09ckDiuqS$a%!AQ1Z>@DM#}-yAP$l`oV`BDYpkqpk(I|+qk!yoo$TwWr6dRzLy(c zi+qbVlYGz0XUq@;Fm3r~_p%by)S&SVWS+wS0rC9bk^3K^_@6N5|2rtF)wI>WJ=;Fz zn8$h<|Dr%kN|nciMwJAv;_%3XG9sDnO@i&pKVNEfziH_gxKy{l zo`2m4rnUT(qenuq9B0<#Iy(RPxP8R)=5~9wBku=%&EBoZ82x1GlV<>R=hIqf0PK!V zw?{z9e^B`bGyg2nH!^x}06oE%J_JLk)^QyHLipoCs2MWIqc>vaxsJj(=gg1ZSa=u{ zt}od#V;e7sA4S(V9^<^TZ#InyVBFT(V#$fvI7Q+pgsr_2X`N~8)IOZtX}e(Bn(;eF zsNj#qOF_bHl$nw5!ULY{lNx@93Fj}%R@lewUuJ*X*1$K`DNAFpE z7_lPE+!}uZ6c?+6NY1!QREg#iFy=Z!OEW}CXBd~wW|r_9%zkUPR0A3m+@Nk%4p>)F zXVut7$aOZ6`w}%+WV$te6-IX7g2yms@aLygaTlIv3=Jl#Nr}nN zp|vH-3L03#%-1-!mY`1z?+K1E>8K09G~JcxfS)%DZbteGQnQhaCGE2Y<{ut#(k-DL zh&5PLpi9x3$HM82dS!M?(Z zEsqW?dx-K_GMQu5K54pYJD=5+Rn&@bGjB?3$xgYl-|`FElp}?zP&RAd<522c$Rv6} zcM%rYClU%JB#GuS>FNb{P2q*oHy}UcQ-pZ2UlT~zXt5*k-ZalE(`p7<`0n7i(r2k{ zb84&^LA7+aW1Gx5!wK!xTbw0slM?6-i32CaOcLC2B>ZRI16d{&-$QBEu1fKF0dVU>GTP05x2>Tmdy`75Qx! z^IG;HB9V1-D5&&)zjJ&~G}VU1-x7EUlT3QgNT<&eIDUPYey$M|RD6%mVkoDe|;2`8Z+_{0&scCq>Mh3hj|E*|W3;y@{$qhu77D)QJ` znD9C1AHCKSAHQqdWBiP`-cAjq7`V%~JFES1=i-s5h6xVT<50kiAH_dn0KQB4t*=ua zz}F@mcKjhB;^7ka@WbSJFZRPeYI&JFkpJ-!B z!ju#!6IzJ;D@$Qhvz9IGY5!%TD&(db3<*sCpZ?U#1^9RWQ zs*O-)j!E85SMKtoZzE^8{w%E0R0b2lwwSJ%@E}Lou)iLmPQyO=eirG8h#o&E4~eew z;h><=|4m0$`ANTOixHQOGpksXlF0yy17E&JksB4_(vKR5s$Ve+i;gco2}^RRJI+~R zWJ82WGigLIUwP!uSELh3AAs9HmY-kz=_EL-w|9}noKE#(a;QBpEx9 z4BT-zY=6dJT>72Hkz=9J1E=}*MC;zzzUWb@x(Ho8cU_aRZ?fxse5_Ru2YOvcr?kg&pt@v;{ai7G--k$LQtoYj+Wjk+nnZty;XzANsrhoH#7=xVqfPIW(p zX5{YF+5=k4_LBnhLUZxX*O?29olfPS?u*ybhM_y z*XHUqM6OLB#lyTB`v<BZ&YRs$N)S@5Kn_b3;gjz6>fh@^j%y2-ya({>Hd@kv{CZZ2e)tva7gxLLp z`HoGW);eRtov~Ro5tetU2y72~ zQh>D`@dt@s^csdfN-*U&o*)i3c4oBufCa0e|BwT2y%Y~=U7A^ny}tx zHwA>Wm|!SCko~UN?hporyQHRUWl3djIc722EKbTIXQ6>>iC!x+cq^sUxVSj~u)dsY zW8QgfZlE*2Os%=K;_vy3wx{0u!2%A)qEG-$R^`($%AOfnA^LpkB_}Dd7AymC)zSQr z>C&N8V57)aeX8ap!|7vWaK6=-3~ko9meugAlBKYGOjc#36+KJwQKRNa_`W@7;a>ot zdRiJkz?+QgC$b}-Owzuaw3zBVLEugOp6UeMHAKo2$m4w zpw?i%Lft^UtuLI}wd4(-9Z^*lVoa}11~+0|Hs6zAgJ01`dEA&^>Ai=mr0nC%eBd_B zzgv2G_~1c1wr*q@QqVW*Wi1zn=}KCtSwLjwT>ndXE_Xa22HHL_xCDhkM( zhbw+j4uZM|r&3h=Z#YrxGo}GX`)AZyv@7#7+nd-D?BZV>thtc|3jt30j$9{aIw9)v zDY)*fsSLPQTNa&>UL^RWH(vpNXT7HBv@9=*=(Q?3#H*crA2>KYx7Ab?-(HU~a275)MBp~`P)hhzSsbj|d`aBe(L*(;zif{iFJu**ZR zkL-tPyh!#*r-JVQJq>5b0?cCy!uSKef+R=$s3iA7*k*_l&*e!$F zYwGI;=S^0)b`mP8&Ry@{R(dPfykD&?H)na^ihVS7KXkxb36TbGm%X1!QSmbV9^#>A z-%X>wljnTMU0#d;tpw?O1W@{X-k*>aOImeG z#N^x?ehaaQd}ReQykp>i;92q@%$a!y1PNyPYDIvMm& zyYVwn;+0({W@3h(r&i#FuCDE)AC(y&Vu>4?1@j0|CWnhHUx4|zL7cdaA32RSk?wl% zMK^n42@i5AU>f70(huWfOwaucbaToxj%+)7hnG^CjH|O`A}+GHZyQ-X57(WuiyRXV zPf>0N3GJ<2Myg!sE4XJY?Z7@K3ZgHy8f7CS5ton0Eq)Cp`iLROAglnsiEXpnI+S8; zZn>g2VqLxi^p8#F#Laf3<00AcT}Qh&kQnd^28u!9l1m^`lfh9+5$VNv=?(~Gl2wAl zx(w$Z2!_oESg_3Kk0hUsBJ<;OTPyL(?z6xj6LG5|Ic4II*P+_=ac7KRJZ`(k2R$L# zv|oWM@116K7r3^EL*j2ktjEEOY9c!IhnyqD&oy7+645^+@z5Y|;0+dyR2X6^%7GD* zXrbPqTO}O={ z4cGaI#DdpP;5u?lcNb($V`l>H7k7otl_jQFu1hh>=(?CTPN#IPO%O_rlVX}_Nq;L< z@YNiY>-W~&E@=EC5%o_z<^3YEw)i_c|NXxHF{=7U7Ev&C`c^0Z4-LGKXu*Hkk&Av= zG&RAv{cR7o4${k~f{F~J48Ks&o(D@j-PQ2`LL@I~b=ifx3q!p6`d>~Y!<-^mMk3)e zhi1;(YLU5KH}zzZNhl^`0HT(r`5FfmDEzxa zk&J7WQ|!v~TyDWdXQ)!AN_Y%xM*!jv^`s)A`|F%;eGg27KYsrCE2H}7*r)zvum6B{ z$k5Har9pv!dcG%f|3hE(#hFH+12RZPycVi?2y`-9I7JHryMn3 z9Y8?==_(vOAJ7PnT<0&85`_jMD0#ipta~Q3M!q5H1D@Nj-YXI$W%OQplM(GWZ5Lpq z-He6ul|3<;ZQsqs!{Y7x`FV@pOQc4|N;)qgtRe(Uf?|YqZv^$k8On7DJ5>f2%M=TV zw~x}9o=mh$JVF{v4H5Su1pq66+mhTG6?F>Do}x{V(TgFwuLfvNP^ijkrp5#s4UT!~ zEU7pr8aA)2z1zb|X9IpmJykQcqI#(rS|A4&=TtWu@g^;JCN`2kL}%+K!KlgC z>P)v+uCeI{1KZpewf>C=?N7%1e10Y3pQCZST1GT5fVyB1`q)JqCLXM zSN0qlreH1=%Zg-5`(dlfSHI&2?^SQdbEE&W4#%Eve2-EnX>NfboD<2l((>>34lE%) zS6PWibEvuBG7)KQo_`?KHSPk+2P;`}#xEs}0!;yPaTrR#j(2H|#-CbVnTt_?9aG`o z(4IPU*n>`cw2V~HM#O`Z^bv|cK|K};buJ|#{reT8R)f+P2<3$0YGh!lqx3&a_wi2Q zN^U|U$w4NP!Z>5|O)>$GjS5wqL3T8jTn%Vfg3_KnyUM{M`?bm)9oqZP&1w1)o=@+(5eUF@=P~ zk2B5AKxQ96n-6lyjh&xD!gHCzD$}OOdKQQk7LXS-fk2uy#h{ktqDo{o&>O!6%B|)` zg?|JgcH{P*5SoE3(}QyGc=@hqlB5w;bnmF#pL4iH`TSuft$dE5j^qP2S)?)@pjRQZ zBfo6g>c!|bN-Y|(Wah2o61Vd|OtXS?1`Fu&mFZ^yzUd4lgu7V|MRdGj3e#V`=mnk- zZ@LHn?@dDi=I^}R?}mZwduik!hC%=Hcl56u{Wrk1|1SxlgnzG&e7Vzh*wNM(6Y!~m z`cm8Ygc1$@z9u9=m5vs1(XXvH;q16fxyX4&e5dP-{!Kd555FD6G^sOXHyaCLka|8j zKKW^E>}>URx736WWNf?U6Dbd37Va3wQkiE;5F!quSnVKnmaIRl)b5rM_ICu4txs+w zj}nsd0I_VG^<%DMR8Zf}vh}kk;heOQTbl ziEoE;9@FBIfR7OO9y4Pwyz02OeA$n)mESpj zdd=xPwA`nO06uGGsXr4n>Cjot7m^~2X~V4yH&- zv2llS{|und45}Pm1-_W@)a-`vFBpD~>eVP(-rVHIIA|HD@%7>k8JPI-O*<7X{L*Ik zh^K`aEN!BteiRaY82FVo6<^8_22=aDIa8P&2A3V<(BQ;;x8Zs-1WuLRWjQvKv1rd2 zt%+fZ!L|ISVKT?$3iCK#7whp|1ivz1rV*R>yc5dS3kIKy_0`)n*%bfNyw%e7Uo}Mnnf>QwDgeH$X5eg_)!pI4EJjh6?kkG2oc6Af0py z(txE}$ukD|Zn=c+R`Oq;m~CSY{ebu9?!is}01sOK_mB?{lSY33E=!KkKtMeI*FO2b z%95awv9;Z|UDp3xm+aP*5I!R-_M2;GxeCRx3ATS0iF<_Do2Mi)Hk2 zjBF35VB>(oamIYjunu?g0O-?LuOvtfs5F(iiIicbu$HMPPF%F>pE@hIRjzT)>aa=m zwe;H9&+2|S!m74!E3xfO{l3E_ab`Q^tZ4yH9=~o2DUEtEMDqG=&D*8!>?2uao%w`&)THr z^>=L3HJquY>6)>dW4pCWbzrIB+>rdr{s}}cL_?#!sOPztRwPm1B=!jP7lQG|Iy6rP zVqZDNA;xaUx&xUt?Ox|;`9?oz`C0#}mc<1Urs#vTW4wd{1_r`eX=BeSV z_9WV*9mz>PH6b^z{VYQJ1nSTSqOFHE9u>cY)m`Q>=w1NzUShxcHsAxasnF2BG;NQ; zqL1tjLjImz_`q=|bAOr_i5_NEijqYZ^;d5y3ZFj6kCYakJh**N_wbfH;ICXq?-p#r z{{ljNDPSytOaG#7=yPmA&5gyYI%^7pLnMOw-RK}#*dk=@usL;|4US?{@K%7esmc&n z5$D*+l&C9)Bo@$d;Nwipd!68&+NnOj^<~vRcKLX>e03E|;to;$ndgR;9~&S-ly5gf z{rzj+j-g$;O|u?;wwxrEpD=8iFzUHQfl{B>bLHqH(9P zI59SS2PEBE;{zJUlcmf(T4DrcO?XRWR}?fekN<($1&AJTRDyW+D*2(Gyi?Qx-i}gy z&BpIO!NeVdLReO!YgdUfnT}7?5Z#~t5rMWqG+$N2n%5o#Np6ccNly}#IZQsW4?|NV zR9hrcyP(l#A+U4XcQvT;4{#i)dU>HK>aS!k1<3s2LyAhm2(!Nu%vRC9T`_yn9D+r} z1i&U~IcQ?4xhZYyH6WL-f%}qIhZkc&}n2N0PM| z6|XA9d-y;!`D{p;xu*gv7a|zaZ*MiQ)}zPzW4GB0mr)}N-DmB&hl1&x`2@sxN572_ zS)RdJyR%<7kW0v3Q_|57JKy&9tUdbqz}|hwn84}U*0r^jt6Ssrp+#1y=JBcZ+F`f(N?O0XL1OFGN`1-r?S<#t4*C9|y~e)!UYZ zRQ3M8m%~M)VriIvn~XzoP;5qeu(ZI>Y#r zAd)J)G9)*BeE%gmm&M@Olg3DI_zokjh9NvdGbT z+u4(Y&uC6tBBefIg~e=J#8i1Zxr>RT)#rGaB2C71usdsT=}mm`<#WY^6V{L*J6v&l z1^Tkr6-+^PA)yC;s1O^3Q!)Reb=fxs)P~I*?i&j{Vbb(Juc?La;cA5(H7#FKIj0Or zgV0BO{DUs`I9HgQ{-!g@5P^Vr|C4}~w6b=#`Zx0XcVSd?(04HUHwK(gJNafgQNB9Z zCi3TgNXAeJ+x|X|b@27$RxuYYuNSUBqo#uyiH6H(b~K*#!@g__4i%HP5wb<+Q7GSb zTZjJw96htUaGZ89$K_iBo4xEOJ#DT#KRu9ozu!GH0cqR>hP$nk=KXM%Y!(%vWQ#}s zy=O#BZ>xjUejMH^F39Bf0}>D}yiAh^toa-ts#gt6Mk9h1D<9_mGMBhLT0Ce2O3d_U znaTkBaxd-8XgwSp5)x-pqX5=+{cSuk6kyl@k|5DQ!5zLUVV%1X9vjY0gerbuG6nwZu5KDMdq(&UMLZ zy?jW#F6joUtVyz`Y?-#Yc0=i*htOFwQ3`hk$8oq35D}0m$FAOp#UFTV3|U3F>@N?d zeXLZCZjRC($%?dz(41e~)CN10qjh^1CdAcY(<=GMGk@`b1ptA&L*{L@_M{%Vd5b*x#b1(qh=7((<_l%ZUaHtmgq} zjchBdiis{Afxf@3CjPR09E*2#X(`W#-n`~6PcbaL_(^3tfDLk?Nb6CkW9v!v#&pWJ3iV-9hz zngp#Q`w`r~2wt&cQ9#S7z0CA^>Mzm7fpt72g<0y-KT{G~l-@L#edmjZQ}7{*$mLgSdJfS$Ge{hrD=mr;GD)uYq8}xS zT>(w_;}894Kb}(P5~FOpFIEjadhmxD(PsZbKwa-qxVa7Oc7~ebPKMeN(pCRzq8s@l z`|l^*X1eK1+Spz--WkSW_nK`Cs@JmkY4+p=U91nJoy{tSH;TzuIyS)Q_(S@;Iakua zpuDo5W54Mo;jY@Ly1dY)j|+M%$FJ0`C=FW#%UvOd&?p}0QqL20Xt!#pr8ujy6CA-2 zFz6Ex5H1i)c9&HUNwG{8K%FRK7HL$RJwvGakleLLo}tsb>t_nBCIuABNo$G--_j!gV&t8L^4N6wC|aLC)l&w04CD6Vc#h^(YH@Zs4nwUGkhc_-yt{dK zMZ<%$swLmUl8`E~RLihGt@J5v;r;vT&*Q!Cx zZ55-zpb;W7_Q{tf$mQvF61(K>kwTq0x{#Din||)B{+6O#ArLi)kiHWVC4`fOT&B(h zw&YV`J1|^FLx~9Q%r-SFhYl4PywI7sF2Q$>4o50~dfp5nn}XHv-_DM?RGs#+4gM;% znU>k=81G~f6u%^Z{bcX&sUv*h|L+|mNq=W43y@{~C zpL-TW3hYPs0^*OqS#KQwA^CGG_A-6#`_{1LBCD&*3nY0UHWJj1D|VP%oQlFxLllaA zVI@2^)HZ%E*=RbQcFOKIP7?+|_xVK+2oG(t_EGl2y;Ovox zZb^qVpe!4^reKvpIBFzx;Ji=PmrV>uu-Hb>`s?k?YZQ?>av45>i(w0V!|n?AP|v5H zm`e&Tgli#lqGEt?=(?~fy<(%#nDU`O@}Vjib6^rfE2xn;qgU6{u36j_+Km%v*2RLnGpsvS+THbZ>p(B zgb{QvqE?~50pkLP^0(`~K& zjT=2Pt2nSnwmnDFi2>;*C|OM1dY|CAZ5R|%SAuU|5KkjRM!LW_)LC*A zf{f>XaD+;rl6Y>Umr>M8y>lF+=nSxZX_-Z7lkTXyuZ(O6?UHw^q; z&$Zsm4U~}KLWz8>_{p*WQ!OgxT1JC&B&>|+LE3Z2mFNTUho<0u?@r^d=2 z-av!n8r#5M|F%l;=D=S1mGLjgFsiYAOODAR}#e^a8 zfVt$k=_o}kt3PTz?EpLkt54dY}kyd$rU zVqc9SN>0c z753j-gdN~UiW*FUDMOpYEkVzP)}{Ds*3_)ZBi)4v26MQr140|QRqhFoP=a|;C{#KS zD^9b-9HM11W+cb1Y)HAuk<^GUUo(ut!5kILBzAe)Vaxwu4Up!7Ql*#DDu z>EB84&xSrh>0jT!*X81jJQq$CRHqNj29!V3FN9DCx)~bvZbLwSlo3l^zPb1sqBnp) zfZpo|amY^H*I==3#8D%x3>zh#_SBf?r2QrD(Y@El!wa;Ja6G9Y1947P*DC|{9~nO& z*vDnnU!8(cV%HevsraF%Y%2{Z>CL0?64eu9r^t#WjW4~3uw8d}WHzsV%oq-T)Y z0-c!FWX5j1{1##?{aTeCW2b$PEnwe;t`VPCm@sQ`+$$L2=3kBR%2XU1{_|__XJ$xt zibjY2QlDVs)RgHH*kl&+jn*JqquF)k_Ypibo00lcc<2RYqsi-G%}k0r(N97H7JEn7@E3ZTH0JK>d8)E~A-D z!B&z9zJw0Bi^fgQZI%LirYaBKnWBXgc`An*qvO^*$xymqKOp(+3}IsnVhu?YnN7qz zNJxDN-JWd7-vIiv2M9ih>x3gNVY%DzzY~dCnA}76IRl!`VM=6=TYQ=o&uuE8kHqZT zoUNod0v+s9D)7aLJ|hVqL0li1hg)%&MAciI(4YJ=%D4H$fGQ&Lu-?@>>@pEgC;ERrL= zI^cS&3q8fvEGTJZgZwL5j&jp%j9U^Of6pR{wA^u=tVt#yCQepXNIbynGnuWbsC_EE zRyMFq{5DK692-*kyGy~An>AdVR9u___fzmmJ4;^s0yAGgO^h{YFmqJ%ZJ_^0BgCET zE6(B*SzeZ4pAxear^B-YW<%BK->X&Cr`g9_;qH~pCle# zdY|UB5cS<}DFRMO;&czbmV(?vzikf)Ks`d$LL801@HTP5@r><}$xp}+Ip`u_AZ~!K zT}{+R9Wkj}DtC=4QIqJok5(~0Ll&_6PPVQ`hZ+2iX1H{YjI8axG_Bw#QJy`6T>1Nn z%u^l`>XJ{^vX`L0 z1%w-ie!dE|!SP<>#c%ma9)8K4gm=!inHn2U+GR+~ zqZVoa!#aS0SP(|**WfQSe?cA=1|Jwk`UDsny%_y{@AV??N>xWekf>_IZLUEK3{Ksi zWWW$if&Go~@Oz)`#=6t_bNtD$d9FMBN#&97+XKa+K2C@I9xWgTE{?Xnhc9_KKPcujj@NprM@e|KtV_SR+ zSpeJ!1FGJ=Te6={;;+;a46-*DW*FjTnBfeuzI_=I1yk8M(}IwEIGWV0Y~wia;}^dg z{BK#G7^J`SE10z4(_Me=kF&4ld*}wpNs91%2Ute>Om`byv9qgK4VfwPj$`axsiZ)wxS4k4KTLb-d~!7I@^Jq`>?TrixHk|9 zqCX7@sWcVfNP8N;(T>>PJgsklQ#GF>F;fz_Rogh3r!dy*0qMr#>hvSua;$d z3TCZ4tlkyWPTD<=5&*bUck~J;oaIzSQ0E03_2x{?weax^jL3o`ZP#uvK{Z5^%H4b6 z%Kbp6K?>{;8>BnQy64Jy$~DN?l(ufkcs6TpaO&i~dC>0fvi-I^7YT#h?m;TVG|nba%CKRG%}3P*wejg) zI(ow&(5X3HR_xk{jrnkA-hbwxEQh|$CET9Qv6UpM+-bY?E!XVorBvHoU59;q<9$hK z%w5K-SK zWT#1OX__$ceoq0cRt>9|)v}$7{PlfwN}%Wh3rwSl;%JD|k~@IBMd5}JD#TOvp=S57 zae=J#0%+oH`-Av}a(Jqhd4h5~eG5ASOD)DfuqujI6p!;xF_GFcc;hZ9k^a7c%%h(J zhY;n&SyJWxju<+r`;pmAAWJmHDs{)V-x7(0-;E?I9FWK@Z6G+?7Py8uLc2~Fh1^0K zzC*V#P88(6U$XBjLmnahi2C!a+|4a)5Ho5>owQw$jaBm<)H2fR=-B*AI8G@@P-8I8 zHios92Q6Nk-n0;;c|WV$Q);Hu4;+y%C@3alP`cJ2{z~*m-@de%OKVgiWp;4Q)qf9n zJ!vmx(C=_>{+??w{U^Bh|LFJ<6t}Er<-Tu{C{dv8eb(kVQ4!fOuopTo!^x1OrG}0D zR{A#SrmN`=7T29bzQ}bwX8OUufW9d9T4>WY2n15=k3_rfGOp6sK0oj7(0xGaEe+-C zVuWa;hS*MB{^$=0`bWF(h|{}?53{5Wf!1M%YxVw}io4u-G2AYN|FdmhI13HvnoK zNS2fStm=?8ZpKt}v1@Dmz0FD(9pu}N@aDG3BY8y`O*xFsSz9f+Y({hFx;P_h>ER_& z`~{z?_vCNS>agYZI?ry*V96_uh;|EFc0*-x*`$f4A$*==p`TUVG;YDO+I4{gJGrj^ zn?ud(B4BlQr;NN?vaz_7{&(D9mfd z8esj=a4tR-ybJjCMtqV8>zn`r{0g$hwoWRUI3}X5=dofN){;vNoftEwX>2t@nUJro z#%7rpie2eH1sRa9i6TbBA4hLE8SBK@blOs=ouBvk{zFCYn4xY;v3QSM%y6?_+FGDn z4A;m)W?JL!gw^*tRx$gqmBXk&VU=Nh$gYp+Swu!h!+e(26(6*3Q!(!MsrMiLri`S= zKItik^R9g!0q7y$lh+L4zBc-?Fsm8`CX1+f>4GK7^X2#*H|oK}reQnT{Mm|0ar<+S zRc_dM%M?a3bC2ILD`|;6vKA`a3*N~(cjw~Xy`zhuY2s{(7KLB{S>QtR3NBQ3>vd+= z#}Q)AJr7Y_-eV(sMN#x!uGX08oE*g=grB*|bBs}%^3!RVA4f%m3=1f0K=T^}iI&2K zuM2GG5_%+#v-&V>?x4W9wQ|jE2Q7Be8mOyJtZrqn#gXy-1fF1P$C8+We&B*-pi#q5 zETp%H6g+%#sH+L4=ww?-h;MRCd2J9zwQUe4gHAbCbH08gDJY;F6F)HtWCRW1fLR;)ysGZanlz*a+|V&@(ipWdB!tz=m_0 z6F}`d$r%33bw?G*azn*}Z;UMr{z4d9j~s`0*foZkUPwpJsGgoR0aF>&@DC;$A&(av z?b|oo;`_jd>_5nye`DVOcMLr-*Nw&nA z82E8Dw^$Lpso)gEMh?N|Uc^X*NIhg=U%enuzZOGi-xcZRUZmkmq~(cP{S|*+A6P;Q zprIkJkIl51@ng)8cR6QSXJtoa$AzT@*(zN3M+6`BTO~ZMo0`9$s;pg0HE3C;&;D@q zd^0zcpT+jC%&=cYJF+j&uzX87d(gP9&kB9|-zN=69ymQS9_K@h3ph&wD5_!4q@qI@ zBMbd`2JJ2%yNX?`3(u&+nUUJLZ=|{t7^Rpw#v-pqD2_3}UEz!QazhRty%|Q~WCo7$ z+sIugHA%Lmm{lBP#bnu_>G}Ja<*6YOvSC;89z67M%iG0dagOt1HDpDn$<&H0DWxMU zxOYaaks6%R@{`l~zlZ*~2}n53mn2|O&gE+j*^ypbrtBv{xd~G(NF?Z%F3>S6+qcry z?ZdF9R*a;3lqX_!rI(Cov8ER_mOqSn6g&ZU(I|DHo7Jj`GJ}mF;T(vax`2+B8)H_D zD0I;%I?*oGD616DsC#j0x*p+ZpBfd=9gR|TvB)832CRhsW_7g&WI@zp@r7dhg}{+4f=(cO2s+)jg0x(*6|^+6W_=YIfSH0lTcK* z%)LyaOL6em@*-_u)}Swe8rU)~#zT-vNiW(D*~?Zp3NWl1y#fo!3sK-5Ek6F$F5l3| zrFFD~WHz1}WHmzzZ!n&O8rTgfytJG*7iE~0`0;HGXgWTgx@2fD`oodipOM*MOWN-} zJY-^>VMEi8v23ZlOn0NXp{7!QV3F1FY_URZjRKMcY(2PV_ms}EIC^x z=EYB5UUQ{@R~$2Mwiw$_JAcF+szKB*n(`MYpDCl>~ss54uDQ%Xf-8|dgO zY)B_qju=IaShS|XsQo=nSYxV$_vQR@hd~;qW)TEfU|BA0&-JSwO}-a*T;^}l;MgLM zz}CjPlJX|W2vCzm3oHw3vqsRc3RY=2()}iw_k2#eKf&VEP7TQ;(DDzEAUgj!z_h2Br;Z3u=K~LqM6YOrlh)v9`!n|6M-s z?XvA~y<5?WJ{+yM~uPh7uVM&g-(;IC3>uA}ud?B3F zelSyc)Nx>(?F=H88O&_70%{ATsLVTAp88F-`+|egQ7C4rpIgOf;1tU1au+D3 zlz?k$jJtTOrl&B2%}D}8d=+$NINOZjY$lb{O<;oT<zXoAp01KYG$Y4*=)!&4g|FL(!54OhR-?)DXC&VS5E|1HGk8LY;)FRJqnz zb_rV2F7=BGwHgDK&4J3{%&IK~rQx<&Kea|qEre;%A~5YD6x`mo>mdR)l?Nd%T2(5U z_ciT02-zt_*C|vn?BYDuqSFrk3R(4B0M@CRFmG{5sovIq4%8AhjXA5UwRGo)MxZlI zI%vz`v8B+#ff*XtGnciczFG}l(I}{YuCco#2E6|+5WJ|>BSDfz0oT+F z%QI^ixD|^(AN`MS6J$ zXlKNTFhb>KDkJp*4*LaZ2WWA5YR~{`={F^hwXGG*rJYQA7kx|nwnC58!eogSIvy{F zm1C#9@$LhK^Tl>&iM0wsnbG7Y^MnQ=q))MgApj4)DQt!Q5S`h+5a%c7M!m%)?+h65 z0NHDiEM^`W+M4)=q^#sk(g!GTpB}edwIe>FJQ+jAbCo#b zXmtd3raGJNH8vnqMtjem<_)9`gU_-RF&ZK!aIenv7B2Y0rZhon=2yh&VsHzM|`y|0x$Zez$bUg5Nqj?@~^ zPN43MB}q0kF&^=#3C;2T*bDBTyO(+#nZnULkVy0JcGJ36or7yl1wt7HI_>V7>mdud zv2II9P61FyEXZuF$=69dn%Z6F;SOwyGL4D5mKfW)q4l$8yUhv7|>>h_-4T*_CwAyu7;DW}_H zo>N_7Gm6eed=UaiEp_7aZko@CC61@(E1be&5I9TUq%AOJW>s^9w%pR5g2{7HW9qyF zh+ZvX;5}PN0!B4q2FUy+C#w5J?0Tkd&S#~94(AP4%fRb^742pgH7Tb1))siXWXHUT z1Wn5CG&!mGtr#jq6(P#!ck@K+FNprcWP?^wA2>mHA03W?kj>5b|P0ErXS) zg2qDTjQ|grCgYhrH-RapWCvMq5vCaF?{R%*mu}1)UDll~6;}3Q*^QOfj!dlt02lSzK z?+P)02Rrq``NbU3j&s*;<%i4Y>y9NK&=&KsYwvEmf5jwTG6?+Pu1q9M8lLlx)uZZ7 zizhr~e0ktGs-=$li-2jz^_48-jk**y&5u0`B2gc#i$T1~t+AS*kEfR*b{^Ec>2-F~ zKYRl&uQ5yO@EtAZX8ZSqx;8+AKf+CqhlUSpp*VfyBMv+%wxN5GukZEi^_to%MFRc0 zdXqJ*jk?#uYT6EJe446@(f6G4vhnxQP|pGeJ?-#|Ksq?g*ky=}x+Qnx+!<>Y(XStN zQIND`{KU}&l)E*ntI^}kJ=ly8DML{!(58Xk4_bzIc@v~e;>wKl_`7G%pGz~4KH*CTp;_|52)d!+ximd$|8v@zzEq%j68QXkgf$7eM~xdM5q5i z{?qFx_W|eq@L03bWJfjy^z@()-iCjzjREuf zb_a(yTz)ZKWCF%Lp>^2-%Q?*t{06}x#DLN3cO=i>h6#-a`z;<5rBGGM6GA(WqvRcX%Pn?Uvs1#e|ePSNJEC%+X(YI$x)`s$%>O#%}D9dgqWfq4yfVz^%FglokdFR}uJQhx|}_w`9Ulx38Ha>ZslKs58c-@IFI&f;?xM zbK>rKNfPFsf>%+k6%(A6=7Aac^_qrOCNqb3ZVJ;8pt!?1DR*ynJb#@II9h?)xB)A~ zm9Kk)Hy}!Z+W}i6ZJDy+?yY_=#kWrzgV)2eZAx_E=}Nh7*#<&mQz`Umfe$+l^P(xd zN}PA2qII4}ddCU+PN+yxkH%y!Qe(;iH3W%bwM3NKbU_saBo<8x9fGNtTAc_SizU=o zC3n2;c%LoU^j90Sz>B_p--Fzqv7x7*?|~-x{haH8RP)p|^u$}S9pD-}5;88pu0J~9 zj}EC`Q^Fw}`^pvAs4qOIuxKvGN@DUdRQ8p-RXh=3S#<`3{+Qv6&nEm)uV|kRVnu6f zco{(rJaWw(T0PWim?kkj9pJ)ZsUk9)dSNLDHf`y&@wbd;_ita>6RXFJ+8XC*-wsiN z(HR|9IF283fn=DI#3Ze&#y3yS5;!yoIBAH(v}3p5_Zr+F99*%+)cp!Sy8e+lG?dOc zuEz<;3X9Z5kkpL_ZYQa`sioR_@_cG z8tT~GOSTWnO~#?$u)AcaBSaV7P~RT?Nn8(OSL1RmzPWRWQ$K2`6*)+&7^zZBeWzud z*xb3|Fc~|R9eH+lQ#4wF#c;)Gka6lL(63C;>(bZob!i8F-3EhYU3|6-JBC0*5`y0| zBs!Frs=s!Sy0qmQNgIH|F`6(SrD1js2prni_QbG9Sv@^Pu2szR9NZl8GU89gWWvVg z2^-b*t+F{Nt>v?js7hnlC`tRU(an0qQG7;h6T~ z-`vf#R-AE$pzk`M{gCaia}F`->O2)60AuGFAJg> z*O2IZqTx=AzDvC49?A92>bQLdb&32_4>0Bgp0ESXXnd4B)!$t$g{*FG%HYdt3b3a^J9#so%BJMyr2 z{y?rzW!>lr097b9(75#&4&@lkB1vT*w&0E>!dS+a|ZOu6t^zro2tiP)bhcNNxn zbJs3_Fz+?t;4bkd8GfDI7ccJ5zU`Bs~ zN~bci`c`a%DoCMel<-KUCBdZRmew`MbZEPYE|R#|*hhvhyhOL#9Yt7$g_)!X?fK^F z8UDz)(zpsvriJ5aro5>qy`Fnz%;IR$@Kg3Z3EE!fv9CAdrAym6QU82=_$_N5*({_1 z7!-=zy(R{xg9S519S6W{HpJZ8Is|kQ!0?`!vxDggmslD59)>iQ15f z7J8NqdR`9f8H|~iFGNsPV!N)(CC9JRmzL9S}7U-K@`X893f3f<8|8Ls!^eA^#(O6nA+ByFIXcz_WLbfeG|nHJ5_sJJ^gNJ%SI9#XEfNRbzV+!RkI zXS$MOVYb2!0vU}Gt7oUy*|WpF^*orBot~b2J@^be?Gq;U%#am8`PmH-UCFZ&uTJlnetYij0z{K1mmivk$bdPbLodu;-R@@#gAV!=d%(caz$E?r zURX0pqAn7UuF6dULnoF1dZ$WM)tHAM{eZK6DbU1J`V5Dw<;xk}Nl`h+nfMO_Rdv z3SyOMzAbYaD;mkxA7_I_DOs#Bk;e5D%gsS3q)hlmi1w{FsjKNJE22`AjmNiAPRnIc zcIkN25;rOn3FipAFd(PnlK9{03w6Q<(68#1Jw`{axEGQE{Ac>^U$h);h2ADICmaNxrfpb`Jdr*)Y1SicpYKCFv$3vf~;5aW>n^7QGa63MJ z;B1+Z>WQ615R2D8JmmT`T{QcgZ+Kz1hTu{9FOL}Q8+iFx-Vyi}ZVVcGjTe>QfA`7W zFoS__+;E_rQIQxd(Bq4$egKeKsk#-9=&A!)(|hBvydsr5ts0Zjp*%*C0lM2sIOx1s zg$xz?Fh?x!P^!vWa|}^+SY8oZHub7f;E!S&Q;F?dZmvBxuFEISC}$^B_x*N-xRRJh zn4W*ThEWaPD*$KBr8_?}XRhHY7h^U1aN6>m=n~?YJQd8+!Uyq_3^)~4>XjelM&!c9 zCo|0KsGq7!KsZ~9@%G?i>LaU7#uSTMpypocm*oqJHR|wOgVWc7_8PVuuw>x{kEG4T z$p^DV`}jUK39zqFc(d5;N+M!Zd3zhZN&?Ww(<@AV-&f!v$uV>%z+dg9((35o@4rqLvTC-se@hkn^6k7+xHiK-vTRvM8{bCejbU;1@U=*r}GTI?Oc$!b6NRcj83-zF; z=TB#ESDB`F`jf4)z=OS76Se}tQDDHh{VKJk#Ad6FDB_=afpK#pyRkGrk~OuzmQG)} z*$t!nZu$KN&B;|O-aD=H<|n6aGGJZ=K9QFLG0y=Jye_ElJFNZJT;fU8P8CZcLBERjioAOC0Vz_pIXIc};)8HjfPwNy zE!g|lkRv3qpmU?shz(BBt5%TbpJC3HzP9!t7k*Fh48!-HlJ4TTgdCr3rCU!iF}kgu z4Qs;K@XOY~4f~N}Jl8V_mGbwzvNLbl&0e9UG4W;kvjTK|5`-Ld+eQ6YRF`N0ct%u% z^3J_{7r#_W1zm|>IPN!yWCRrN)N!7v`~ptNkIXKipQ6ogFvcnI5ugxdoa{d;uD67g zgo^}QuZRkB540Vc!@c80(wFG=$ct}oHq(#W0+-XX(;Rrt`x=<45X}ficNtI2(&}=~ zb(!}tNz?s`wm{gK?2tdf+OEF;tzx<(3fMd7_tM@Ghs$Z(Os-H(kYq#qB|J-aC9Ku?fsWwJhB36c)A zu|a7ZF?V8X7l2g5~xqZf>2=6Dsi5lfo zKIRL&@MLJyaBE)V_9=pJYu%U2wxR*-(0MI5_|yqP`?h@cks(5LR@XUKLMI_xuVtiu zRvpDS8MyUMRFM6`P+Sjc!A_e^H38Qu7b{b7QZ>NHyA6k-YYygQuW&C_OGO(7V7?}r)zedSVpBI zuk29Z4GW3C0GpfozbZQya454sjt@ndQmsp=DA&@sWw&xmOlDk1JIcMNp~-ES$&A~k zG#W(6hBj?!Fu8Q4WYexoSBa8_5=v20xnx6H?e;$t)5|f&{7=vOye^&3_c-Ug?|a@e z=X`&qT_5B7N9vZoPBhXOTEDV;4&x2Je4}T(UB~O-$D#CjX77$R?RZ*`ed~$G;$4YS z4n*|Pop(!NN79Hk2}U#cfEEwdxM)xQm}$~rV03xc=#U@@Y*}qEmot5KvDb=8{!E-n zl4p?}&g2h^sUGyTcGh=0aQzQb*k;K;dvbeZUgmwEv>%#(EPtj=gHKdi|E8@w+|>KC zxEU>b>P+9Xf}pEyQK(}#QrBG4Jaf!iE!qpMbTu>gb!gtdq<`@xO+roQl+S_7)!G(% zdy)$iGmJ1cwP?F=IyyV1-$|kf|EKM3B@I&lZ%NI@VV;*mQdLWjc#t|Vbk_Q~>&O03 zIcSr$(qLAINj7a z;!||v&1D5SX#X@5jNd}jUsi-CH_Scjyht&}q2p*CJCC-`&NyXf)vD5{e!HO629D-O z%bZelTcq=DoRX>zeWCa^RmR3*{x9;3lZ75M#S)!W0bRIFH#P6b%{|HRSZ5!!I#s)W z_|XXZQ<0_`>b^^0Z>LU64Yg1w)8}#M^9se(OZ9~baZ7fsKFc;EtnB>kesci#>=icG zuHdjax2^=!_(9?0l7;G7^-}9>Y#M zm;9*GT~dBuYWdk49%mZM0=H#FY1)}7NE5DE_vsqrA0`?0R0q535qHjWXcl|gz9Fq$ zMKxgL;68l!gm3y0durIr3LHv~y*ABm` zYhQG0UW#hg@*A{&G!;$FS43}rIF$e6yRdGJWVR<}uuJ_5_8qa3xaHH^!VzUteVp;> z<0`M>3tnY$ZFb$(`0sg93TwGyP;`9UYUWxO&CvAnSzei&ap))NcW;R`tA=y^?mBmG+M*&bqW5kL$V(O;(p)aEk`^ci?2Jwxu>0sy>a7+Wa9t z5#I2o;+gr^9^&km^z7>xJWbN&Ft>Vna34E zI@BBzwX)R}K3SL?)enrDJ45QLt;-7CFJk{`cF3L4Z^CtG_r5)0)HV>BOYPIUh#D%| zYQAu31f{bm-D*`_k7DTTr?Nkw_gY%J1cb2&TdtibY?V=|SSIOlA;|5C!2@?YQ z-$?G0jj^mG|MP>DmbF7}T~C$H6=CpZ~hd zZ1C|xV@=h#^~`3LSCnmI(vZ|5r3>eq5*UB)dhdy``*gKY3Eg%jSK8I-`G+OWWlD)T zt$wSQ=||lSkiKy}YF-k}@W9EiS?)z`hK{R!dd-$BCJvBtAN-yXn3njU$MisEtp!?Q z%Vk-*(wy9dd15(-WFw_&^tT;;IpF?ox1`Qq3-0zVTk+$W_?q}GfAQlPcrB^?&tWSI z2BB!K=sH7FUYmXa_dcV^Z3>5z8}~W{S!$jVR_3hu_|wl2|gmRH8ftn^z@fW75*;-`;wU+fY+BR_yx6BZnE5_Hna({jrPiubRp$jZ=T=t$hx&NeCV1!vuCcl4PJ0p0Fjp>6K} zHkoD1gQk=P2hYcT%)cJ2Q5WuA|5_x+dX0%hnozfTF>$#Wz~X!MY>){H4#fB#7^ID* z1*o2Hzp}?WVs&gbS?Uq(CT0sP+F)u9{xfgg6o_{8J#m;|NeJqDHhb(Q8%z8aM_qeM zn83>d`uDd47WIuKp78JBYo2SYupGcNXIzeou^eMY`@%Bv8elZ>q~3uq#~IX)g%g;h zoUXymEd>|kVsMkyb&1l~lrE-`w(0PObapYa35DJ4Y03Jv_!DKp}0HTbOgZRM=;PSsuAJJJ1 zItc+tu9;ANG;qHaCI|T85!euhFK~VK^G2LZV1+cbzS?>ar@>emg;JTI5VAn1g5U~| zU=p&k0OlSzc$U=s#9_uL3&n|6A1X$XvrE9vFV@`A4G#!D1QcFCeE`F2N(deJx>)*A z$XIW0P~-NbAd=5i6`s<~(vAQX9t$dbVqc5|E|CHRtb$1(l&KSNh_t2#k_l95KnP86 z)ns_DGspv-M0z0#h2a+*oH|{5~j{ zXGD=}cLrBSESQ0u$XmQlFfWMCAWaS;wKK%#aSSYK=qljBiY(s zT$v;We24&$w=avIILsMt0%1fDyah|AlLNg#WL$Lu)tf}YfqO%+pH~QC*bZO4aM*i9 zrPFf|5!hv@XY8CzaFh*Dy9vH|2fKKr(@x}`L#9^*vOae|lk`adG#oZZAyk|TOV8`9L zc-sQu%y1MQes&J?)a1}Zc*>-P!6j-T#75V$lLC!TuMB(!G-+D2;XptUxymSPFI-K&0x}B1?h$ z3-9**-9!);fwyiWB5gS$i;P~c=^}5-6G@{4TWDBRDc6(M|%qa-mS`z`u9kWo{Xl_uc;hXOkRd diff --git a/support/test/kotlin/gradle/wrapper/gradle-wrapper.properties b/support/test/kotlin/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 622ab64..0000000 --- a/support/test/kotlin/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/support/test/kotlin/gradlew b/support/test/kotlin/gradlew deleted file mode 100755 index fbd7c51..0000000 --- a/support/test/kotlin/gradlew +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# 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 -# -# https://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. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" diff --git a/support/test/kotlin/gradlew.bat b/support/test/kotlin/gradlew.bat deleted file mode 100644 index a9f778a..0000000 --- a/support/test/kotlin/gradlew.bat +++ /dev/null @@ -1,104 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega From 7bd22d2bbd2b6f8a88db5bf47481536febad52b9 Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Sat, 2 Jan 2021 15:41:16 -0500 Subject: [PATCH 427/609] bump debugpy gadget to version 1.2.1 1.2.1 is, as of this commit, the most recent version of the gadget. The version currently used, 1.0.0b12 is not compatible with Python 3.9: the build errors out on missing arguments for _PyEval_EvalFrameDefault. Python 3.9 is the version of Python that will be shipped with the upcoming debian release. Support for 3.9 was merged in before the final 1.0.0 version. So, upgrading the gadget will bring in python 3.9 support --- python3/vimspector/gadgets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 0271069..d167fed 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -115,10 +115,10 @@ GADGETS = { 'url': 'https://github.com/microsoft/debugpy/archive/${file_name}' }, 'all': { - 'version': '1.0.0b12', - 'file_name': 'v1.0.0b12.zip', + 'version': '1.2.1', + 'file_name': 'v1.2.1.zip', 'checksum': - '210632bba2221fbb841c9785a615258819ceec401d1abdbeb5f2326f12cc72a1' + '29a6c5d1053d2b6f3b1a63e1a8ecff93f951d3cc0b7548431592e9e3007239e6' }, 'do': lambda name, root, gadget: installer.InstallDebugpy( name, root, From b72aa18dec9b239b4bb454613c2c2a62bcc6a2cb Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 11:06:28 +0000 Subject: [PATCH 428/609] Add a list to tcl tets --- support/test/tcl/test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/support/test/tcl/test b/support/test/tcl/test index e77a486..a77f56f 100644 --- a/support/test/tcl/test +++ b/support/test/tcl/test @@ -3,6 +3,8 @@ set SCALAR g array set ARRAY {key1 value1 key2 value2} +set LIST [list a b c {def} {g h i j} k l m] + proc Wrap { body } { uplevel 1 $body } From d52eb3a6e9cd1daa208f1616d1fea3ca4eee70b0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 11:30:17 +0000 Subject: [PATCH 429/609] Fix thread state and PC for starting node app The problem is the sequence of events sent by the debug adapter. Vimspector requests threads: * When the initialisation exchange completes (requests all threads) * Whenever a thread event is received * whenever a stopped event is received. If any of those happens while any other request is in progress, we cache the request and handle it later. The latest request is processed when the response to the outstanding request is received. The problem is if the event is a stopped event, it is the handling of the threads request that actually sets the thread state internally to stopped. In a sequence where the first event is a stopped event, we end up discarding the stopped event. like: 1. Stopped event (thread 1 = stopped) (request threads) 2. Initialisation complete (cache request) 3. threads response received (discard response and process cached request) 4. response received (but forgotten about the stopped event). The solution is to always process the thread response, even if we send the cached request. To avoid flicker, we don't draw the screen, or expand any threads/stacks in the case where we're sending a cached request. --- python3/vimspector/stack_trace.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index df650b9..f38e4ba 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -188,11 +188,12 @@ class StackTraceView( object ): return def consume_threads( message ): + requesting = False if self._requesting_threads == StackTraceView.ThreadRequestState.PENDING: # We may have hit a thread event, so try again. self._requesting_threads = StackTraceView.ThreadRequestState.NO self.LoadThreads( *self._pending_thread_request ) - return + requesting = True self._requesting_threads = StackTraceView.ThreadRequestState.NO self._pending_thread_request = None @@ -211,8 +212,6 @@ class StackTraceView( object ): stoppedThreadId = stopEvent.get( 'threadId' ) allThreadsStopped = stopEvent.get( 'allThreadsStopped', False ) - requesting = False - # FIXME: This is horribly inefficient for t in message[ 'body' ][ 'threads' ]: thread = None @@ -240,7 +239,10 @@ class StackTraceView( object ): # If this is a stopped event, load the stack trace for the "current" # thread. Don't do this on other thrads requests because some servers # just break when that happens. - if infer_current_frame: + # + # Don't do this if we're also satisfying a cached request already (we'll + # do it then) + if infer_current_frame and not requesting: if thread.id == self._current_thread: if thread.CanExpand(): self._LoadStackTrace( thread, True, reason ) From 4206d0e57cbe48c541ec64c09ecbe3ad96b851bd Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 15:54:45 +0000 Subject: [PATCH 430/609] Use 127.0.0.1 rather than localhost to avoid issues with ipv6, and long timeout connecting the socket --- autoload/vimspector/internal/channel.vim | 2 +- autoload/vimspector/internal/neochannel.vim | 2 +- docs/schema/vimspector.schema.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/autoload/vimspector/internal/channel.vim b/autoload/vimspector/internal/channel.vim index b05d8e8..b44436b 100644 --- a/autoload/vimspector/internal/channel.vim +++ b/autoload/vimspector/internal/channel.vim @@ -62,7 +62,7 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort \ ) endif - let l:addr = get( a:config, 'host', 'localhost' ) . ':' . a:config[ 'port' ] + let l:addr = get( a:config, 'host', '127.0.0.1' ) . ':' . a:config[ 'port' ] echo 'Connecting to ' . l:addr . '... (waiting fo up to 10 seconds)' let s:ch = ch_open( l:addr, diff --git a/autoload/vimspector/internal/neochannel.vim b/autoload/vimspector/internal/neochannel.vim index 5414568..f20684d 100644 --- a/autoload/vimspector/internal/neochannel.vim +++ b/autoload/vimspector/internal/neochannel.vim @@ -66,7 +66,7 @@ function! vimspector#internal#neochannel#StartDebugSession( config ) abort endtry endif - let l:addr = get( a:config, 'host', 'localhost' ) . ':' . a:config[ 'port' ] + let l:addr = get( a:config, 'host', '127.0.0.1' ) . ':' . a:config[ 'port' ] let attempt = 1 while attempt <= 10 diff --git a/docs/schema/vimspector.schema.json b/docs/schema/vimspector.schema.json index 97fea75..cd79f11 100644 --- a/docs/schema/vimspector.schema.json +++ b/docs/schema/vimspector.schema.json @@ -200,7 +200,7 @@ "properties": { "host": { "type": "string", - "default": "localhost", + "default": "127.0.0.1", "description": "Connect to this host in multi-session mode" }, "port": { From e010d3217c0ccc12f715ccb76c0414af451fa34c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 9 Jan 2021 12:02:30 +0000 Subject: [PATCH 431/609] add some links to the new issue page --- .github/ISSUE_TEMPLATE/config.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..f182c32 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,12 @@ +blank_issues_enabled: false +contact_links: + - name: Questions and support + url: http://gitter.im/vimspector/Lobby + about: Please ask and answer questions here. + - name: Discussions + url: https://github.com/puremourning/vimspector/discussions + about: Please post questions and useful hints here + - name: Support for additional languages + url: https://github.com/puremourning/vimspector/wiki/languages + about: Please see here for information on support for additional languages + From 1f20115960ed18d9843496dedc3bcd02692ff6a1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 9 Jan 2021 12:15:27 +0000 Subject: [PATCH 432/609] Update docs bundle to stfu security warnings --- docs/Gemfile | 2 ++ docs/Gemfile.lock | 71 ++++++++++++++++++++++++++--------------------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/docs/Gemfile b/docs/Gemfile index 3f49941..18f79a6 100644 --- a/docs/Gemfile +++ b/docs/Gemfile @@ -28,3 +28,5 @@ gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] # Performance-booster for watching directories on Windows gem "wdm", "~> 0.1.0" if Gem.win_platform? + +gem "webrick", "~> 1.7" diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index c28eebb..d2eb55f 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.3.2) + activesupport (6.0.3.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -17,37 +17,40 @@ GEM commonmarker (0.17.13) ruby-enum (~> 0.5) concurrent-ruby (1.1.7) - dnsruby (1.61.4) + dnsruby (1.61.5) simpleidn (~> 0.1) - em-websocket (0.5.1) + em-websocket (0.5.2) eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) ethon (0.12.0) ffi (>= 1.3.0) eventmachine (1.2.7) execjs (2.7.0) - faraday (1.0.1) + faraday (1.3.0) + faraday-net_http (~> 1.0) multipart-post (>= 1.2, < 3) - ffi (1.13.1) + ruby2_keywords + faraday-net_http (1.0.0) + ffi (1.14.2) forwardable-extended (2.6.0) gemoji (3.0.1) - github-pages (207) + github-pages (209) github-pages-health-check (= 1.16.1) jekyll (= 3.9.0) jekyll-avatar (= 0.7.0) jekyll-coffeescript (= 1.1.1) jekyll-commonmark-ghpages (= 0.1.6) jekyll-default-layout (= 0.1.4) - jekyll-feed (= 0.13.0) + jekyll-feed (= 0.15.1) jekyll-gist (= 1.5.0) jekyll-github-metadata (= 2.13.0) - jekyll-mentions (= 1.5.1) + jekyll-mentions (= 1.6.0) jekyll-optional-front-matter (= 0.3.2) jekyll-paginate (= 1.1.0) jekyll-readme-index (= 0.3.0) - jekyll-redirect-from (= 0.15.0) + jekyll-redirect-from (= 0.16.0) jekyll-relative-links (= 0.6.1) - jekyll-remote-theme (= 0.4.1) + jekyll-remote-theme (= 0.4.2) jekyll-sass-converter (= 1.5.2) jekyll-seo-tag (= 2.6.1) jekyll-sitemap (= 1.4.0) @@ -55,7 +58,7 @@ GEM jekyll-theme-architect (= 0.1.1) jekyll-theme-cayman (= 0.1.1) jekyll-theme-dinky (= 0.1.1) - jekyll-theme-hacker (= 0.1.1) + jekyll-theme-hacker (= 0.1.2) jekyll-theme-leap-day (= 0.1.1) jekyll-theme-merlot (= 0.1.1) jekyll-theme-midnight (= 0.1.1) @@ -66,14 +69,14 @@ GEM jekyll-theme-tactile (= 0.1.1) jekyll-theme-time-machine (= 0.1.1) jekyll-titles-from-headings (= 0.5.3) - jemoji (= 0.11.1) + jemoji (= 0.12.0) kramdown (= 2.3.0) kramdown-parser-gfm (= 1.1.0) liquid (= 4.0.3) mercenary (~> 0.3) minima (= 2.5.1) nokogiri (>= 1.10.4, < 2.0) - rouge (= 3.19.0) + rouge (= 3.23.0) terminal-table (~> 1.4) github-pages-health-check (1.16.1) addressable (~> 2.3) @@ -114,14 +117,14 @@ GEM rouge (>= 2.0, < 4.0) jekyll-default-layout (0.1.4) jekyll (~> 3.0) - jekyll-feed (0.13.0) + jekyll-feed (0.15.1) jekyll (>= 3.7, < 5.0) jekyll-gist (1.5.0) octokit (~> 4.2) jekyll-github-metadata (2.13.0) jekyll (>= 3.4, < 5.0) octokit (~> 4.0, != 4.4.0) - jekyll-mentions (1.5.1) + jekyll-mentions (1.6.0) html-pipeline (~> 2.3) jekyll (>= 3.7, < 5.0) jekyll-optional-front-matter (0.3.2) @@ -129,14 +132,15 @@ GEM jekyll-paginate (1.1.0) jekyll-readme-index (0.3.0) jekyll (>= 3.0, < 5.0) - jekyll-redirect-from (0.15.0) + jekyll-redirect-from (0.16.0) jekyll (>= 3.3, < 5.0) jekyll-relative-links (0.6.1) jekyll (>= 3.3, < 5.0) - jekyll-remote-theme (0.4.1) + jekyll-remote-theme (0.4.2) addressable (~> 2.0) jekyll (>= 3.5, < 5.0) - rubyzip (>= 1.3.0) + jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) + rubyzip (>= 1.3.0, < 3.0) jekyll-sass-converter (1.5.2) sass (~> 3.4) jekyll-seo-tag (2.6.1) @@ -153,8 +157,8 @@ GEM jekyll-theme-dinky (0.1.1) jekyll (~> 3.5) jekyll-seo-tag (~> 2.0) - jekyll-theme-hacker (0.1.1) - jekyll (~> 3.5) + jekyll-theme-hacker (0.1.2) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) jekyll-theme-leap-day (0.1.1) jekyll (~> 3.5) @@ -188,7 +192,7 @@ GEM jekyll (>= 3.3, < 5.0) jekyll-watch (2.2.1) listen (~> 3.0) - jemoji (0.11.1) + jemoji (0.12.0) gemoji (~> 3.0) html-pipeline (~> 2.2) jekyll (>= 3.0, < 5.0) @@ -197,32 +201,35 @@ GEM kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) liquid (4.0.3) - listen (3.2.1) + listen (3.4.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) - mini_portile2 (2.4.0) + mini_portile2 (2.5.0) minima (2.5.1) jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.14.1) + minitest (5.14.3) multipart-post (2.1.1) - nokogiri (1.10.10) - mini_portile2 (~> 2.4.0) - octokit (4.18.0) + nokogiri (1.11.1) + mini_portile2 (~> 2.5.0) + racc (~> 1.4) + octokit (4.20.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (3.1.1) + racc (1.5.2) rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) rexml (3.2.4) - rouge (3.19.0) + rouge (3.23.0) ruby-enum (0.8.0) i18n + ruby2_keywords (0.0.2) rubyzip (2.3.0) safe_yaml (1.0.5) sass (3.7.4) @@ -240,13 +247,14 @@ GEM thread_safe (0.3.6) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (1.2.7) + tzinfo (1.2.9) thread_safe (~> 0.1) unf (0.1.4) unf_ext unf_ext (0.0.7.7) unicode-display_width (1.7.0) - zeitwerk (2.4.0) + webrick (1.7.0) + zeitwerk (2.4.2) PLATFORMS ruby @@ -256,6 +264,7 @@ DEPENDENCIES jekyll-feed (~> 0.6) minima (~> 2.0) tzinfo-data + webrick (~> 1.7) BUNDLED WITH - 2.1.4 + 2.2.3 From d38da376c2dbb19b7443d1b0e8f507e45229070b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 9 Jan 2021 12:46:13 +0000 Subject: [PATCH 433/609] There are mappings --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 8bc0a0d..5eae743 100644 --- a/README.md +++ b/README.md @@ -602,9 +602,6 @@ That said, many people are familiar with particular debuggers, so the following mappings can be enabled by setting `g:vimspector_enable_mappings` to the specified value. -Please note: Currently there are no `` mappings. These will be added in -future to make custom mappings much easier. - ## Visual Studio / VSCode To use Visual Studio-like mappings, add the following to your `vimrc` **before From 35f7e08fbbd3e9330ed956def0ab5d83f13c01b1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 9 Jan 2021 13:14:14 +0000 Subject: [PATCH 434/609] Bit of readme restructuring and improve docs on breakpionts API --- README.md | 256 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 155 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index 5eae743..6460adb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ # vimspector - A multi language graphical debugger for Vim For a tutorial and usage overview, take a look at the -[Vimspector website][website] +[Vimspector website][website]. + +For detailed explanatin of the `.vimspector.json` format, see the +[reference guide][vimspector-ref]. ![Build](https://github.com/puremourning/vimspector/workflows/Build/badge.svg?branch=master) [![Gitter](https://badges.gitter.im/vimspector/Lobby.svg)](https://gitter.im/vimspector/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) @@ -27,15 +30,21 @@ For a tutorial and usage overview, take a look at the * [Background](#background) * [Status](#status) * [Experimental](#experimental) + * [Motivation](#motivation) + * [License](#license) + * [Sponsorship](#sponsorship) * [Mappings](#mappings) * [Visual Studio / VSCode](#visual-studio--vscode) * [Human Mode](#human-mode) - * [Usage](#usage) + * [Usage and API](#usage-and-api) * [Launch and attach by PID:](#launch-and-attach-by-pid) * [Launch with options](#launch-with-options) * [Debug configuration selection](#debug-configuration-selection) * [Get configurations](#get-configurations) * [Breakpoints](#breakpoints) + * [Summary](#summary) + * [Line breakpoints](#line-breakpoints) + * [Conditional breakpoints](#conditional-breakpoints) * [Exception breakpoints](#exception-breakpoints) * [Clear breakpoints](#clear-breakpoints) * [Run to Cursor](#run-to-cursor) @@ -49,14 +58,14 @@ For a tutorial and usage overview, take a look at the * [Console autocompletion](#console-autocompletion) * [Log View](#log-view) * [Closing debugger](#closing-debugger) - * [Debug adapter configuration](#debug-adapter-configuration) + * [Debug profile configuration](#debug-profile-configuration) * [C, C , Rust, etc.](#c-c-rust-etc) + * [C Remote debugging](#c-remote-debugging) + * [C Remote launch and attach](#c-remote-launch-and-attach) * [Rust](#rust) - * [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) + * [Python Remote Debugging](#python-remote-debugging) + * [Python Remote launch and attach](#python-remote-launch-and-attach) * [Legacy: vscode-python](#legacy-vscode-python) * [TCL](#tcl) * [C♯](#c) @@ -79,11 +88,8 @@ For a tutorial and usage overview, take a look at the * [Customising the WinBar](#customising-the-winbar) * [Example](#example) * [FAQ](#faq) - * [Motivation](#motivation) - * [License](#license) - * [Sponsorship](#sponsorship) - + @@ -570,6 +576,65 @@ However, I commit to only doing this in the most extreme cases and to annouce such changes on Gitter well in advance. There's nothing more annoying than stuff just breaking on you. I get that. +## Motivation + +A message from the author about the motivation for this plugin: + +> Many development environments have a built-in debugger. I spend an inordinate +> amount of my time in Vim. I do all my development in Vim and I have even +> customised my workflows for building code, running tests etc. +> +> For many years I have observed myself, friends and colleagues have been +> writing `printf`, `puts`, `print`, etc. debugging statements in all sorts of +> files simply because there is no _easy_ way to run a debugger for _whatever_ +> language we happen to be developing in. +> +> I truly believe that interactive, graphical debugging environments are the +> best way to understand and reason about both unfamiliar and familiar code, and +> that the lack of ready, simple access to a debugger is a huge hidden +> productivity hole for many. +> +> Don't get me wrong, I know there are literally millions of developers out +> there that are more than competent at developing without a graphical debugger, +> but I maintain that if they had the ability to _just press a key_ and jump +> into the debugger, it would be faster and more enjoyable that just cerebral +> code comprehension. +> +> I created Vimspector because I find changing tools frustrating. `gdb` for c++, +> `pdb` for python, etc. Each has its own syntax. Each its own lexicon. Each its +> own foibles. +> +> I designed the configuration system in such a way that the configuration can +> be committed to source control so that it _just works_ for any of your +> colleagues, friends, collaborators or complete strangers. +> +> I made remote debugging a first-class feature because that's a primary use +> case for me in my job. +> +> With Vimspector I can _just hit ``_ in all of the languages I develop in +> and debug locally or remotely using the exact same workflow, mappings and UI. +> I have integrated this with my Vim in such a way that I can hit a button and +> _run the test under the cursor in Vimspector_. This kind of integration has +> massively improved my workflow and productivity. It's even made the process +> of learning a new codebase... fun. +> +> \- Ben Jackson, Creator. + +## License + +[Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) + +Copyright © 2018 Ben Jackson + +## Sponsorship + +If you like Vimspector so much that you're wiling to part with your hard-earned cash, please consider donating to one of the following charities, which are meaningful to the author of Vimspector (in order of prefernce): + +* [Greyhound Rescue Wales](https://greyhoundrescuewales.co.uk) +* [Cancer Research UK](https://www.cancerresearchuk.org) +* [ICCF Holland](https://iccf.nl) +* Any charity of your choosing. + # Mappings By default, vimspector does not change any of your mappings. Mappings are very @@ -650,7 +715,12 @@ let g:vimspector_enable_mappings = 'HUMAN' | `F11` | Step Into | `vimspector#StepInto()` | | `F12` | Step out of current function scope | `vimspector#StepOut()` | -# Usage +# Usage and API + +This section defines detailed usage instructions, organised by feature. For most +users, the [mappings](#mappings) section contains the most common commands and +default usage. This section can be used as a reference to create your own +mappings or custom behaviours. ## Launch and attach by PID: @@ -714,13 +784,54 @@ For example, to get an array of configurations and fuzzy matching on the result ## Breakpoints -* Use `vimspector#ToggleBreakpoint([ { 'condition': '' } ])` - to set/disable/delete a line breakpoint, with optional condition. -* Use `vimspector#AddFunctionBreakpoint( '' [, { 'condition': '' } ] )` - to add a function breakpoint with optional condition. +See the [mappings](€mappings) section for the default mappngs for working with +breakpoints. This section describes the full API in vimscript functions. -Both of these functions take a single optional argument which is a dictionary of -options. The dictionary can have the following keys: +### Summary + +* Use `vimspector#ToggleBreakpoint( { options dict } )` to set/disable/delete + a line breakpoint. The argument is optional (see below). +* Use `vimspector#AddFunctionBreakpoint( '', { options dict} )` + to add a function breakpoint. The second argument is optional (see below). +* Use `vimspector#SetLineBreakpoint( file_name, line_num, { options dict } )` to + set a breakpoint at a specific file/line. The last argument is optional + (see below) +* Use `vimspector#ClearLineBreakpoint( file_name, line_num )` to + remove a breakpoint at a specific file/line +* Use `vimspector#ClearBreakpoints()` to clear all breakpoints + +Examples: + +* `call vimspector#ToggleBreakpoint()` - toggle breakpoint on current line +* `call vimspector#SetLineBreakpoint( 'some_file.py', 10 )` - set a breakpoint + on `some_filepy:10` +* `call vimspector#AddFunctionBreakpoint( 'main' )` - add a function breakpoint + on the `main` funciton +* `call vimspector#ToggleBreakpoint( { 'condition': 'i > 5' } )` - add a + breakpoint on the current line that triggers only when `i > 5` is `true` +* `call vimspector#SetLineBreakpoint( 'some_file.py', 10, { 'condition': 'i > 5' } )` - add a + breakpoint at `some_file.py:10` that triggers only when `i > 5` is `true` +* `call vimspector#ClearLineBreakpoint( 'some_file.py', 10 )` - delete the + breakpoint at `some_file.py:10` +* `call vimspector#ClearBreakpoints()` - clear all breakpoints + +### Line breakpoints + +The simplest and most common form of breakpoint is a line breakpoint. Exectuion +is paused when the specified line is executed. + +For most debugging scenarios, users will just hit `` to create a line +breakpoint on the current line and `` to launch the application. + +### Conditional breakpoints + +Some debug adatpers support conditional breakpionts. Note that vimspector does +not tell you if the debugger doesn't support conditional breakpoints (yet). A +conditional breakpiont is a breakpiont which only triggers if some expression +evaluates to true, or has some other constraints met. + +Some of these functions above take a single optional argument which is a +dictionary of options. The dictionary can have the following keys: * `condition`: An optional expression evaluated to deterimie if the breakpoint should fire. Not supported by all debug adapters. For example, to break when @@ -738,9 +849,10 @@ expressions in a command line (with history). ### Exception breakpoints -When starting debugging, you may be asekd a few questions about how to handle -exceptoins. These are "exception breakpoints" and vimspector remembers your -choices while Vim is still running. +Exception breakpoints typically fire when an exception is throw or other error +condition occurs. Depending on the debugger, when starting debugging, you may be +asekd a few questions about how to handle exceptoins. These are "exception +breakpoints" and vimspector remembers your choices while Vim is still running. Typically you can accept the defaults (just keep pressing ``!) as most debug adapter defaults are sane, but if you want to break on, say `uncaught exception` @@ -751,14 +863,14 @@ You can configure your choices in the `.vimspector.json`. See ### Clear breakpoints -* Use `vimspector#ClearBreakpoints()` - to clear all breakpoints including the memory of exception breakpoint choices. +Use `vimspector#ClearBreakpoints()` +to clear all breakpoints including the memory of exception breakpoint choices. ### Run to Cursor -* Use `vimspector#RunToCursor` or ``: this creates a temporary - breakpoint on the current line, then continues execution, clearing the - breakpiont when it is hit. +Use `vimspector#RunToCursor` or ``: this creates a temporary +breakpoint on the current line, then continues execution, clearing the +breakpoint when it is hit. ## Stepping @@ -844,7 +956,7 @@ The stack trace is represented by the buffer `vimspector.StackTrace`. ## Program Output -* In the outputs window use the WinBar to select the output channel. +* In the outputs window, use the WinBar to select the output channel. * Alternatively, use `:VimspectorShowOutput `. Use command-line completion to see the categories. * The debugee prints to the stdout channel. @@ -859,7 +971,7 @@ options). ### Console The console window is a prompt buffer, where that's available, and can be used -as an interactive CLI for the debug adapter. Support for this varies amongt +as an interactive CLI for the debug adapter. Support for this varies amongst adapters. * Enter insert mode to enter a command to evaluate. @@ -906,7 +1018,7 @@ To close the debugger, use: * `:VimspectorReset` when the WinBar is not available. * `call vimspector#Reset()` -# Debug adapter configuration +# Debug profile configuration For an introduction to the configuration of `.vimspector.json`, take a look at the Getting Started section of the [Vimspector website][website]. @@ -983,6 +1095,17 @@ licensing. } ``` +### C++ Remote debugging + +The cpptools documentation describes how to attach cpptools to gdbserver using +`miDebuggerAddress`. Note that when doing this you should use the +`"request": "attach"`. + +### C++ Remote launch and attach + +If you're feeling fancy, checkout the [reference guide][remote-debugging] for +an example of getting Vimspector to remotely launch and attach. + * CodeLLDB (MacOS) CodeLLDB is superior to vscode-cpptools in a number of ways on macOS at least. @@ -1049,17 +1172,6 @@ Rust is supported with any gdb/lldb-based debugger. So it works fine with -### Remote debugging - -The cpptools documentation describes how to attach cpptools to gdbserver using -`miDebuggerAddress`. Note that when doing this you should use the -`"request": "attach"`. - -### Remote launch and attach - -If you're feeling fancy, checkout the [reference guide][remote-debugging] for -an example of getting Vimspector to remotely launch and attach. - ## Python * Python: [debugpy][] @@ -1094,7 +1206,7 @@ an example of getting Vimspector to remotely launch and attach. } ``` -### Remote Debugging +### Python Remote Debugging In order to use remote debugging with debugpy, you have to connect Vimspector directly to the application that is being debugged. This is easy, but it's a @@ -1133,7 +1245,7 @@ Additional documentation, including how to do this when the remote machine can only be contacted via SSH [are provided by debugpy](https://github.com/microsoft/debugpy/wiki/Debugging-over-SSH). -### Remote launch and attach +### Python Remote launch and attach If you're feeling fancy, checkout the [reference guide][remote-debugging] for an example of getting Vimspector to remotely launch and attach. @@ -1559,7 +1671,7 @@ define them in your `vimrc`. | Sign | Description | Priority | |------------------------|-------------------------------------|----------| | `vimspectorBP` | Line breakpoint | 9 | -| `vimspectorBPCond` | Conditional line breakpiont | 9 | +| `vimspectorBPCond` | Conditional line breakpoint | 9 | | `vimspectorBPDisabled` | Disabled breakpoint | 9 | | `vimspectorPC` | Program counter (i.e. current line) | 200 | | `vimspectorPCBP` | Program counter and breakpoint | 200 | @@ -1834,64 +1946,6 @@ hi link jsonComment Comment 8. The signs and winbar display funny symbols. How do i fix them? See [this](#changing-the-default-signs) and [this](#customising-the-winbar) -# Motivation - -A message from the author about the motivation for this plugin: - -> Many development environments have a built-in debugger. I spend an inordinate -> amount of my time in Vim. I do all my development in Vim and I have even -> customised my workflows for building code, running tests etc. -> -> For many years I have observed myself, friends and colleagues have been -> writing `printf`, `puts`, `print`, etc. debugging statements in all sorts of -> files simply because there is no _easy_ way to run a debugger for _whatever_ -> language we happen to be developing in. -> -> I truly believe that interactive, graphical debugging environments are the -> best way to understand and reason about both unfamiliar and familiar code, and -> that the lack of ready, simple access to a debugger is a huge hidden -> productivity hole for many. -> -> Don't get me wrong, I know there are literally millions of developers out -> there that are more than competent at developing without a graphical debugger, -> but I maintain that if they had the ability to _just press a key_ and jump -> into the debugger, it would be faster and more enjoyable that just cerebral -> code comprehension. -> -> I created Vimspector because I find changing tools frustrating. `gdb` for c++, -> `pdb` for python, etc. Each has its own syntax. Each its own lexicon. Each its -> own foibles. -> -> I designed the configuration system in such a way that the configuration can -> be committed to source control so that it _just works_ for any of your -> colleagues, friends, collaborators or complete strangers. -> -> I made remote debugging a first-class feature because that's a primary use -> case for me in my job. -> -> With Vimspector I can _just hit ``_ in all of the languages I develop in -> and debug locally or remotely using the exact same workflow, mappings and UI. -> I have integrated this with my Vim in such a way that I can hit a button and -> _run the test under the cursor in Vimspector_. This kind of integration has -> massively improved my workflow and productivity. It's even made the process -> of learning a new codebase... fun. -> -> \- Ben Jackson, Creator. - -# License - -[Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) - -Copyright © 2018 Ben Jackson - -# Sponsorship - -If you like Vimspector so much that you're wiling to part with your hard-earned cash, please consider donating to one of the following charities, which are meaningful to the author of Vimspector (in order of prefernce): - -* [Greyhound Rescue Wales](https://greyhoundrescuewales.co.uk) -* [Cancer Research UK](https://www.cancerresearchuk.org) -* [ICCF Holland](https://iccf.nl) -* Any charity of your choosing. [ycmd]: https://github.com/Valloric/ycmd [gitter]: https://gitter.im/vimspector/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link From 75e8450cf3ae7faf31c2c2891e763cf3acea2141 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sat, 9 Jan 2021 07:47:33 -0800 Subject: [PATCH 435/609] fixed some typos in the readme --- README.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 6460adb..0917bc9 100644 --- a/README.md +++ b/README.md @@ -340,7 +340,7 @@ In order for Vimspector to be useful, you need to have some adapters installed. There are a few ways to do this: -* If you downloaded a tarball, gadgets for main supported langauges are already +* If you downloaded a tarball, gadgets for main supported languages are already installed for you. * Using `:VimspectorInstall ` (use TAB `wildmenu` to see the options, also accepts any `install_gadget.py` option) @@ -350,7 +350,7 @@ There are a few ways to do this: * Using `:VimspectorUpdate` to install the latest supported versions of the gadgets. -Here's a demo of doing somee installs and an upgrade: +Here's a demo of doing some installs and an upgrade: [![asciicast](https://asciinema.org/a/Hfu4ZvuyTZun8THNen9FQbTay.svg)](https://asciinema.org/a/Hfu4ZvuyTZun8THNen9FQbTay) @@ -364,7 +364,7 @@ they will: install the gadgets manually. * Perform any necessary post-installation actions, such as: * Building any binary components - * Ensuring scripts are executable, because the VSIX pacakges are usually + * Ensuring scripts are executable, because the VSIX packages are usually broken in this regard. * Set up the `gadgetDir` symlinks for the platform. @@ -570,7 +570,7 @@ The plugin is currently _experimental_. That means that any part of it can (and probably will) change, including things like: - breaking changes to the configuration -- keys, layout, functionatlity of the UI +- keys, layout, functionality of the UI However, I commit to only doing this in the most extreme cases and to annouce such changes on Gitter well in advance. There's nothing more annoying than stuff @@ -628,7 +628,7 @@ Copyright © 2018 Ben Jackson ## Sponsorship -If you like Vimspector so much that you're wiling to part with your hard-earned cash, please consider donating to one of the following charities, which are meaningful to the author of Vimspector (in order of prefernce): +If you like Vimspector so much that you're wiling to part with your hard-earned cash, please consider donating to one of the following charities, which are meaningful to the author of Vimspector (in order of preference): * [Greyhound Rescue Wales](https://greyhoundrescuewales.co.uk) * [Cancer Research UK](https://www.cancerresearchuk.org) @@ -784,7 +784,7 @@ For example, to get an array of configurations and fuzzy matching on the result ## Breakpoints -See the [mappings](€mappings) section for the default mappngs for working with +See the [mappings](€mappings) section for the default mappings for working with breakpoints. This section describes the full API in vimscript functions. ### Summary @@ -806,7 +806,7 @@ Examples: * `call vimspector#SetLineBreakpoint( 'some_file.py', 10 )` - set a breakpoint on `some_filepy:10` * `call vimspector#AddFunctionBreakpoint( 'main' )` - add a function breakpoint - on the `main` funciton + on the `main` function * `call vimspector#ToggleBreakpoint( { 'condition': 'i > 5' } )` - add a breakpoint on the current line that triggers only when `i > 5` is `true` * `call vimspector#SetLineBreakpoint( 'some_file.py', 10, { 'condition': 'i > 5' } )` - add a @@ -817,7 +817,7 @@ Examples: ### Line breakpoints -The simplest and most common form of breakpoint is a line breakpoint. Exectuion +The simplest and most common form of breakpoint is a line breakpoint. Execution is paused when the specified line is executed. For most debugging scenarios, users will just hit `` to create a line @@ -825,7 +825,7 @@ breakpoint on the current line and `` to launch the application. ### Conditional breakpoints -Some debug adatpers support conditional breakpionts. Note that vimspector does +Some debug adapters support conditional breakpionts. Note that vimspector does not tell you if the debugger doesn't support conditional breakpoints (yet). A conditional breakpiont is a breakpiont which only triggers if some expression evaluates to true, or has some other constraints met. @@ -833,11 +833,11 @@ evaluates to true, or has some other constraints met. Some of these functions above take a single optional argument which is a dictionary of options. The dictionary can have the following keys: -* `condition`: An optional expression evaluated to deterimie if the breakpoint +* `condition`: An optional expression evaluated to determine if the breakpoint should fire. Not supported by all debug adapters. For example, to break when `abc` is `10`, enter something like `abc == 10`, depending on the language. * `hitCondition`: An optional expression evaluated to determine a number of - times the breakpoint should be ignored. Should (probablty?) not be used in + times the breakpoint should be ignored. Should (probably?) not be used in combination with `condition`. Not supported by all debug adapters. For example, to break on the 3rd time hitting this line, enter `3`. @@ -851,7 +851,7 @@ expressions in a command line (with history). Exception breakpoints typically fire when an exception is throw or other error condition occurs. Depending on the debugger, when starting debugging, you may be -asekd a few questions about how to handle exceptoins. These are "exception +asked a few questions about how to handle exceptions. These are "exception breakpoints" and vimspector remembers your choices while Vim is still running. Typically you can accept the defaults (just keep pressing ``!) as most debug @@ -912,7 +912,7 @@ The watches are represented by the buffer `vimspector.StackTrace`. ### Watch autocompletion The watch prompt buffer has its `omnifunc` set to a function that will -calcualte completion for the current expression. This is trivailly used with +calculate completion for the current expression. This is trivially used with `` (see `:help ins-completion`), or integrated with your favourite completion system. The filetype in the buffer is set to `VimspectorPrompt`. @@ -927,8 +927,8 @@ let g:ycm_semantic_triggers = { ## Stack Traces -The stack trace window shows the state of each progream thread. Threads which -are stopped can be expanded to show the strack trace of that thread. +The stack trace window shows the state of each program thread. Threads which +are stopped can be expanded to show the stack trace of that thread. Often, but not always, all threads are stopped when a breakpoint is hit. The status of a thread is show in parentheses after the thread's name. Where @@ -988,7 +988,7 @@ If the output window is closed, a new one can be opened with ### Console autocompletion The console prompt buffer has its `omnifunc` set to a function that will -calcualte completion for the current command/expression. This is trivailly used +calculate completion for the current command/expression. This is trivially used with `` (see `:help ins-completion`), or integrated with your favourite completion system. The filetype in the buffer is set to `VimspectorPrompt`. @@ -1659,7 +1659,7 @@ This debugger uses stdio to communicate with the running process, so calls to # Customisation -There is very limited support for customistaion of the UI. +There is very limited support for customisation of the UI. ## Changing the default signs @@ -1687,7 +1687,7 @@ sign define vimspectorPCBP text=●▶ texthl=MatchParen linehl=CursorLin ``` If the signs don't display properly, your font probably doesn't contain these -glyphs. You can easily change them by deifining the sign in your vimrc. For +glyphs. You can easily change them by defining the sign in your vimrc. For example, you could put this in your `vimrc` to use some simple ASCII symbols: ```viml @@ -1733,7 +1733,7 @@ smaller ones. ## 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 +change at any time. I will endeavour to reduce the impact of this and announce changes in Gitter. The following options control the default sizes of the UI windows (all of them @@ -1786,7 +1786,7 @@ 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 +change at any time. I will endeavour to reduce the impact of this and announce changes in Gitter. The above customisation of window sizes is limited intentionally to keep things @@ -1818,7 +1818,7 @@ keys: * `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. + containing 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. @@ -1877,7 +1877,7 @@ augroup END 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. +commands, primarily `win_gotoid` function and the `wincmd` ex command. To try this out `vim -Nu support/custom_ui_vimrc `. @@ -1937,7 +1937,7 @@ hi link jsonComment Comment ``` 7. What is the difference between a `gadget` and an `adapter`? A gadget is - somethin you install with `:VimspectorInstall` or `install_gadget.py`, an + something you install with `:VimspectorInstall` or `install_gadget.py`, an `adapter` is something that Vimspector talks to (actually it's the Vimspector config describing that thing). These are _usually_ one-to-one, but in theory a single gadget can supply multiple `adapter` configs. From 52eff3265181bbbe5338818dc5bc196e234cea45 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 10 Jan 2021 12:59:59 +0000 Subject: [PATCH 436/609] Telemetry data myth busted. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0917bc9..4dc3580 100644 --- a/README.md +++ b/README.md @@ -1945,6 +1945,10 @@ hi link jsonComment Comment for, say remote debugging, or debugging in a container, etc. 8. The signs and winbar display funny symbols. How do i fix them? See [this](#changing-the-default-signs) and [this](#customising-the-winbar) +9. What's thie telemetry stuff all about? Are you sending my data to evil companies? + Debug adapters (for some reason) send telemetry data to clients. Vimspector simply + displays this information in the output window. It *does not* and *will not ever* + collect, use, forward or otherwise share any data with any third parties. [ycmd]: https://github.com/Valloric/ycmd From 30eec0d93cd265dad9dfdc07479b6a3d014572fb Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 6 Feb 2021 20:05:33 +0000 Subject: [PATCH 437/609] Fix variable substitution for multiple defaulted vars The problem was that the python regex engine is strictly left-to-right, so matching `[^}]|\\}` against \\}} meant that the `\\` was consumed by the left of the `|`. The solution is to just switch them around. Also add a way to run python tests from within vim, so we can actually test this stuff. --- python3/vimspector/utils.py | 4 +- tests/lib/run_test.vim | 14 +++- tests/python/Test_ExpandReferencesInDict.py | 79 +++++++++++++++++++++ tests/utils.test.vim | 28 ++++++++ 4 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 tests/python/Test_ExpandReferencesInDict.py create mode 100644 tests/utils.test.vim diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 5ab9872..770b750 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -460,8 +460,8 @@ VAR_MATCH = re.compile( {(?P # or An {id:default} - default param, as (?P[_a-z][_a-z0-9]*) # an ID : # then a colon - (?P(?:[^}]|\})*) # then anything up to }, or a \} - )} | # + (?P(?:\\}|[^}])+) # then anything up to }, or a \} + )} | # then a } (?P) # or Something else - invalid ) """, diff --git a/tests/lib/run_test.vim b/tests/lib/run_test.vim index 9006af1..b2f1095 100644 --- a/tests/lib/run_test.vim +++ b/tests/lib/run_test.vim @@ -82,6 +82,16 @@ func! Abort( timer_id ) qa! endfunc +func! TestLog( msg ) + if type( a:msg ) == v:t_string + let msg = [ a:msg ] + else + let msg = a:msg + endif + + call extend( s:messages, msg ) +endfunc + func RunTheTest(test) echo 'Executing ' . a:test @@ -152,8 +162,6 @@ func RunTheTest(test) augroup END exe 'call ' . a:test - - au! EarlyExit catch /^\cskipped/ call add(s:messages, ' Skipped') call add(s:skipped, @@ -193,6 +201,8 @@ func RunTheTest(test) call s:TestFailed() endtry + au! EarlyExit + call timer_stop( timer ) " In case 'insertmode' was set and something went wrong, make sure it is diff --git a/tests/python/Test_ExpandReferencesInDict.py b/tests/python/Test_ExpandReferencesInDict.py new file mode 100644 index 0000000..db1ce14 --- /dev/null +++ b/tests/python/Test_ExpandReferencesInDict.py @@ -0,0 +1,79 @@ +import sys +import unittest +from unittest.mock import patch +from vimspector import utils + + +class TestExpandReferencesInDict( unittest.TestCase ): + def test_ExpandReferencesInDict( self ): + mapping = { + 'one': 'one', + 'two': 'TWO', + 'bool': True, + 'words': 'these are some words' + } + calculus = { + 'three': lambda : 1 + 2 + } + CHOICES = { + 'five': '5ive!' + } + + def AskForInput( prompt, default_value = None ): + if default_value is not None: + return default_value + + return 'typed text' + + d = { + 'dollar': '$$', + 'not_a_var': '$${test}', + 'one': '${one}', + 'two': '${one} and ${two}', + 'three': '${three}', + 'four': '${four}', + 'five': '${five}', + 'list': [ '*${words}' ], + 'list1': [ 'start', '*${words}', 'end' ], + 'list2': [ '*${words}', '${three}' ], + 'list3': [ '${one}', '*${words}', 'three' ], + 'dict#json': '{ "key": "value" }', + 'bool#json': 'false', + 'one_default': '${one_default:one}', + 'two_default': '${two_default_1:one} and ${two_default_2:two}', + 'one_default2': '${one_default2:${one\\}}', + 'two_default2': + '${two_default2_1:${one\\}} and ${two_default2_2:${two\\}}', + } + + e = { + 'dollar': '$', + 'not_a_var': '${test}', + 'one': 'one', + 'two': 'one and TWO', + 'three': '3', + 'four': 'typed text', + 'five': '5ive!', + 'list': [ 'these', 'are', 'some', 'words' ], + 'list1': [ 'start', 'these', 'are', 'some', 'words', 'end' ], + 'list2': [ 'these', 'are', 'some', 'words', '3' ], + 'list3': [ 'one', 'these', 'are', 'some', 'words', 'three' ], + 'dict': { + 'key': 'value', + }, + 'bool': False, + 'one_default': 'one', + 'two_default': 'one and two', + 'one_default2': 'one', + 'two_default2': 'one and TWO', + } + + with patch( 'vimspector.utils.AskForInput', side_effect = AskForInput ): + utils.ExpandReferencesInDict( d, mapping, calculus, CHOICES ) + + self.assertDictEqual( d, e ) + + +unittest.main( module=__name__, + testRunner=unittest.TextTestRunner( sys.stdout ), + exit=False ) diff --git a/tests/utils.test.vim b/tests/utils.test.vim new file mode 100644 index 0000000..ad485ce --- /dev/null +++ b/tests/utils.test.vim @@ -0,0 +1,28 @@ +function! SetUp() + call vimspector#test#setup#SetUpWithMappings( v:none ) + py3 import vim + py3 __import__( 'vimspector' ) +endfunction + +function! ClearDown() + call vimspector#test#setup#ClearDown() +endfunction + +function! s:RunPyFile( file_name ) + redir => py_output + try + let v:errmsg = '' + silent! execute 'py3file python/' .. a:file_name + finally + redir END + call TestLog( [ a:file_name .. ' output:' ] + split( py_output, '\n' ) ) + endtry + + if v:errmsg !=# '' + call assert_report( v:errmsg ) + endif +endfunction + +function! Test_ExpandReferencesInDict() + call s:RunPyFile( 'Test_ExpandReferencesInDict.py' ) +endfunction From 7f77842ab89524d2d3b45a4fe7198fb51ac9e0a8 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 6 Feb 2021 22:07:53 +0000 Subject: [PATCH 438/609] Make sure that tests fail properly; ensure that empty string is a valid default --- python3/vimspector/utils.py | 2 +- tests/python/Test_ExpandReferencesInDict.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 770b750..e621d33 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -460,7 +460,7 @@ VAR_MATCH = re.compile( {(?P # or An {id:default} - default param, as (?P[_a-z][_a-z0-9]*) # an ID : # then a colon - (?P(?:\\}|[^}])+) # then anything up to }, or a \} + (?P(?:\\}|[^}])*) # then anything up to }, or a \} )} | # then a } (?P) # or Something else - invalid ) diff --git a/tests/python/Test_ExpandReferencesInDict.py b/tests/python/Test_ExpandReferencesInDict.py index db1ce14..4998350 100644 --- a/tests/python/Test_ExpandReferencesInDict.py +++ b/tests/python/Test_ExpandReferencesInDict.py @@ -5,6 +5,10 @@ from vimspector import utils class TestExpandReferencesInDict( unittest.TestCase ): + def __init__( self, *args, **kwargs ): + super().__init__( *args, **kwargs ) + self.maxDiff = 4096 + def test_ExpandReferencesInDict( self ): mapping = { 'one': 'one', @@ -13,7 +17,7 @@ class TestExpandReferencesInDict( unittest.TestCase ): 'words': 'these are some words' } calculus = { - 'three': lambda : 1 + 2 + 'three': lambda : 1 + 2, } CHOICES = { 'five': '5ive!' @@ -44,6 +48,8 @@ class TestExpandReferencesInDict( unittest.TestCase ): 'one_default2': '${one_default2:${one\\}}', 'two_default2': '${two_default2_1:${one\\}} and ${two_default2_2:${two\\}}', + 'unlikely_name#json#s': 'true', + 'empty_splice': [ '*${empty:}' ], } e = { @@ -66,6 +72,8 @@ class TestExpandReferencesInDict( unittest.TestCase ): 'two_default': 'one and two', 'one_default2': 'one', 'two_default2': 'one and TWO', + 'unlikely_name#json': 'true', + 'empty_splice': [], } with patch( 'vimspector.utils.AskForInput', side_effect = AskForInput ): @@ -74,6 +82,6 @@ class TestExpandReferencesInDict( unittest.TestCase ): self.assertDictEqual( d, e ) -unittest.main( module=__name__, - testRunner=unittest.TextTestRunner( sys.stdout ), - exit=False ) +assert unittest.main( module=__name__, + testRunner=unittest.TextTestRunner( sys.stdout ), + exit=False ).result.wasSuccessful() From 0cc4322b18b54239304723d93c6a3fb76eeb7755 Mon Sep 17 00:00:00 2001 From: Mark Wu Date: Wed, 10 Feb 2021 20:01:18 +0800 Subject: [PATCH 439/609] Update vscode-php-debug to 1.14.9, it works for both Xdebug 2/3 --- python3/vimspector/gadgets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index fecb363..d1b6872 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -376,14 +376,14 @@ GADGETS = { 'enabled': False, 'download': { 'url': - 'https://github.com/felixfbecker/vscode-php-debug/releases/download/' + 'https://github.com/xdebug/vscode-php-debug/releases/download/' '${version}/${file_name}', }, 'all': { - 'version': 'v1.13.0', + 'version': 'v1.14.9', 'file_name': 'php-debug.vsix', 'checksum': - '8a51e593458fd14623c1c89ebab87347b087d67087717f18bcf77bb788052718', + '0c5709cbbffe26b12aa63a88142195a9a045a5d8fca7fe63d62c789fe601630d', }, 'adapters': { 'vscode-php-debug': { From cb922bdc839200092f85485c8332782416e90d0d Mon Sep 17 00:00:00 2001 From: Tim Tyrrell Date: Sun, 14 Feb 2021 11:56:27 -0700 Subject: [PATCH 440/609] Update README typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4dc3580..8e7a036 100644 --- a/README.md +++ b/README.md @@ -1446,7 +1446,7 @@ php Requires: * `install_gadget.py --force-enable-node` -* For installation, a Node.js environemt that is < node 12. I believe this is an +* For installation, a Node.js environment that is < node 12. I believe this is an incompatibility with gulp. Advice, use [nvm][] with `nvm install --lts 10; nvm use --lts 10; ./install_gadget.py --force-enable-node ...` * Options described here: From 7c4eef909626e6cc1e9fdf9baba201e42210e7d0 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 29 Nov 2020 00:53:11 -0500 Subject: [PATCH 441/609] finally got float window working with variable evaluation --- autoload/vimspector/internal/balloon.vim | 12 ++++++++++++ autoload/vimspector/internal/state.vim | 22 ++++++++++++++++++++++ python3/vimspector/utils.py | 4 ++++ 3 files changed, 38 insertions(+) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 360ed08..bdceec4 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -29,6 +29,18 @@ function! vimspector#internal#balloon#BalloonExpr() abort \ . 'vim.eval( "v:beval_text" ) )' ) endfunction +" Returns: py.ShowBalloon( winnr, expresssion ) +function! vimspector#internal#balloon#Tooltip() abort + " winnr + 1 because for *no good reason* winnr is 0 based here unlike + " everywhere else + " int() because for *no good reason* winnr is a string. + return py3eval('_vimspector_session.ShowBalloon(' + \ . 'int( vim.eval( "v:beval_winnr" ) ) + 1,' + \ . 'vim.eval( "expand(\"\")" ) )' ) +endfunction + + + " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index f1e690a..d9d8cd2 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -47,6 +47,28 @@ function! vimspector#internal#state#GetAPIPrefix() abort return s:prefix endfunction +" REMOVEME: this is just a temporary thing to get float window working +" Returns: py.ShowBalloon( winnr, expresssion ) +function! vimspector#internal#state#Tooltip() abort + " winnr + 1 because for *no good reason* winnr is 0 based here unlike + " everywhere else + " int() because for *no good reason* winnr is a string. + return py3eval('_vimspector_session.ShowBalloon(' + \ . 'int( vim.eval( "winnr()" ) ) ,' + \ . 'vim.eval( "expand(\"\")" ) )' ) +endfunction + +function! vimspector#internal#state#TooltipExec(body) abort + let buf = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(buf, 0, -1, v:true, a:body) + let opts = { 'relative': 'cursor', 'width': 40, 'height': 2, 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } + let g:float_win = nvim_open_win(buf, 0, opts) + + augroup vimspector#internal#balloon#nvim_float + autocmd! + autocmd CursorMoved * :call nvim_win_close(g:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float + augroup END +endfunction " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index e621d33..576c493 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -635,6 +635,10 @@ def ParseVariables( variables_list, def DisplayBaloon( is_term, display ): + if int(vim.eval("has('nvim')")): + vim.eval("vimspector#internal#state#TooltipExec({})".format(display)) + return + if not is_term: display = '\n'.join( display ) # To enable the Windows GUI to display the balloon correctly From 4c0ba92ac61db2ea422270d9b997d0099116825d Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 8 Dec 2020 21:03:40 -0500 Subject: [PATCH 442/609] working on variables view --- autoload/vimspector/internal/state.vim | 20 +++++- python3/vimspector/variables.py | 91 ++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index d9d8cd2..ade14bb 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -58,10 +58,28 @@ function! vimspector#internal#state#Tooltip() abort \ . 'vim.eval( "expand(\"\")" ) )' ) endfunction +function! vimspector#internal#state#CreateTooltip() abort + +endfunction + function! vimspector#internal#state#TooltipExec(body) abort let buf = nvim_create_buf(v:false, v:true) call nvim_buf_set_lines(buf, 0, -1, v:true, a:body) - let opts = { 'relative': 'cursor', 'width': 40, 'height': 2, 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } + + " get the max width on a line + let width = 0 + let maxWidth = winwidth() + + for w in a:body + let width = max(len(w), width) + " reached the max size, no point in looping more + if width > maxWidth + let width = maxWidth + break + endif + endfor + + let opts = { 'relative': 'cursor', 'width': width, 'height': len(a:body), 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } let g:float_win = nvim_open_win(buf, 0, opts) augroup vimspector#internal#balloon#nvim_float diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 4028722..a67c06e 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -371,6 +371,10 @@ class VariablesView( object ): }, } ) + + + + def _DrawVariables( self, view, variables, indent ): assert indent > 0 for variable in variables: @@ -531,5 +535,92 @@ class VariablesView( object ): syntax, self._vars.buf, self._watch.buf ) +class VariableEval + def __init__(self, stackTrace): + self.stackTrace = stackTrace + self._connection = None + self.variable = Variable() + def _ConsumeVariables(self, parent, message): + new_variables = [] + for variable_body in message[ 'body' ][ 'variables' ]: + if parent.variables is None: + parent.variables = [] + + # Find the variable in parent + found = False + for index, v in enumerate( parent.variables ): + if v.variable[ 'name' ] == variable_body[ 'name' ]: + variable = v + found = True + break + + if not found: + variable = Variable( variable_body ) + else: + variable.Update( variable_body ) + + new_variables.append( variable ) + + if variable.IsExpandable() and variable.IsExpanded(): + self._connection.DoRequest( partial( self._ConsumeVariables, + draw, + variable ), { + 'command': 'variables', + 'arguments': { + 'variablesReference': variable.VariablesReference() + }, + } ) + + parent.variables = new_variables + + def _DrawVariables( self, variables, indent ): + assert indent > 0 + for variable in variables: + line = utils.AppendToBuffer( + view.buf, + '{indent}{marker}{icon} {name} ({type_}): {value}'.format( + # We borrow 1 space of indent to draw the change marker + indent = ' ' * ( indent - 1 ), + marker = '*' if variable.changed else ' ', + icon = '+' if ( variable.IsExpandable() + and not variable.IsExpanded() ) else '-', + name = variable.variable[ 'name' ], + type_ = variable.variable.get( 'type', '' ), + value = variable.variable.get( 'value', + '' ) ).split( '\n' ) ) + view.lines[ line ] = variable + + if variable.ShouldDrawDrillDown(): + self._DrawVariables( view, variable.variables, indent + 2 ) + + def AddVariableEval( self, bufid ): + current_symbol = vim.eval("expand('')") + + def handler( message ): + # TODO: this result count be expandable, but we have no way to allow the + # user to interact with the balloon to expand it, unless we use a popup + # instead, but even then we don't really want to trap the cursor. + _ConsumeVariables(self, self.variable, message) + variableEvalView = View( variables_win, {}, self._DrawScopes ) + _DrawVariables(self, self.variable, 0) + + + def failure_handler(reason, message): + vim.eval("echom {}".format(message)) + + self._connection.DoRequest( handler, { + 'command': 'evaluate', + 'arguments': { + 'expression': expression, + 'frameId': frame[ 'id' ], + 'context': 'hover', + } + }, failure_handler ) + + def ConnectionUp( self, connection ): + self._connection = connection + + def ConnectionClosed(self): + self._connection = None # vim: sw=2 From 07858cc250291f18b5621a88615fa6061c65302b Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 8 Dec 2020 23:00:13 -0500 Subject: [PATCH 443/609] adding on the fly eval of inside a floating window --- autoload/vimspector/internal/state.vim | 12 +- python3/vimspector/debug_session.py | 20 +++ python3/vimspector/variables.py | 165 +++++++++++-------------- 3 files changed, 103 insertions(+), 94 deletions(-) diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index ade14bb..6d3aad4 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -53,7 +53,7 @@ function! vimspector#internal#state#Tooltip() abort " winnr + 1 because for *no good reason* winnr is 0 based here unlike " everywhere else " int() because for *no good reason* winnr is a string. - return py3eval('_vimspector_session.ShowBalloon(' + return py3eval('_vimspector_session.ShowTooltip(' \ . 'int( vim.eval( "winnr()" ) ) ,' \ . 'vim.eval( "expand(\"\")" ) )' ) endfunction @@ -62,16 +62,20 @@ function! vimspector#internal#state#CreateTooltip() abort endfunction +function! vimspector#internal#state#ShowTooltip() abort + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ) )') +endfunction + function! vimspector#internal#state#TooltipExec(body) abort let buf = nvim_create_buf(v:false, v:true) call nvim_buf_set_lines(buf, 0, -1, v:true, a:body) " get the max width on a line let width = 0 - let maxWidth = winwidth() + let maxWidth = winwidth(0) for w in a:body - let width = max(len(w), width) + let width = max([len(w), width]) " reached the max size, no point in looping more if width > maxWidth let width = maxWidth @@ -79,8 +83,10 @@ function! vimspector#internal#state#TooltipExec(body) abort endif endfor + let opts = { 'relative': 'cursor', 'width': width, 'height': len(a:body), 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } let g:float_win = nvim_open_win(buf, 0, opts) + call setwinvar(g:float_win, '&wrap', 0) augroup vimspector#internal#balloon#nvim_float autocmd! diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index ea72651..e741d88 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -538,6 +538,26 @@ class DebugSession( object ): def DeleteWatch( self ): self._variablesView.DeleteWatch() + + @IfConnected() + def ShowTooltip(self, winnr, expression): + """Proxy: ballonexpr -> variables.ShowBallon""" + frame = self._stackTraceView.GetCurrentFrame() + # Check if RIP is in a frame + if frame is None: + self._logger.debug( 'Balloon: Not in a stack frame' ) + return '' + + # Check if cursor in code window + if winnr != int( self._codeView._window.number ): + self._logger.debug( 'Winnr %s is not the code window %s', + winnr, + self._codeView._window.number ) + return '' + + # Return variable aware function + return self._variablesView.VariableEval(frame, expression) + @IfConnected() def ShowBalloon( self, winnr, expression ): """Proxy: ballonexpr -> variables.ShowBallon""" diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index a67c06e..ec18a71 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -125,9 +125,9 @@ class View: def __init__( self, win, lines, draw ): self.lines = lines self.draw = draw - self.buf = win.buffer - - utils.SetUpUIWindow( win ) + if (win is not None): + self.buf = win.buffer + utils.SetUpUIWindow( win ) class VariablesView( object ): @@ -138,6 +138,8 @@ class VariablesView( object ): self._connection = None self._current_syntax = '' + self._variable_eval = None + def AddExpandMappings(): vim.command( 'nnoremap ' ':call vimspector#ExpandVariable()' ) @@ -267,6 +269,75 @@ class VariablesView( object ): }, } ) + def _DrawEval(self): + # TODO: create vim functin that creates a floating window and returns window id + # use that window id to retrieve the buffer + # use that buffer in order to populate the results of the query + indent = 0 + icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' + + line = utils.AppendToBuffer( self._vars.buf, + '{0}{1} Scope: {2}'.format( + ' ' * indent, + icon, + self._variable_eval.scope[ 'name' ] ) ) + self._vars.lines[ line ] = self._variable_eval + + if self._variable_eval.ShouldDrawDrillDown(): + indent += 2 + # replace with a newly created view for the purposes of evaluation + self._DrawVariables( self._vars, self._variable_eval.variables, indent ) + + # vim.eval("vimspector#internal#state#TooltipExec({})".format([self._variable_eval])) + + + def VariableEval(self, frame, expression): + """Callback to display variable under cursor `:h ballonexpr`""" + if not self._connection: + return '' + + def handler( message ): + # TODO: this result count be expandable, but we have no way to allow the + # user to interact with the balloon to expand it, unless we use a popup + # instead, but even then we don't really want to trap the cursor. + body = message[ 'body' ] + result = body[ 'result' ] + if result is None: + result = 'null' + display = [ + 'Type: ' + body.get( 'type', '' ), + 'Value: ' + result + ] + + self._variable_eval = Scope(body) + + self._connection.DoRequest( partial( self._ConsumeVariables, + self._DrawEval, + self._variable_eval ), { + 'command': 'variables', + 'arguments': { + 'variablesReference': self._variable_eval.VariablesReference(), + }, + } ) + + + def failure_handler( reason, message ): + display = [ reason ] + utils.DisplayBaloon( self._is_term, display ) + + # Send async request + self._connection.DoRequest( handler, { + 'command': 'evaluate', + 'arguments': { + 'expression': expression, + 'frameId': frame[ 'id' ], + 'context': 'hover', + } + }, failure_handler ) + + # Return working (meanwhile) + return '...' + def AddWatch( self, frame, expression ): watch = { 'expression': expression, @@ -535,92 +606,4 @@ class VariablesView( object ): syntax, self._vars.buf, self._watch.buf ) -class VariableEval - def __init__(self, stackTrace): - self.stackTrace = stackTrace - self._connection = None - self.variable = Variable() - - def _ConsumeVariables(self, parent, message): - new_variables = [] - for variable_body in message[ 'body' ][ 'variables' ]: - if parent.variables is None: - parent.variables = [] - - # Find the variable in parent - found = False - for index, v in enumerate( parent.variables ): - if v.variable[ 'name' ] == variable_body[ 'name' ]: - variable = v - found = True - break - - if not found: - variable = Variable( variable_body ) - else: - variable.Update( variable_body ) - - new_variables.append( variable ) - - if variable.IsExpandable() and variable.IsExpanded(): - self._connection.DoRequest( partial( self._ConsumeVariables, - draw, - variable ), { - 'command': 'variables', - 'arguments': { - 'variablesReference': variable.VariablesReference() - }, - } ) - - parent.variables = new_variables - - def _DrawVariables( self, variables, indent ): - assert indent > 0 - for variable in variables: - line = utils.AppendToBuffer( - view.buf, - '{indent}{marker}{icon} {name} ({type_}): {value}'.format( - # We borrow 1 space of indent to draw the change marker - indent = ' ' * ( indent - 1 ), - marker = '*' if variable.changed else ' ', - icon = '+' if ( variable.IsExpandable() - and not variable.IsExpanded() ) else '-', - name = variable.variable[ 'name' ], - type_ = variable.variable.get( 'type', '' ), - value = variable.variable.get( 'value', - '' ) ).split( '\n' ) ) - view.lines[ line ] = variable - - if variable.ShouldDrawDrillDown(): - self._DrawVariables( view, variable.variables, indent + 2 ) - - def AddVariableEval( self, bufid ): - current_symbol = vim.eval("expand('')") - - def handler( message ): - # TODO: this result count be expandable, but we have no way to allow the - # user to interact with the balloon to expand it, unless we use a popup - # instead, but even then we don't really want to trap the cursor. - _ConsumeVariables(self, self.variable, message) - variableEvalView = View( variables_win, {}, self._DrawScopes ) - _DrawVariables(self, self.variable, 0) - - - def failure_handler(reason, message): - vim.eval("echom {}".format(message)) - - self._connection.DoRequest( handler, { - 'command': 'evaluate', - 'arguments': { - 'expression': expression, - 'frameId': frame[ 'id' ], - 'context': 'hover', - } - }, failure_handler ) - - def ConnectionUp( self, connection ): - self._connection = connection - - def ConnectionClosed(self): - self._connection = None # vim: sw=2 From 7432be95329c0fe1ed531c75acb672be4646e835 Mon Sep 17 00:00:00 2001 From: dsych Date: Wed, 9 Dec 2020 23:19:32 -0500 Subject: [PATCH 444/609] implemented dynamic float windows for nvim --- autoload/vimspector/internal/state.vim | 27 ++++++---- python3/vimspector/variables.py | 74 +++++++++++++------------- 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index 6d3aad4..b80a4b7 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -47,19 +47,24 @@ function! vimspector#internal#state#GetAPIPrefix() abort return s:prefix endfunction -" REMOVEME: this is just a temporary thing to get float window working -" Returns: py.ShowBalloon( winnr, expresssion ) -function! vimspector#internal#state#Tooltip() abort - " winnr + 1 because for *no good reason* winnr is 0 based here unlike - " everywhere else - " int() because for *no good reason* winnr is a string. - return py3eval('_vimspector_session.ShowTooltip(' - \ . 'int( vim.eval( "winnr()" ) ) ,' - \ . 'vim.eval( "expand(\"\")" ) )' ) -endfunction - function! vimspector#internal#state#CreateTooltip() abort + let buf = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(buf, 0, -1, v:true, []) + " default the dimensions for now. they can be easily overwritten later + let opts = { 'relative': 'cursor', 'width': 50, 'height': 2, 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } + let g:float_win = nvim_open_win(buf, 0, opts) + call setwinvar(g:float_win, '&wrap', 0) + + call win_gotoid(g:float_win) + + " make sure we clean up the float after it loses focus + augroup vimspector#internal#balloon#nvim_float + autocmd! + autocmd WinLeave * :call nvim_win_close(g:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float + augroup END + + return g:float_win endfunction function! vimspector#internal#state#ShowTooltip() abort diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index ec18a71..7019168 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -139,6 +139,8 @@ class VariablesView( object ): self._current_syntax = '' self._variable_eval = None + self._variable_eval_view = None + self._variable_eval_win = None def AddExpandMappings(): vim.command( 'nnoremap ' @@ -270,25 +272,12 @@ class VariablesView( object ): } ) def _DrawEval(self): - # TODO: create vim functin that creates a floating window and returns window id - # use that window id to retrieve the buffer - # use that buffer in order to populate the results of the query - indent = 0 - icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' + self._variable_eval_win.height = min(5, len(self._variable_eval.variables)) + with utils.RestoreCursorPosition(): + utils.ClearBuffer( self._variable_eval_view.buf ) + icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' - line = utils.AppendToBuffer( self._vars.buf, - '{0}{1} Scope: {2}'.format( - ' ' * indent, - icon, - self._variable_eval.scope[ 'name' ] ) ) - self._vars.lines[ line ] = self._variable_eval - - if self._variable_eval.ShouldDrawDrillDown(): - indent += 2 - # replace with a newly created view for the purposes of evaluation - self._DrawVariables( self._vars, self._variable_eval.variables, indent ) - - # vim.eval("vimspector#internal#state#TooltipExec({})".format([self._variable_eval])) + self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 ) def VariableEval(self, frame, expression): @@ -297,28 +286,38 @@ class VariablesView( object ): return '' def handler( message ): - # TODO: this result count be expandable, but we have no way to allow the - # user to interact with the balloon to expand it, unless we use a popup - # instead, but even then we don't really want to trap the cursor. body = message[ 'body' ] - result = body[ 'result' ] - if result is None: - result = 'null' - display = [ - 'Type: ' + body.get( 'type', '' ), - 'Value: ' + result - ] self._variable_eval = Scope(body) - self._connection.DoRequest( partial( self._ConsumeVariables, - self._DrawEval, - self._variable_eval ), { - 'command': 'variables', - 'arguments': { - 'variablesReference': self._variable_eval.VariablesReference(), - }, - } ) + float_win_id = vim.eval("vimspector#internal#state#CreateTooltip()") + self._variable_eval_win = vim.current.window + + with utils.LetCurrentWindow( self._variable_eval_win ): + vim.command( 'nnoremap ' + ':call vimspector#ExpandVariable()' ) + vim.command( 'nnoremap ' + ':quit' ) + vim.command( 'nnoremap <2-LeftMouse> ' + ':call vimspector#ExpandVariable()' ) + + + if(self._variable_eval.VariablesReference() > 0): + self._variable_eval_view = View(self._variable_eval_win, {}, self._DrawEval) + self._connection.DoRequest( partial( self._ConsumeVariables, + self._DrawEval, + self._variable_eval ), { + 'command': 'variables', + 'arguments': { + 'variablesReference': self._variable_eval.VariablesReference(), + }, + } ) + else: + # in case that there is nothing to expand, we need to simulate a response from 'variables' request + # it returns [Variable] + self._variable_eval_view = View(self._variable_eval_win, {}, self._DrawEval) + self._variable_eval.variables = [Variable({'name': expression, 'value': body['result']})] + self._DrawEval() def failure_handler( reason, message ): @@ -414,6 +413,8 @@ class VariablesView( object ): view = self._vars elif vim.current.buffer == self._watch.buf: view = self._watch + elif vim.current.buffer == self._variable_eval_view.buf: + view = self._variable_eval_view else: return @@ -542,7 +543,6 @@ class VariablesView( object ): variable = v found = True break - if not found: variable = Variable( variable_body ) else: From 4e994968ffff31cdf30030f66b94a36b861468d7 Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 12 Dec 2020 17:48:50 -0500 Subject: [PATCH 445/609] added popup support for vim, still need to figure out how to expand variables --- autoload/vimspector/internal/state.vim | 97 +++++++++++++++++++++----- python3/vimspector/variables.py | 21 ++---- 2 files changed, 88 insertions(+), 30 deletions(-) diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index b80a4b7..bf2abde 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -47,24 +47,89 @@ function! vimspector#internal#state#GetAPIPrefix() abort return s:prefix endfunction +let s:float_win = 0 + function! vimspector#internal#state#CreateTooltip() abort - let buf = nvim_create_buf(v:false, v:true) - call nvim_buf_set_lines(buf, 0, -1, v:true, []) + if has('nvim') + let buf = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(buf, 0, -1, v:true, []) - " default the dimensions for now. they can be easily overwritten later - let opts = { 'relative': 'cursor', 'width': 50, 'height': 2, 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } - let g:float_win = nvim_open_win(buf, 0, opts) - call setwinvar(g:float_win, '&wrap', 0) + " default the dimensions for now. they can be easily overwritten later + let opts = { + \ 'relative': 'cursor', + \ 'width': 50, + \ 'height': 2, + \ 'col': 0, + \ 'row': 1, + \ 'anchor': 'NW', + \ 'style': 'minimal' + \ } + let s:float_win = nvim_open_win(buf, 0, opts) + call setwinvar(s:float_win, '&wrap', 1) + call setwinvar(s:float_win, '&cursorline', 1) - call win_gotoid(g:float_win) + call win_gotoid(s:float_win) - " make sure we clean up the float after it loses focus - augroup vimspector#internal#balloon#nvim_float - autocmd! - autocmd WinLeave * :call nvim_win_close(g:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float - augroup END + nnoremap :call vimspector#ExpandVariable() + nnoremap :quit + nnoremap <2-LeftMouse>:call vimspector#ExpandVariable() - return g:float_win + " make sure we clean up the float after it loses focus + augroup vimspector#internal#balloon#nvim_float + autocmd! + autocmd WinLeave * :call nvim_win_close(s:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float + augroup END + + else + " assume we are inside vim + func! MyFilter(winid, key) + if index(['j', 'k', 'h', 'l'], a:key) >= 0 + call win_execute(a:winid, ':normal '.a:key) + " do something + return 1 + elseif a:key == "\" + echo 'pressed left mouse' + let mouse_coords = getmousepos() + " close the popup if mouse is clicked outside the window + if mouse_coords['winid'] != a:winid + call popup_close(a:winid) + endif + + echo 'clicked line '.mouse_coords['line'] + call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") + return 1 + elseif a:key == "\" + echo 'pressed enter at line '.line(".", a:winid) + echo 'here' + call vimspector#ExpandVariable() + + return 1 + elseif a:key == "\" + call popup_close(a:winid) + let s:float_win = 0 + return 1 + endif + return 0 + endfunc + + if s:float_win != 0 + popup_close(s:float_win) + endif + + let s:float_win = popup_create([], #{ + \ filter: "MyFilter", + \ cursorline: 1, + \ wrap: 1, + \ filtermode: "n", + \ maxwidth: 50, + \ maxheight: 5, + \ scrollbar: 1, + \ moved: "any", + \ }) + + endif + + return s:float_win endfunction function! vimspector#internal#state#ShowTooltip() abort @@ -90,12 +155,12 @@ function! vimspector#internal#state#TooltipExec(body) abort let opts = { 'relative': 'cursor', 'width': width, 'height': len(a:body), 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } - let g:float_win = nvim_open_win(buf, 0, opts) - call setwinvar(g:float_win, '&wrap', 0) + let s:float_win = nvim_open_win(buf, 0, opts) + call setwinvar(s:float_win, '&wrap', 0) augroup vimspector#internal#balloon#nvim_float autocmd! - autocmd CursorMoved * :call nvim_win_close(g:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float + autocmd CursorMoved * :call nvim_win_close(s:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float augroup END endfunction " Boilerplate {{{ diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 7019168..f7f8f02 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -140,7 +140,6 @@ class VariablesView( object ): self._variable_eval = None self._variable_eval_view = None - self._variable_eval_win = None def AddExpandMappings(): vim.command( 'nnoremap ' @@ -272,7 +271,6 @@ class VariablesView( object ): } ) def _DrawEval(self): - self._variable_eval_win.height = min(5, len(self._variable_eval.variables)) with utils.RestoreCursorPosition(): utils.ClearBuffer( self._variable_eval_view.buf ) icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' @@ -291,19 +289,14 @@ class VariablesView( object ): self._variable_eval = Scope(body) float_win_id = vim.eval("vimspector#internal#state#CreateTooltip()") - self._variable_eval_win = vim.current.window - - with utils.LetCurrentWindow( self._variable_eval_win ): - vim.command( 'nnoremap ' - ':call vimspector#ExpandVariable()' ) - vim.command( 'nnoremap ' - ':quit' ) - vim.command( 'nnoremap <2-LeftMouse> ' - ':call vimspector#ExpandVariable()' ) + float_buf_nr = int(vim.eval("winbufnr({})".format(float_win_id))) + # since vim's popup cant be focused there is no way + # to get a reference to its window + # we will emulate python's window object overselves + self._variable_eval_view = View(type('__vim__window__', (object,), {'options': {}, 'buffer': vim.buffers[float_buf_nr]}), {}, self._DrawEval) if(self._variable_eval.VariablesReference() > 0): - self._variable_eval_view = View(self._variable_eval_win, {}, self._DrawEval) self._connection.DoRequest( partial( self._ConsumeVariables, self._DrawEval, self._variable_eval ), { @@ -315,7 +308,6 @@ class VariablesView( object ): else: # in case that there is nothing to expand, we need to simulate a response from 'variables' request # it returns [Variable] - self._variable_eval_view = View(self._variable_eval_win, {}, self._DrawEval) self._variable_eval.variables = [Variable({'name': expression, 'value': body['result']})] self._DrawEval() @@ -418,7 +410,8 @@ class VariablesView( object ): else: return - current_line = vim.current.window.cursor[ 0 ] + current_line = int(vim.eval("getbufinfo({})['lnum']".format(view.buf.name))) + print(current_line) if current_line not in view.lines: return From 819d6366aca4808e2b9fe8be562fa1adf6b64ae2 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 13 Dec 2020 20:52:16 -0500 Subject: [PATCH 446/609] fully implemented the hover function for vim --- autoload/vimspector.vim | 4 + autoload/vimspector/internal/balloon.vim | 93 ++++++++++++++++++ autoload/vimspector/internal/state.vim | 116 ----------------------- python3/vimspector/debug_session.py | 7 +- python3/vimspector/variables.py | 21 ++-- 5 files changed, 112 insertions(+), 129 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 35ecd03..fd4b0c5 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -523,6 +523,10 @@ function! vimspector#OnBufferCreated( file_name ) abort py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) ) endfunction +function! vimspector#ShowTooltip() abort + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ) )') +endfunction + " Boilerplate {{{ let &cpoptions=s:save_cpo diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index bdceec4..dc34261 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -40,6 +40,99 @@ function! vimspector#internal#balloon#Tooltip() abort endfunction +let s:float_win = 0 + +function! vimspector#internal#balloon#closeCallback() abort + let s:float_win = 0 + return py3eval('_vimspector_session._CleanUpTooltip()') +endfunction + +function! vimspector#internal#balloon#CreateTooltip() abort + if has('nvim') + let buf = nvim_create_buf(v:false, v:true) + " call nvim_buf_set_option(buf, 'modifiable', v:false) + call nvim_buf_set_lines(buf, 0, -1, v:true, []) + + " default the dimensions for now. they can be easily overwritten later + let opts = #{ + \ relative: 'cursor', + \ width: 50, + \ height: 5, + \ col: 0, + \ row: 1, + \ anchor: 'NW', + \ style: 'minimal' + \ } + let s:float_win = nvim_open_win(buf, 0, opts) + call nvim_win_set_option(s:float_win, 'wrap', v:true) + call nvim_win_set_option(s:float_win, 'cursorline', v:true) + call nvim_win_set_option(s:float_win, 'signcolumn', 'no') + call nvim_win_set_option(s:float_win, 'relativenumber', v:false) + call nvim_win_set_option(s:float_win, 'number', v:false) + + noa call win_gotoid(s:float_win) + + nnoremap :call vimspector#ExpandVariable() + nnoremap :quit + nnoremap <2-LeftMouse>:call vimspector#ExpandVariable() + + " make sure we clean up the float after it loses focus + augroup vimspector#internal#balloon#nvim_float + autocmd! + autocmd WinLeave * :call nvim_win_close(s:float_win, 1) | :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float + augroup END + + else + " assume we are inside vim + func! MyFilter(winid, key) + if index(['j', 'k'], a:key) >= 0 + call win_execute(a:winid, ':normal '.a:key) + " do something + return 1 + elseif a:key == "\" + let mouse_coords = getmousepos() + " close the popup if mouse is clicked outside the window + if mouse_coords['winid'] != a:winid + call popup_close(a:winid) + endif + + call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") + return 1 + elseif a:key == "\" + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") + + return 1 + elseif a:key == "\" + call popup_close(a:winid) + call vimspector#internal#balloon#closeCallback() + return 1 + endif + return 0 + endfunc + + if s:float_win != 0 + call popup_close(s:float_win) + call vimspector#internal#balloon#closeCallback() + endif + + let s:float_win = popup_create([], #{ + \ filter: "MyFilter", + \ cursorline: 1, + \ wrap: 1, + \ filtermode: "n", + \ maxwidth: 50, + \ maxheight: 5, + \ scrollbar: 1, + \ moved: "any", + \ }) + " call setbufvar(winbufnr(s:float_win), '&buflisted', 1) + + endif + + return s:float_win +endfunction " Boilerplate {{{ let &cpoptions=s:save_cpo diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index bf2abde..f1e690a 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -47,122 +47,6 @@ function! vimspector#internal#state#GetAPIPrefix() abort return s:prefix endfunction -let s:float_win = 0 - -function! vimspector#internal#state#CreateTooltip() abort - if has('nvim') - let buf = nvim_create_buf(v:false, v:true) - call nvim_buf_set_lines(buf, 0, -1, v:true, []) - - " default the dimensions for now. they can be easily overwritten later - let opts = { - \ 'relative': 'cursor', - \ 'width': 50, - \ 'height': 2, - \ 'col': 0, - \ 'row': 1, - \ 'anchor': 'NW', - \ 'style': 'minimal' - \ } - let s:float_win = nvim_open_win(buf, 0, opts) - call setwinvar(s:float_win, '&wrap', 1) - call setwinvar(s:float_win, '&cursorline', 1) - - call win_gotoid(s:float_win) - - nnoremap :call vimspector#ExpandVariable() - nnoremap :quit - nnoremap <2-LeftMouse>:call vimspector#ExpandVariable() - - " make sure we clean up the float after it loses focus - augroup vimspector#internal#balloon#nvim_float - autocmd! - autocmd WinLeave * :call nvim_win_close(s:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float - augroup END - - else - " assume we are inside vim - func! MyFilter(winid, key) - if index(['j', 'k', 'h', 'l'], a:key) >= 0 - call win_execute(a:winid, ':normal '.a:key) - " do something - return 1 - elseif a:key == "\" - echo 'pressed left mouse' - let mouse_coords = getmousepos() - " close the popup if mouse is clicked outside the window - if mouse_coords['winid'] != a:winid - call popup_close(a:winid) - endif - - echo 'clicked line '.mouse_coords['line'] - call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") - return 1 - elseif a:key == "\" - echo 'pressed enter at line '.line(".", a:winid) - echo 'here' - call vimspector#ExpandVariable() - - return 1 - elseif a:key == "\" - call popup_close(a:winid) - let s:float_win = 0 - return 1 - endif - return 0 - endfunc - - if s:float_win != 0 - popup_close(s:float_win) - endif - - let s:float_win = popup_create([], #{ - \ filter: "MyFilter", - \ cursorline: 1, - \ wrap: 1, - \ filtermode: "n", - \ maxwidth: 50, - \ maxheight: 5, - \ scrollbar: 1, - \ moved: "any", - \ }) - - endif - - return s:float_win -endfunction - -function! vimspector#internal#state#ShowTooltip() abort - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ) )') -endfunction - -function! vimspector#internal#state#TooltipExec(body) abort - let buf = nvim_create_buf(v:false, v:true) - call nvim_buf_set_lines(buf, 0, -1, v:true, a:body) - - " get the max width on a line - let width = 0 - let maxWidth = winwidth(0) - - for w in a:body - let width = max([len(w), width]) - " reached the max size, no point in looping more - if width > maxWidth - let width = maxWidth - break - endif - endfor - - - let opts = { 'relative': 'cursor', 'width': width, 'height': len(a:body), 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } - let s:float_win = nvim_open_win(buf, 0, opts) - call setwinvar(s:float_win, '&wrap', 0) - - augroup vimspector#internal#balloon#nvim_float - autocmd! - autocmd CursorMoved * :call nvim_win_close(s:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float - augroup END -endfunction " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index e741d88..8b2196f 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -520,8 +520,8 @@ class DebugSession( object ): self._stackTraceView.SetCurrentThread() @IfConnected() - def ExpandVariable( self ): - self._variablesView.ExpandVariable() + def ExpandVariable( self, lineNum = -1 ): + self._variablesView.ExpandVariable(lineNum) @IfConnected() def AddWatch( self, expression ): @@ -558,6 +558,9 @@ class DebugSession( object ): # Return variable aware function return self._variablesView.VariableEval(frame, expression) + def _CleanUpTooltip(self): + return self._variablesView._CleanUpTooltip() + @IfConnected() def ShowBalloon( self, winnr, expression ): """Proxy: ballonexpr -> variables.ShowBallon""" diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index f7f8f02..2e9d2b2 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -122,7 +122,7 @@ class View: lines: typing.Dict[ int, Expandable ] draw: typing.Callable - def __init__( self, win, lines, draw ): + def __init__( self, win, lines, draw): self.lines = lines self.draw = draw if (win is not None): @@ -277,6 +277,10 @@ class VariablesView( object ): self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 ) + def _CleanUpTooltip(self): + # remove reference to old tooltip window + self._variable_eval_view = None + return '' def VariableEval(self, frame, expression): """Callback to display variable under cursor `:h ballonexpr`""" @@ -288,12 +292,12 @@ class VariablesView( object ): self._variable_eval = Scope(body) - float_win_id = vim.eval("vimspector#internal#state#CreateTooltip()") + float_win_id = vim.eval("vimspector#internal#balloon#CreateTooltip()") float_buf_nr = int(vim.eval("winbufnr({})".format(float_win_id))) # since vim's popup cant be focused there is no way # to get a reference to its window - # we will emulate python's window object overselves + # we will emulate python's window object ourselves self._variable_eval_view = View(type('__vim__window__', (object,), {'options': {}, 'buffer': vim.buffers[float_buf_nr]}), {}, self._DrawEval) if(self._variable_eval.VariablesReference() > 0): @@ -400,18 +404,17 @@ class VariablesView( object ): watch.result = WatchFailure( reason ) self._DrawWatches() - def ExpandVariable( self ): + def ExpandVariable( self, lineNum = -1 ): if vim.current.buffer == self._vars.buf: view = self._vars elif vim.current.buffer == self._watch.buf: view = self._watch - elif vim.current.buffer == self._variable_eval_view.buf: + elif self._variable_eval_view is not None: view = self._variable_eval_view else: return - current_line = int(vim.eval("getbufinfo({})['lnum']".format(view.buf.name))) - print(current_line) + current_line = vim.current.window.cursor[0] if lineNum <= 0 else lineNum if current_line not in view.lines: return @@ -436,10 +439,6 @@ class VariablesView( object ): }, } ) - - - - def _DrawVariables( self, view, variables, indent ): assert indent > 0 for variable in variables: From d2990d7baed2c31aaf79a777f5b337d6d0b16064 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 13 Dec 2020 22:25:27 -0500 Subject: [PATCH 447/609] making sure to open popup relative to cursor --- autoload/vimspector.vim | 2 +- autoload/vimspector/internal/balloon.vim | 31 ++++++++++++++---------- python3/vimspector/debug_session.py | 4 +-- python3/vimspector/utils.py | 12 +++------ python3/vimspector/variables.py | 11 ++++----- 5 files changed, 29 insertions(+), 31 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index fd4b0c5..d8018a0 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -524,7 +524,7 @@ function! vimspector#OnBufferCreated( file_name ) abort endfunction function! vimspector#ShowTooltip() abort - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ) )') + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ), 0)') endfunction diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index dc34261..89f2b11 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -30,13 +30,8 @@ function! vimspector#internal#balloon#BalloonExpr() abort endfunction " Returns: py.ShowBalloon( winnr, expresssion ) -function! vimspector#internal#balloon#Tooltip() abort - " winnr + 1 because for *no good reason* winnr is 0 based here unlike - " everywhere else - " int() because for *no good reason* winnr is a string. - return py3eval('_vimspector_session.ShowBalloon(' - \ . 'int( vim.eval( "v:beval_winnr" ) ) + 1,' - \ . 'vim.eval( "expand(\"\")" ) )' ) +function! vimspector#internal#balloon#HoverTooltip() abort + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') endfunction @@ -47,11 +42,16 @@ function! vimspector#internal#balloon#closeCallback() abort return py3eval('_vimspector_session._CleanUpTooltip()') endfunction -function! vimspector#internal#balloon#CreateTooltip() abort +function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) + let body = [] + if a:0 > 0 + let body = a:1 + endif + if has('nvim') let buf = nvim_create_buf(v:false, v:true) " call nvim_buf_set_option(buf, 'modifiable', v:false) - call nvim_buf_set_lines(buf, 0, -1, v:true, []) + call nvim_buf_set_lines(buf, 0, -1, v:true, body) " default the dimensions for now. they can be easily overwritten later let opts = #{ @@ -117,7 +117,7 @@ function! vimspector#internal#balloon#CreateTooltip() abort call vimspector#internal#balloon#closeCallback() endif - let s:float_win = popup_create([], #{ + let config = #{ \ filter: "MyFilter", \ cursorline: 1, \ wrap: 1, @@ -125,9 +125,14 @@ function! vimspector#internal#balloon#CreateTooltip() abort \ maxwidth: 50, \ maxheight: 5, \ scrollbar: 1, - \ moved: "any", - \ }) - " call setbufvar(winbufnr(s:float_win), '&buflisted', 1) + \ } + if a:is_hover + let config['mousemoved'] = "word" + let s:float_win = popup_beval(body, config) + else + let config['moved'] = "any" + let s:float_win = popup_atcursor(body, config) + endif endif diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 8b2196f..de5a7c6 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -540,7 +540,7 @@ class DebugSession( object ): @IfConnected() - def ShowTooltip(self, winnr, expression): + def ShowTooltip(self, winnr, expression, is_hover): """Proxy: ballonexpr -> variables.ShowBallon""" frame = self._stackTraceView.GetCurrentFrame() # Check if RIP is in a frame @@ -556,7 +556,7 @@ class DebugSession( object ): return '' # Return variable aware function - return self._variablesView.VariableEval(frame, expression) + return self._variablesView.VariableEval(frame, expression, is_hover) def _CleanUpTooltip(self): return self._variablesView._CleanUpTooltip() diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 576c493..788f03e 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -634,19 +634,13 @@ def ParseVariables( variables_list, return new_variables -def DisplayBaloon( is_term, display ): - if int(vim.eval("has('nvim')")): - vim.eval("vimspector#internal#state#TooltipExec({})".format(display)) - return - +def DisplayBaloon( is_term, display, is_hover = False ): if not is_term: - display = '\n'.join( display ) # To enable the Windows GUI to display the balloon correctly # Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685 - vim.eval( "balloon_show( '' )" ) + display = '\n'.join( display ) - vim.eval( "balloon_show( {0} )".format( - json.dumps( display ) ) ) + return int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format(is_hover, json.dumps( display )) ) ) def GetBufferFilepath( buf ): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 2e9d2b2..71abc45 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -186,7 +186,7 @@ class VariablesView( object ): 'balloonexpr': vim.options[ 'balloonexpr' ], 'balloondelay': vim.options[ 'balloondelay' ], } - vim.options[ 'balloonexpr' ] = 'vimspector#internal#balloon#BalloonExpr()' + vim.options[ 'balloonexpr' ] = 'vimspector#internal#balloon#HoverTooltip()' vim.options[ 'balloondelay' ] = 250 if has_balloon: @@ -282,7 +282,7 @@ class VariablesView( object ): self._variable_eval_view = None return '' - def VariableEval(self, frame, expression): + def VariableEval(self, frame, expression, is_hover): """Callback to display variable under cursor `:h ballonexpr`""" if not self._connection: return '' @@ -291,8 +291,7 @@ class VariablesView( object ): body = message[ 'body' ] self._variable_eval = Scope(body) - - float_win_id = vim.eval("vimspector#internal#balloon#CreateTooltip()") + float_win_id = utils.DisplayBaloon(self._is_term, [], is_hover) float_buf_nr = int(vim.eval("winbufnr({})".format(float_win_id))) # since vim's popup cant be focused there is no way @@ -318,7 +317,7 @@ class VariablesView( object ): def failure_handler( reason, message ): display = [ reason ] - utils.DisplayBaloon( self._is_term, display ) + utils.DisplayBaloon( self._is_term, display, is_hover ) # Send async request self._connection.DoRequest( handler, { @@ -331,7 +330,7 @@ class VariablesView( object ): }, failure_handler ) # Return working (meanwhile) - return '...' + return '' def AddWatch( self, frame, expression ): watch = { From 747ad9b8042a9d0af3635628e1285381b1413fac Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 22 Dec 2020 13:03:37 -0500 Subject: [PATCH 448/609] removing literal dictionary syntax, as it is not included in nvim-4.4 --- autoload/vimspector/internal/balloon.vim | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 89f2b11..866eb65 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -54,14 +54,14 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) call nvim_buf_set_lines(buf, 0, -1, v:true, body) " default the dimensions for now. they can be easily overwritten later - let opts = #{ - \ relative: 'cursor', - \ width: 50, - \ height: 5, - \ col: 0, - \ row: 1, - \ anchor: 'NW', - \ style: 'minimal' + let opts = { + \ 'relative': 'cursor', + \ 'width': 50, + \ 'height': 5, + \ 'col': 0, + \ 'row': 1, + \ 'anchor': 'NW', + \ 'style': 'minimal' \ } let s:float_win = nvim_open_win(buf, 0, opts) call nvim_win_set_option(s:float_win, 'wrap', v:true) @@ -117,14 +117,14 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) call vimspector#internal#balloon#closeCallback() endif - let config = #{ - \ filter: "MyFilter", - \ cursorline: 1, - \ wrap: 1, - \ filtermode: "n", - \ maxwidth: 50, - \ maxheight: 5, - \ scrollbar: 1, + let config = { + \ 'filter': "MyFilter", + \ 'cursorline': 1, + \ 'wrap': 1, + \ 'filtermode': "n", + \ 'maxwidth': 50, + \ 'maxheight': 5, + \ 'scrollbar': 1, \ } if a:is_hover let config['mousemoved'] = "word" From 0ced5eb1d42269c03ef9aa3ad7ff8a58e68857d7 Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 22 Dec 2020 13:57:25 -0500 Subject: [PATCH 449/609] splitting mouse and cursor filtering to avoid trapping keyboard for hover --- autoload/vimspector/internal/balloon.vim | 34 ++++++++++++++++++------ 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 866eb65..007c8b0 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -84,19 +84,34 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) else " assume we are inside vim - func! MyFilter(winid, key) - if index(['j', 'k'], a:key) >= 0 - call win_execute(a:winid, ':normal '.a:key) - " do something - return 1 - elseif a:key == "\" + func! MouseFilter(winid, key) + if index(["\", "\<2-leftmouse>"], a:key) >= 0 let mouse_coords = getmousepos() " close the popup if mouse is clicked outside the window if mouse_coords['winid'] != a:winid call popup_close(a:winid) + call vimspector#internal#balloon#closeCallback() endif + " place the cursor according to the click call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") + + " expand the variable if we got double click + if a:key == "\<2-leftmouse>" && mouse_coords['winid'] == a:winid + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") + endif + + return 1 + endif + return 0 + endfunc + + func! CursorFiler(winid, key) + if index(['j', 'k'], a:key) >= 0 + call win_execute(a:winid, ':normal '.a:key) + return 1 elseif a:key == "\" " forward line number to python, since vim does not allow us to focus @@ -107,8 +122,10 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) elseif a:key == "\" call popup_close(a:winid) call vimspector#internal#balloon#closeCallback() + return 1 endif + return 0 endfunc @@ -118,7 +135,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endif let config = { - \ 'filter': "MyFilter", \ 'cursorline': 1, \ 'wrap': 1, \ 'filtermode': "n", @@ -127,9 +143,11 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'scrollbar': 1, \ } if a:is_hover - let config['mousemoved'] = "word" + let config['filter'] = "MouseFilter" + let config['mousemoved'] = [0, 0, 0] let s:float_win = popup_beval(body, config) else + let config['filter'] = "CursorFiler" let config['moved'] = "any" let s:float_win = popup_atcursor(body, config) endif From 4617250f412017a38afe7b2f77ecfa962e17bada Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 22 Dec 2020 23:56:18 -0500 Subject: [PATCH 450/609] adding border to popup --- autoload/vimspector/internal/balloon.vim | 63 +++++++++++++++++++----- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 007c8b0..e2f041d 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -32,13 +32,23 @@ endfunction " Returns: py.ShowBalloon( winnr, expresssion ) function! vimspector#internal#balloon#HoverTooltip() abort return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') + endfunction let s:float_win = 0 +let s:nvim_related_win = 0 function! vimspector#internal#balloon#closeCallback() abort + if has('nvim') + call nvim_win_close(s:float_win, v:true) + call nvim_win_close(s:nvim_related_win, v:true) + else + call popup_close(s:float_win) + endif + let s:float_win = 0 + let s:nvim_related_win = 0 return py3eval('_vimspector_session._CleanUpTooltip()') endfunction @@ -48,22 +58,53 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let body = a:1 endif + " tooltip dimensions + let max_height = 5 + let max_width = 50 + if has('nvim') - let buf = nvim_create_buf(v:false, v:true) - " call nvim_buf_set_option(buf, 'modifiable', v:false) - call nvim_buf_set_lines(buf, 0, -1, v:true, body) + " generate border for the float window by creating a background buffer and + " overlaying the content buffer + " see https://github.com/neovim/neovim/issues/9718#issuecomment-546603628 + let top = "╭" . repeat("─", max_width) . "╮" + let mid = "│" . repeat(" ", max_width) . "│" + let bot = "╰" . repeat("─", max_width) . "╯" + let lines = [top] + repeat([mid], max_height) + [bot] + + let buf_id = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(buf_id, 0, -1, v:true, lines) " default the dimensions for now. they can be easily overwritten later let opts = { \ 'relative': 'cursor', - \ 'width': 50, - \ 'height': 5, + \ 'width': max_width + 2, + \ 'height': max_height + 2, \ 'col': 0, \ 'row': 1, \ 'anchor': 'NW', \ 'style': 'minimal' \ } - let s:float_win = nvim_open_win(buf, 0, opts) + " this is the border window + let s:nvim_related_win = nvim_open_win(buf_id, 0, opts) + call nvim_win_set_option(s:nvim_related_win, 'wrap', v:true) + call nvim_win_set_option(s:nvim_related_win, 'cursorline', v:true) + call nvim_win_set_option(s:nvim_related_win, 'signcolumn', 'no') + call nvim_win_set_option(s:nvim_related_win, 'relativenumber', v:false) + call nvim_win_set_option(s:nvim_related_win, 'number', v:false) + + " when calculating where to display the content window, we need to account + " for the border + set winhl=Normal:Floating + let opts.row += 1 + let opts.height -= 2 + let opts.col += 2 + let opts.width -= 4 + + " create the content window + let buf_id = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(buf_id, 0, -1, v:true, body) + let s:float_win = nvim_open_win(buf_id, v:false, opts) + call nvim_win_set_option(s:float_win, 'wrap', v:true) call nvim_win_set_option(s:float_win, 'cursorline', v:true) call nvim_win_set_option(s:float_win, 'signcolumn', 'no') @@ -79,7 +120,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) " make sure we clean up the float after it loses focus augroup vimspector#internal#balloon#nvim_float autocmd! - autocmd WinLeave * :call nvim_win_close(s:float_win, 1) | :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float + autocmd WinLeave * :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float augroup END else @@ -89,7 +130,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let mouse_coords = getmousepos() " close the popup if mouse is clicked outside the window if mouse_coords['winid'] != a:winid - call popup_close(a:winid) call vimspector#internal#balloon#closeCallback() endif @@ -120,7 +160,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) return 1 elseif a:key == "\" - call popup_close(a:winid) call vimspector#internal#balloon#closeCallback() return 1 @@ -130,7 +169,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endfunc if s:float_win != 0 - call popup_close(s:float_win) call vimspector#internal#balloon#closeCallback() endif @@ -138,9 +176,10 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'cursorline': 1, \ 'wrap': 1, \ 'filtermode': "n", - \ 'maxwidth': 50, - \ 'maxheight': 5, + \ 'maxwidth': max_width, + \ 'maxheight': max_height, \ 'scrollbar': 1, + \ 'border': [] \ } if a:is_hover let config['filter'] = "MouseFilter" From b95ae00054accd645dba06affc026dad85c331d7 Mon Sep 17 00:00:00 2001 From: dsych Date: Wed, 23 Dec 2020 00:17:19 -0500 Subject: [PATCH 451/609] enabling a close button for popup --- autoload/vimspector/internal/balloon.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index e2f041d..fd37e79 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -184,6 +184,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) if a:is_hover let config['filter'] = "MouseFilter" let config['mousemoved'] = [0, 0, 0] + let config['close'] = "button" let s:float_win = popup_beval(body, config) else let config['filter'] = "CursorFiler" From f60b259dbc499b25cb5c8549cd365e22e2e8f653 Mon Sep 17 00:00:00 2001 From: dsych Date: Wed, 23 Dec 2020 23:12:57 -0500 Subject: [PATCH 452/609] adding type information for simple types --- python3/vimspector/variables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 71abc45..f5930f8 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -311,7 +311,7 @@ class VariablesView( object ): else: # in case that there is nothing to expand, we need to simulate a response from 'variables' request # it returns [Variable] - self._variable_eval.variables = [Variable({'name': expression, 'value': body['result']})] + self._variable_eval.variables = [Variable({'name': expression, 'type': body['type'], 'value': body['result']})] self._DrawEval() From 04a5e889f95d0f09615d782b94661e615f578447 Mon Sep 17 00:00:00 2001 From: dsych Date: Wed, 23 Dec 2020 23:32:37 -0500 Subject: [PATCH 453/609] making sure that float window is not modifiable inside nvim --- autoload/vimspector/internal/balloon.vim | 1 + python3/vimspector/variables.py | 1 + 2 files changed, 2 insertions(+) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index fd37e79..467de90 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -103,6 +103,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) " create the content window let buf_id = nvim_create_buf(v:false, v:true) call nvim_buf_set_lines(buf_id, 0, -1, v:true, body) + call nvim_buf_set_option(buf_id, 'modifiable', v:false) let s:float_win = nvim_open_win(buf_id, v:false, opts) call nvim_win_set_option(s:float_win, 'wrap', v:true) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index f5930f8..a81991a 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -272,6 +272,7 @@ class VariablesView( object ): def _DrawEval(self): with utils.RestoreCursorPosition(): + with utils.ModifiableScratchBuffer( self._variable_eval_view.buf ): utils.ClearBuffer( self._variable_eval_view.buf ) icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' From b9ed7f34b46f30d451a174005b64fd151ac14764 Mon Sep 17 00:00:00 2001 From: dsych Date: Thu, 24 Dec 2020 00:08:02 -0500 Subject: [PATCH 454/609] removing redundant name for simple types, shortening variable type to 20 char and variable value to 100 chars to avoid overflowing tooltip window if the wrapped line is longer than the max size of the tooltip --- python3/vimspector/variables.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index a81991a..b30c473 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -276,7 +276,7 @@ class VariablesView( object ): utils.ClearBuffer( self._variable_eval_view.buf ) icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' - self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 ) + self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 , True) def _CleanUpTooltip(self): # remove reference to old tooltip window @@ -312,7 +312,7 @@ class VariablesView( object ): else: # in case that there is nothing to expand, we need to simulate a response from 'variables' request # it returns [Variable] - self._variable_eval.variables = [Variable({'name': expression, 'type': body['type'], 'value': body['result']})] + self._variable_eval.variables = [Variable({ 'type': body['type'], 'value': body['result']})] self._DrawEval() @@ -439,9 +439,18 @@ class VariablesView( object ): }, } ) - def _DrawVariables( self, view, variables, indent ): + def _DrawVariables( self, view, variables, indent, is_short = False ): assert indent > 0 for variable in variables: + type_ = variable.variable.get( 'type', '' ) + value = variable.variable.get( 'value', '' ) + + if is_short: + if( len(type_) > 20): + type_ = type_[0:16] + " ..." + if( len(value) > 100 ): + value = value[0:96] + " ..." + line = utils.AppendToBuffer( view.buf, '{indent}{marker}{icon} {name} ({type_}): {value}'.format( @@ -450,14 +459,13 @@ class VariablesView( object ): marker = '*' if variable.changed else ' ', icon = '+' if ( variable.IsExpandable() and not variable.IsExpanded() ) else '-', - name = variable.variable[ 'name' ], - type_ = variable.variable.get( 'type', '' ), - value = variable.variable.get( 'value', - '' ) ).split( '\n' ) ) + name = variable.variable.get( 'name', '' ), + type_ = type_, + value = value ).split( '\n' )) view.lines[ line ] = variable if variable.ShouldDrawDrillDown(): - self._DrawVariables( view, variable.variables, indent + 2 ) + self._DrawVariables( view, variable.variables, indent + 2, is_short ) def _DrawScopes( self ): # FIXME: The drawing is dumb and draws from scratch every time. This is From cb441be7bc1a4479cffb7a22d890e685cc6dcbe6 Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 2 Jan 2021 12:27:46 -0500 Subject: [PATCH 455/609] allowing resizing and dragging with a mouse --- autoload/vimspector/internal/balloon.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 467de90..48e1d05 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -175,7 +175,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let config = { \ 'cursorline': 1, - \ 'wrap': 1, + \ 'wrap': 0, \ 'filtermode': "n", \ 'maxwidth': max_width, \ 'maxheight': max_height, @@ -186,6 +186,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let config['filter'] = "MouseFilter" let config['mousemoved'] = [0, 0, 0] let config['close'] = "button" + let config['drag'] = 1 + let config['resize'] = 1 let s:float_win = popup_beval(body, config) else let config['filter'] = "CursorFiler" From 2f1c93a2ace0feffde737ddffa3dad3ef7df866a Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 2 Jan 2021 12:29:18 -0500 Subject: [PATCH 456/609] only diplaying relevant info like name and value for nested types and value only for simple types --- python3/vimspector/variables.py | 44 +++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index b30c473..4f6e4ff 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -274,9 +274,15 @@ class VariablesView( object ): with utils.RestoreCursorPosition(): with utils.ModifiableScratchBuffer( self._variable_eval_view.buf ): utils.ClearBuffer( self._variable_eval_view.buf ) - icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' - - self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 , True) + # if we only have a single non-expandable variable, it means we ran into a simple type + # hence, there is no need to draw additional fluff around the value + if(len(self._variable_eval.variables) == 1 and self._variable_eval.variables[0].IsExpandable() == False): + utils.AppendToBuffer( + self._variable_eval_view.buf, + self._variable_eval.variables[0].variable.get( 'value', '' ) + ) + else: + self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 , True) def _CleanUpTooltip(self): # remove reference to old tooltip window @@ -442,26 +448,32 @@ class VariablesView( object ): def _DrawVariables( self, view, variables, indent, is_short = False ): assert indent > 0 for variable in variables: - type_ = variable.variable.get( 'type', '' ) - value = variable.variable.get( 'value', '' ) - + text = '' if is_short: - if( len(type_) > 20): - type_ = type_[0:16] + " ..." - if( len(value) > 100 ): - value = value[0:96] + " ..." - - line = utils.AppendToBuffer( - view.buf, - '{indent}{marker}{icon} {name} ({type_}): {value}'.format( + text = '{indent}{icon} {name}: {value}'.format( + # We borrow 1 space of indent to draw the change marker + indent = ' ' * ( indent - 1 ), + icon = '+' if ( variable.IsExpandable() + and not variable.IsExpanded() ) else '-', + name = variable.variable.get( 'name', '' ), + value = variable.variable.get( 'value', '' ) + ) + else: + text = '{indent}{marker}{icon} {name} ({type_}): {value}'.format( # We borrow 1 space of indent to draw the change marker indent = ' ' * ( indent - 1 ), marker = '*' if variable.changed else ' ', icon = '+' if ( variable.IsExpandable() and not variable.IsExpanded() ) else '-', name = variable.variable.get( 'name', '' ), - type_ = type_, - value = value ).split( '\n' )) + type_ = variable.variable.get( 'type', '' ), + value = variable.variable.get( 'value', '' ) + ) + + line = utils.AppendToBuffer( + view.buf, + text.split( '\n' )) + view.lines[ line ] = variable if variable.ShouldDrawDrillDown(): From 3c857cebf41f554e80330c710140adcb7ab0600b Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 2 Jan 2021 15:55:52 -0500 Subject: [PATCH 457/609] dynamically adjusting window size for nvim's floating window based on the buffer size --- autoload/vimspector/internal/balloon.vim | 68 +++++++++++++++++++----- python3/vimspector/utils.py | 6 ++- python3/vimspector/variables.py | 2 + 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 48e1d05..3c0f02e 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -38,6 +38,12 @@ endfunction let s:float_win = 0 let s:nvim_related_win = 0 +" +" tooltip dimensions +let s:min_width = 1 +let s:min_height = 1 +let s:max_width = 50 +let s:max_height = 5 function! vimspector#internal#balloon#closeCallback() abort if has('nvim') @@ -52,33 +58,66 @@ function! vimspector#internal#balloon#closeCallback() abort return py3eval('_vimspector_session._CleanUpTooltip()') endfunction +function! vimspector#internal#balloon#nvim_generate_border(width, height) + let top = "╭" . repeat("─",a:width + 2) . "╮" + let mid = "│" . repeat(" ",a:width + 2) . "│" + let bot = "╰" . repeat("─",a:width + 2) . "╯" + let lines = [top] + repeat([mid], a:height) + [bot] + + return lines +endfunction + +function! vimspector#internal#balloon#nvim_resize_tooltip() + if !has('nvim') || s:float_win <= 0 || s:nvim_related_win <= 0 + return + endif + + noa call win_gotoid(s:float_win) + let buf_lines = getline(1, '$') + + let width = s:min_width + let height = min([max([s:min_height, len(buf_lines)]), s:max_height]) + + " calculate the longest line + for l in buf_lines + let width = max([width, len(l)]) + endfor + + let width = min([width, s:max_width]) + + let opts = { + \ 'width': width, + \ 'height': height, + \ } + " resize the content window + call nvim_win_set_config(s:float_win, opts) + + " resize the border window + let opts['width'] = width + 4 + let opts['height'] = height + 2 + call nvim_win_set_config(s:nvim_related_win, opts) + call nvim_buf_set_lines(nvim_win_get_buf(s:nvim_related_win), 0, -1, v:true, vimspector#internal#balloon#nvim_generate_border(width, height)) + +endfunction + function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let body = [] if a:0 > 0 let body = a:1 endif - " tooltip dimensions - let max_height = 5 - let max_width = 50 - if has('nvim') " generate border for the float window by creating a background buffer and " overlaying the content buffer " see https://github.com/neovim/neovim/issues/9718#issuecomment-546603628 - let top = "╭" . repeat("─", max_width) . "╮" - let mid = "│" . repeat(" ", max_width) . "│" - let bot = "╰" . repeat("─", max_width) . "╯" - let lines = [top] + repeat([mid], max_height) + [bot] - let buf_id = nvim_create_buf(v:false, v:true) - call nvim_buf_set_lines(buf_id, 0, -1, v:true, lines) + call nvim_buf_set_lines(buf_id, 0, -1, v:true, vimspector#internal#balloon#nvim_generate_border(s:max_width, s:max_height)) " default the dimensions for now. they can be easily overwritten later let opts = { \ 'relative': 'cursor', - \ 'width': max_width + 2, - \ 'height': max_height + 2, + \ 'width': s:max_width + 2, + \ 'height': s:max_height + 2, \ 'col': 0, \ 'row': 1, \ 'anchor': 'NW', @@ -122,6 +161,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) augroup vimspector#internal#balloon#nvim_float autocmd! autocmd WinLeave * :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float + autocmd BufModifiedSet * :echo execute(eval("nvim_win_get_buf(".s:float_win.")")."bufdo echom &modified") augroup END else @@ -177,8 +217,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'cursorline': 1, \ 'wrap': 0, \ 'filtermode': "n", - \ 'maxwidth': max_width, - \ 'maxheight': max_height, + \ 'maxwidth': s:max_width, + \ 'maxheight': s:max_height, \ 'scrollbar': 1, \ 'border': [] \ } diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 788f03e..05df29c 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -640,7 +640,11 @@ def DisplayBaloon( is_term, display, is_hover = False ): # Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685 display = '\n'.join( display ) - return int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format(is_hover, json.dumps( display )) ) ) + rc = int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format(is_hover, json.dumps( display )) ) ) + + vim.eval("vimspector#internal#balloon#nvim_resize_tooltip()") + + return rc def GetBufferFilepath( buf ): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 4f6e4ff..205efe7 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -284,6 +284,8 @@ class VariablesView( object ): else: self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 , True) + vim.eval("vimspector#internal#balloon#nvim_resize_tooltip()") + def _CleanUpTooltip(self): # remove reference to old tooltip window self._variable_eval_view = None From e0b1d6ed8164b42cebd8900a039dc644129405e6 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 3 Jan 2021 00:42:06 -0500 Subject: [PATCH 458/609] fixing linting errors in python files --- python3/vimspector/debug_session.py | 8 ++-- python3/vimspector/utils.py | 8 +++- python3/vimspector/variables.py | 70 +++++++++++++++++++---------- 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index de5a7c6..9ebc6ab 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -521,7 +521,7 @@ class DebugSession( object ): @IfConnected() def ExpandVariable( self, lineNum = -1 ): - self._variablesView.ExpandVariable(lineNum) + self._variablesView.ExpandVariable( lineNum ) @IfConnected() def AddWatch( self, expression ): @@ -540,7 +540,7 @@ class DebugSession( object ): @IfConnected() - def ShowTooltip(self, winnr, expression, is_hover): + def ShowTooltip( self, winnr, expression, is_hover ): """Proxy: ballonexpr -> variables.ShowBallon""" frame = self._stackTraceView.GetCurrentFrame() # Check if RIP is in a frame @@ -556,9 +556,9 @@ class DebugSession( object ): return '' # Return variable aware function - return self._variablesView.VariableEval(frame, expression, is_hover) + return self._variablesView.VariableEval( frame, expression, is_hover ) - def _CleanUpTooltip(self): + def _CleanUpTooltip( self ): return self._variablesView._CleanUpTooltip() @IfConnected() diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 05df29c..0fbe03a 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -640,9 +640,13 @@ def DisplayBaloon( is_term, display, is_hover = False ): # Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685 display = '\n'.join( display ) - rc = int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format(is_hover, json.dumps( display )) ) ) + rc = int( vim.eval( + "vimspector#internal#balloon#CreateTooltip({}, {})".format( + is_hover, json.dumps( display ) + ) + ) ) - vim.eval("vimspector#internal#balloon#nvim_resize_tooltip()") + vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) return rc diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 205efe7..234fb16 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -122,10 +122,10 @@ class View: lines: typing.Dict[ int, Expandable ] draw: typing.Callable - def __init__( self, win, lines, draw): + def __init__( self, win, lines, draw ): self.lines = lines self.draw = draw - if (win is not None): + if ( win is not None ): self.buf = win.buffer utils.SetUpUIWindow( win ) @@ -138,8 +138,8 @@ class VariablesView( object ): self._connection = None self._current_syntax = '' - self._variable_eval = None - self._variable_eval_view = None + self._variable_eval: Scope = None + self._variable_eval_view: View = None def AddExpandMappings(): vim.command( 'nnoremap ' @@ -186,7 +186,9 @@ class VariablesView( object ): 'balloonexpr': vim.options[ 'balloonexpr' ], 'balloondelay': vim.options[ 'balloondelay' ], } - vim.options[ 'balloonexpr' ] = 'vimspector#internal#balloon#HoverTooltip()' + vim.options[ 'balloonexpr' ] = "vimspector#internal#" + "balloon#HoverTooltip()" + vim.options[ 'balloondelay' ] = 250 if has_balloon: @@ -270,28 +272,38 @@ class VariablesView( object ): }, } ) - def _DrawEval(self): + def _DrawEval( self ): with utils.RestoreCursorPosition(): with utils.ModifiableScratchBuffer( self._variable_eval_view.buf ): utils.ClearBuffer( self._variable_eval_view.buf ) - # if we only have a single non-expandable variable, it means we ran into a simple type + # if we only have a single non-expandable variable, + # it means we ran into a simple type # hence, there is no need to draw additional fluff around the value - if(len(self._variable_eval.variables) == 1 and self._variable_eval.variables[0].IsExpandable() == False): + if( + len( self._variable_eval.variables ) == 1 + and self._variable_eval.variables[ 0 ].IsExpandable() is False + ): utils.AppendToBuffer( self._variable_eval_view.buf, - self._variable_eval.variables[0].variable.get( 'value', '' ) - ) + self._variable_eval.variables[ 0 ] + .variable.get( 'value', '' ) + ) else: - self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 , True) + self._DrawVariables( + self._variable_eval_view, + self._variable_eval.variables, + 2, + True + ) - vim.eval("vimspector#internal#balloon#nvim_resize_tooltip()") + vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) - def _CleanUpTooltip(self): + def _CleanUpTooltip( self ) : # remove reference to old tooltip window self._variable_eval_view = None return '' - def VariableEval(self, frame, expression, is_hover): + def VariableEval( self, frame, expression, is_hover ): """Callback to display variable under cursor `:h ballonexpr`""" if not self._connection: return '' @@ -299,16 +311,24 @@ class VariablesView( object ): def handler( message ): body = message[ 'body' ] - self._variable_eval = Scope(body) - float_win_id = utils.DisplayBaloon(self._is_term, [], is_hover) - float_buf_nr = int(vim.eval("winbufnr({})".format(float_win_id))) + self._variable_eval = Scope( body ) + float_win_id = utils.DisplayBaloon( self._is_term, [], is_hover ) + float_buf_nr = int( vim.eval( "winbufnr({})".format( float_win_id ) ) ) # since vim's popup cant be focused there is no way # to get a reference to its window # we will emulate python's window object ourselves - self._variable_eval_view = View(type('__vim__window__', (object,), {'options': {}, 'buffer': vim.buffers[float_buf_nr]}), {}, self._DrawEval) + self._variable_eval_view = View( + type( + '__vim__window__', + ( object, ), + { 'options': {}, 'buffer': vim.buffers[ float_buf_nr ] } + ), + {}, + self._DrawEval + ) - if(self._variable_eval.VariablesReference() > 0): + if( self._variable_eval.VariablesReference() > 0 ): self._connection.DoRequest( partial( self._ConsumeVariables, self._DrawEval, self._variable_eval ), { @@ -318,9 +338,12 @@ class VariablesView( object ): }, } ) else: - # in case that there is nothing to expand, we need to simulate a response from 'variables' request + # in case that there is nothing to expand, + # we need to simulate a response from 'variables' request # it returns [Variable] - self._variable_eval.variables = [Variable({ 'type': body['type'], 'value': body['result']})] + self._variable_eval.variables = [ + Variable( { 'type': body[ 'type' ], 'value': body[ 'result' ] } ) + ] self._DrawEval() @@ -422,7 +445,7 @@ class VariablesView( object ): else: return - current_line = vim.current.window.cursor[0] if lineNum <= 0 else lineNum + current_line = vim.current.window.cursor[ 0 ] if lineNum <= 0 else lineNum if current_line not in view.lines: return @@ -474,7 +497,8 @@ class VariablesView( object ): line = utils.AppendToBuffer( view.buf, - text.split( '\n' )) + text.split( '\n' ) + ) view.lines[ line ] = variable From 93568ba05d2c69abfff18eea66c39b75bdf49a54 Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 5 Jan 2021 22:13:20 -0500 Subject: [PATCH 459/609] removing test code --- autoload/vimspector/internal/balloon.vim | 1 - 1 file changed, 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 3c0f02e..dc8f543 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -161,7 +161,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) augroup vimspector#internal#balloon#nvim_float autocmd! autocmd WinLeave * :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float - autocmd BufModifiedSet * :echo execute(eval("nvim_win_get_buf(".s:float_win.")")."bufdo echom &modified") augroup END else From 052d63dee5929dbfbf91ee7d761c9eaf30010895 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 5 Jan 2021 22:11:48 +0000 Subject: [PATCH 460/609] Fix mouse interraction with the popup - don't handle events outside the window --- autoload/vimspector/internal/balloon.vim | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index dc8f543..7575be4 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -166,29 +166,29 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) else " assume we are inside vim func! MouseFilter(winid, key) + let handled = 0 if index(["\", "\<2-leftmouse>"], a:key) >= 0 let mouse_coords = getmousepos() " close the popup if mouse is clicked outside the window if mouse_coords['winid'] != a:winid call vimspector#internal#balloon#closeCallback() + else + " place the cursor according to the click + call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") + + " expand the variable if we got double click + if a:key == "\<2-leftmouse>" && mouse_coords['winid'] == a:winid + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") + let handled = 1 + endif endif - - " place the cursor according to the click - call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") - - " expand the variable if we got double click - if a:key == "\<2-leftmouse>" && mouse_coords['winid'] == a:winid - " forward line number to python, since vim does not allow us to focus - " the correct window - call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") - endif - - return 1 endif - return 0 + return handled endfunc - func! CursorFiler(winid, key) + func! CursorFilter(winid, key) if index(['j', 'k'], a:key) >= 0 call win_execute(a:winid, ':normal '.a:key) @@ -229,7 +229,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let config['resize'] = 1 let s:float_win = popup_beval(body, config) else - let config['filter'] = "CursorFiler" + let config['filter'] = "CursorFilter" let config['moved'] = "any" let s:float_win = popup_atcursor(body, config) endif From 0895c5e82900bd20de97d80668a28d7723a09c77 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 5 Jan 2021 22:12:14 +0000 Subject: [PATCH 461/609] Add 1 cell of padding to the x direction which makes the popup more readable --- autoload/vimspector/internal/balloon.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 7575be4..0b37c68 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -219,7 +219,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'maxwidth': s:max_width, \ 'maxheight': s:max_height, \ 'scrollbar': 1, - \ 'border': [] + \ 'border': [], + \ 'padding': [ 0, 1, 0, 1] \ } if a:is_hover let config['filter'] = "MouseFilter" From 64e38b57abfee39cb232fa9f013bf7b45b685085 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 5 Jan 2021 22:13:25 +0000 Subject: [PATCH 462/609] Fix crash; enable syntax highlighting in hover popup; use a Watch for the popup and re-use existing drawing code --- python3/vimspector/variables.py | 117 +++++++++++++++++--------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 234fb16..797a6a9 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -117,14 +117,27 @@ class Watch: self.expression = expression self.result = None + @staticmethod + def New( frame, expression, context ): + watch = { + 'expression': expression, + 'context': context, + } + if frame: + watch[ 'frameId' ] = frame[ 'id' ] + + return Watch( watch ) + class View: lines: typing.Dict[ int, Expandable ] draw: typing.Callable + syntax: str def __init__( self, win, lines, draw ): self.lines = lines self.draw = draw + self.syntax = None if ( win is not None ): self.buf = win.buffer utils.SetUpUIWindow( win ) @@ -186,8 +199,8 @@ class VariablesView( object ): 'balloonexpr': vim.options[ 'balloonexpr' ], 'balloondelay': vim.options[ 'balloondelay' ], } - vim.options[ 'balloonexpr' ] = "vimspector#internal#" - "balloon#HoverTooltip()" + vim.options[ 'balloonexpr' ] = ( "vimspector#internal#" + "balloon#HoverTooltip()" ) vim.options[ 'balloondelay' ] = 250 @@ -273,28 +286,25 @@ class VariablesView( object ): } ) def _DrawEval( self ): + watch = self._variable_eval + view = self._variable_eval_view + with utils.RestoreCursorPosition(): - with utils.ModifiableScratchBuffer( self._variable_eval_view.buf ): - utils.ClearBuffer( self._variable_eval_view.buf ) - # if we only have a single non-expandable variable, - # it means we ran into a simple type - # hence, there is no need to draw additional fluff around the value - if( - len( self._variable_eval.variables ) == 1 - and self._variable_eval.variables[ 0 ].IsExpandable() is False - ): - utils.AppendToBuffer( - self._variable_eval_view.buf, - self._variable_eval.variables[ 0 ] - .variable.get( 'value', '' ) - ) - else: - self._DrawVariables( - self._variable_eval_view, - self._variable_eval.variables, - 2, - True - ) + with utils.ModifiableScratchBuffer( view.buf ): + utils.ClearBuffer( view.buf ) + # FIXME: This probably doesn't work reliably + view.syntax = utils.SetSyntax( view.syntax, + self._current_syntax, + view.buf ) + + utils.AppendToBuffer( + view.buf, + f"Expression: { watch.expression[ 'expression' ] }" ) + + self._DrawWatchResult( view, + 2, + watch, + is_short = True ) vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) @@ -309,9 +319,13 @@ class VariablesView( object ): return '' def handler( message ): - body = message[ 'body' ] - self._variable_eval = Scope( body ) + watch = self._variable_eval + if watch.result is None: + watch.result = WatchResult( message[ 'body' ] ) + else: + watch.result.Update( message[ 'body' ] ) + float_win_id = utils.DisplayBaloon( self._is_term, [], is_hover ) float_buf_nr = int( vim.eval( "winbufnr({})".format( float_win_id ) ) ) @@ -328,51 +342,41 @@ class VariablesView( object ): self._DrawEval ) - if( self._variable_eval.VariablesReference() > 0 ): + if watch.result.IsExpandable(): + # Always expand the first level + watch.result.expanded = Expandable.EXPANDED_BY_US + + if watch.result.IsExpanded(): self._connection.DoRequest( partial( self._ConsumeVariables, - self._DrawEval, - self._variable_eval ), { + self._variable_eval_view.draw, + watch.result ), { 'command': 'variables', 'arguments': { - 'variablesReference': self._variable_eval.VariablesReference(), + 'variablesReference': watch.result.VariablesReference(), }, } ) - else: - # in case that there is nothing to expand, - # we need to simulate a response from 'variables' request - # it returns [Variable] - self._variable_eval.variables = [ - Variable( { 'type': body[ 'type' ], 'value': body[ 'result' ] } ) - ] - self._DrawEval() + self._DrawEval() def failure_handler( reason, message ): display = [ reason ] utils.DisplayBaloon( self._is_term, display, is_hover ) + self._variable_eval = Watch.New( frame, + expression, + 'hover' ) + # Send async request self._connection.DoRequest( handler, { 'command': 'evaluate', - 'arguments': { - 'expression': expression, - 'frameId': frame[ 'id' ], - 'context': 'hover', - } + 'arguments': self._variable_eval.expression, }, failure_handler ) # Return working (meanwhile) return '' def AddWatch( self, frame, expression ): - watch = { - 'expression': expression, - 'context': 'watch', - } - if frame: - watch[ 'frameId' ] = frame[ 'id' ] - - self._watches.append( Watch( watch ) ) + self._watches.append( Watch.New( frame, expression, 'watch' ) ) self.EvaluateWatches() def DeleteWatch( self ): @@ -470,7 +474,7 @@ class VariablesView( object ): }, } ) - def _DrawVariables( self, view, variables, indent, is_short = False ): + def _DrawVariables( self, view, variables, indent, is_short = False ): assert indent > 0 for variable in variables: text = '' @@ -530,7 +534,7 @@ class VariablesView( object ): 'Expression: ' + watch.expression[ 'expression' ] ) watch.line = line - self._DrawWatchResult( 2, watch ) + self._DrawWatchResult( self._watch, 2, watch ) def _DrawScope( self, indent, scope ): icon = '+' if scope.IsExpandable() and not scope.IsExpanded() else '-' @@ -546,7 +550,7 @@ class VariablesView( object ): indent += 2 self._DrawVariables( self._vars, scope.variables, indent ) - def _DrawWatchResult( self, indent, watch ): + def _DrawWatchResult( self, view, indent, watch, is_short = False ): if not watch.result: return @@ -561,12 +565,12 @@ class VariablesView( object ): icon = icon, result = watch.result.result.get( 'result', '' ) ) - line = utils.AppendToBuffer( self._watch.buf, line.split( '\n' ) ) - self._watch.lines[ line ] = watch.result + line = utils.AppendToBuffer( view.buf, line.split( '\n' ) ) + view.lines[ line ] = watch.result if watch.result.ShouldDrawDrillDown(): indent = 4 - self._DrawVariables( self._watch, watch.result.variables, indent ) + self._DrawVariables( view, watch.result.variables, indent, is_short ) def _ConsumeVariables( self, draw, parent, message ): new_variables = [] @@ -640,6 +644,7 @@ class VariablesView( object ): def SetSyntax( self, syntax ): + # TODO: Switch to View.syntax self._current_syntax = utils.SetSyntax( self._current_syntax, syntax, self._vars.buf, From 0b4da4c82ba9f1be03aca66a62fffadd71e49624 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Jan 2021 14:34:37 +0000 Subject: [PATCH 463/609] Remove more text from the hover popup --- python3/vimspector/variables.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 797a6a9..d8f2f53 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -297,12 +297,8 @@ class VariablesView( object ): self._current_syntax, view.buf ) - utils.AppendToBuffer( - view.buf, - f"Expression: { watch.expression[ 'expression' ] }" ) - self._DrawWatchResult( view, - 2, + 0, watch, is_short = True ) @@ -554,15 +550,25 @@ class VariablesView( object ): if not watch.result: return - assert indent > 0 - icon = '+' if ( watch.result.IsExpandable() and - not watch.result.IsExpanded() ) else '-' + assert is_short or indent > 0 - line = '{indent}{marker}{icon} Result: {result}'.format( + if is_short: + # The first result is always expanded in a hover (short format) + icon = '' + marker = '' + leader = '' + else: + icon = '+' if ( watch.result.IsExpandable() and + not watch.result.IsExpanded() ) else '-' + marker = '*' if watch.result.changed else ' ', + leader = ' Result: ' + + line = '{indent}{marker}{icon}{leader}{result}'.format( # We borrow 1 space of indent to draw the change marker indent = ' ' * ( indent - 1 ), - marker = '*' if watch.result.changed else ' ', + marker = marker, icon = icon, + leader = leader, result = watch.result.result.get( 'result', '' ) ) line = utils.AppendToBuffer( view.buf, line.split( '\n' ) ) From cfae062da14bf3af6c9a177501eaa94bf1cfab41 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Jan 2021 14:48:00 +0000 Subject: [PATCH 464/609] Disable cursorLine for the hover-only popup --- autoload/vimspector/internal/balloon.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 0b37c68..6007c31 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -213,7 +213,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endif let config = { - \ 'cursorline': 1, \ 'wrap': 0, \ 'filtermode': "n", \ 'maxwidth': s:max_width, @@ -232,6 +231,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) else let config['filter'] = "CursorFilter" let config['moved'] = "any" + let config['cursorline'] = 1 let s:float_win = popup_atcursor(body, config) endif From f3c39e12ab3449b74603c025133445441d8cb490 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Jan 2021 14:51:35 +0000 Subject: [PATCH 465/609] FixUp: Display in watch window, and indent in popup --- python3/vimspector/variables.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index d8f2f53..e657824 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -560,7 +560,7 @@ class VariablesView( object ): else: icon = '+' if ( watch.result.IsExpandable() and not watch.result.IsExpanded() ) else '-' - marker = '*' if watch.result.changed else ' ', + marker = '*' if watch.result.changed else ' ' leader = ' Result: ' line = '{indent}{marker}{icon}{leader}{result}'.format( @@ -575,8 +575,7 @@ class VariablesView( object ): view.lines[ line ] = watch.result if watch.result.ShouldDrawDrillDown(): - indent = 4 - self._DrawVariables( view, watch.result.variables, indent, is_short ) + self._DrawVariables( view, watch.result.variables, indent + 2, is_short ) def _ConsumeVariables( self, draw, parent, message ): new_variables = [] From 0d703779dc58cb1e11d09c0a74348c0995df1661 Mon Sep 17 00:00:00 2001 From: dsych Date: Thu, 7 Jan 2021 00:20:23 -0500 Subject: [PATCH 466/609] evaluating select range --- autoload/vimspector.vim | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index d8018a0..7f752e4 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -523,8 +523,39 @@ function! vimspector#OnBufferCreated( file_name ) abort py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) ) endfunction -function! vimspector#ShowTooltip() abort - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ), 0)') +function! vimspector#ShowTooltipForSelection() range + let [start, end] = [[line("'<"), col("'<") - 1], [line("'>"), col("'>")]] + " restor cursor position, since command mode puts it to the begining of the + " current line when invoked from visual mode + call cursor(end) + + " retrive the lines selected in visual mode + let lines = getbufline(bufnr('%'), start[0], end[0]) + + " make sure the leave only the parts we care about if multiple lines are + " selected + let lines[0] = strcharpart(lines[0], start[1]) + let lines_len = len(lines) - 1 + + if len(lines) == 1 + let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1] - start[1]) + else + let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1]) + endif + + let str = join(lines) + + call vimspector#ShowTooltip(str) +endfunction + +function! vimspector#ShowTooltip(...) abort + let str = "" + + if a:0 > 0 + let str = a:1 + endif + + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"'.str.'\")" ), 0)') endfunction From 2d082cc923c104650224a75af8a2cfb815e6c698 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 21:28:48 +0000 Subject: [PATCH 467/609] Use a more generous maximum size for the popup (TODO: option for this ?) --- autoload/vimspector/internal/balloon.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 6007c31..d5f0b6c 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -42,8 +42,8 @@ let s:nvim_related_win = 0 " tooltip dimensions let s:min_width = 1 let s:min_height = 1 -let s:max_width = 50 -let s:max_height = 5 +let s:max_width = 80 +let s:max_height = 20 function! vimspector#internal#balloon#closeCallback() abort if has('nvim') From cb174c176d75ee22e4858c139355a11e29205732 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 21:58:45 +0000 Subject: [PATCH 468/609] Disable wrapping long lines in neovim popup too --- autoload/vimspector/internal/balloon.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index d5f0b6c..4f4e064 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -145,7 +145,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) call nvim_buf_set_option(buf_id, 'modifiable', v:false) let s:float_win = nvim_open_win(buf_id, v:false, opts) - call nvim_win_set_option(s:float_win, 'wrap', v:true) + call nvim_win_set_option(s:float_win, 'wrap', v:false) call nvim_win_set_option(s:float_win, 'cursorline', v:true) call nvim_win_set_option(s:float_win, 'signcolumn', 'no') call nvim_win_set_option(s:float_win, 'relativenumber', v:false) From 64f2c8eb01d4d446b571c7366d356fff92aacd17 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 21:59:02 +0000 Subject: [PATCH 469/609] Use a fancy single line border in vim popup when we can --- autoload/vimspector/internal/balloon.vim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 4f4e064..47544c5 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -221,6 +221,11 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'border': [], \ 'padding': [ 0, 1, 0, 1] \ } + + if &ambiwidth ==# 'single' && &encoding == 'utf-8' + let config['borderchars'] = [ '─', '│', '─', '│', '╭', '╮', '╯', '╰' ] + endif + if a:is_hover let config['filter'] = "MouseFilter" let config['mousemoved'] = [0, 0, 0] From 672ac78fefdea0d5597e6f6b4a2e565e3083aa20 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 21:59:18 +0000 Subject: [PATCH 470/609] Fix flaky syntax highlighting toggling on/off --- python3/vimspector/variables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index e657824..138453e 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -293,7 +293,7 @@ class VariablesView( object ): with utils.ModifiableScratchBuffer( view.buf ): utils.ClearBuffer( view.buf ) # FIXME: This probably doesn't work reliably - view.syntax = utils.SetSyntax( view.syntax, + view.syntax = utils.SetSyntax( None, self._current_syntax, view.buf ) From fccafd6739e1a31905ea454fbe14aa537f3bc672 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:32:05 +0000 Subject: [PATCH 471/609] FixUp: border characters - indicate that the corner can be dragged --- autoload/vimspector/internal/balloon.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 47544c5..d9861cb 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -19,6 +19,8 @@ let s:save_cpo = &cpoptions set cpoptions&vim " }}} +scriptencoding utf-8 + " Returns: py.ShowBalloon( winnr, expresssion ) function! vimspector#internal#balloon#BalloonExpr() abort " winnr + 1 because for *no good reason* winnr is 0 based here unlike @@ -223,7 +225,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ } if &ambiwidth ==# 'single' && &encoding == 'utf-8' - let config['borderchars'] = [ '─', '│', '─', '│', '╭', '╮', '╯', '╰' ] + let config['borderchars'] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ] endif if a:is_hover From d2ed8a828c65eaeaca10fcfec2fd8ce5e8bbedfc Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:32:52 +0000 Subject: [PATCH 472/609] Use popup_filter_menu for the keyboard-popup rather than 100% our own --- autoload/vimspector/internal/balloon.vim | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index d9861cb..88c6dd0 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -179,7 +179,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") " expand the variable if we got double click - if a:key == "\<2-leftmouse>" && mouse_coords['winid'] == a:winid + if a:key == "\<2-leftmouse>" " forward line number to python, since vim does not allow us to focus " the correct window call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") @@ -191,23 +191,16 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endfunc func! CursorFilter(winid, key) - if index(['j', 'k'], a:key) >= 0 - call win_execute(a:winid, ':normal '.a:key) - - return 1 - elseif a:key == "\" + if a:key == "\" " forward line number to python, since vim does not allow us to focus " the correct window call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") - - return 1 - elseif a:key == "\" - call vimspector#internal#balloon#closeCallback() - return 1 + elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 + return MouseFilter( a:winid, a:key ) endif - return 0 + return popup_filter_menu( a:winid, a:key ) endfunc if s:float_win != 0 From bc1146df3bb8d0ba925d0f8e42c49935dd19850c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:33:31 +0000 Subject: [PATCH 473/609] Use normal highlighting for the popup as it's probably more readable (debatable?) --- autoload/vimspector/internal/balloon.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 88c6dd0..1803a35 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -214,7 +214,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'maxheight': s:max_height, \ 'scrollbar': 1, \ 'border': [], - \ 'padding': [ 0, 1, 0, 1] + \ 'padding': [ 0, 1, 0, 1], + \ 'highlight': 'Normal', \ } if &ambiwidth ==# 'single' && &encoding == 'utf-8' From 322b7e0a383d26635c563b9e33331d412d2bedaf Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:33:52 +0000 Subject: [PATCH 474/609] Enable mouse interraction for the both popups in vim --- autoload/vimspector/internal/balloon.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 1803a35..caf906f 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -216,6 +216,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'border': [], \ 'padding': [ 0, 1, 0, 1], \ 'highlight': 'Normal', + \ 'drag': 1, + \ 'resize': 1 \ } if &ambiwidth ==# 'single' && &encoding == 'utf-8' @@ -226,8 +228,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let config['filter'] = "MouseFilter" let config['mousemoved'] = [0, 0, 0] let config['close'] = "button" - let config['drag'] = 1 - let config['resize'] = 1 let s:float_win = popup_beval(body, config) else let config['filter'] = "CursorFilter" From 894ca522d305ac2cf929d0aef2480e6fe1f7dae1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:48:20 +0000 Subject: [PATCH 475/609] Use the popup callback rather than manually trying to do it --- autoload/vimspector/internal/balloon.vim | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index caf906f..491acf5 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -47,14 +47,19 @@ let s:min_height = 1 let s:max_width = 80 let s:max_height = 20 -function! vimspector#internal#balloon#closeCallback() abort +function! vimspector#internal#balloon#Close() abort if has('nvim') call nvim_win_close(s:float_win, v:true) call nvim_win_close(s:nvim_related_win, v:true) + + call vimspector#internal#balloon#CloseCallback() else call popup_close(s:float_win) endif +endfunction + +function! vimspector#internal#balloon#CloseCallback( ... ) abort let s:float_win = 0 let s:nvim_related_win = 0 return py3eval('_vimspector_session._CleanUpTooltip()') @@ -162,7 +167,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) " make sure we clean up the float after it loses focus augroup vimspector#internal#balloon#nvim_float autocmd! - autocmd WinLeave * :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float + autocmd WinLeave * :call vimspector#internal#balloon#Close() | autocmd! vimspector#internal#balloon#nvim_float augroup END else @@ -173,7 +178,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let mouse_coords = getmousepos() " close the popup if mouse is clicked outside the window if mouse_coords['winid'] != a:winid - call vimspector#internal#balloon#closeCallback() + call vimspector#internal#balloon#Close() else " place the cursor according to the click call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") @@ -204,7 +209,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endfunc if s:float_win != 0 - call vimspector#internal#balloon#closeCallback() + call vimspector#internal#balloon#Close() endif let config = { @@ -217,7 +222,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'padding': [ 0, 1, 0, 1], \ 'highlight': 'Normal', \ 'drag': 1, - \ 'resize': 1 + \ 'resize': 1, + \ 'callback': 'vimspector#internal#balloon#CloseCallback' \ } if &ambiwidth ==# 'single' && &encoding == 'utf-8' From e5e13ffcdd09bfae55335c57626a93b0ce4d5afc Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:48:34 +0000 Subject: [PATCH 476/609] Fix typo: Baloon -> Balloon --- python3/vimspector/utils.py | 2 +- python3/vimspector/variables.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 0fbe03a..2bd3c08 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -634,7 +634,7 @@ def ParseVariables( variables_list, return new_variables -def DisplayBaloon( is_term, display, is_hover = False ): +def DisplayBalloon( is_term, display, is_hover = False ): if not is_term: # To enable the Windows GUI to display the balloon correctly # Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685 diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 138453e..c9b263c 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -322,7 +322,7 @@ class VariablesView( object ): else: watch.result.Update( message[ 'body' ] ) - float_win_id = utils.DisplayBaloon( self._is_term, [], is_hover ) + float_win_id = utils.DisplayBalloon( self._is_term, [], is_hover ) float_buf_nr = int( vim.eval( "winbufnr({})".format( float_win_id ) ) ) # since vim's popup cant be focused there is no way @@ -356,7 +356,7 @@ class VariablesView( object ): def failure_handler( reason, message ): display = [ reason ] - utils.DisplayBaloon( self._is_term, display, is_hover ) + utils.DisplayBalloon( self._is_term, display, is_hover ) self._variable_eval = Watch.New( frame, expression, @@ -628,11 +628,11 @@ class VariablesView( object ): 'Type: ' + body.get( 'type', '' ), 'Value: ' + result ] - utils.DisplayBaloon( self._is_term, display ) + utils.DisplayBalloon( self._is_term, display ) def failure_handler( reason, message ): display = [ reason ] - utils.DisplayBaloon( self._is_term, display ) + utils.DisplayBalloon( self._is_term, display ) # Send async request self._connection.DoRequest( handler, { From 31e44548d38a28169bd3a36f6f4dca660179bd1f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 9 Jan 2021 10:45:27 +0000 Subject: [PATCH 477/609] Tidy up MouseFilter --- autoload/vimspector/internal/balloon.vim | 42 +++++++++++++----------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 491acf5..c292d5f 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -171,27 +171,31 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) augroup END else - " assume we are inside vim func! MouseFilter(winid, key) - let handled = 0 - if index(["\", "\<2-leftmouse>"], a:key) >= 0 - let mouse_coords = getmousepos() - " close the popup if mouse is clicked outside the window - if mouse_coords['winid'] != a:winid - call vimspector#internal#balloon#Close() - else - " place the cursor according to the click - call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") - - " expand the variable if we got double click - if a:key == "\<2-leftmouse>" - " forward line number to python, since vim does not allow us to focus - " the correct window - call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") - let handled = 1 - endif - endif + if index(["\", "\<2-leftmouse>"], a:key) < 0 + return 0 endif + + let handled = 0 + let mouse_coords = getmousepos() + + " close the popup if mouse is clicked outside the window + if mouse_coords['winid'] != a:winid + call vimspector#internal#balloon#Close() + return 0 + endif + + " place the cursor according to the click + call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") + + " expand the variable if we got double click + if a:key == "\<2-leftmouse>" + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") + let handled = 1 + endif + return handled endfunc From 3239963893b285eaa456a2421c8a48e5fba39a51 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 9 Jan 2021 11:21:53 +0000 Subject: [PATCH 478/609] Set the correct highlights in both popups in neovim --- autoload/vimspector/internal/balloon.vim | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index c292d5f..0f0c8d9 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -140,7 +140,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) " when calculating where to display the content window, we need to account " for the border - set winhl=Normal:Floating let opts.row += 1 let opts.height -= 2 let opts.col += 2 @@ -158,7 +157,16 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) call nvim_win_set_option(s:float_win, 'relativenumber', v:false) call nvim_win_set_option(s:float_win, 'number', v:false) - noa call win_gotoid(s:float_win) + let old_curwin = win_getid() + try + noautocmd call win_gotoid(s:nvim_related_win) + set winhl=Normal:Floating + finally + noautocmd call win_gotoid(old_curwin) + endtry + + noautocmd call win_gotoid(s:float_win) + set winhl=Normal:Floating nnoremap :call vimspector#ExpandVariable() nnoremap :quit From 91bebc182602a1c98d17a0ae039f1a0ad95a6362 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 10 Jan 2021 14:56:23 -0500 Subject: [PATCH 479/609] adding a close button for keyboard triggered popup, since drag and resize are also enabled. removing wrap from border. --- autoload/vimspector/internal/balloon.vim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 0f0c8d9..1cb91c8 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -132,7 +132,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ } " this is the border window let s:nvim_related_win = nvim_open_win(buf_id, 0, opts) - call nvim_win_set_option(s:nvim_related_win, 'wrap', v:true) call nvim_win_set_option(s:nvim_related_win, 'cursorline', v:true) call nvim_win_set_option(s:nvim_related_win, 'signcolumn', 'no') call nvim_win_set_option(s:nvim_related_win, 'relativenumber', v:false) @@ -229,12 +228,15 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'filtermode': "n", \ 'maxwidth': s:max_width, \ 'maxheight': s:max_height, + \ 'minwidth': s:min_width, + \ 'minheight': s:min_height, \ 'scrollbar': 1, \ 'border': [], \ 'padding': [ 0, 1, 0, 1], \ 'highlight': 'Normal', \ 'drag': 1, \ 'resize': 1, + \ 'close': 'button', \ 'callback': 'vimspector#internal#balloon#CloseCallback' \ } @@ -245,7 +247,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) if a:is_hover let config['filter'] = "MouseFilter" let config['mousemoved'] = [0, 0, 0] - let config['close'] = "button" let s:float_win = popup_beval(body, config) else let config['filter'] = "CursorFilter" From 13102dc7117d988415b17f78b48f39aecb725696 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 10 Jan 2021 15:25:02 -0500 Subject: [PATCH 480/609] reverting back the highlight group --- autoload/vimspector/internal/balloon.vim | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 1cb91c8..b7d101b 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -132,7 +132,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ } " this is the border window let s:nvim_related_win = nvim_open_win(buf_id, 0, opts) - call nvim_win_set_option(s:nvim_related_win, 'cursorline', v:true) call nvim_win_set_option(s:nvim_related_win, 'signcolumn', 'no') call nvim_win_set_option(s:nvim_related_win, 'relativenumber', v:false) call nvim_win_set_option(s:nvim_related_win, 'number', v:false) @@ -156,16 +155,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) call nvim_win_set_option(s:float_win, 'relativenumber', v:false) call nvim_win_set_option(s:float_win, 'number', v:false) - let old_curwin = win_getid() - try - noautocmd call win_gotoid(s:nvim_related_win) - set winhl=Normal:Floating - finally - noautocmd call win_gotoid(old_curwin) - endtry - noautocmd call win_gotoid(s:float_win) - set winhl=Normal:Floating nnoremap :call vimspector#ExpandVariable() nnoremap :quit @@ -233,7 +223,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'scrollbar': 1, \ 'border': [], \ 'padding': [ 0, 1, 0, 1], - \ 'highlight': 'Normal', \ 'drag': 1, \ 'resize': 1, \ 'close': 'button', From 4466fce20b15df37ad1463676f09e3d0825235f0 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 10 Jan 2021 15:37:17 -0500 Subject: [PATCH 481/609] fixing styling inside vim files --- autoload/vimspector.vim | 4 +-- autoload/vimspector/internal/balloon.vim | 36 ++++++++++++------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 7f752e4..941ec40 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -523,7 +523,7 @@ function! vimspector#OnBufferCreated( file_name ) abort py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) ) endfunction -function! vimspector#ShowTooltipForSelection() range +function! vimspector#ShowTooltipForSelection() range abort let [start, end] = [[line("'<"), col("'<") - 1], [line("'>"), col("'>")]] " restor cursor position, since command mode puts it to the begining of the " current line when invoked from visual mode @@ -549,7 +549,7 @@ function! vimspector#ShowTooltipForSelection() range endfunction function! vimspector#ShowTooltip(...) abort - let str = "" + let str = '' if a:0 > 0 let str = a:1 diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index b7d101b..b922397 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -65,16 +65,16 @@ function! vimspector#internal#balloon#CloseCallback( ... ) abort return py3eval('_vimspector_session._CleanUpTooltip()') endfunction -function! vimspector#internal#balloon#nvim_generate_border(width, height) - let top = "╭" . repeat("─",a:width + 2) . "╮" - let mid = "│" . repeat(" ",a:width + 2) . "│" - let bot = "╰" . repeat("─",a:width + 2) . "╯" +function! vimspector#internal#balloon#nvim_generate_border(width, height) abort + let top = '╭' . repeat('─',a:width + 2) . '╮' + let mid = '│' . repeat(' ',a:width + 2) . '│' + let bot = '╰' . repeat('─',a:width + 2) . '╯' let lines = [top] + repeat([mid], a:height) + [bot] return lines endfunction -function! vimspector#internal#balloon#nvim_resize_tooltip() +function! vimspector#internal#balloon#nvim_resize_tooltip() abort if !has('nvim') || s:float_win <= 0 || s:nvim_related_win <= 0 return endif @@ -107,7 +107,7 @@ function! vimspector#internal#balloon#nvim_resize_tooltip() endfunction -function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) +function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) abort let body = [] if a:0 > 0 let body = a:1 @@ -168,7 +168,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) augroup END else - func! MouseFilter(winid, key) + func! MouseFilter(winid, key) abort if index(["\", "\<2-leftmouse>"], a:key) < 0 return 0 endif @@ -183,24 +183,24 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endif " place the cursor according to the click - call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") + call win_execute(a:winid, ':call cursor('.mouse_coords['line'].', '.mouse_coords['column'].')') " expand the variable if we got double click - if a:key == "\<2-leftmouse>" + if a:key ==? "\<2-leftmouse>" " forward line number to python, since vim does not allow us to focus " the correct window - call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") + call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') let handled = 1 endif return handled endfunc - func! CursorFilter(winid, key) - if a:key == "\" + func! CursorFilter(winid, key) abort + if a:key ==? "\" " forward line number to python, since vim does not allow us to focus " the correct window - call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") + call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') return 1 elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 return MouseFilter( a:winid, a:key ) @@ -215,7 +215,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let config = { \ 'wrap': 0, - \ 'filtermode': "n", + \ 'filtermode': 'n', \ 'maxwidth': s:max_width, \ 'maxheight': s:max_height, \ 'minwidth': s:min_width, @@ -229,17 +229,17 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'callback': 'vimspector#internal#balloon#CloseCallback' \ } - if &ambiwidth ==# 'single' && &encoding == 'utf-8' + if &ambiwidth ==# 'single' && &encoding ==? 'utf-8' let config['borderchars'] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ] endif if a:is_hover - let config['filter'] = "MouseFilter" + let config['filter'] = 'MouseFilter' let config['mousemoved'] = [0, 0, 0] let s:float_win = popup_beval(body, config) else - let config['filter'] = "CursorFilter" - let config['moved'] = "any" + let config['filter'] = 'CursorFilter' + let config['moved'] = 'any' let config['cursorline'] = 1 let s:float_win = popup_atcursor(body, config) endif From 789377eab47ba3dee165443693146068853aa11a Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 00:35:24 -0500 Subject: [PATCH 482/609] adding the first test! --- python3/vimspector/debug_session.py | 1 + python3/vimspector/variables.py | 3 ++ tests/variables.test.vim | 64 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 9ebc6ab..18c4726 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -702,6 +702,7 @@ class DebugSession( object ): '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(): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index c9b263c..e96a585 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -307,6 +307,7 @@ class VariablesView( object ): def _CleanUpTooltip( self ) : # remove reference to old tooltip window self._variable_eval_view = None + vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = None return '' def VariableEval( self, frame, expression, is_hover ): @@ -323,6 +324,8 @@ class VariablesView( object ): watch.result.Update( message[ 'body' ] ) float_win_id = utils.DisplayBalloon( self._is_term, [], is_hover ) + # record the global eval window id + vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( float_win_id ) float_buf_nr = int( vim.eval( "winbufnr({})".format( float_win_id ) ) ) # since vim's popup cant be focused there is no way diff --git a/tests/variables.test.vim b/tests/variables.test.vim index 9152875..5cf575f 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -591,3 +591,67 @@ function! Test_EvaluateFailure() call vimspector#test#setup#Reset() %bwipe! endfunction + +function! Test_VariableEval() + let fn = 'testdata/cpp/simple/struct.cpp' + call s:StartDebugging( #{ fn: fn, line: 24, col: 1, launch: #{ + \ configuration: 'run-to-breakpoint' + \ } } ) + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) + + "evaluate the prev line + " call win_gotoid( g:vimspector_session_windows.code ) + call setpos('.', [ 0, 24, 8 ]) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) + call vimspector#ShowTooltip() + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '{...}', + \ ' - i: 0', + \ ' - c: 0 ''\\0\{1,3}''', + \ ' - fffff: 0', + \ ' + another_test: ', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.eval ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Expand + call win_execute( g:vimspector_session_windows.eval, [ + \ 'call feedkeys( ''jjjj'', ''xt'' )', + \ 'call feedkeys( "\", ''xt'' )' + \ ] ) + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '{...}', + \ ' - i: 0', + \ ' - c: 0 ''\\0\{1,3}''', + \ ' - fffff: 0', + \ ' - another_test: ', + \ ' - choo: 0 ''\\0\{1,3}''', + \ ' + ints: ' + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.eval ), + \ 1, + \ '$' ) + \ ) + \ } ) + + "Close + call win_execute( g:vimspector_session_windows.eval, 'call feedkeys( "\", ''xt'')') + + call WaitForAssert( {-> + \ assert_equal( 0, g:vimspector_session_windows.eval ) + \ } ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction From 0c79384529f413ee58d453a4c6ec41a5cde8b15d Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 08:50:39 -0500 Subject: [PATCH 483/609] there is no need to execute feedkeys inside a popup context, since keys are added into the global input buffer --- tests/variables.test.vim | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index 5cf575f..c587dea 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -623,10 +623,8 @@ function! Test_VariableEval() \ } ) " Expand - call win_execute( g:vimspector_session_windows.eval, [ - \ 'call feedkeys( ''jjjj'', ''xt'' )', - \ 'call feedkeys( "\", ''xt'' )' - \ ] ) + call feedkeys( 'jjjj', 'xt' ) + call feedkeys( "\", 'xt' ) call WaitForAssert( {-> \ AssertMatchist( @@ -646,11 +644,9 @@ function! Test_VariableEval() \ } ) "Close - call win_execute( g:vimspector_session_windows.eval, 'call feedkeys( "\", ''xt'')') + call feedkeys( "\", 'xt' ) - call WaitForAssert( {-> - \ assert_equal( 0, g:vimspector_session_windows.eval ) - \ } ) + call assert_equal( v:none, g:vimspector_session_windows.eval ) call vimspector#test#setup#Reset() %bwipe! From 0ff9dc5f9e6677288c4a015bc550270e184955dc Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 23:06:35 -0500 Subject: [PATCH 484/609] making sure that select marks were set before attempting to extract text --- autoload/vimspector.vim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 941ec40..1458060 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -532,6 +532,10 @@ function! vimspector#ShowTooltipForSelection() range abort " retrive the lines selected in visual mode let lines = getbufline(bufnr('%'), start[0], end[0]) + if(len(lines) < 1) + return + endif + " make sure the leave only the parts we care about if multiple lines are " selected let lines[0] = strcharpart(lines[0], start[1]) From e0b0a7f3d2edfaf447788adc744856b120b57995 Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 23:07:01 -0500 Subject: [PATCH 485/609] recording popup win id on the failed evaluation --- python3/vimspector/variables.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index e96a585..7666954 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -359,7 +359,9 @@ class VariablesView( object ): def failure_handler( reason, message ): display = [ reason ] - utils.DisplayBalloon( self._is_term, display, is_hover ) + float_win_id = utils.DisplayBalloon( self._is_term, display, is_hover ) + # record the global eval window id + vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( float_win_id ) self._variable_eval = Watch.New( frame, expression, From 5a23ec5bebd1a0b527ffd27b70990fc5221429c4 Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 23:07:37 -0500 Subject: [PATCH 486/609] adding tests for select range, invalid eval and expand/collapse --- tests/variables.test.vim | 130 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 3 deletions(-) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index c587dea..caf2a67 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -602,11 +602,118 @@ function! Test_VariableEval() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) "evaluate the prev line - " call win_gotoid( g:vimspector_session_windows.code ) call setpos('.', [ 0, 24, 8 ]) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) call vimspector#ShowTooltip() + call WaitForAssert( {-> + \ assert_notequal( v:none, g:vimspector_session_windows.eval ) + \ } ) + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '{...}', + \ ' - i: 0', + \ ' - c: 0 ''\\0\{1,3}''', + \ ' - fffff: 0', + \ ' + another_test: ', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.eval ), + \ 1, + \ '$' ) + \ ) + \ } ) + + "Close + call feedkeys( "\", 'xt' ) + + call assert_equal( v:none, g:vimspector_session_windows.eval ) + + " test selection + call setpos('.', [ 0, 24, 8 ]) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) + + " enter visual mode + " this is a hack, since usually, when user enters command mode from inside + " visual mode, the latter is immediately interrupted and the '<' '>' marks are + " set. for some odd reason, visual mode is not interupted from the script, + " so we need to manually escape and re-trigger previous visual selection + call execute('normal v') + call feedkeys("lllll\", 'xt') + call execute("normal gv") + + call vimspector#ShowTooltipForSelection() + + call WaitForAssert( {-> + \ assert_notequal( v:none, g:vimspector_session_windows.eval ) + \ } ) + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '{...}', + \ ' - i: 0', + \ ' - c: 0 ''\\0\{1,3}''', + \ ' - fffff: 0', + \ ' + another_test: ', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.eval ), + \ 1, + \ '$' ) + \ ) + \ } ) + + "Close + call feedkeys( "\", 'xt' ) + + call assert_equal( v:none, g:vimspector_session_windows.eval ) + + " Evaluation error + call setpos('.', [ 0, 25, 1 ]) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 25, 1 ) + call vimspector#ShowTooltip() + + call WaitForAssert( {-> + \ assert_notequal( v:none, g:vimspector_session_windows.eval ) + \ } ) + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ 'Evaluation error', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.eval ), + \ 1, + \ '$' ) + \ ) + \ } ) + + "Close + call feedkeys( "\", 'xt' ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_VariableEvalExpand() + let fn = 'testdata/cpp/simple/struct.cpp' + call s:StartDebugging( #{ fn: fn, line: 24, col: 1, launch: #{ + \ configuration: 'run-to-breakpoint' + \ } } ) + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) + + "evaluate the prev line + call setpos('.', [ 0, 24, 8 ]) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) + call vimspector#ShowTooltip() + + call WaitForAssert( {-> + \ assert_notequal( v:none, g:vimspector_session_windows.eval ) + \ } ) + call WaitForAssert( {-> \ AssertMatchist( \ [ @@ -623,8 +730,7 @@ function! Test_VariableEval() \ } ) " Expand - call feedkeys( 'jjjj', 'xt' ) - call feedkeys( "\", 'xt' ) + call feedkeys( "jjjj\", 'xt' ) call WaitForAssert( {-> \ AssertMatchist( @@ -643,6 +749,24 @@ function! Test_VariableEval() \ ) \ } ) + "Collapse + call feedkeys( "\", 'xt' ) + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '{...}', + \ ' - i: 0', + \ ' - c: 0 ''\\0\{1,3}''', + \ ' - fffff: 0', + \ ' + another_test: ', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.eval ), + \ 1, + \ '$' ) + \ ) + \ } ) + "Close call feedkeys( "\", 'xt' ) From 639e89f5db9e750fdbac45bccb3268aa798fb1cf Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 23:16:02 -0500 Subject: [PATCH 487/609] fixing linting --- autoload/vimspector.vim | 2 +- tests/variables.test.vim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 1458060..75ed961 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -541,7 +541,7 @@ function! vimspector#ShowTooltipForSelection() range abort let lines[0] = strcharpart(lines[0], start[1]) let lines_len = len(lines) - 1 - if len(lines) == 1 + if len( lines ) == 1 let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1] - start[1]) else let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1]) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index caf2a67..1e4a9ec 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -641,7 +641,7 @@ function! Test_VariableEval() " so we need to manually escape and re-trigger previous visual selection call execute('normal v') call feedkeys("lllll\", 'xt') - call execute("normal gv") + call execute('normal gv') call vimspector#ShowTooltipForSelection() From 8c39a861bdba156d6897a18aeef6594be7dba304 Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 23:36:26 -0500 Subject: [PATCH 488/609] hopefully fixing tests on mac --- tests/variables.test.vim | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index 1e4a9ec..ae51ad8 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -628,7 +628,9 @@ function! Test_VariableEval() "Close call feedkeys( "\", 'xt' ) - call assert_equal( v:none, g:vimspector_session_windows.eval ) + call WaitForAssert( {-> + \ assert_equal( v:none, g:vimspector_session_windows.eval ) + \ } ) " test selection call setpos('.', [ 0, 24, 8 ]) @@ -665,9 +667,13 @@ function! Test_VariableEval() \ } ) "Close - call feedkeys( "\", 'xt' ) + " we need to send esc twice because of the weird interactions between visual + " mode and tests + call feedkeys( "\\", 'xt' ) - call assert_equal( v:none, g:vimspector_session_windows.eval ) + call WaitForAssert( {-> + \ assert_equal( v:none, g:vimspector_session_windows.eval ) + \ } ) " Evaluation error call setpos('.', [ 0, 25, 1 ]) @@ -692,6 +698,10 @@ function! Test_VariableEval() "Close call feedkeys( "\", 'xt' ) + call WaitForAssert( {-> + \ assert_equal( v:none, g:vimspector_session_windows.eval ) + \ } ) + call vimspector#test#setup#Reset() %bwipe! endfunction @@ -770,7 +780,9 @@ function! Test_VariableEvalExpand() "Close call feedkeys( "\", 'xt' ) - call assert_equal( v:none, g:vimspector_session_windows.eval ) + call WaitForAssert( {-> + \ assert_equal( v:none, g:vimspector_session_windows.eval ) + \ } ) call vimspector#test#setup#Reset() %bwipe! From 47680565c433be8947fa3b44de2b0e5474d611ac Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 2 Feb 2021 20:53:31 -0500 Subject: [PATCH 489/609] adding docs --- README.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8e7a036..a18a89f 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ For detailed explanatin of the `.vimspector.json` format, see the * [Run to Cursor](#run-to-cursor) * [Stepping](#stepping) * [Variables and scopes](#variables-and-scopes) + * [Variable/selection hover evaluation](#variable-eval) * [Watches](#watches) * [Watch autocompletion](#watch-autocompletion) * [Stack Traces](#stack-traces) @@ -102,7 +103,7 @@ language that Visual Studio Code supports (but see caveats). The [Vimspector website][website] has an overview of the UI, along with basic instructions for configuration and setup. -But for now, here's a (rather old) screenshot of Vimsepctor debugging Vim: +But for now, here's a (rather old) screenshot of Vimspector debugging Vim: ![vimspector-vim-screenshot](https://puremourning.github.io/vimspector-web/img/vimspector-overview.png) @@ -249,7 +250,7 @@ neovim doesn't implement some features Vimspector relies on: the output window's current output. * Prompt Buffers - used to send commands in the Console and add Watches. (*Note*: prompt buffers are available in neovim nightly) -* Balloons - used to display the values of variables when debugging. +* Tooltips - used to display the values of variables when debugging. Workarounds are in place as follows: @@ -258,9 +259,9 @@ Workarounds are in place as follows: [`:VimspectorReset`](#closing-debugger) * Prompt Buffers - There are [`:VimspectorEval`](#console) and [`:VimspectorWatch`](#watches) - -There is no workaroud for the lack of balloons; you'll just have to use -`:VimspectorEval` or `:VimspectorWatch`, or switch to Vim. +* Functions - There are + [`:call vimspector#ShowTooltip()`](#variable-eval) and + [`:call vimspector#ShowTooltipForSelection()`](#variable-eval) ## Windows differences @@ -890,6 +891,17 @@ breakpoint when it is hit. Scopes and variables are represented by the buffer `vimspector.Variables`. +## Variable/selection hover evaluation + +All rules for `Variables and scopes` apply plus the following: +* With mouse enabled, hover over a variable and get the value it evaluates to. +* Use your mouse to hightlight a expression (e.g. a + b) and the result of the expression. +* Call `vimspector#ShowTooltip()` or `vimspector#ShowTooltipForSelection()` to evaluate expressions without mouse (the only way to use this feature in nvim). +* Use regular nagivation keys to chose the current selection and `` (or leave the tooltip window) to close the tooltip. + +Note: using a selection evaluation might lead to undesired consequences, since **the expression is actually executed**. E.g. `c = a + b;` once this entire line is evaluated, value of `c` becomes the sum of `a` and `b`. + + ## Watches The watch window is used to inspect variables and expressions. Expressions are From d6c68d691cecb5326b2686712ee04c67f62947e8 Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 2 Feb 2021 21:03:57 -0500 Subject: [PATCH 490/609] streamlining the docs --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a18a89f..a7519e8 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ For detailed explanatin of the `.vimspector.json` format, see the * [Run to Cursor](#run-to-cursor) * [Stepping](#stepping) * [Variables and scopes](#variables-and-scopes) - * [Variable/selection hover evaluation](#variable-eval) + * [Variable/selection hover evaluation](#variable-or-selection-hover-evaluation) * [Watches](#watches) * [Watch autocompletion](#watch-autocompletion) * [Stack Traces](#stack-traces) @@ -260,8 +260,8 @@ Workarounds are in place as follows: * Prompt Buffers - There are [`:VimspectorEval`](#console) and [`:VimspectorWatch`](#watches) * Functions - There are - [`:call vimspector#ShowTooltip()`](#variable-eval) and - [`:call vimspector#ShowTooltipForSelection()`](#variable-eval) + [`:call vimspector#ShowTooltip()`](#variable-or-selection-hover-evaluation) and + [`:call vimspector#ShowTooltipForSelection()`](#variable-or-selection-hover-evaluation) ## Windows differences @@ -891,13 +891,13 @@ breakpoint when it is hit. Scopes and variables are represented by the buffer `vimspector.Variables`. -## Variable/selection hover evaluation +## Variable or selection hover evaluation All rules for `Variables and scopes` apply plus the following: * With mouse enabled, hover over a variable and get the value it evaluates to. -* Use your mouse to hightlight a expression (e.g. a + b) and the result of the expression. +* Use your mouse to perform a visual selection of an expression (e.g. `a + b`) and get its result. * Call `vimspector#ShowTooltip()` or `vimspector#ShowTooltipForSelection()` to evaluate expressions without mouse (the only way to use this feature in nvim). -* Use regular nagivation keys to chose the current selection and `` (or leave the tooltip window) to close the tooltip. +* Use regular nagivation keys to chose the current selection; `` (or leave the tooltip window) to close the tooltip. Note: using a selection evaluation might lead to undesired consequences, since **the expression is actually executed**. E.g. `c = a + b;` once this entire line is evaluated, value of `c` becomes the sum of `a` and `b`. From ae137ecdd0a5daf47280e1672ff4fd4294fb0cde Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 7 Feb 2021 16:41:05 -0500 Subject: [PATCH 491/609] removing old balloon code --- autoload/vimspector/internal/balloon.vim | 17 ----------- python3/vimspector/debug_session.py | 23 ++------------- python3/vimspector/variables.py | 37 ------------------------ 3 files changed, 2 insertions(+), 75 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index b922397..86611f6 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -21,23 +21,6 @@ set cpoptions&vim scriptencoding utf-8 -" Returns: py.ShowBalloon( winnr, expresssion ) -function! vimspector#internal#balloon#BalloonExpr() abort - " winnr + 1 because for *no good reason* winnr is 0 based here unlike - " everywhere else - " int() because for *no good reason* winnr is a string. - return py3eval('_vimspector_session.ShowBalloon(' - \ . 'int( vim.eval( "v:beval_winnr" ) ) + 1,' - \ . 'vim.eval( "v:beval_text" ) )' ) -endfunction - -" Returns: py.ShowBalloon( winnr, expresssion ) -function! vimspector#internal#balloon#HoverTooltip() abort - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') - -endfunction - - let s:float_win = 0 let s:nvim_related_win = 0 " diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 18c4726..e6fff61 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -541,11 +541,11 @@ class DebugSession( object ): @IfConnected() def ShowTooltip( self, winnr, expression, is_hover ): - """Proxy: ballonexpr -> variables.ShowBallon""" + """Proxy: ballonexpr -> variables.ShowTooltip""" frame = self._stackTraceView.GetCurrentFrame() # Check if RIP is in a frame if frame is None: - self._logger.debug( 'Balloon: Not in a stack frame' ) + self._logger.debug( 'Tooltip: Not in a stack frame' ) return '' # Check if cursor in code window @@ -561,25 +561,6 @@ class DebugSession( object ): def _CleanUpTooltip( self ): return self._variablesView._CleanUpTooltip() - @IfConnected() - def ShowBalloon( self, winnr, expression ): - """Proxy: ballonexpr -> variables.ShowBallon""" - frame = self._stackTraceView.GetCurrentFrame() - # Check if RIP is in a frame - if frame is None: - self._logger.debug( 'Balloon: Not in a stack frame' ) - return '' - - # Check if cursor in code window - if winnr != int( self._codeView._window.number ): - self._logger.debug( 'Winnr %s is not the code window %s', - winnr, - self._codeView._window.number ) - return '' - - # Return variable aware function - return self._variablesView.ShowBalloon( frame, expression ) - @IfConnected() def ExpandFrameOrThread( self ): self._stackTraceView.ExpandFrameOrThread() diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 7666954..c0cb2fd 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -616,43 +616,6 @@ class VariablesView( object ): draw() - def ShowBalloon( self, frame, expression ): - """Callback to display variable under cursor `:h ballonexpr`""" - if not self._connection: - return '' - - def handler( message ): - # TODO: this result count be expandable, but we have no way to allow the - # user to interact with the balloon to expand it, unless we use a popup - # instead, but even then we don't really want to trap the cursor. - body = message[ 'body' ] - result = body[ 'result' ] - if result is None: - result = 'null' - display = [ - 'Type: ' + body.get( 'type', '' ), - 'Value: ' + result - ] - utils.DisplayBalloon( self._is_term, display ) - - def failure_handler( reason, message ): - display = [ reason ] - utils.DisplayBalloon( self._is_term, display ) - - # Send async request - self._connection.DoRequest( handler, { - 'command': 'evaluate', - 'arguments': { - 'expression': expression, - 'frameId': frame[ 'id' ], - 'context': 'hover', - } - }, failure_handler ) - - # Return working (meanwhile) - return '...' - - def SetSyntax( self, syntax ): # TODO: Switch to View.syntax self._current_syntax = utils.SetSyntax( self._current_syntax, From 51d551fe52ad5d12d22f81d9d110fc7cb506e243 Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 19 Feb 2021 23:39:54 -0500 Subject: [PATCH 492/609] replacing function calls with plug command --- autoload/vimspector.vim | 40 ++++-------------------- autoload/vimspector/internal/balloon.vim | 5 +++ plugin/vimspector.vim | 6 ++++ python3/vimspector/debug_session.py | 19 +++++++++++ python3/vimspector/utils.py | 23 +++++++++++++- 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 75ed961..9ac7ff1 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -523,43 +523,15 @@ function! vimspector#OnBufferCreated( file_name ) abort py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) ) endfunction -function! vimspector#ShowTooltipForSelection() range abort - let [start, end] = [[line("'<"), col("'<") - 1], [line("'>"), col("'>")]] - " restor cursor position, since command mode puts it to the begining of the - " current line when invoked from visual mode - call cursor(end) - - " retrive the lines selected in visual mode - let lines = getbufline(bufnr('%'), start[0], end[0]) - - if(len(lines) < 1) - return - endif - - " make sure the leave only the parts we care about if multiple lines are - " selected - let lines[0] = strcharpart(lines[0], start[1]) - let lines_len = len(lines) - 1 - - if len( lines ) == 1 - let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1] - start[1]) +function! vimspector#ShowEvalBalloon( is_visual ) abort + if a:is_visual + let expr = py3eval( '__import__( "vimspector", fromlist = [ "utils" ] ).utils.GetVisualSelection( int( vim.eval( "winbufnr( winnr() )" ) ) )' ) + let expr = join(expr) else - let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1]) + let expr = expand('') endif - let str = join(lines) - - call vimspector#ShowTooltip(str) -endfunction - -function! vimspector#ShowTooltip(...) abort - let str = '' - - if a:0 > 0 - let str = a:1 - endif - - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"'.str.'\")" ), 0)') + return py3eval( '_vimspector_session.ShowEvalBalloon( int( vim.eval( "winnr()" ) ), "'.expr.'", 0 )' ) endfunction diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 86611f6..bc445ba 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -21,6 +21,11 @@ set cpoptions&vim scriptencoding utf-8 +" Returns: py.ShowBalloon( winnr, expresssion ) +function! vimspector#internal#balloon#HoverTooltip() abort + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') +endfunction + let s:float_win = 0 let s:nvim_related_win = 0 " diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 05245cd..6465c21 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -60,6 +60,12 @@ nnoremap VimspectorStepOut nnoremap VimspectorRunToCursor \ :call vimspector#RunToCursor() +nnoremap VimspectorBalloonEval + \ :call vimspector#ShowEvalBalloon(0) + +xnoremap VimspectorBalloonEval + \ :call vimspector#ShowEvalBalloon(1) + if s:mappings ==# 'VISUAL_STUDIO' nmap VimspectorContinue nmap VimspectorStop diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index e6fff61..db14ebc 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -539,6 +539,25 @@ class DebugSession( object ): self._variablesView.DeleteWatch() + @IfConnected() + def ShowEvalBalloon( self, winnr, expression, is_hover ): + frame = self._stackTraceView.GetCurrentFrame() + # Check if RIP is in a frame + if frame is None: + self._logger.debug( 'Tooltip: Not in a stack frame' ) + return '' + + # Check if cursor in code window + if winnr != int( self._codeView._window.number ): + self._logger.debug( 'Winnr %s is not the code window %s', + winnr, + self._codeView._window.number ) + return '' + + # Return variable aware function + return self._variablesView.VariableEval( frame, expression, is_hover ) + + @IfConnected() def ShowTooltip( self, winnr, expression, is_hover ): """Proxy: ballonexpr -> variables.ShowTooltip""" diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 2bd3c08..287f919 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -34,6 +34,12 @@ _log_handler = logging.FileHandler( LOG_FILE, mode = 'w' ) _log_handler.setFormatter( logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) ) +# this is the "large number" that vim returns for col num, when getting '> mark +# with getpos() for line wise visual selection(V) +# see https://github.com/vim/vim/blob/eed9d46293f0842aad0d50ff3a526f9a48b12421/src/evalfunc.c#L4077 +# and https://github.com/vim/vim/blob/064095012c0b8e4e43e75834b337115950898fbf/src/vim.h#L1699 +MAX_COL = 2147483647 + def SetUpLogging( logger ): logger.setLevel( logging.DEBUG ) @@ -642,7 +648,7 @@ def DisplayBalloon( is_term, display, is_hover = False ): rc = int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format( - is_hover, json.dumps( display ) + int( is_hover ), json.dumps( display ) ) ) ) @@ -725,6 +731,21 @@ def GetBufferFiletypes( buf ): return ft.split( '.' ) +def GetVisualSelection( bufnr ): + start_line, start_col = vim.current.buffer.mark( "<" ) + end_line, end_col = vim.current.buffer.mark( ">" ) + + # lines are 1 based, but columns are 0 based + # don't as me why... + lines = vim.buffers[ bufnr ][ start_line - 1 : end_line ] + lines[ 0 ] = lines[ 0 ][ start_col : ] + # only trim the end if we are not in line-wise select mode + if( end_col < MAX_COL ): + lines[ -1 ] = lines[ -1 ][ : end_col + 1 ] + + return lines + + def DisplaySplash( api_prefix, splash, text ): if splash: return Call( f'vimspector#internal#{api_prefix}popup#UpdateSplash', From 0313efa06f009d1bb814c115b11bb0afa7f2db4d Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 19 Feb 2021 23:44:20 -0500 Subject: [PATCH 493/609] removing redundant check for array bounds --- python3/vimspector/utils.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 287f919..d03a344 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -34,13 +34,6 @@ _log_handler = logging.FileHandler( LOG_FILE, mode = 'w' ) _log_handler.setFormatter( logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) ) -# this is the "large number" that vim returns for col num, when getting '> mark -# with getpos() for line wise visual selection(V) -# see https://github.com/vim/vim/blob/eed9d46293f0842aad0d50ff3a526f9a48b12421/src/evalfunc.c#L4077 -# and https://github.com/vim/vim/blob/064095012c0b8e4e43e75834b337115950898fbf/src/vim.h#L1699 -MAX_COL = 2147483647 - - def SetUpLogging( logger ): logger.setLevel( logging.DEBUG ) if _log_handler not in logger.handlers: @@ -739,9 +732,7 @@ def GetVisualSelection( bufnr ): # don't as me why... lines = vim.buffers[ bufnr ][ start_line - 1 : end_line ] lines[ 0 ] = lines[ 0 ][ start_col : ] - # only trim the end if we are not in line-wise select mode - if( end_col < MAX_COL ): - lines[ -1 ] = lines[ -1 ][ : end_col + 1 ] + lines[ -1 ] = lines[ -1 ][ : end_col + 1 ] return lines From 44711899cbb68c6f2b10b11ef3a842b7659d8f14 Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 20 Feb 2021 00:15:31 -0500 Subject: [PATCH 494/609] moving stuff around --- autoload/vimspector/internal/balloon.vim | 90 ++++++++++++------------ python3/vimspector/debug_session.py | 19 ----- 2 files changed, 45 insertions(+), 64 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index bc445ba..16ae4c8 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -21,9 +21,8 @@ set cpoptions&vim scriptencoding utf-8 -" Returns: py.ShowBalloon( winnr, expresssion ) function! vimspector#internal#balloon#HoverTooltip() abort - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') + return py3eval('_vimspector_session.ShowEvalBalloon(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') endfunction let s:float_win = 0 @@ -35,6 +34,47 @@ let s:min_height = 1 let s:max_width = 80 let s:max_height = 20 +function! vimspector#internal#balloon#MouseFilter(winid, key) abort + if index(["\", "\<2-leftmouse>"], a:key) < 0 + return 0 + endif + + let handled = 0 + let mouse_coords = getmousepos() + + " close the popup if mouse is clicked outside the window + if mouse_coords['winid'] != a:winid + call vimspector#internal#balloon#Close() + return 0 + endif + + " place the cursor according to the click + call win_execute(a:winid, ':call cursor('.mouse_coords['line'].', '.mouse_coords['column'].')') + + " expand the variable if we got double click + if a:key ==? "\<2-leftmouse>" + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') + let handled = 1 + endif + + return handled +endfunction + +function! vimspector#internal#balloon#CursorFilter(winid, key) abort + if a:key ==? "\" + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') + return 1 + elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 + return vimspector#internal#balloon#MouseFilter( a:winid, a:key ) + endif + + return popup_filter_menu( a:winid, a:key ) +endfunction + function! vimspector#internal#balloon#Close() abort if has('nvim') call nvim_win_close(s:float_win, v:true) @@ -152,50 +192,10 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) abort " make sure we clean up the float after it loses focus augroup vimspector#internal#balloon#nvim_float autocmd! - autocmd WinLeave * :call vimspector#internal#balloon#Close() | autocmd! vimspector#internal#balloon#nvim_float + autocmd BufLeave * :call vimspector#internal#balloon#Close() | autocmd! vimspector#internal#balloon#nvim_float augroup END else - func! MouseFilter(winid, key) abort - if index(["\", "\<2-leftmouse>"], a:key) < 0 - return 0 - endif - - let handled = 0 - let mouse_coords = getmousepos() - - " close the popup if mouse is clicked outside the window - if mouse_coords['winid'] != a:winid - call vimspector#internal#balloon#Close() - return 0 - endif - - " place the cursor according to the click - call win_execute(a:winid, ':call cursor('.mouse_coords['line'].', '.mouse_coords['column'].')') - - " expand the variable if we got double click - if a:key ==? "\<2-leftmouse>" - " forward line number to python, since vim does not allow us to focus - " the correct window - call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') - let handled = 1 - endif - - return handled - endfunc - - func! CursorFilter(winid, key) abort - if a:key ==? "\" - " forward line number to python, since vim does not allow us to focus - " the correct window - call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') - return 1 - elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 - return MouseFilter( a:winid, a:key ) - endif - - return popup_filter_menu( a:winid, a:key ) - endfunc if s:float_win != 0 call vimspector#internal#balloon#Close() @@ -222,11 +222,11 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) abort endif if a:is_hover - let config['filter'] = 'MouseFilter' + let config['filter'] = 'vimspector#internal#balloon#MouseFilter' let config['mousemoved'] = [0, 0, 0] let s:float_win = popup_beval(body, config) else - let config['filter'] = 'CursorFilter' + let config['filter'] = 'vimspector#internal#balloon#CursorFilter' let config['moved'] = 'any' let config['cursorline'] = 1 let s:float_win = popup_atcursor(body, config) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index db14ebc..c91caa4 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -558,25 +558,6 @@ class DebugSession( object ): return self._variablesView.VariableEval( frame, expression, is_hover ) - @IfConnected() - def ShowTooltip( self, winnr, expression, is_hover ): - """Proxy: ballonexpr -> variables.ShowTooltip""" - frame = self._stackTraceView.GetCurrentFrame() - # Check if RIP is in a frame - if frame is None: - self._logger.debug( 'Tooltip: Not in a stack frame' ) - return '' - - # Check if cursor in code window - if winnr != int( self._codeView._window.number ): - self._logger.debug( 'Winnr %s is not the code window %s', - winnr, - self._codeView._window.number ) - return '' - - # Return variable aware function - return self._variablesView.VariableEval( frame, expression, is_hover ) - def _CleanUpTooltip( self ): return self._variablesView._CleanUpTooltip() From 7dcb15f11ccb89f3a0a29df8976d2cf9bfe71e0b Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 20 Feb 2021 00:27:11 -0500 Subject: [PATCH 495/609] cleaning up --- python3/vimspector/utils.py | 4 ++-- python3/vimspector/variables.py | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index d03a344..8010fb9 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -639,7 +639,7 @@ def DisplayBalloon( is_term, display, is_hover = False ): # Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685 display = '\n'.join( display ) - rc = int( vim.eval( + created_win_id = int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format( int( is_hover ), json.dumps( display ) ) @@ -647,7 +647,7 @@ def DisplayBalloon( is_term, display, is_hover = False ): vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) - return rc + return created_win_id def GetBufferFilepath( buf ): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index c0cb2fd..7890e1b 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -138,7 +138,7 @@ class View: self.lines = lines self.draw = draw self.syntax = None - if ( win is not None ): + if win is not None: self.buf = win.buffer utils.SetUpUIWindow( win ) @@ -285,7 +285,7 @@ class VariablesView( object ): }, } ) - def _DrawEval( self ): + def _DrawBalloonEval( self ): watch = self._variable_eval view = self._variable_eval_view @@ -308,7 +308,6 @@ class VariablesView( object ): # remove reference to old tooltip window self._variable_eval_view = None vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = None - return '' def VariableEval( self, frame, expression, is_hover ): """Callback to display variable under cursor `:h ballonexpr`""" @@ -338,7 +337,7 @@ class VariablesView( object ): { 'options': {}, 'buffer': vim.buffers[ float_buf_nr ] } ), {}, - self._DrawEval + self._DrawBalloonEval ) if watch.result.IsExpandable(): @@ -355,7 +354,7 @@ class VariablesView( object ): }, } ) - self._DrawEval() + self._DrawBalloonEval() def failure_handler( reason, message ): display = [ reason ] From cc84e159329fbed068754b0bb6d3237f0ce5d14d Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 00:34:51 +0000 Subject: [PATCH 496/609] Tidy up, refactor and fix some bugs --- autoload/vimspector.vim | 13 +- autoload/vimspector/internal/balloon.vim | 411 +++++++++++++---------- plugin/vimspector.vim | 7 +- python3/vimspector/debug_session.py | 8 +- python3/vimspector/utils.py | 17 +- python3/vimspector/variables.py | 61 ++-- 6 files changed, 307 insertions(+), 210 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 9ac7ff1..d4bcfa7 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -525,13 +525,18 @@ endfunction function! vimspector#ShowEvalBalloon( is_visual ) abort if a:is_visual - let expr = py3eval( '__import__( "vimspector", fromlist = [ "utils" ] ).utils.GetVisualSelection( int( vim.eval( "winbufnr( winnr() )" ) ) )' ) - let expr = join(expr) + let expr = py3eval( '__import__( "vimspector", fromlist = [ "utils" ] )' + \ . '.utils.GetVisualSelection(' + \ . ' int( vim.eval( "winbufnr( winnr() )" ) ) )' ) + let expr = join( expr, '\n' ) else - let expr = expand('') + let expr = expand( '' ) endif - return py3eval( '_vimspector_session.ShowEvalBalloon( int( vim.eval( "winnr()" ) ), "'.expr.'", 0 )' ) + return py3eval( '_vimspector_session.ShowEvalBalloon(' + \ . ' int( vim.eval( "winnr()" ) ), "' + \ . expr + \ . '", 0 )' ) endfunction diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 16ae4c8..5f25f3a 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -21,12 +21,8 @@ set cpoptions&vim scriptencoding utf-8 -function! vimspector#internal#balloon#HoverTooltip() abort - return py3eval('_vimspector_session.ShowEvalBalloon(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') -endfunction - -let s:float_win = 0 -let s:nvim_related_win = 0 +let s:popup_win_id = 0 +let s:nvim_border_win_id = 0 " " tooltip dimensions let s:min_width = 1 @@ -34,173 +30,31 @@ let s:min_height = 1 let s:max_width = 80 let s:max_height = 20 -function! vimspector#internal#balloon#MouseFilter(winid, key) abort - if index(["\", "\<2-leftmouse>"], a:key) < 0 - return 0 - endif +let s:is_neovim = has( 'nvim' ) - let handled = 0 - let mouse_coords = getmousepos() - " close the popup if mouse is clicked outside the window - if mouse_coords['winid'] != a:winid - call vimspector#internal#balloon#Close() - return 0 - endif - - " place the cursor according to the click - call win_execute(a:winid, ':call cursor('.mouse_coords['line'].', '.mouse_coords['column'].')') - - " expand the variable if we got double click - if a:key ==? "\<2-leftmouse>" - " forward line number to python, since vim does not allow us to focus - " the correct window - call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') - let handled = 1 - endif - - return handled +" This is used as the balloonexpr in vim to show the Tooltip at the hover +" position +function! vimspector#internal#balloon#HoverTooltip() abort + return py3eval( '_vimspector_session.ShowEvalBalloon(' + \ . ' int( vim.eval( "v:beval_winnr" ) ) + 1,' + \ . ' vim.eval( "v:beval_text"),' + \ . ' 1 )' ) endfunction -function! vimspector#internal#balloon#CursorFilter(winid, key) abort - if a:key ==? "\" - " forward line number to python, since vim does not allow us to focus - " the correct window - call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') - return 1 - elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 - return vimspector#internal#balloon#MouseFilter( a:winid, a:key ) - endif - - return popup_filter_menu( a:winid, a:key ) -endfunction - -function! vimspector#internal#balloon#Close() abort - if has('nvim') - call nvim_win_close(s:float_win, v:true) - call nvim_win_close(s:nvim_related_win, v:true) - - call vimspector#internal#balloon#CloseCallback() - else - call popup_close(s:float_win) - endif - -endfunction - -function! vimspector#internal#balloon#CloseCallback( ... ) abort - let s:float_win = 0 - let s:nvim_related_win = 0 - return py3eval('_vimspector_session._CleanUpTooltip()') -endfunction - -function! vimspector#internal#balloon#nvim_generate_border(width, height) abort - let top = '╭' . repeat('─',a:width + 2) . '╮' - let mid = '│' . repeat(' ',a:width + 2) . '│' - let bot = '╰' . repeat('─',a:width + 2) . '╯' - let lines = [top] + repeat([mid], a:height) + [bot] - - return lines -endfunction - -function! vimspector#internal#balloon#nvim_resize_tooltip() abort - if !has('nvim') || s:float_win <= 0 || s:nvim_related_win <= 0 - return - endif - - noa call win_gotoid(s:float_win) - let buf_lines = getline(1, '$') - - let width = s:min_width - let height = min([max([s:min_height, len(buf_lines)]), s:max_height]) - - " calculate the longest line - for l in buf_lines - let width = max([width, len(l)]) - endfor - - let width = min([width, s:max_width]) - - let opts = { - \ 'width': width, - \ 'height': height, - \ } - " resize the content window - call nvim_win_set_config(s:float_win, opts) - - " resize the border window - let opts['width'] = width + 4 - let opts['height'] = height + 2 - call nvim_win_set_config(s:nvim_related_win, opts) - call nvim_buf_set_lines(nvim_win_get_buf(s:nvim_related_win), 0, -1, v:true, vimspector#internal#balloon#nvim_generate_border(width, height)) - -endfunction - -function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) abort +function! vimspector#internal#balloon#CreateTooltip( is_hover, ... ) abort let body = [] if a:0 > 0 let body = a:1 endif - if has('nvim') - " generate border for the float window by creating a background buffer and - " overlaying the content buffer - " see https://github.com/neovim/neovim/issues/9718#issuecomment-546603628 - let buf_id = nvim_create_buf(v:false, v:true) - call nvim_buf_set_lines(buf_id, 0, -1, v:true, vimspector#internal#balloon#nvim_generate_border(s:max_width, s:max_height)) - - " default the dimensions for now. they can be easily overwritten later - let opts = { - \ 'relative': 'cursor', - \ 'width': s:max_width + 2, - \ 'height': s:max_height + 2, - \ 'col': 0, - \ 'row': 1, - \ 'anchor': 'NW', - \ 'style': 'minimal' - \ } - " this is the border window - let s:nvim_related_win = nvim_open_win(buf_id, 0, opts) - call nvim_win_set_option(s:nvim_related_win, 'signcolumn', 'no') - call nvim_win_set_option(s:nvim_related_win, 'relativenumber', v:false) - call nvim_win_set_option(s:nvim_related_win, 'number', v:false) - - " when calculating where to display the content window, we need to account - " for the border - let opts.row += 1 - let opts.height -= 2 - let opts.col += 2 - let opts.width -= 4 - - " create the content window - let buf_id = nvim_create_buf(v:false, v:true) - call nvim_buf_set_lines(buf_id, 0, -1, v:true, body) - call nvim_buf_set_option(buf_id, 'modifiable', v:false) - let s:float_win = nvim_open_win(buf_id, v:false, opts) - - call nvim_win_set_option(s:float_win, 'wrap', v:false) - call nvim_win_set_option(s:float_win, 'cursorline', v:true) - call nvim_win_set_option(s:float_win, 'signcolumn', 'no') - call nvim_win_set_option(s:float_win, 'relativenumber', v:false) - call nvim_win_set_option(s:float_win, 'number', v:false) - - noautocmd call win_gotoid(s:float_win) - - nnoremap :call vimspector#ExpandVariable() - nnoremap :quit - nnoremap <2-LeftMouse>:call vimspector#ExpandVariable() - - " make sure we clean up the float after it loses focus - augroup vimspector#internal#balloon#nvim_float - autocmd! - autocmd BufLeave * :call vimspector#internal#balloon#Close() | autocmd! vimspector#internal#balloon#nvim_float - augroup END + if s:popup_win_id != 0 + call vimspector#internal#balloon#Close() + endif + if s:is_neovim + call s:CreateNeovimTooltip( body ) else - - if s:float_win != 0 - call vimspector#internal#balloon#Close() - endif - let config = { \ 'wrap': 0, \ 'filtermode': 'n', @@ -217,26 +71,241 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) abort \ 'callback': 'vimspector#internal#balloon#CloseCallback' \ } + " When ambiwidth is single, use prettier characters for the border. This + " would look silly when ambiwidth is double. if &ambiwidth ==# 'single' && &encoding ==? 'utf-8' - let config['borderchars'] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ] + let config[ 'borderchars' ] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ] endif if a:is_hover - let config['filter'] = 'vimspector#internal#balloon#MouseFilter' - let config['mousemoved'] = [0, 0, 0] - let s:float_win = popup_beval(body, config) + let config[ 'filter' ] = 'vimspector#internal#balloon#MouseFilter' + let config[ 'mousemoved' ] = [ 0, 0, 0 ] + let s:popup_win_id = popup_beval( body, config ) else - let config['filter'] = 'vimspector#internal#balloon#CursorFilter' - let config['moved'] = 'any' - let config['cursorline'] = 1 - let s:float_win = popup_atcursor(body, config) + let config[ 'filter' ] = 'vimspector#internal#balloon#CursorFilter' + let config[ 'moved' ] = 'any' + let config[ 'cursorline' ] = 1 + let s:popup_win_id = popup_atcursor( body, config ) endif endif - return s:float_win + return s:popup_win_id endfunction +" Filters for vim {{{ +function! vimspector#internal#balloon#MouseFilter( winid, key ) abort + if a:key ==# "\" + call vimspector#internal#balloon#Close() + return 0 + endif + + if index( [ "\", "\<2-leftmouse>" ], a:key ) < 0 + return 0 + endif + + let handled = 0 + let mouse_coords = getmousepos() + + " close the popup if mouse is clicked outside the window + if mouse_coords[ 'winid' ] != a:winid + call vimspector#internal#balloon#Close() + return 0 + endif + + " place the cursor according to the click + call win_execute( a:winid, + \ ':call cursor( ' + \ . mouse_coords[ 'line' ] + \ . ', ' + \ . mouse_coords[ 'column' ] + \ . ' )' ) + + " expand the variable if we got double click + if a:key ==? "\<2-leftmouse>" + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval( '_vimspector_session.ExpandVariable(' + \ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],' + \ . 'line_num = ' . line( '.', a:winid ) + \ . ')' ) + let handled = 1 + endif + + return handled +endfunction + +function! vimspector#internal#balloon#CursorFilter( winid, key ) abort + if a:key ==? "\" + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval( '_vimspector_session.ExpandVariable(' + \ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],' + \ . 'line_num = ' . line( '.', a:winid ) + \ . ')' ) + return 1 + elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 + return vimspector#internal#balloon#MouseFilter( a:winid, a:key ) + endif + + return popup_filter_menu( a:winid, a:key ) +endfunction + +" }}} + +" Closing {{{ + +function! vimspector#internal#balloon#CloseCallback( ... ) abort + let s:popup_win_id = 0 + let s:nvim_border_win_id = 0 + return py3eval( '_vimspector_session.CleanUpTooltip()' ) +endfunction + +function! vimspector#internal#balloon#Close() abort + if s:is_neovim + call nvim_win_close( s:popup_win_id, v:true ) + call nvim_win_close( s:nvim_border_win_id, v:true ) + + call vimspector#internal#balloon#CloseCallback() + else + call popup_close(s:popup_win_id) + endif +endfunction + +" }}} + +" Neovim pollyfill {{{ + +function! vimspector#internal#balloon#ResizeTooltip() abort + if !s:is_neovim + " Vim does this for us + return + endif + + if s:popup_win_id <= 0 || s:nvim_border_win_id <= 0 + " nothing to resize + return + endif + + noautocmd call win_gotoid( s:popup_win_id ) + let buf_lines = getline( 1, '$' ) + + let width = s:min_width + let height = min( [ max( [ s:min_height, len( buf_lines ) ] ), + \ s:max_height ] ) + + " calculate the longest line + for l in buf_lines + let width = max( [ width, len( l ) ] ) + endfor + + let width = min( [ width, s:max_width ] ) + + let opts = { + \ 'width': width, + \ 'height': height, + \ } + + " resize the content window + call nvim_win_set_config( s:popup_win_id, opts ) + + " resize the border window + let opts[ 'width' ] = width + 4 + let opts[ 'height' ] = height + 2 + + call nvim_win_set_config( s:nvim_border_win_id, opts ) + call nvim_buf_set_lines( nvim_win_get_buf( s:nvim_border_win_id ), + \ 0, + \ -1, + \ v:true, + \ s:GenerateBorder( width, height ) ) +endfunction + +" neovim doesn't have the border support, so we have to make our own. +" FIXME: This will likely break if the user has `ambiwidth=2` +function! s:GenerateBorder( width, height ) abort + + let top = '╭' . repeat('─',a:width + 2) . '╮' + let mid = '│' . repeat(' ',a:width + 2) . '│' + let bot = '╰' . repeat('─',a:width + 2) . '╯' + let lines = [ top ] + repeat( [ mid ], a:height ) + [ bot ] + + return lines +endfunction + +function! s:CreateNeovimTooltip( body ) abort + " generate border for the float window by creating a background buffer and + " overlaying the content buffer + " see https://github.com/neovim/neovim/issues/9718#issuecomment-546603628 + let buf_id = nvim_create_buf( v:false, v:true ) + call nvim_buf_set_lines( buf_id, + \ 0, + \ -1, + \ v:true, + \ s:GenerateBorder( s:max_width, s:max_height ) ) + + " default the dimensions initially, then we'll calculate the real size and + " resize it. + let opts = { + \ 'relative': 'cursor', + \ 'width': s:max_width + 2, + \ 'height': s:max_height + 2, + \ 'col': 0, + \ 'row': 1, + \ 'anchor': 'NW', + \ 'style': 'minimal' + \ } + + " this is the border window + let s:nvim_border_win_id = nvim_open_win( buf_id, 0, opts ) + call nvim_win_set_option( s:nvim_border_win_id, 'signcolumn', 'no' ) + call nvim_win_set_option( s:nvim_border_win_id, 'relativenumber', v:false ) + call nvim_win_set_option( s:nvim_border_win_id, 'number', v:false ) + + " when calculating where to display the content window, we need to account + " for the border + let opts.row += 1 + let opts.height -= 2 + let opts.col += 2 + let opts.width -= 4 + + " create the content window + let buf_id = nvim_create_buf( v:false, v:true ) + call nvim_buf_set_lines( buf_id, 0, -1, v:true, a:body ) + call nvim_buf_set_option( buf_id, 'modifiable', v:false ) + let s:popup_win_id = nvim_open_win( buf_id, v:false, opts ) + + call nvim_win_set_option( s:popup_win_id, 'wrap', v:false ) + call nvim_win_set_option( s:popup_win_id, 'cursorline', v:true ) + call nvim_win_set_option( s:popup_win_id, 'signcolumn', 'no' ) + call nvim_win_set_option( s:popup_win_id, 'relativenumber', v:false ) + call nvim_win_set_option( s:popup_win_id, 'number', v:false ) + + " Move the cursor into the popup window, as this is the only way we can + " interract with the popup in neovim + noautocmd call win_gotoid( s:popup_win_id ) + + nnoremap + \ call vimspector#ExpandVariable() + nnoremap + \ quit + nnoremap <2-LeftMouse> + \ call vimspector#ExpandVariable() + + " Close the popup whenever we leave this window + augroup vimspector#internal#balloon#nvim_float + autocmd! + autocmd WinLeave + \ :call vimspector#internal#balloon#Close() + \ | autocmd! vimspector#internal#balloon#nvim_float + augroup END + + call vimspector#internal#balloon#ResizeTooltip() +endfunction + +" }}} + + " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 6465c21..6691a2a 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -60,11 +60,12 @@ nnoremap VimspectorStepOut nnoremap VimspectorRunToCursor \ :call vimspector#RunToCursor() +" Eval for normal mode nnoremap VimspectorBalloonEval - \ :call vimspector#ShowEvalBalloon(0) - + \ :call vimspector#ShowEvalBalloon( 0 ) +" And for visual modes xnoremap VimspectorBalloonEval - \ :call vimspector#ShowEvalBalloon(1) + \ :call vimspector#ShowEvalBalloon( 1 ) if s:mappings ==# 'VISUAL_STUDIO' nmap VimspectorContinue diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index c91caa4..36e6feb 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -520,8 +520,8 @@ class DebugSession( object ): self._stackTraceView.SetCurrentThread() @IfConnected() - def ExpandVariable( self, lineNum = -1 ): - self._variablesView.ExpandVariable( lineNum ) + def ExpandVariable( self, buf = None, line_num = None ): + self._variablesView.ExpandVariable( buf, line_num ) @IfConnected() def AddWatch( self, expression ): @@ -558,8 +558,8 @@ class DebugSession( object ): return self._variablesView.VariableEval( frame, expression, is_hover ) - def _CleanUpTooltip( self ): - return self._variablesView._CleanUpTooltip() + def CleanUpTooltip( self ): + return self._variablesView.CleanUpTooltip() @IfConnected() def ExpandFrameOrThread( self ): diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 8010fb9..e2f96b0 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -645,8 +645,6 @@ def DisplayBalloon( is_term, display, is_hover = False ): ) ) ) - vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) - return created_win_id @@ -728,11 +726,20 @@ def GetVisualSelection( bufnr ): start_line, start_col = vim.current.buffer.mark( "<" ) end_line, end_col = vim.current.buffer.mark( ">" ) + # lines are 1 based, but columns are 0 based - # don't as me why... - lines = vim.buffers[ bufnr ][ start_line - 1 : end_line ] - lines[ 0 ] = lines[ 0 ][ start_col : ] + # don't ask me why... + start_line -= 1 + end_line -= 1 + + lines = vim.buffers[ bufnr ][ start_line : end_line + 1 ] + # Do end first, in case it's on the same line as start (as doing start first + # would change the offset) lines[ -1 ] = lines[ -1 ][ : end_col + 1 ] + lines[ 0 ] = lines[ 0 ][ start_col : ] + + _logger.debug( f'Visual selection: { lines } from ' + f'{ start_line }/{ start_col } -> { end_line }/{ end_col }' ) return lines diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 7890e1b..74626ac 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -143,6 +143,12 @@ class View: utils.SetUpUIWindow( win ) +class BufView( View ): + def __init__( self, buf, lines, draw ): + super().__init__( None, lines, draw ) + self.buf = buf + + class VariablesView( object ): def __init__( self, variables_win, watches_win ): self._logger = logging.getLogger( __name__ ) @@ -219,6 +225,7 @@ class VariablesView( object ): utils.ClearBuffer( self._vars.buf ) with utils.ModifiableScratchBuffer( self._watch.buf ): utils.ClearBuffer( self._watch.buf ) + self.ClearTooltip() self._current_syntax = '' def ConnectionUp( self, connection ): @@ -234,6 +241,8 @@ class VariablesView( object ): utils.CleanUpHiddenBuffer( self._vars.buf ) utils.CleanUpHiddenBuffer( self._watch.buf ) + self.ClearTooltip() + def LoadScopes( self, frame ): def scopes_consumer( message ): @@ -302,9 +311,14 @@ class VariablesView( object ): watch, is_short = True ) - vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) + vim.eval( "vimspector#internal#balloon#ResizeTooltip()" ) - def _CleanUpTooltip( self ) : + def ClearTooltip( self ): + # This will actually end up calling CleanUpTooltip via the popup close + # callback + vim.eval( 'vimspector#internal#balloon#Close()' ) + + def CleanUpTooltip( self ) : # remove reference to old tooltip window self._variable_eval_view = None vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = None @@ -322,22 +336,17 @@ class VariablesView( object ): else: watch.result.Update( message[ 'body' ] ) - float_win_id = utils.DisplayBalloon( self._is_term, [], is_hover ) + popup_win_id = utils.DisplayBalloon( self._is_term, [], is_hover ) # record the global eval window id - vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( float_win_id ) - float_buf_nr = int( vim.eval( "winbufnr({})".format( float_win_id ) ) ) + vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( popup_win_id ) + popup_bufnr = int( vim.eval( "winbufnr({})".format( popup_win_id ) ) ) - # since vim's popup cant be focused there is no way - # to get a reference to its window - # we will emulate python's window object ourselves - self._variable_eval_view = View( - type( - '__vim__window__', - ( object, ), - { 'options': {}, 'buffer': vim.buffers[ float_buf_nr ] } - ), - {}, - self._DrawBalloonEval + # We don't need to do any UI window setup here, as it's already done as + # part of the popup creation, so just pass the buffer to the View instance + self._variable_eval_view = BufView( + vim.buffers[ popup_bufnr ], + {}, + self._DrawBalloonEval ) if watch.result.IsExpandable(): @@ -439,21 +448,27 @@ class VariablesView( object ): watch.result = WatchFailure( reason ) self._DrawWatches() - def ExpandVariable( self, lineNum = -1 ): - if vim.current.buffer == self._vars.buf: + def ExpandVariable( self, buf = None, line_num = None ): + if buf is None: + buf = vim.current.buffer + + if line_num is None: + line_num = vim.current.window.cursor[ 0 ] + + if buf == self._vars.buf: view = self._vars - elif vim.current.buffer == self._watch.buf: + elif buf == self._watch.buf: view = self._watch - elif self._variable_eval_view is not None: + elif ( self._variable_eval_view is not None + and buf == self._variable_eval_view.buf ): view = self._variable_eval_view else: return - current_line = vim.current.window.cursor[ 0 ] if lineNum <= 0 else lineNum - if current_line not in view.lines: + if line_num not in view.lines: return - variable = view.lines[ current_line ] + variable = view.lines[ line_num ] if variable.IsExpanded(): # Collapse From 5a1eb9250a16491b520d980659c39944ebb65943 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 16:26:48 +0000 Subject: [PATCH 497/609] Fix test now that we're using a mapping --- tests/variables.test.vim | 40 ++++++++++++++++++++-------------------- tests/vimrc | 4 +++- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index ae51ad8..a43fafd 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -601,10 +601,14 @@ function! Test_VariableEval() call vimspector#StepOver() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) + " leader is , + xmap d VimspectorBalloonEval + nmap d VimspectorBalloonEval + "evaluate the prev line - call setpos('.', [ 0, 24, 8 ]) + call setpos( '.', [ 0, 24, 8 ] ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) - call vimspector#ShowTooltip() + call feedkeys( ',d', 'xt' ) call WaitForAssert( {-> \ assert_notequal( v:none, g:vimspector_session_windows.eval ) @@ -633,19 +637,10 @@ function! Test_VariableEval() \ } ) " test selection - call setpos('.', [ 0, 24, 8 ]) + call setpos( '.', [ 0, 24, 8 ] ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) - " enter visual mode - " this is a hack, since usually, when user enters command mode from inside - " visual mode, the latter is immediately interrupted and the '<' '>' marks are - " set. for some odd reason, visual mode is not interupted from the script, - " so we need to manually escape and re-trigger previous visual selection - call execute('normal v') - call feedkeys("lllll\", 'xt') - call execute('normal gv') - - call vimspector#ShowTooltipForSelection() + call feedkeys( 'viw,d', 'xt' ) call WaitForAssert( {-> \ assert_notequal( v:none, g:vimspector_session_windows.eval ) @@ -667,18 +662,19 @@ function! Test_VariableEval() \ } ) "Close - " we need to send esc twice because of the weird interactions between visual - " mode and tests - call feedkeys( "\\", 'xt' ) + call feedkeys( "\", 'xt' ) call WaitForAssert( {-> \ assert_equal( v:none, g:vimspector_session_windows.eval ) \ } ) + " Get back to normal mode + call feedkeys( "\", 'xt' ) + " Evaluation error - call setpos('.', [ 0, 25, 1 ]) + call setpos( '.', [ 0, 25, 1 ] ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 25, 1 ) - call vimspector#ShowTooltip() + call feedkeys( ',d', 'xt' ) call WaitForAssert( {-> \ assert_notequal( v:none, g:vimspector_session_windows.eval ) @@ -715,10 +711,14 @@ function! Test_VariableEvalExpand() call vimspector#StepOver() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) + " leader is , + xmap d VimspectorBalloonEval + nmap d VimspectorBalloonEval + "evaluate the prev line - call setpos('.', [ 0, 24, 8 ]) + call setpos( '.', [ 0, 24, 8 ] ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) - call vimspector#ShowTooltip() + call feedkeys( ',d', 'xt' ) call WaitForAssert( {-> \ assert_notequal( v:none, g:vimspector_session_windows.eval ) diff --git a/tests/vimrc b/tests/vimrc index e362824..c65efa8 100644 --- a/tests/vimrc +++ b/tests/vimrc @@ -1,8 +1,10 @@ let g:vimspector_test_plugin_path = expand( ':p:h:h' ) set mouse=a set noequalalways +let mapleader = ',' +let maplocalleader = "\" -let &rtp = &rtp . ',' . g:vimspector_test_plugin_path +let &runtimepath = &runtimepath . ',' . g:vimspector_test_plugin_path filetype plugin indent on syntax enable From 323e22b8a97a1d226d6e076c5476eda075a99ad5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 16:58:45 +0000 Subject: [PATCH 498/609] Update readme --- README.md | 50 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a7519e8..8475ab2 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ And a couple of brief demos: - locals and globals display - watch expressions with autocompletion - call stack display and navigation -- variable value display hover +- hierarchical variable value display popup (see `VimspectorBalloonEval`) - interactive debug console with autocompletion - launch debugee within Vim's embedded terminal - logging/stdout display @@ -231,8 +231,8 @@ Why such a new vim ? Well 2 reasons: if you hit them. Why is neovim experimental? Because the author doesn't use neovim regularly, and -there are no regression tests for vimspector in neovim, so it's likely to break -frequently. Issue reports are handled on best-efforts basis, and PRs are +there are no regression tests for vimspector in neovim, so it may break +occasionally. Issue reports are handled on best-efforts basis, and PRs are welcome to fix bugs. See also the next section descibing differences for neovim vs vim. @@ -250,7 +250,8 @@ neovim doesn't implement some features Vimspector relies on: the output window's current output. * Prompt Buffers - used to send commands in the Console and add Watches. (*Note*: prompt buffers are available in neovim nightly) -* Tooltips - used to display the values of variables when debugging. +* Balloons - this allows for the variable evaluation popup to be displayed when + hovering the mouse. See below for how to create a keyboard mapping instead. Workarounds are in place as follows: @@ -259,9 +260,18 @@ Workarounds are in place as follows: [`:VimspectorReset`](#closing-debugger) * Prompt Buffers - There are [`:VimspectorEval`](#console) and [`:VimspectorWatch`](#watches) -* Functions - There are - [`:call vimspector#ShowTooltip()`](#variable-or-selection-hover-evaluation) and - [`:call vimspector#ShowTooltipForSelection()`](#variable-or-selection-hover-evaluation) +* Balloons - There is the `VimspectorBalloonEval` mapping. There is no +default mapping for this, so I recommend something like this to get variable +display in a popup: + +```viml +" mnemonic 'di' = 'debug inspect' (pick your own, if you prefer!) + +" for normal mode - the word under the cursor +nmap di VimspectorBalloonEval +" for visual mode, the visually selected text +xmap di VimspectorBalloonEval +``` ## Windows differences @@ -654,6 +664,7 @@ features to set your own mappings. To that end, Vimspector defines the following * `VimspectorStepInto` * `VimspectorStepOut` * `VimspectorRunToCursor` +* `VimspectorBalloonEval` These map roughly 1-1 with the API functions below. @@ -716,6 +727,18 @@ let g:vimspector_enable_mappings = 'HUMAN' | `F11` | Step Into | `vimspector#StepInto()` | | `F12` | Step out of current function scope | `vimspector#StepOut()` | +In addition, I recommend adding a mapping to `VimspectorBalloonEval`, in +normal and visual modes, for example: + +```viml +" mnemonic 'di' = 'debug inspect' (pick your own, if you prefer!) + +" for normal mode - the word under the cursor +nmap di VimspectorBalloonEval +" for visual mode, the visually selected text +xmap di VimspectorBalloonEval +``` + # Usage and API This section defines detailed usage instructions, organised by feature. For most @@ -894,13 +917,16 @@ Scopes and variables are represented by the buffer `vimspector.Variables`. ## Variable or selection hover evaluation All rules for `Variables and scopes` apply plus the following: + * With mouse enabled, hover over a variable and get the value it evaluates to. -* Use your mouse to perform a visual selection of an expression (e.g. `a + b`) and get its result. -* Call `vimspector#ShowTooltip()` or `vimspector#ShowTooltipForSelection()` to evaluate expressions without mouse (the only way to use this feature in nvim). -* Use regular nagivation keys to chose the current selection; `` (or leave the tooltip window) to close the tooltip. - -Note: using a selection evaluation might lead to undesired consequences, since **the expression is actually executed**. E.g. `c = a + b;` once this entire line is evaluated, value of `c` becomes the sum of `a` and `b`. +* Use your mouse to perform a visual selection of an expression (e.g. `a + b`) + and get its result. +* Make a normal mode (`nmap`) and visual mode (`xmap`) mapping to + `VimspectorBalloonEval` to manually trigger the popup. +* Use regular nagivation keys (`j`, `k`) to chose the current selection; `` + (or leave the tooltip window) to close the tooltip. +![variable eval hover](https://puremourning.github.io/vimspector-web/img/vimspector-variable-eval-hover.png) ## Watches From 6b546cd621d171883d152f01c46ce6c8fcc1c77b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 16:59:41 +0000 Subject: [PATCH 499/609] Update TOC --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8475ab2..b3088b7 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ For detailed explanatin of the `.vimspector.json` format, see the * [Run to Cursor](#run-to-cursor) * [Stepping](#stepping) * [Variables and scopes](#variables-and-scopes) - * [Variable/selection hover evaluation](#variable-or-selection-hover-evaluation) + * [Variable or selection hover evaluation](#variable-or-selection-hover-evaluation) * [Watches](#watches) * [Watch autocompletion](#watch-autocompletion) * [Stack Traces](#stack-traces) @@ -90,7 +90,7 @@ For detailed explanatin of the `.vimspector.json` format, see the * [Example](#example) * [FAQ](#faq) - + From 4958de92d39809236433792197470cc672453e2b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 18:05:59 +0000 Subject: [PATCH 500/609] Fix flake8 error --- python3/vimspector/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index e2f96b0..4022315 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -34,6 +34,7 @@ _log_handler = logging.FileHandler( LOG_FILE, mode = 'w' ) _log_handler.setFormatter( logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) ) + def SetUpLogging( logger ): logger.setLevel( logging.DEBUG ) if _log_handler not in logger.handlers: From 5754e96067a5048308b84f402847776d1cac3d59 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 18:17:14 +0000 Subject: [PATCH 501/609] FixUp: Change of mapleader --- tests/breakpoints.test.vim | 8 ++++---- tests/breakpoints_doublewidth.test.vim | 8 ++++---- tests/mappings.test.vim | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index 2e3eef8..877c1cf 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -280,8 +280,8 @@ function! Test_Conditional_Line_Breakpoint() 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' ) + " Add the conditional breakpoint (note , is the mapleader) + call feedkeys( ",\argc==0\\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', \ 16, \ 'vimspectorBPCond', @@ -360,8 +360,8 @@ function! Test_Conditional_Line_Breakpoint_Hit() exe 'edit' fn call setpos( '.', [ 0, 14, 1 ] ) - " Add the conditional breakpoint (3 times) - call feedkeys( "\\\\3\", 'xt' ) + " Add the conditional breakpoint (3 times) (note , is the mapleader) + call feedkeys( ",\\3\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 14, diff --git a/tests/breakpoints_doublewidth.test.vim b/tests/breakpoints_doublewidth.test.vim index c646c3e..4bc0571 100644 --- a/tests/breakpoints_doublewidth.test.vim +++ b/tests/breakpoints_doublewidth.test.vim @@ -293,8 +293,8 @@ function! Test_Conditional_Line_Breakpoint() 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' ) + " Add the conditional breakpoint (, is mapleader) + call feedkeys( ",\argc==0\\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP', \ 16, \ 'vimspectorBPCond', @@ -370,8 +370,8 @@ function! Test_Conditional_Line_Breakpoint_Hit() exe 'edit' fn call setpos( '.', [ 0, 14, 1 ] ) - " Add the conditional breakpoint (3 times) - call feedkeys( "\\\\3\", 'xt' ) + " Add the conditional breakpoint (3 times) (, is mapleader) + call feedkeys( ",\\3\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 14, diff --git a/tests/mappings.test.vim b/tests/mappings.test.vim index fcd19fe..0f9995d 100644 --- a/tests/mappings.test.vim +++ b/tests/mappings.test.vim @@ -106,9 +106,9 @@ function! Test_Use_Mappings_HUMAN() \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 16 ) \ } ) - " Run to cursor + " Run to cursor (note , is the mapleader) call cursor( 9, 1 ) - call feedkeys( "\\\", 'xt' ) + call feedkeys( ",\", 'xt' ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 9, 1 ) call WaitForAssert( {-> \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 9 ) From d81bdf30efaefc017eb4e6fc707d69e5dd94d02a Mon Sep 17 00:00:00 2001 From: __ <__@__> Date: Sat, 6 Feb 2021 00:52:11 +0100 Subject: [PATCH 502/609] Emit User VimspectorFrameWasSet event --- python3/vimspector/debug_session.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 36e6feb..275fa72 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -710,6 +710,7 @@ class DebugSession( object ): self._stackTraceView.SetSyntax( self._codeView.current_syntax ) self._variablesView.LoadScopes( frame ) self._variablesView.EvaluateWatches() + vim.command( 'doautocmd User VimspectorFrameWasSet' ) if reason == 'stopped': self._breakpoints.ClearTemporaryBreakpoint( frame[ 'source' ][ 'path' ], From 0bb8416e116254047e7822c0271006daeb71dce3 Mon Sep 17 00:00:00 2001 From: __ <__@__> Date: Sat, 6 Feb 2021 13:10:38 +0100 Subject: [PATCH 503/609] Add test --- tests/ui.test.vim | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/ui.test.vim b/tests/ui.test.vim index b6c8feb..ea90f82 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -409,3 +409,18 @@ function! Test_CustomWinBar() call vimspector#test#setup#Reset() %bwipe! endfunction + +function! Test_VimspectorFrameWasSet() + augroup TestVimspectorFrameWasSet + au! + au User VimspectorFrameWasSet let b:vimspectorStepIsThere = 'foo' + augroup END + + call s:StartDebugging() + call assert_equal( 'foo', getbufvar(bufnr(), 'vimspectorStepIsThere', 0) ) + + au! TestVimspectorFrameWasSet + unlet b:vimspectorStepIsThere + call vimspector#test#setup#Reset() + %bwipe! +endfunction From 840ee09242b107e815aad7306f3bfe7e34e3ac18 Mon Sep 17 00:00:00 2001 From: __ <__@__> Date: Sat, 6 Feb 2021 14:48:39 +0100 Subject: [PATCH 504/609] Send an event before leave buffer in window --- python3/vimspector/debug_session.py | 1 - python3/vimspector/utils.py | 7 +++++++ tests/ui.test.vim | 8 ++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 275fa72..36e6feb 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -710,7 +710,6 @@ class DebugSession( object ): self._stackTraceView.SetSyntax( self._codeView.current_syntax ) self._variablesView.LoadScopes( frame ) self._variablesView.EvaluateWatches() - vim.command( 'doautocmd User VimspectorFrameWasSet' ) if reason == 'stopped': self._breakpoints.ClearTemporaryBreakpoint( frame[ 'source' ][ 'path' ], diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 4022315..eda958b 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -76,7 +76,14 @@ def WindowForBuffer( buf ): def OpenFileInCurrentWindow( file_name ): buffer_number = BufferNumberForFile( file_name ) try: + if "vimspectorStep" in vim.current.buffer.vars: + del vim.current.buffer.vars["vimspectorStep"] + # @todo: what if the same buffer is still visited by another thread + vim.command( 'doautocmd User VimspectorFrameLeavePre' ) + vim.current.buffer = vim.buffers[ buffer_number ] + vim.current.buffer.vars["vimspectorStep"] = 1 + vim.command( 'doautocmd User VimspectorFrameWasSet' ) except vim.error as e: if 'E325' not in str( e ): raise diff --git a/tests/ui.test.vim b/tests/ui.test.vim index ea90f82..91cd214 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -410,16 +410,16 @@ function! Test_CustomWinBar() %bwipe! endfunction -function! Test_VimspectorFrameWasSet() - augroup TestVimspectorFrameWasSet +function! Test_VimspectorFrameEnter() + augroup TestVimspectorFrameEnter au! - au User VimspectorFrameWasSet let b:vimspectorStepIsThere = 'foo' + au User VimspectorFrameEnter let b:vimspectorStepIsThere = 'foo' augroup END call s:StartDebugging() call assert_equal( 'foo', getbufvar(bufnr(), 'vimspectorStepIsThere', 0) ) - au! TestVimspectorFrameWasSet + au! TestVimspectorFrameEnter unlet b:vimspectorStepIsThere call vimspector#test#setup#Reset() %bwipe! From 716181e056eda0778179ff767cb9b5446a4f8aaf Mon Sep 17 00:00:00 2001 From: __ <__@__> Date: Sat, 6 Feb 2021 14:50:01 +0100 Subject: [PATCH 505/609] fixup! Send an event before leave buffer in window --- 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 eda958b..3ab31bc 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -83,7 +83,7 @@ def OpenFileInCurrentWindow( file_name ): vim.current.buffer = vim.buffers[ buffer_number ] vim.current.buffer.vars["vimspectorStep"] = 1 - vim.command( 'doautocmd User VimspectorFrameWasSet' ) + vim.command( 'doautocmd User VimspectorFrameEnter' ) except vim.error as e: if 'E325' not in str( e ): raise From 50dc55e0e827d82babeab8c534cebdff3113ad90 Mon Sep 17 00:00:00 2001 From: __ <__@__> Date: Sat, 6 Feb 2021 15:01:54 +0100 Subject: [PATCH 506/609] Fix errors reported by flake8 --- python3/vimspector/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 3ab31bc..5281ec8 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -77,12 +77,12 @@ def OpenFileInCurrentWindow( file_name ): buffer_number = BufferNumberForFile( file_name ) try: if "vimspectorStep" in vim.current.buffer.vars: - del vim.current.buffer.vars["vimspectorStep"] + del vim.current.buffer.vars[ "vimspectorStep" ] # @todo: what if the same buffer is still visited by another thread vim.command( 'doautocmd User VimspectorFrameLeavePre' ) vim.current.buffer = vim.buffers[ buffer_number ] - vim.current.buffer.vars["vimspectorStep"] = 1 + vim.current.buffer.vars[ "vimspectorStep" ] = 1 vim.command( 'doautocmd User VimspectorFrameEnter' ) except vim.error as e: if 'E325' not in str( e ): From bcf4120ba48afea716f6fa1149265da79f0c2610 Mon Sep 17 00:00:00 2001 From: przepompownia Date: Wed, 17 Feb 2021 01:04:49 +0100 Subject: [PATCH 507/609] Do not send event after leave buffer. --- python3/vimspector/code.py | 1 + python3/vimspector/utils.py | 7 ------- tests/ui.test.vim | 8 ++++---- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index d3c6324..98aeca5 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -134,6 +134,7 @@ class CodeView( object ): utils.JumpToWindow( self._window ) try: utils.OpenFileInCurrentWindow( frame[ 'source' ][ 'path' ] ) + vim.command( 'doautocmd User VimspectorJumpedToFrame' ) except vim.error: self._logger.exception( 'Unexpected vim error opening file {}'.format( frame[ 'source' ][ 'path' ] ) ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 5281ec8..4022315 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -76,14 +76,7 @@ def WindowForBuffer( buf ): def OpenFileInCurrentWindow( file_name ): buffer_number = BufferNumberForFile( file_name ) try: - if "vimspectorStep" in vim.current.buffer.vars: - del vim.current.buffer.vars[ "vimspectorStep" ] - # @todo: what if the same buffer is still visited by another thread - vim.command( 'doautocmd User VimspectorFrameLeavePre' ) - vim.current.buffer = vim.buffers[ buffer_number ] - vim.current.buffer.vars[ "vimspectorStep" ] = 1 - vim.command( 'doautocmd User VimspectorFrameEnter' ) except vim.error as e: if 'E325' not in str( e ): raise diff --git a/tests/ui.test.vim b/tests/ui.test.vim index 91cd214..390f870 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -410,16 +410,16 @@ function! Test_CustomWinBar() %bwipe! endfunction -function! Test_VimspectorFrameEnter() - augroup TestVimspectorFrameEnter +function! Test_VimspectorJumpedToFrame() + augroup TestVimspectorJumpedToFrame au! - au User VimspectorFrameEnter let b:vimspectorStepIsThere = 'foo' + au User VimspectorJumpedToFrame let b:vimspectorStepIsThere = 'foo' augroup END call s:StartDebugging() call assert_equal( 'foo', getbufvar(bufnr(), 'vimspectorStepIsThere', 0) ) - au! TestVimspectorFrameEnter + au! TestVimspectorJumpedToFrame unlet b:vimspectorStepIsThere call vimspector#test#setup#Reset() %bwipe! From 3c54cd268f210b9eefa802b7ac8a5eb09e23683c Mon Sep 17 00:00:00 2001 From: przepompownia Date: Wed, 17 Feb 2021 01:05:09 +0100 Subject: [PATCH 508/609] Send VimspectorDebugEnded event --- python3/vimspector/debug_session.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 36e6feb..954d2c6 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -412,6 +412,7 @@ class DebugSession( object ): self._outputView.Reset() self._codeView.Reset() vim.command( 'tabclose!' ) + vim.command( 'doautocmd User VimspectorDebugEnded' ) self._stackTraceView = None self._variablesView = None self._outputView = None From d561c4aea5fdedee2b44a2951310cd85baf43d45 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 20 Feb 2021 22:37:02 +0000 Subject: [PATCH 509/609] Add demo of new commands for local mappings --- support/custom_ui_vimrc | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index 95c3360..4e9f635 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -1,5 +1,7 @@ execute 'source' expand( ':p:h' ) . '/minimal_vimrc' set noequalalways +let mapleader = ',' +let maplocalleader = "\" function! s:CustomiseUI() let wins = g:vimspector_session_windows @@ -59,11 +61,52 @@ function! s:CustomiseWinBar() nnoremenu WinBar.✕\ ᶠ⁸ :call vimspector#Reset() endfunction +let s:mapped = {} + +function! s:OnJumpToFrame() abort + if has_key( s:mapped, string( bufnr() ) ) + return + endif + + nmap dn VimspectorStepOver + nmap ds VimspectorStepInto + nmap df VimspectorStepOut + nmap dc VimspectorContinue + + let s:mapped[ string( bufnr() ) ] = 1 +endfunction + +function! s:OnDebugEnd() abort + + let original_buf = bufnr() + let hidden = &hidden + + try + set hidden + for bufnr in keys( s:mapped ) + try + execute 'noautocmd buffer' bufnr + silent! nunmap dn + silent! nunmap ds + silent! nunmap df + silent! nunmap dc + endtry + endfor + finally + execute 'noautocmd buffer' original_buf + let &hidden = hidden + endtry + + let s:mapped = {} +endfunction + augroup TestUICustomistaion autocmd! autocmd User VimspectorUICreated call s:CustomiseUI() autocmd User VimspectorTerminalOpened call s:SetUpTerminal() autocmd User VimspectorUICreated call s:CustomiseWinBar() + autocmd User VimspectorJumpedToFrame call s:OnJumpToFrame() + autocmd User VimspectorDebugEnded call s:OnDebugEnd() augroup END let g:vimspector_sign_priority = { From 3c7311e33a9a77d7d315cc473ccffc600119e2b3 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 20:49:56 +0000 Subject: [PATCH 510/609] Test that the commands are fired when stepping through and continuing --- tests/ui.test.vim | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/tests/ui.test.vim b/tests/ui.test.vim index 390f870..f251539 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -411,16 +411,54 @@ function! Test_CustomWinBar() endfunction function! Test_VimspectorJumpedToFrame() + let s:ended = 0 + let s:au_visited_buffers = {} + augroup TestVimspectorJumpedToFrame au! - au User VimspectorJumpedToFrame let b:vimspectorStepIsThere = 'foo' + au User VimspectorJumpedToFrame + \ let s:au_visited_buffers[ bufname() ] = get( s:au_visited_buffers, + \ bufname(), + \ 0 ) + 1 + au User VimspectorDebugEnded + \ let s:ended = 1 augroup END - call s:StartDebugging() - call assert_equal( 'foo', getbufvar(bufnr(), 'vimspectorStepIsThere', 0) ) + lcd ../support/test/python/multiple_files + edit moo.py + + let moo = 'moo.py' + let cow = getcwd() . '/cow.py' + + call vimspector#SetLineBreakpoint( 'moo.py', 13 ) + call vimspector#Launch() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 1, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 1 ) + let expected = {} + let expected[ moo ] = 1 + call assert_equal( expected, s:au_visited_buffers ) + + call vimspector#Continue() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'moo.py', 13, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'moo.py', 13 ) + let expected[ moo ] += 1 + call assert_equal( expected, s:au_visited_buffers ) + + call vimspector#SetLineBreakpoint( 'cow.py', 2 ) + call vimspector#Continue() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'cow.py', 2, 1 ) + call vimspector#test#signs#AssertPCIsAtLineInBuffer( 'cow.py', 2 ) + let expected[ cow ] = 1 + call assert_equal( expected, s:au_visited_buffers ) + + VimspectorReset + call WaitForAssert( { -> assert_equal( s:ended, 1 ) } ) au! TestVimspectorJumpedToFrame - unlet b:vimspectorStepIsThere + unlet! s:au_visited_buffers + unlet! s:ended + call vimspector#test#setup#Reset() + lcd - %bwipe! endfunction From ae289a88c76fe69531293eb6ce311028d3e78dfd Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 21:16:54 +0000 Subject: [PATCH 511/609] Update readme with example --- README.md | 30 ++++++++++++++++++++++++++- support/custom_ui_vimrc | 46 +++++++++++++++++++++++++++++++---------- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b3088b7..e13aae0 100644 --- a/README.md +++ b/README.md @@ -85,12 +85,13 @@ For detailed explanatin of the `.vimspector.json` format, see the * [Sign priority](#sign-priority) * [Changing the default window sizes](#changing-the-default-window-sizes) * [Changing the terminal size](#changing-the-terminal-size) + * [Custom mappings while debugging](#custom-mappings-while-debugging) * [Advanced UI customisation](#advanced-ui-customisation) * [Customising the WinBar](#customising-the-winbar) * [Example](#example) * [FAQ](#faq) - + @@ -675,6 +676,10 @@ appropriate place, such as your `vimrc` (hint: run `:e $MYVIMRC`). nmap VimspectorContinue ``` +In addition, many users probably want to only enable certain Vimspector mappings +while debugging is active. This is also possible, though it requires writing +[some vimscipt](#custom-mappings-while-debugging). + That said, many people are familiar with particular debuggers, so the following mappings can be enabled by setting `g:vimspector_enable_mappings` to the specified value. @@ -1821,6 +1826,29 @@ let g:vimspector_terminal_maxwidth = 75 let g:vimspector_terminal_minwidth = 20 ``` +## Custom mappings while debugging + +It's useful to be able to define mappings only while debugging and remove those +mappings when debugging is complete. For this purpose, Vimspector provides 2 +`User` autocommds: + +* `VimspectorJumpedToFrame` - triggered whenever a 'break' event happens, or + when selecting a stack from to jump to. This can be used to create (for + example) buffer-local mappings for any files opened in the code window. +* `VimspectorDebugEnded` - triggered when the debug session is terminated + (actually when Vimspector is fully reset) + +An example way to use this is included in `support/custom_ui_vimrc`. In there, +these autocommands are used to create buffer-local mappings for any files +visited while debugging and to clear them when completing debugging. This is +particularly useful for commadns like `VimspectorBalloonEval` which only +make sense while debugging (and only in the code window). Check the commented +section `Custom mappings while debugging`. + +NOTE: This is a fairly advanced feature requiring some nontrivial vimscript. +It's possible that this feature will be incorporated into Vimspector in future +as it is a common requirement. + ## Advanced UI customisation > ***Please Note***: This cusomiation API is ***unstable***, meaning that it may diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index 4e9f635..51a7304 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -1,7 +1,12 @@ +" setup boilerplate to make this file usable with vim -Nu {{{ +scriptencoding utf-8 execute 'source' expand( ':p:h' ) . '/minimal_vimrc' set noequalalways let mapleader = ',' let maplocalleader = "\" +" }}} + +" Custom Layout {{{ function! s:CustomiseUI() let wins = g:vimspector_session_windows @@ -61,6 +66,27 @@ function! s:CustomiseWinBar() nnoremenu WinBar.✕\ ᶠ⁸ :call vimspector#Reset() endfunction +augroup TestUICustomistaion + autocmd! + autocmd User VimspectorUICreated call s:CustomiseUI() + autocmd User VimspectorTerminalOpened call s:SetUpTerminal() + autocmd User VimspectorUICreated call s:CustomiseWinBar() +augroup END + +" }}} + +" Custom sign priority {{{ + +let g:vimspector_sign_priority = { + \ 'vimspectorBP': 3, + \ 'vimspectorBPCond': 2, + \ 'vimspectorBPDisabled': 1, + \ 'vimspectorPC': 999, + \ } + +" }}} + +" Custom mappings while debuggins {{{ let s:mapped = {} function! s:OnJumpToFrame() abort @@ -72,6 +98,8 @@ function! s:OnJumpToFrame() abort nmap ds VimspectorStepInto nmap df VimspectorStepOut nmap dc VimspectorContinue + nmap di VimspectorBalloonEval + xmap di VimspectorBalloonEval let s:mapped[ string( bufnr() ) ] = 1 endfunction @@ -90,6 +118,8 @@ function! s:OnDebugEnd() abort silent! nunmap ds silent! nunmap df silent! nunmap dc + silent! nunmap di + silent! xunmap di endtry endfor finally @@ -100,18 +130,12 @@ function! s:OnDebugEnd() abort let s:mapped = {} endfunction -augroup TestUICustomistaion - autocmd! - autocmd User VimspectorUICreated call s:CustomiseUI() - autocmd User VimspectorTerminalOpened call s:SetUpTerminal() - autocmd User VimspectorUICreated call s:CustomiseWinBar() +augroup TestCustomMappings + au! autocmd User VimspectorJumpedToFrame call s:OnJumpToFrame() autocmd User VimspectorDebugEnded call s:OnDebugEnd() augroup END -let g:vimspector_sign_priority = { - \ 'vimspectorBP': 3, - \ 'vimspectorBPCond': 2, - \ 'vimspectorBPDisabled': 1, - \ 'vimspectorPC': 999, - \ } +" }}} + +" vim: foldmethod=marker From 448ee33a6af11a32e4efb18814d1eb778089e58c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 21:24:02 +0000 Subject: [PATCH 512/609] prevent annoying 'no matching autocmds' message --- plugin/vimspector.vim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 6691a2a..8482dfd 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -125,6 +125,8 @@ augroup VimspectorUserAutoCmds autocmd! autocmd User VimspectorUICreated silent autocmd User VimspectorTerminalOpened silent + autocmd user VimspectorJumpedToFrame silent + autocmd user VimspectorDebugEnded silent augroup END augroup Vimspector From 0e0cc6d4aec83c0020858e11663f0d82e45287ba Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 22 Feb 2021 11:49:25 +0000 Subject: [PATCH 513/609] Fix restart when using CodeLLDB Fix restarts always getting stuck "Initializing" when using CodeLLDB. When using the restart command we re-use the configuration dict as-is, so always re-use the same TCP port for the lldb socket. Originally it was thought this was due to a race condition, having the port still open, but it's not. When doing a restart, or reset, we shut down the server after we get the response to the disconnect message. CodeLLDB then also sends a 'terminated' message. Previously we were forcefully closing the socket before killing the app, after we get the 'disconnect' response. This meant that the OS buffer for the socket to localhost: still contained the terminated message at the point that we force-closed the socket and killed the server. The result was that the firt messages read from the "new" socket to locahost: were the last messagse written by the previous process, trikcing vimspector into thinking that the server terminated the process (before responding to the initialize request). ANyway, the solution is to ensure that we read all messages from the previous instance before considering it done. This is done by killing the server if there is one *first* and then trying to read any messages from the socket until it closes (reads EOF). The tricky part is for when we didn't start the server (i.e. in a multi-session setup). Here we simply _have_ to close the socket because we can't know when we've received all of the messages, and we shouldn't expect to receive any 'terminated' events after 'disconnect'. --- autoload/vimspector/internal/channel.vim | 39 +++++++++++++++++++----- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/autoload/vimspector/internal/channel.vim b/autoload/vimspector/internal/channel.vim index b44436b..e033cff 100644 --- a/autoload/vimspector/internal/channel.vim +++ b/autoload/vimspector/internal/channel.vim @@ -95,21 +95,44 @@ EOF endfunction function! vimspector#internal#channel#StopDebugSession() abort - if exists( 's:ch' ) && ch_status( s:ch ) ==# 'open' + + if exists( 's:job' ) + " We started the job, so we need to kill it and wait to read all the data + " from the socket + + if job_status( s:job ) ==# 'run' + call job_stop( s:job, 'term' ) + endif + + while job_status( s:job ) ==# 'run' + call job_stop( s:job, 'kill' ) + endwhile + + unlet s:job + + if exists( 's:ch' ) && count( [ 'closed', 'fail' ], ch_status( s:ch ) ) == 0 + " We're going to block on this channel reading, then manually call the + " close callback, so remove the automatic close callback to avoid tricky + " re-entrancy + call ch_setoptions( s:ch, { 'close_cb': '' } ) + endif + + elseif exists( 's:ch' ) && + \ count( [ 'closed', 'fail' ], ch_status( s:ch ) ) == 0 + " channel is open, close it and trigger the callback. The callback is _not_ " triggered when manually calling ch_close. if we get here and the channel " is not open, then we there is a _OnClose callback waiting for us, so do " nothing. call ch_close( s:ch ) - call s:_OnClose( s:ch ) endif - if exists( 's:job' ) - if job_status( s:job ) ==# 'run' - call job_stop( s:job, 'kill' ) - endif - unlet s:job - endif + " block until we've read all data from the socket and handled it. + while count( [ 'open', 'buffered' ], ch_status( s:ch ) ) == 1 + let data = ch_read( s:ch, { 'timeout': 10 } ) + call s:_OnServerData( s:ch, data ) + endwhile + call s:_OnClose( s:ch ) endfunction function! vimspector#internal#channel#Reset() abort From 476300f815c1378aa1b88447229c08739c01f283 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 22 Feb 2021 13:39:50 +0000 Subject: [PATCH 514/609] Fix errors resetting in neovim --- autoload/vimspector/internal/balloon.vim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 5f25f3a..384b18a 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -162,6 +162,10 @@ function! vimspector#internal#balloon#CloseCallback( ... ) abort endfunction function! vimspector#internal#balloon#Close() abort + if s:popup_win_id == 0 + return + endif + if s:is_neovim call nvim_win_close( s:popup_win_id, v:true ) call nvim_win_close( s:nvim_border_win_id, v:true ) From 0810d7154c483e25e53d4a996f9cb1bb75f77c15 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 23 Feb 2021 09:03:45 +0000 Subject: [PATCH 515/609] Fix syntax occasionally not working in popup, and custom vimrc crashing in neovim --- autoload/vimspector/internal/balloon.vim | 1 + python3/vimspector/utils.py | 2 +- python3/vimspector/variables.py | 3 +-- support/custom_ui_vimrc | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 384b18a..a51d092 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -279,6 +279,7 @@ function! s:CreateNeovimTooltip( body ) abort call nvim_buf_set_option( buf_id, 'modifiable', v:false ) let s:popup_win_id = nvim_open_win( buf_id, v:false, opts ) + " Apparently none of these work, when 'style' is 'minimal' call nvim_win_set_option( s:popup_win_id, 'wrap', v:false ) call nvim_win_set_option( s:popup_win_id, 'cursorline', v:true ) call nvim_win_set_option( s:popup_win_id, 'signcolumn', 'no' ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 4022315..10da7e1 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -707,7 +707,7 @@ def SetSyntax( current_syntax, syntax, *args ): syntax = '' if current_syntax == syntax: - return + return syntax # We use set syn= because just setting vim.Buffer.options[ 'syntax' ] # doesn't actually trigger the Syntax autocommand, and i'm not sure that diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 74626ac..9cd0b3e 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -301,8 +301,7 @@ class VariablesView( object ): with utils.RestoreCursorPosition(): with utils.ModifiableScratchBuffer( view.buf ): utils.ClearBuffer( view.buf ) - # FIXME: This probably doesn't work reliably - view.syntax = utils.SetSyntax( None, + view.syntax = utils.SetSyntax( view.syntax, self._current_syntax, view.buf ) diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index 51a7304..b1ffda1 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -38,6 +38,10 @@ function! s:CustomiseUI() endfunction function s:SetUpTerminal() + if !has_key( g:vimspector_session_windows, 'terminal' ) + " There's a neovim bug which means that this doesn't work in neovim + return + endif let terminal_win = g:vimspector_session_windows.terminal " Make the terminal window at most 80 columns wide, ensuring there is enough From 6fac220ee55af66c0c2ab3dae630086da5b0263f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 23 Feb 2021 09:42:57 +0000 Subject: [PATCH 516/609] Disable mappings in the popup to ensure navigaion works --- autoload/vimspector/internal/balloon.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index a51d092..4d6aca2 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -68,7 +68,8 @@ function! vimspector#internal#balloon#CreateTooltip( is_hover, ... ) abort \ 'drag': 1, \ 'resize': 1, \ 'close': 'button', - \ 'callback': 'vimspector#internal#balloon#CloseCallback' + \ 'callback': 'vimspector#internal#balloon#CloseCallback', + \ 'mapping': 0 \ } " When ambiwidth is single, use prettier characters for the border. This From fd0c6e767588170784b0645dd6a49c696c9d9221 Mon Sep 17 00:00:00 2001 From: Ygor Oliveira Date: Tue, 23 Feb 2021 19:39:29 +0100 Subject: [PATCH 517/609] Fix typos in README --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index e13aae0..40bb2ab 100644 --- a/README.md +++ b/README.md @@ -346,7 +346,7 @@ See support/doc/example_vimrc.vim for a minimal example. Vimspector is a generic client for Debug Adapters. Debug Adapters (referred to as 'gadgets' or 'adapters') are what actually do the work of talking to the real -debugers. +debuggers. In order for Vimspector to be useful, you need to have some adapters installed. @@ -464,7 +464,7 @@ You essentially need to get a working installation of the debug adapter, find out how to start it, and configure that in an `adapters` entry in either your `.vimspector.json` or in `.gadgets.json`. -The simplest way in practice is to install or start Visusal Studio Code and use +The simplest way in practice is to install or start Visual Studio Code and use its extension manager to install the relevant extension. You can then configure the adapter manually in the `adapters` section of your `.vimspector.json` or in a `gadgets.json`. @@ -545,7 +545,7 @@ format as `.gadgets.json` but are not overwritten when running ## Upgrade After updating the Vimspector code (either via `git pull` or whatever package -manager), run `:VimspectorUpdate` to update any already-installed gadets. +manager), run `:VimspectorUpdate` to update any already-installed gadgets. # About @@ -770,7 +770,7 @@ The argument is a `dict` with the following keys: * `configuration`: (optional) Name of the debug configuration to launch * ``: (optional) Name of a variable to set -This allows for some intergration and automation. For example, if you have a +This allows for some integration and automation. For example, if you have a configuration named `Run Test` that contains a [replacement variable][vimspector-ref-var] named `${Test}` you could write a mapping which ultimately executes: @@ -854,9 +854,9 @@ breakpoint on the current line and `` to launch the application. ### Conditional breakpoints -Some debug adapters support conditional breakpionts. Note that vimspector does +Some debug adapters support conditional breakpoints. Note that vimspector does not tell you if the debugger doesn't support conditional breakpoints (yet). A -conditional breakpiont is a breakpiont which only triggers if some expression +conditional breakpoint is a breakpoint which only triggers if some expression evaluates to true, or has some other constraints met. Some of these functions above take a single optional argument which is a @@ -928,7 +928,7 @@ All rules for `Variables and scopes` apply plus the following: and get its result. * Make a normal mode (`nmap`) and visual mode (`xmap`) mapping to `VimspectorBalloonEval` to manually trigger the popup. -* Use regular nagivation keys (`j`, `k`) to chose the current selection; `` +* Use regular nagivation keys (`j`, `k`) to choose the current selection; `` (or leave the tooltip window) to close the tooltip. ![variable eval hover](https://puremourning.github.io/vimspector-web/img/vimspector-variable-eval-hover.png) @@ -1775,7 +1775,7 @@ smaller ones. ## Changing the default window sizes -> ***Please Note***: This cusomiation API is ***unstable***, meaning that it may +> ***Please Note***: This customisation API is ***unstable***, meaning that it may change at any time. I will endeavour to reduce the impact of this and announce changes in Gitter. @@ -1797,7 +1797,7 @@ 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 +The terminal is typically created as a vertical split to the right 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. @@ -1816,7 +1816,7 @@ 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. +to satisfy the other constraints. Example: @@ -1830,7 +1830,7 @@ let g:vimspector_terminal_minwidth = 20 It's useful to be able to define mappings only while debugging and remove those mappings when debugging is complete. For this purpose, Vimspector provides 2 -`User` autocommds: +`User` autocommands: * `VimspectorJumpedToFrame` - triggered whenever a 'break' event happens, or when selecting a stack from to jump to. This can be used to create (for @@ -1841,7 +1841,7 @@ mappings when debugging is complete. For this purpose, Vimspector provides 2 An example way to use this is included in `support/custom_ui_vimrc`. In there, these autocommands are used to create buffer-local mappings for any files visited while debugging and to clear them when completing debugging. This is -particularly useful for commadns like `VimspectorBalloonEval` which only +particularly useful for commands like `VimspectorBalloonEval` which only make sense while debugging (and only in the code window). Check the commented section `Custom mappings while debugging`. @@ -1851,7 +1851,7 @@ as it is a common requirement. ## Advanced UI customisation -> ***Please Note***: This cusomiation API is ***unstable***, meaning that it may +> ***Please Note***: This customisation API is ***unstable***, meaning that it may change at any time. I will endeavour to reduce the impact of this and announce changes in Gitter. @@ -1898,7 +1898,7 @@ In addition, the following key is added when triggering the ## Customising the WinBar You can even customise the WinBar buttons by simply running the usual `menu` -(and `unmanu`) commands. +(and `unmenu`) commands. By default, Vimspector uses something a bit like this: @@ -2009,9 +2009,9 @@ hi link jsonComment Comment but in theory a single gadget can supply multiple `adapter` configs. Typically this happens when a `gadget` supplies different `adapter` config for, say remote debugging, or debugging in a container, etc. -8. The signs and winbar display funny symbols. How do i fix them? See +8. The signs and winbar display funny symbols. How do I fix them? See [this](#changing-the-default-signs) and [this](#customising-the-winbar) -9. What's thie telemetry stuff all about? Are you sending my data to evil companies? +9. What's this telemetry stuff all about? Are you sending my data to evil companies? Debug adapters (for some reason) send telemetry data to clients. Vimspector simply displays this information in the output window. It *does not* and *will not ever* collect, use, forward or otherwise share any data with any third parties. From 52019952792050b2eb26ecbb4e633692305e5ca4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 24 Feb 2021 15:10:57 +0000 Subject: [PATCH 518/609] Ask the user about terminating the debuggee CodeLLDB seems to actually support the terminateDebugee flag, so rather than just forcefully killing things, ask the user if they want to. --- python3/vimspector/debug_session.py | 26 +++++++++++++++++++------- python3/vimspector/stack_trace.py | 12 ++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 954d2c6..e82febf 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -793,7 +793,12 @@ class DebugSession( object ): arguments = {} if self._server_capabilities.get( 'supportTerminateDebuggee' ): # If we attached, we should _not_ terminate the debuggee - arguments[ 'terminateDebuggee' ] = False + if self._stackTraceView.AnyThreadsRunning(): + choice = utils.AskForInput( "Terminate debugee [Y/N/default]? ", "" ) + if choice == "Y" or choice == "y": + arguments[ 'terminateDebuggee' ] = True + elif choice == "N" or choice == 'n': + arguments[ 'terminateDebuggee' ] = False self._connection.DoRequest( handler, { 'command': 'disconnect', @@ -1164,10 +1169,22 @@ class DebugSession( object ): self._connection.DoResponse( message, None, response ) + def OnEvent_terminated( self, message ): + # The debugging _session_ has terminated. This does not mean that the + # debugee has terminated (that's the exited event). + # + # We will handle this when the server actually exists. + # + # FIXME we should always wait for this event before disconnecting closing + # any socket connection + self.SetCurrentFrame( None ) + + def OnEvent_exited( self, message ): utils.UserMessage( 'The debugee exited with status code: {}'.format( message[ 'body' ][ 'exitCode' ] ) ) - self.SetCurrentFrame( None ) + self._stackTraceView.OnExited( message ) + self._codeView.SetCurrentFrame( None ) def OnEvent_process( self, message ): utils.UserMessage( 'The debugee was started: {}'.format( @@ -1210,11 +1227,6 @@ class DebugSession( object ): else: self._logger.debug( "No server exit handler" ) - def OnEvent_terminated( self, message ): - # We will handle this when the server actually exists - utils.UserMessage( "Debugging was terminated by the server." ) - self.SetCurrentFrame( None ) - def OnEvent_output( self, message ): if self._outputView: self._outputView.OnOutput( message[ 'body' ] ) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index f38e4ba..177998f 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -364,6 +364,14 @@ class StackTraceView( object ): self._JumpToFrame( frame ) + def AnyThreadsRunning( self ): + for thread in self._threads: + if thread.state != Thread.TERMINATED: + return True + + return False + + def _JumpToFrame( self, frame, reason = '' ): def do_jump(): if 'line' in frame and frame[ 'line' ] > 0: @@ -459,6 +467,10 @@ class StackTraceView( object ): self.LoadThreads( infer_current_frame ) + def OnExited( self, event ): + for thread in self._threads: + thread.Exited() + def _DrawStackTrace( self, thread: Thread ): if not thread.IsExpanded(): return From 09efcf5e509f262cdc52fb41398bf8f069d3cfcf Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 24 Feb 2021 16:19:48 +0000 Subject: [PATCH 519/609] hide the controvertial telemetry data; it's not like anyone will ever look at it --- python3/vimspector/output.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index cf2a213..d3fe13e 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -33,7 +33,7 @@ BUFFER_MAP = { 'console': 'Console', 'stdout': 'Console', 'stderr': 'stderr', - 'telemetry': 'Telemetry', + 'telemetry': None, } @@ -77,6 +77,10 @@ class OutputView( object ): self._Print( category, text_lines ) def _Print( self, category, text_lines ): + if category is None: + # This category is supressed + return + if category not in self._buffers: self._CreateBuffer( category ) @@ -251,7 +255,8 @@ class DAPOutputView( OutputView ): self._connection = None for b in set( BUFFER_MAP.values() ): - self._CreateBuffer( b ) + if b is not None: + self._CreateBuffer( b ) self.AddLogFileView() self._ShowOutput( 'Console' ) From d8eb6a04639bd3f1171c53d64c142fd4ec8973ad Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 24 Feb 2021 18:00:08 +0000 Subject: [PATCH 520/609] Only prompt in 'interactive' contexts to avoid annoying questions --- README.md | 35 ++++++++++++++++++++++------- autoload/vimspector.vim | 18 +++++++++++---- plugin/vimspector.vim | 2 +- python3/vimspector/debug_session.py | 29 ++++++++++++------------ 4 files changed, 57 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 40bb2ab..b6009da 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ And a couple of brief demos: - call stack display and navigation - hierarchical variable value display popup (see `VimspectorBalloonEval`) - interactive debug console with autocompletion -- launch debugee within Vim's embedded terminal +- launch debuggee within Vim's embedded terminal - logging/stdout display - simple stable API for custom tooling (e.g. integrate with language server) @@ -698,7 +698,7 @@ let g:vimspector_enable_mappings = 'VISUAL_STUDIO' | `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` | | `Shift F5` | Stop debugging. | `vimspector#Stop()` | | `Ctrl Shift F5` | Restart debugging with the same configuration. | `vimspector#Restart()` | -| `F6` | Pause debugee. | `vimspector#Pause()` | +| `F6` | Pause debuggee. | `vimspector#Pause()` | | `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` | | `Shift F9` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '' )` | | `F10` | Step Over | `vimspector#StepOver()` | @@ -723,7 +723,7 @@ let g:vimspector_enable_mappings = 'HUMAN' | `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` | | `F3` | Stop debugging. | `vimspector#Stop()` | | `F4` | Restart debugging with the same configuration. | `vimspector#Restart()` | -| `F6` | Pause debugee. | `vimspector#Pause()` | +| `F6` | Pause debuggee. | `vimspector#Pause()` | | `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` | | `F9` | Toggle conditional line breakpoint on the current line. | `vimspector#ToggleBreakpoint( { trigger expr, hit count expr } )` | | `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '' )` | @@ -1002,7 +1002,7 @@ The stack trace is represented by the buffer `vimspector.StackTrace`. * In the outputs window, use the WinBar to select the output channel. * Alternatively, use `:VimspectorShowOutput `. Use command-line completion to see the categories. -* The debugee prints to the stdout channel. +* The debuggee prints to the stdout channel. * Other channels may be useful for debugging. ![output window](https://puremourning.github.io/vimspector-web/img/vimspector-output-window.png) @@ -1057,10 +1057,29 @@ which will tail it in a little window (doesn't work on Windows). To close the debugger, use: -* `Reset` WinBar button (`set mouse=a`) +* `Reset` WinBar button * `:VimspectorReset` when the WinBar is not available. * `call vimspector#Reset()` + +## Terminate debuggee + +If the debuggee is still running when stopping or resetting, then some debug +adapters allow you to specify what should happen to it when finishing debugging. +Typically, the default behaviour is sensible, and this is what happens most of +the time. These are the defaults according to DAP: + +* If the request was 'launch': terminate the debuggee +* If the request was 'attach': don't terminate the debuggee + +Some debug adapters allow you to choose what to do when disconnecting. If you +wish to control this behaviour, use `:VimspectorReset` or call +`vimspector#Reset( { 'interactive': v:true } )`. If the debug adapter offers a +choice as to whether or not to terminate the debuggee, you will be prompted to +choose. The same applies for `vimspector#Stop()` which can take an argument: +`vimspector#Stop( { 'interactive': v:true } )`. + + # Debug profile configuration For an introduction to the configuration of `.vimspector.json`, take a look at @@ -1903,14 +1922,14 @@ You can even customise the WinBar buttons by simply running the usual `menu` By default, Vimspector uses something a bit like this: ```viml -nnoremenu WinBar.■\ Stop :call vimspector#Stop() +nnoremenu WinBar.■\ Stop :call vimspector#Stop( { 'interactive': v:false } ) nnoremenu WinBar.▶\ Cont :call vimspector#Continue() nnoremenu WinBar.▷\ Pause :call vimspector#Pause() nnoremenu WinBar.↷\ Next :call vimspector#StepOver() nnoremenu WinBar.→\ Step :call vimspector#StepInto() nnoremenu WinBar.←\ Out :call vimspector#StepOut() nnoremenu WinBar.⟲: :call vimspector#Restart() -nnoremenu WinBar.✕ :call vimspector#Reset() +nnoremenu WinBar.✕ :call vimspector#Reset( { 'interactive': v:false } ) ``` If you prefer a different layout or if the unicode symbols don't render @@ -1923,7 +1942,7 @@ func! CustomiseUI() " Clear the existing WinBar created by Vimspector nunmenu WinBar " Cretae our own WinBar - nnoremenu WinBar.Kill :call vimspector#Stop() + nnoremenu WinBar.Kill :call vimspector#Stop( { 'interactive': v:true } ) nnoremenu WinBar.Continue :call vimspector#Continue() nnoremenu WinBar.Pause :call vimspector#Pause() nnoremenu WinBar.Step\ Over :call vimspector#StepOver() diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index d4bcfa7..e2e3833 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -55,11 +55,16 @@ function! vimspector#LaunchWithSettings( settings ) abort py3 _vimspector_session.Start( launch_variables = vim.eval( 'a:settings' ) ) endfunction -function! vimspector#Reset() abort +function! vimspector#Reset( ... ) abort if !s:Enabled() return endif - py3 _vimspector_session.Reset() + if a:0 == 0 + let options = {} + else + let options = a:1 + endif + py3 _vimspector_session.Reset( **vim.eval( 'options' ) ) endfunction function! vimspector#Restart() abort @@ -185,11 +190,16 @@ function! vimspector#SetCurrentThread() abort py3 _vimspector_session.SetCurrentThread() endfunction -function! vimspector#Stop() abort +function! vimspector#Stop( ... ) abort if !s:Enabled() return endif - py3 _vimspector_session.Stop() + if a:0 == 0 + options = {} + else + options = a:1 + endif + py3 _vimspector_session.Stop( **vim.eval( 'options' ) ) endfunction function! vimspector#ExpandVariable() abort diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 8482dfd..50f7e3d 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -105,7 +105,7 @@ command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr \ call vimspector#Evaluate( ) command! -bar \ VimspectorReset - \ call vimspector#Reset() + \ call vimspector#Reset( { 'interactive': v:true } ) " Installer commands command! -bar -bang -nargs=* -complete=custom,vimspector#CompleteInstall diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index e82febf..b36113d 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -320,7 +320,7 @@ class DebugSession( object ): if self._connection: self._logger.debug( "_StopDebugAdapter with callback: start" ) - self._StopDebugAdapter( start ) + self._StopDebugAdapter( interactive = False, callback = start ) return start() @@ -385,14 +385,15 @@ class DebugSession( object ): self._connection = None @IfConnected() - def Stop( self ): + def Stop( self, interactive = False ): self._logger.debug( "Stop debug adapter with no callback" ) - self._StopDebugAdapter() + self._StopDebugAdapter( interactive = interactive ) - def Reset( self ): + def Reset( self, interactive = False ): if self._connection: self._logger.debug( "Stop debug adapter with callback : self._Reset()" ) - self._StopDebugAdapter( lambda: self._Reset() ) + self._StopDebugAdapter( interactive = interactive, + callback = lambda: self._Reset() ) else: self._Reset() @@ -772,7 +773,7 @@ class DebugSession( object ): self._logger.info( 'Debug Adapter Started' ) - def _StopDebugAdapter( self, callback = None ): + def _StopDebugAdapter( self, interactive = False, callback = None ): self._splash_screen = utils.DisplaySplash( self._api_prefix, self._splash_screen, @@ -791,10 +792,10 @@ class DebugSession( object ): self._connection_type ) ) arguments = {} - if self._server_capabilities.get( 'supportTerminateDebuggee' ): - # If we attached, we should _not_ terminate the debuggee + if ( interactive and + self._server_capabilities.get( 'supportTerminateDebuggee' ) ): if self._stackTraceView.AnyThreadsRunning(): - choice = utils.AskForInput( "Terminate debugee [Y/N/default]? ", "" ) + choice = utils.AskForInput( "Terminate debuggee [Y/N/default]? ", "" ) if choice == "Y" or choice == "y": arguments[ 'terminateDebuggee' ] = True elif choice == "N" or choice == 'n': @@ -1028,14 +1029,14 @@ class DebugSession( object ): self._splash_screen = utils.DisplaySplash( self._api_prefix, self._splash_screen, - "Attaching to debugee..." ) + "Attaching to debuggee..." ) self._PrepareAttach( self._adapter, self._launch_config ) elif request == "launch": self._splash_screen = utils.DisplaySplash( self._api_prefix, self._splash_screen, - "Launching debugee..." ) + "Launching debuggee..." ) # FIXME: This cmdLine hack is not fun. self._PrepareLaunch( self._configuration.get( 'remote-cmdLine', [] ), @@ -1171,7 +1172,7 @@ class DebugSession( object ): def OnEvent_terminated( self, message ): # The debugging _session_ has terminated. This does not mean that the - # debugee has terminated (that's the exited event). + # debuggee has terminated (that's the exited event). # # We will handle this when the server actually exists. # @@ -1181,13 +1182,13 @@ class DebugSession( object ): def OnEvent_exited( self, message ): - utils.UserMessage( 'The debugee exited with status code: {}'.format( + utils.UserMessage( 'The debuggee exited with status code: {}'.format( message[ 'body' ][ 'exitCode' ] ) ) self._stackTraceView.OnExited( message ) self._codeView.SetCurrentFrame( None ) def OnEvent_process( self, message ): - utils.UserMessage( 'The debugee was started: {}'.format( + utils.UserMessage( 'The debuggee was started: {}'.format( message[ 'body' ][ 'name' ] ) ) def OnEvent_module( self, message ): From 11edcddd9c6208d1290c1ff1fc1c598a3b963aca Mon Sep 17 00:00:00 2001 From: Jake Zimmerman Date: Thu, 25 Feb 2021 03:04:12 -0800 Subject: [PATCH 521/609] Fix using double-quotes in VimspectorEval The VimspectorEval command used `-bar` but this prevented the use of double quotes. This seems much more useful than a vim comment in this scenario, so remove the `-bar`. This is _techncially_ breaking change, but I don't think it's likely anyone will be relying on doing `VimspectorEval x | something else`. If they are, sorry. --- plugin/vimspector.vim | 2 +- tests/variables.test.vim | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 50f7e3d..c69414b 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -100,7 +100,7 @@ command! -bar -nargs=? -complete=custom,vimspector#CompleteOutput command! -bar \ VimspectorToggleLog \ call vimspector#ToggleLog() -command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr +command! -nargs=1 -complete=custom,vimspector#CompleteExpr \ VimspectorEval \ call vimspector#Evaluate( ) command! -bar diff --git a/tests/variables.test.vim b/tests/variables.test.vim index a43fafd..2da9c7f 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -513,6 +513,49 @@ function Test_EvaluateConsole() endfunction +function Test_EvaluateInput() + 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 vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 27, 1 ) + + VimspectorEval -exec print (int) printf("hello") + + call assert_equal( bufnr( 'vimspector.Console' ), + \ winbufnr( g:vimspector_session_windows.output ) ) + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ '' + \ ], + \ getbufline( bufnr( 'vimspector.Console' ), '$', '$' ) + \ ) + \ } ) + + let len = getbufinfo( 'vimspector.Console' )[ 0 ].linecount + + call WaitForAssert( {-> + \ assert_equal( + \ [ + \ 'Evaluating: -exec print (int) printf("hello")', + \ ], + \ getbufline( bufnr( 'vimspector.Console' ), len-2, len-2 ) + \ ) + \ } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( + \ 'vimspector.Console', len, v:null ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction + + function Test_EvaluatePromptConsole() let fn = 'testdata/cpp/simple/struct.cpp' call s:StartDebugging( #{ fn: fn, line: 24, col: 1, launch: #{ From f40ac5db236772c66f5d72f60257337e744261fa Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 25 Feb 2021 21:24:40 +0000 Subject: [PATCH 522/609] Delay launching the python interpreter until needed --- autoload/vimspector.vim | 24 +----------------------- python3/vimspector/debug_session.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index e2e3833..319c39b 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -320,28 +320,6 @@ function! vimspector#CompleteOutput( ArgLead, CmdLine, CursorPos ) abort return join( buffers, "\n" ) endfunction -py3 < Date: Wed, 24 Feb 2021 16:17:35 +0000 Subject: [PATCH 523/609] Add ability to set a variable value This works only for things which have known variablesReference, so particularly currently only for scopes and theoretically for members. I think this can work for watches too. will need to check. --- autoload/vimspector.vim | 7 +++ python3/vimspector/debug_session.py | 6 ++ python3/vimspector/variables.py | 96 ++++++++++++++++++++++++++--- 3 files changed, 101 insertions(+), 8 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 319c39b..7cecca5 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -209,6 +209,13 @@ function! vimspector#ExpandVariable() abort py3 _vimspector_session.ExpandVariable() endfunction +function! vimspector#SetVariableValue() abort + if !s:Enabled() + return + endif + py3 _vimspector_session.SetVariableValue() +endfunction + function! vimspector#DeleteWatch() abort if !s:Enabled() return diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index e65af46..95524c5 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -525,6 +525,11 @@ class DebugSession( object ): def ExpandVariable( self, buf = None, line_num = None ): self._variablesView.ExpandVariable( buf, line_num ) + @IfConnected() + def SetVariableValue( self ): + # TODO: , buf = None, line_num = None ): + self._variablesView.SetVariableValue() + @IfConnected() def AddWatch( self, expression ): self._variablesView.AddWatch( self._stackTraceView.GetCurrentFrame(), @@ -999,6 +1004,7 @@ class DebugSession( object ): def handle_initialize_response( msg ): self._server_capabilities = msg.get( 'body' ) or {} self._breakpoints.SetServerCapabilities( self._server_capabilities ) + self._variablesView.SetServerCapabilities( self._server_capabilities ) self._Launch() self._connection.DoRequest( handle_initialize_response, { diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 9cd0b3e..8db007e 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -32,8 +32,9 @@ class Expandable: a 'variablesReference' to be resolved by the 'variables' request. Records the current state expanded/collapsed. Implementations just implement VariablesReference to get the variables.""" - def __init__( self ): + def __init__( self, container: 'Expandable' = None ): self.variables: typing.List[ 'Variable' ] = None + self.container: Expandable = container # None is Falsy and represents collapsed _by default_. WHen set to False, # this means the user explicitly collapsed it. When True, the user expanded # it (or we expanded it by default). @@ -48,6 +49,9 @@ class Expandable: def IsExpandable( self ): return self.VariablesReference() > 0 + def IsContained( self ): + return self.container is not None + @abc.abstractmethod def VariablesReference( self ): assert False @@ -92,8 +96,8 @@ class WatchFailure( WatchResult ): class Variable( Expandable ): """Holds one level of an expanded value tree. Also itself expandable.""" - def __init__( self, variable: dict ): - super().__init__() + def __init__( self, container: Expandable, variable: dict ): + super().__init__( container = container ) self.variable = variable # A new variable appearing is marked as changed self.changed = True @@ -156,6 +160,7 @@ class VariablesView( object ): self._connection = None self._current_syntax = '' + self._server_capabilities = None self._variable_eval: Scope = None self._variable_eval_view: View = None @@ -165,12 +170,17 @@ class VariablesView( object ): ':call vimspector#ExpandVariable()' ) vim.command( 'nnoremap <2-LeftMouse> ' ':call vimspector#ExpandVariable()' ) + vim.command( 'nnoremap ' + ':call vimspector#SetVariableValue()' ) # Set up the "Variables" buffer in the variables_win self._scopes: typing.List[ Scope ] = [] self._vars = View( variables_win, {}, self._DrawScopes ) utils.SetUpHiddenBuffer( self._vars.buf, 'vimspector.Variables' ) with utils.LetCurrentWindow( variables_win ): + if utils.UseWinBar(): + vim.command( 'nnoremenu 1.1 WinBar.Set ' + ':call vimspector#SetVariableValue()' ) AddExpandMappings() # Set up the "Watches" buffer in the watches_win (and create a WinBar in @@ -194,6 +204,8 @@ class VariablesView( object ): ':call vimspector#ExpandVariable()' ) vim.command( 'nnoremenu 1.3 WinBar.Delete ' ':call vimspector#DeleteWatch()' ) + vim.command( 'nnoremenu 1.1 WinBar.Set ' + ':call vimspector#SetVariableValue()' ) # Set the (global!) balloon expr if supported has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) ) @@ -231,11 +243,21 @@ class VariablesView( object ): def ConnectionUp( self, connection ): self._connection = connection + def SetServerCapabilities( self, capabilities ): + self._server_capabilities = capabilities + + if self._server_capabilities.get( 'supportsSetVariable' ): + # TODO add a winbar item ? add a mapping ? + pass + def ConnectionClosed( self ): self.Clear() self._connection = None + self._server_capabilities = None def Reset( self ): + self._server_capabilities = None + for k, v in self._oldoptions.items(): vim.options[ k ] = v @@ -447,7 +469,7 @@ class VariablesView( object ): watch.result = WatchFailure( reason ) self._DrawWatches() - def ExpandVariable( self, buf = None, line_num = None ): + def _GetVariable( self, buf = None, line_num = None ): if buf is None: buf = vim.current.buffer @@ -462,12 +484,17 @@ class VariablesView( object ): and buf == self._variable_eval_view.buf ): view = self._variable_eval_view else: - return + return None if line_num not in view.lines: - return + return None - variable = view.lines[ line_num ] + return view.lines[ line_num ], view + + def ExpandVariable( self, buf = None, line_num = None ): + variable, view = self._GetVariable( buf, line_num ) + if variable is None: + return if variable.IsExpanded(): # Collapse @@ -488,6 +515,59 @@ class VariablesView( object ): }, } ) + def SetVariableValue( self ): + variable: Variable + view: View + + variable, view = self._GetVariable( buf = None, line_num = None ) + if variable is None: + return + + if not variable.IsContained(): + return + + new_value = utils.AskForInput( 'New Value: ', + variable.variable.get( 'value', '' ) ) + + if new_value is None: + return + + + def handler( message ): + # Annoyingly the response to setVariable request doesn't return a + # Variable, but some part of it, so take a copy of the existing Variable + # dict and update it, then call its update method with the updated copy. + new_variable = dict( variable.variable ) + new_variable.update( message[ 'body' ] ) + + # Clear any existing known children (FIXME: Is this the right thing to do) + variable.variables = None + + # If the variable is expanded, re-request its children + if variable.IsExpanded(): + self._connection.DoRequest( partial( self._ConsumeVariables, + view.draw, + variable ), { + 'command': 'variables', + 'arguments': { + 'variablesReference': variable.VariablesReference() + }, + } ) + + variable.Update( new_variable ) + view.draw() + + self._connection.DoRequest( handler, { + 'command': 'setVariable', + 'arguments': { + 'variablesReference': variable.container.VariablesReference(), + 'name': variable.variable[ 'name' ], + 'value': new_value + }, + } ) + + + def _DrawVariables( self, view, variables, indent, is_short = False ): assert indent > 0 for variable in variables: @@ -609,7 +689,7 @@ class VariablesView( object ): found = True break if not found: - variable = Variable( variable_body ) + variable = Variable( parent, variable_body ) else: variable.Update( variable_body ) From 32f9a6ec43f7acba6c262e59d6547f8af96ba6e9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 24 Feb 2021 16:20:05 +0000 Subject: [PATCH 524/609] Use silent for winbar menus --- python3/vimspector/output.py | 2 +- python3/vimspector/stack_trace.py | 6 +++--- python3/vimspector/variables.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index d3fe13e..c453417 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -234,7 +234,7 @@ class OutputView( object ): raise vim.command( - "nnoremenu 1.{0} WinBar.{1}{2} " + "nnoremenu 1.{0} WinBar.{1}{2} " ":call vimspector#ShowOutputInWindow( {3}, '{1}' )".format( tab_buffer.index, utils.Escape( category ), diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 177998f..df32462 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -116,11 +116,11 @@ class StackTraceView( object ): ':call vimspector#GoToFrame()' ) if utils.UseWinBar(): - vim.command( 'nnoremenu 1.1 WinBar.Pause/Continue ' + vim.command( 'nnoremenu 1.1 WinBar.Pause/Continue ' ':call vimspector#PauseContinueThread()' ) - vim.command( 'nnoremenu 1.2 WinBar.Expand/Collapse ' + vim.command( 'nnoremenu 1.2 WinBar.Expand/Collapse ' ':call vimspector#GoToFrame()' ) - vim.command( 'nnoremenu 1.3 WinBar.Focus ' + vim.command( 'nnoremenu 1.3 WinBar.Focus ' ':call vimspector#SetCurrentThread()' ) win.options[ 'cursorline' ] = False diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 8db007e..edd8f60 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -198,11 +198,11 @@ class VariablesView( object ): 'nnoremap :call vimspector#DeleteWatch()' ) if utils.UseWinBar(): - vim.command( 'nnoremenu 1.1 WinBar.New ' + vim.command( 'nnoremenu 1.1 WinBar.New ' ':call vimspector#AddWatch()' ) - vim.command( 'nnoremenu 1.2 WinBar.Expand/Collapse ' + vim.command( 'nnoremenu 1.2 WinBar.Expand/Collapse ' ':call vimspector#ExpandVariable()' ) - vim.command( 'nnoremenu 1.3 WinBar.Delete ' + vim.command( 'nnoremenu 1.3 WinBar.Delete ' ':call vimspector#DeleteWatch()' ) vim.command( 'nnoremenu 1.1 WinBar.Set ' ':call vimspector#SetVariableValue()' ) From 9e1a1ab4b5b383819732a4c2f465777d7148ad02 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 24 Feb 2021 16:48:33 +0000 Subject: [PATCH 525/609] Report failures --- python3/vimspector/variables.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index edd8f60..123aa54 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -557,6 +557,9 @@ class VariablesView( object ): variable.Update( new_variable ) view.draw() + def failure_handler( reason, message ): + utils.UserMessage( f'Cannot set value: { reason }', error = True ) + self._connection.DoRequest( handler, { 'command': 'setVariable', 'arguments': { @@ -564,7 +567,7 @@ class VariablesView( object ): 'name': variable.variable[ 'name' ], 'value': new_value }, - } ) + }, failure_handler = failure_handler ) From c2082cffaeb9b9ebbbcd7f7306d462ad098432ed Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 24 Feb 2021 17:23:30 +0000 Subject: [PATCH 526/609] Support setting from the balloon --- autoload/vimspector/internal/balloon.vim | 12 ++++++++---- python3/vimspector/debug_session.py | 5 ++--- python3/vimspector/variables.py | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 4d6aca2..1622d10 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -124,8 +124,6 @@ function! vimspector#internal#balloon#MouseFilter( winid, key ) abort " expand the variable if we got double click if a:key ==? "\<2-leftmouse>" - " forward line number to python, since vim does not allow us to focus - " the correct window call py3eval( '_vimspector_session.ExpandVariable(' \ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],' \ . 'line_num = ' . line( '.', a:winid ) @@ -138,13 +136,17 @@ endfunction function! vimspector#internal#balloon#CursorFilter( winid, key ) abort if a:key ==? "\" - " forward line number to python, since vim does not allow us to focus - " the correct window call py3eval( '_vimspector_session.ExpandVariable(' \ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],' \ . 'line_num = ' . line( '.', a:winid ) \ . ')' ) return 1 + elseif a:key ==? "\" + call py3eval( '_vimspector_session.SetVariableValue(' + \ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],' + \ . 'line_num = ' . line( '.', a:winid ) + \ . ')' ) + return 1 elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 return vimspector#internal#balloon#MouseFilter( a:winid, a:key ) endif @@ -293,6 +295,8 @@ function! s:CreateNeovimTooltip( body ) abort nnoremap \ call vimspector#ExpandVariable() + nnoremap + \ call vimspector#SetVariableValue() nnoremap \ quit nnoremap <2-LeftMouse> diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 95524c5..f5a62df 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -526,9 +526,8 @@ class DebugSession( object ): self._variablesView.ExpandVariable( buf, line_num ) @IfConnected() - def SetVariableValue( self ): - # TODO: , buf = None, line_num = None ): - self._variablesView.SetVariableValue() + def SetVariableValue( self, buf = None, line_num = None ): + self._variablesView.SetVariableValue( buf, line_num ) @IfConnected() def AddWatch( self, expression ): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 123aa54..9b9b372 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -515,11 +515,11 @@ class VariablesView( object ): }, } ) - def SetVariableValue( self ): + def SetVariableValue( self, buf = None, line_num = None ): variable: Variable view: View - variable, view = self._GetVariable( buf = None, line_num = None ) + variable, view = self._GetVariable( buf, line_num ) if variable is None: return From 131cfcdd33280dcfd81fd7cd7a43274a1095e00d Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 24 Feb 2021 18:06:35 +0000 Subject: [PATCH 527/609] Don't try and set a value if not supported --- python3/vimspector/variables.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 9b9b372..9e74345 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -246,10 +246,6 @@ class VariablesView( object ): def SetServerCapabilities( self, capabilities ): self._server_capabilities = capabilities - if self._server_capabilities.get( 'supportsSetVariable' ): - # TODO add a winbar item ? add a mapping ? - pass - def ConnectionClosed( self ): self.Clear() self._connection = None @@ -519,6 +515,9 @@ class VariablesView( object ): variable: Variable view: View + if not self._server_capabilities.get( 'supportsSetVariable' ): + return + variable, view = self._GetVariable( buf, line_num ) if variable is None: return From ec9122284ed480da9a3c1df2fa81c9ed8e590023 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 24 Feb 2021 19:58:29 +0000 Subject: [PATCH 528/609] Add some notes on setting values to the readme --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6009da..00416e3 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,8 @@ And a couple of brief demos: - remote launch, remote attach - locals and globals display - watch expressions with autocompletion +- variable inspection tooltip on hover +- set variable value in locals, watch and hover windows - call stack display and navigation - hierarchical variable value display popup (see `VimspectorBalloonEval`) - interactive debug console with autocompletion @@ -912,6 +914,7 @@ breakpoint when it is hit. * Current scope shows values of locals. * Use ``, or double-click with left mouse to expand/collapse (+, -). +* Set the value of the variable with `` (control + ``) * When changing the stack frame the locals window updates. * While paused, hover to see values @@ -928,8 +931,9 @@ All rules for `Variables and scopes` apply plus the following: and get its result. * Make a normal mode (`nmap`) and visual mode (`xmap`) mapping to `VimspectorBalloonEval` to manually trigger the popup. -* Use regular nagivation keys (`j`, `k`) to choose the current selection; `` - (or leave the tooltip window) to close the tooltip. + * Set the value of the variable with `` (control + ``) + * Use regular nagivation keys (`j`, `k`) to choose the current selection; `` + (or leave the tooltip window) to close the tooltip. ![variable eval hover](https://puremourning.github.io/vimspector-web/img/vimspector-variable-eval-hover.png) @@ -946,6 +950,7 @@ to add a new watch expression. * Alternatively, use `:VimspectorWatch `. Tab-completion for expression is available in some debug adapters. * Expand result with ``, or double-click with left mouse. +* Set the value of the variable with `` (control + ``) * Delete with ``. ![watch window](https://puremourning.github.io/vimspector-web/img/vimspector-watch-window.png) From 26452289a81eaa5ada15274a8ed216215ac3e213 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 24 Feb 2021 22:10:23 +0000 Subject: [PATCH 529/609] Allow overriding the variables/stack trace mappings in config --- autoload/vimspector/internal/balloon.vim | 30 +++++++++-- python3/vimspector/settings.py | 63 +++++++++++++++++++++--- python3/vimspector/stack_trace.py | 17 ++++--- python3/vimspector/variables.py | 22 +++++---- 4 files changed, 106 insertions(+), 26 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 1622d10..4770d81 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -134,21 +134,43 @@ function! vimspector#internal#balloon#MouseFilter( winid, key ) abort return handled endfunction +function! s:MatchKey( key, candidates ) abort + let mapped = '' + for candidate in a:candidates + try + " Try and expand the key code + execute 'let mapped = "\' . candidate . '"' + if mapped ==# a:key + return v:true + endif + endtry + endfor + + return v:false +endfunction + function! vimspector#internal#balloon#CursorFilter( winid, key ) abort - if a:key ==? "\" + let mappings = py3eval( + \ "__import__( 'vimspector'," + \." fromlist = [ 'settings' ] ).settings.Dict(" + \." 'mappings' )[ 'variables' ]" ) + + if index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 + return vimspector#internal#balloon#MouseFilter( a:winid, a:key ) + endif + + if s:MatchKey( a:key, mappings.expand_collapse ) call py3eval( '_vimspector_session.ExpandVariable(' \ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],' \ . 'line_num = ' . line( '.', a:winid ) \ . ')' ) return 1 - elseif a:key ==? "\" + elseif s:MatchKey( a:key, mappings.set_value ) call py3eval( '_vimspector_session.SetVariableValue(' \ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],' \ . 'line_num = ' . line( '.', a:winid ) \ . ')' ) return 1 - elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 - return vimspector#internal#balloon#MouseFilter( a:winid, a:key ) endif return popup_filter_menu( a:winid, a:key ) diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index 5ff729b..53aafd2 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -17,6 +17,7 @@ import vim import builtins from vimspector import utils +from collections.abc import Mapping DEFAULTS = { # UI @@ -38,6 +39,19 @@ DEFAULTS = { # Installer 'install_gadgets': [], + + # Mappings + 'mappings': { + 'variables': { + 'expand_collapse': [ '', '<2-LeftMouse>' ], + 'delete': [ '' ], + 'set_value': [ '' ] + }, + 'stack_trace': { + 'expand_or_jump': [ '', '<2-LeftMouse>' ], + 'focus_thread': [ '' ], + } + } } @@ -69,9 +83,46 @@ if hasattr( vim, 'Dictionary' ): DICT_TYPE = vim.Dictionary -def Dict( option: str ): - d = DICT_TYPE( DEFAULTS.get( option, {} ) ) - d.update( utils.GetVimValue( vim.vars, - f'vimspector_{ option }', - {} ) ) - return d +def Dict( option ): + return _UpdateDict( DICT_TYPE( DEFAULTS.get( option, {} ) ), + utils.GetVimValue( vim.vars, + f'vimspector_{ option }', + {} ) ) + + +def _UpdateDict( target, override ): + """Apply the updates in |override| to the dict |target|. This is like + dict.update, but recursive. i.e. if the existing element is a dict, then + override elements of the sub-dict rather than wholesale replacing. + e.g. + UpdateDict( + { + 'outer': { 'inner': { 'key': 'oldValue', 'existingKey': True } } + }, + { + 'outer': { 'inner': { 'key': 'newValue' } }, + 'newKey': { 'newDict': True }, + } + ) + yields: + { + 'outer': { + 'inner': { + 'key': 'newValue', + 'existingKey': True + } + }, + 'newKey': { newDict: True } + } + """ + + for key, value in override.items(): + current_value = target.get( key ) + if not isinstance( current_value, Mapping ): + target[ key ] = value + elif isinstance( value, Mapping ): + target[ key ] = _UpdateDict( current_value, value ) + else: + target[ key ] = value + + return target diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index df32462..c32cead 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -18,7 +18,7 @@ import os import logging import typing -from vimspector import utils, signs +from vimspector import utils, signs, settings class Thread: @@ -107,13 +107,16 @@ class StackTraceView( object ): utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' ) utils.SetUpUIWindow( win ) + mappings = settings.Dict( 'mappings' )[ 'stack_trace' ] + with utils.LetCurrentWindow( win ): - vim.command( 'nnoremap ' - ':call vimspector#GoToFrame()' ) - vim.command( 'nnoremap ' - ':call vimspector#SetCurrentThread()' ) - vim.command( 'nnoremap <2-LeftMouse> ' - ':call vimspector#GoToFrame()' ) + for mapping in utils.GetVimList( mappings, 'expand_or_jump' ): + vim.command( f'nnoremap { mapping } ' + ':call vimspector#GoToFrame()' ) + + for mapping in utils.GetVimList( mappings, 'focus_thread' ): + vim.command( f'nnoremap { mapping } ' + ':call vimspector#SetCurrentThread()' ) if utils.UseWinBar(): vim.command( 'nnoremenu 1.1 WinBar.Pause/Continue ' diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 9e74345..e418a5e 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -19,7 +19,7 @@ import logging from functools import partial import typing -from vimspector import utils +from vimspector import utils, settings class Expandable: @@ -165,13 +165,16 @@ class VariablesView( object ): self._variable_eval: Scope = None self._variable_eval_view: View = None + mappings = settings.Dict( 'mappings' )[ 'variables' ] + def AddExpandMappings(): - vim.command( 'nnoremap ' - ':call vimspector#ExpandVariable()' ) - vim.command( 'nnoremap <2-LeftMouse> ' - ':call vimspector#ExpandVariable()' ) - vim.command( 'nnoremap ' - ':call vimspector#SetVariableValue()' ) + for mapping in utils.GetVimList( mappings, 'expand_collapse' ): + vim.command( f'nnoremap { mapping } ' + ':call vimspector#ExpandVariable()' ) + + for mapping in utils.GetVimList( mappings, 'set_value' ): + vim.command( f'nnoremap { mapping } ' + ':call vimspector#SetVariableValue()' ) # Set up the "Variables" buffer in the variables_win self._scopes: typing.List[ Scope ] = [] @@ -194,8 +197,9 @@ class VariablesView( object ): 'vimspector#OmniFuncWatch' ) with utils.LetCurrentWindow( watches_win ): AddExpandMappings() - vim.command( - 'nnoremap :call vimspector#DeleteWatch()' ) + for mapping in utils.GetVimList( mappings, 'delete' ): + vim.command( + f'nnoremap { mapping } :call vimspector#DeleteWatch()' ) if utils.UseWinBar(): vim.command( 'nnoremenu 1.1 WinBar.New ' From e1078375fec6446fec1655c120dd629331bacd8f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 24 Feb 2021 22:12:02 +0000 Subject: [PATCH 530/609] Also allow in case modifyOtherKeys mode doesn't work --- python3/vimspector/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index 53aafd2..e72bedf 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -45,7 +45,7 @@ DEFAULTS = { 'variables': { 'expand_collapse': [ '', '<2-LeftMouse>' ], 'delete': [ '' ], - 'set_value': [ '' ] + 'set_value': [ '', '' ] }, 'stack_trace': { 'expand_or_jump': [ '', '<2-LeftMouse>' ], From 675a68c601b9fc3d721379955619971ed68759e5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 24 Feb 2021 22:18:43 +0000 Subject: [PATCH 531/609] Make it work in neovim too --- autoload/vimspector/internal/balloon.vim | 12 ++++-------- python3/vimspector/variables.py | 25 +++++++++++++----------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 4770d81..4dc5096 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -315,14 +315,10 @@ function! s:CreateNeovimTooltip( body ) abort " interract with the popup in neovim noautocmd call win_gotoid( s:popup_win_id ) - nnoremap - \ call vimspector#ExpandVariable() - nnoremap - \ call vimspector#SetVariableValue() - nnoremap - \ quit - nnoremap <2-LeftMouse> - \ call vimspector#ExpandVariable() + nnoremap quit + call py3eval( "__import__( 'vimspector', " + \." fromlist = [ 'variables' ] )." + \.' variables.AddExpandMappings()' ) " Close the popup whenever we leave this window augroup vimspector#internal#balloon#nvim_float diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index e418a5e..507c495 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -153,6 +153,18 @@ class BufView( View ): self.buf = buf +def AddExpandMappings( mappings = None ): + if mappings is None: + mappings = settings.Dict( 'mappings' )[ 'variables' ] + for mapping in utils.GetVimList( mappings, 'expand_collapse' ): + vim.command( f'nnoremap { mapping } ' + ':call vimspector#ExpandVariable()' ) + + for mapping in utils.GetVimList( mappings, 'set_value' ): + vim.command( f'nnoremap { mapping } ' + ':call vimspector#SetVariableValue()' ) + + class VariablesView( object ): def __init__( self, variables_win, watches_win ): self._logger = logging.getLogger( __name__ ) @@ -167,15 +179,6 @@ class VariablesView( object ): mappings = settings.Dict( 'mappings' )[ 'variables' ] - def AddExpandMappings(): - for mapping in utils.GetVimList( mappings, 'expand_collapse' ): - vim.command( f'nnoremap { mapping } ' - ':call vimspector#ExpandVariable()' ) - - for mapping in utils.GetVimList( mappings, 'set_value' ): - vim.command( f'nnoremap { mapping } ' - ':call vimspector#SetVariableValue()' ) - # Set up the "Variables" buffer in the variables_win self._scopes: typing.List[ Scope ] = [] self._vars = View( variables_win, {}, self._DrawScopes ) @@ -184,7 +187,7 @@ class VariablesView( object ): if utils.UseWinBar(): vim.command( 'nnoremenu 1.1 WinBar.Set ' ':call vimspector#SetVariableValue()' ) - AddExpandMappings() + AddExpandMappings( mappings ) # Set up the "Watches" buffer in the watches_win (and create a WinBar in # there) @@ -196,7 +199,7 @@ class VariablesView( object ): 'vimspector#AddWatchPrompt', 'vimspector#OmniFuncWatch' ) with utils.LetCurrentWindow( watches_win ): - AddExpandMappings() + AddExpandMappings( mappings ) for mapping in utils.GetVimList( mappings, 'delete' ): vim.command( f'nnoremap { mapping } :call vimspector#DeleteWatch()' ) From ba83a59e888cfbddfa58dfabbf091a7a873f3df7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 25 Feb 2021 12:04:35 +0000 Subject: [PATCH 532/609] TEst overriding the mappings --- autoload/vimspector/internal/balloon.vim | 5 ++++- python3/vimspector/settings.py | 9 +++------ support/custom_ui_vimrc | 11 +++++++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 4dc5096..b8be41a 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -138,7 +138,10 @@ function! s:MatchKey( key, candidates ) abort let mapped = '' for candidate in a:candidates try - " Try and expand the key code + " Try and expand the key code. Note this won't work for non-special keys + " and it won't work for multple keys, which is kinda shitty. + " + " There's no vim api to run expand_keycodes() i don't think. execute 'let mapped = "\' . candidate . '"' if mapped ==# a:key return v:true diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index e72bedf..9ad9e41 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -17,7 +17,6 @@ import vim import builtins from vimspector import utils -from collections.abc import Mapping DEFAULTS = { # UI @@ -85,9 +84,7 @@ if hasattr( vim, 'Dictionary' ): def Dict( option ): return _UpdateDict( DICT_TYPE( DEFAULTS.get( option, {} ) ), - utils.GetVimValue( vim.vars, - f'vimspector_{ option }', - {} ) ) + vim.vars.get( f'vimspector_{ option }', DICT_TYPE() ) ) def _UpdateDict( target, override ): @@ -118,9 +115,9 @@ def _UpdateDict( target, override ): for key, value in override.items(): current_value = target.get( key ) - if not isinstance( current_value, Mapping ): + if not isinstance( current_value, DICT_TYPE ): target[ key ] = value - elif isinstance( value, Mapping ): + elif isinstance( value, DICT_TYPE ): target[ key ] = _UpdateDict( current_value, value ) else: target[ key ] = value diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index b1ffda1..3b132b4 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -142,4 +142,15 @@ augroup END " }}} +" Custom mappings for special buffers {{{ + +let g:vimspector_mappings = { + \ 'stack_trace': {}, + \ 'variables': { + \ 'set_value': [ '', '' ], + \ }, + \ } + +" }}} + " vim: foldmethod=marker From 94242fa5326aef0dd350ac1cfe1f91648651291b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 25 Feb 2021 16:41:55 +0000 Subject: [PATCH 533/609] CustomUI: Make buffers non-modifiable when opened for debugging --- support/custom_ui_vimrc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index 3b132b4..934d9f0 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -105,7 +105,10 @@ function! s:OnJumpToFrame() abort nmap di VimspectorBalloonEval xmap di VimspectorBalloonEval - let s:mapped[ string( bufnr() ) ] = 1 + let s:mapped[ string( bufnr() ) ] = { 'modifiable': &modifiable } + + setlocal nomodifiable + endfunction function! s:OnDebugEnd() abort @@ -124,6 +127,8 @@ function! s:OnDebugEnd() abort silent! nunmap dc silent! nunmap di silent! xunmap di + + let &l:modifiable = s:mapped[ bufnr ][ 'modifiable' ] endtry endfor finally @@ -148,7 +153,7 @@ let g:vimspector_mappings = { \ 'stack_trace': {}, \ 'variables': { \ 'set_value': [ '', '' ], - \ }, + \ } \ } " }}} From f2d407256e752a8f943e4638358871730aea5c83 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 25 Feb 2021 16:53:55 +0000 Subject: [PATCH 534/609] Use expression completion for watch and set --- autoload/vimspector.vim | 4 +++- python3/vimspector/utils.py | 17 +++++++++++------ python3/vimspector/variables.py | 3 ++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 7cecca5..168a239 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -235,7 +235,9 @@ function! vimspector#AddWatch( ... ) abort return endif if a:0 == 0 - let expr = input( 'Enter watch expression: ' ) + let expr = input( 'Enter watch expression: ', + \ '', + \ 'custom,vimspector#CompleteExpr' ) else let expr = a:1 endif diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 10da7e1..e7cb537 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -356,16 +356,21 @@ def SelectFromList( prompt, options ): return None -def AskForInput( prompt, default_value = None ): +def AskForInput( prompt, default_value = None, completion = None ): if default_value is None: - default_option = '' - else: - default_option = ", '{}'".format( Escape( default_value ) ) + default_value = '' + + args = [ prompt, default_value ] + + if completion is not None: + if completion == 'expr': + args.append( 'custom,vimspector#CompleteExpr' ) + else: + args.append( completion ) with InputSave(): try: - return vim.eval( "input( '{}' {} )".format( Escape( prompt ), - default_option ) ) + return Call( 'input', *args ) except ( KeyboardInterrupt, vim.error ): return None diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 507c495..fb9080e 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -533,7 +533,8 @@ class VariablesView( object ): return new_value = utils.AskForInput( 'New Value: ', - variable.variable.get( 'value', '' ) ) + variable.variable.get( 'value', '' ), + completion = 'expr' ) if new_value is None: return From 49a9a4b36792a99a4fa2c35124934c12831cf2ec Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 25 Feb 2021 21:14:05 +0000 Subject: [PATCH 535/609] Allow mappings which aren't special chars --- autoload/vimspector/internal/balloon.vim | 25 +++++++++++++----------- support/custom_ui_vimrc | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index b8be41a..32d17ab 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -135,18 +135,21 @@ function! vimspector#internal#balloon#MouseFilter( winid, key ) abort endfunction function! s:MatchKey( key, candidates ) abort - let mapped = '' for candidate in a:candidates - try - " Try and expand the key code. Note this won't work for non-special keys - " and it won't work for multple keys, which is kinda shitty. - " - " There's no vim api to run expand_keycodes() i don't think. - execute 'let mapped = "\' . candidate . '"' - if mapped ==# a:key - return v:true - endif - endtry + " If the mapping string looks like a special character, then try and + " expand it. This is... a hack. The whole thing only works if the mapping + " is a single key (anyway), and so we assume any string starting with < is a + " special key (which will be the common case) and try and map it. If it + " fails... it fails. + if candidate[ 0 ] == '<' + try + execute 'let candidate = "\' . candidate . '"' + endtry + endif + + if candidate ==# a:key + return v:true + endif endfor return v:false diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index 934d9f0..a8812cb 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -152,7 +152,7 @@ augroup END let g:vimspector_mappings = { \ 'stack_trace': {}, \ 'variables': { - \ 'set_value': [ '', '' ], + \ 'set_value': [ '', '', 'C' ], \ } \ } From 804b49928645f9ef77cb7dc59fb1e1e33827d70f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 25 Feb 2021 21:52:27 +0000 Subject: [PATCH 536/609] Allow setting the value via the api --- autoload/vimspector.vim | 8 ++++++-- plugin/vimspector.vim | 1 + python3/vimspector/debug_session.py | 4 ++-- python3/vimspector/variables.py | 9 +++++---- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 168a239..41d30dd 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -209,11 +209,15 @@ function! vimspector#ExpandVariable() abort py3 _vimspector_session.ExpandVariable() endfunction -function! vimspector#SetVariableValue() abort +function! vimspector#SetVariableValue( ... ) abort if !s:Enabled() return endif - py3 _vimspector_session.SetVariableValue() + if a:0 == 0 + py3 _vimspector_session.SetVariableValue() + else + py3 _vimspector_session.SetVariableValue( new_value = vim.eval( 'a:1' ) ) + endif endfunction function! vimspector#DeleteWatch() abort diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index c69414b..53855ef 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -129,6 +129,7 @@ augroup VimspectorUserAutoCmds autocmd user VimspectorDebugEnded silent augroup END +" FIXME: Only register this _while_ debugging is active augroup Vimspector autocmd! autocmd BufNew * call vimspector#OnBufferCreated( expand( '' ) ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index f5a62df..6fb0b16 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -526,8 +526,8 @@ class DebugSession( object ): self._variablesView.ExpandVariable( buf, line_num ) @IfConnected() - def SetVariableValue( self, buf = None, line_num = None ): - self._variablesView.SetVariableValue( buf, line_num ) + def SetVariableValue( self, new_value = None, buf = None, line_num = None ): + self._variablesView.SetVariableValue( new_value, buf, line_num ) @IfConnected() def AddWatch( self, expression ): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index fb9080e..d225af0 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -518,7 +518,7 @@ class VariablesView( object ): }, } ) - def SetVariableValue( self, buf = None, line_num = None ): + def SetVariableValue( self, new_value = None, buf = None, line_num = None ): variable: Variable view: View @@ -532,9 +532,10 @@ class VariablesView( object ): if not variable.IsContained(): return - new_value = utils.AskForInput( 'New Value: ', - variable.variable.get( 'value', '' ), - completion = 'expr' ) + if new_value is None: + new_value = utils.AskForInput( 'New Value: ', + variable.variable.get( 'value', '' ), + completion = 'expr' ) if new_value is None: return From 06f9bfc0572c113ffb406bcb34eabe0b51724039 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 25 Feb 2021 21:52:41 +0000 Subject: [PATCH 537/609] Add basic tests for set variable value --- tests/variables.test.vim | 289 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index 2da9c7f..c7d373b 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -830,3 +830,292 @@ function! Test_VariableEvalExpand() call vimspector#test#setup#Reset() %bwipe! endfunction + +function! Test_SetVariableValue_Local() + 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 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( {-> + \ AssertMatchist( + \ [ + \ '- Scope: Locals', + \ ' \*- 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, + \ '$' ) + \ ) + \ } ) + + call setpos( '.', [ 0, 3, 1 ] ) + + " We can't just fire the keys to the inpit prompt because we use inputsave() + " and inputrestore(), so mock that out and fire away. + py3 <\100\", "xt" )' ) +EOF + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '- Scope: Locals', + \ ' \*- t (Test): {...}', + \ ' \*- i (int): 100', + \ ' \*- c (char): 0 ''\\0\{1,3}''', + \ ' \*- fffff (float): 0', + \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Now set it via the more comforable scripting interface + call vimspector#SetVariableValue( '1234' ) + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '- Scope: Locals', + \ ' \*- t (Test): {...}', + \ ' \*- i (int): 1234', + \ ' \*- c (char): 0 ''\\0\{1,3}''', + \ ' \*- fffff (float): 0', + \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Something fails + call vimspector#SetVariableValue( 'this is invalid' ) + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '- Scope: Locals', + \ ' \*- t (Test): {...}', + \ ' \*- i (int): 1234', + \ ' \*- c (char): 0 ''\\0\{1,3}''', + \ ' \*- fffff (float): 0', + \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.variables ), + \ 1, + \ '$' ) + \ ) + \ } ) + + + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_SetVariableValue_Watch() + 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( {-> + \ AssertMatchist( + \ [ + \ '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, + \ '$' ) + \ ) + \ } ) + + call setpos( '.', [ 0, 4, 1 ] ) + + " We can't just fire the keys to the inpit prompt because we use inputsave() + " and inputrestore(), so mock that out and fire away. + " Note: mapleder is , + py3 <\100\", "xt" )' ) +EOF + + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ 'Watches: ----', + \ 'Expression: t', + \ ' \*- Result: {...}', + \ ' \*- i (int): 100', + \ ' \*- c (char): 0 ''\\0\{1,3}''', + \ ' \*- fffff (float): 0', + \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Now set it via the more comforable scripting interface + call vimspector#SetVariableValue( '1234' ) + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ 'Watches: ----', + \ 'Expression: t', + \ ' \*- Result: {...}', + \ ' \*- i (int): 1234', + \ ' \*- c (char): 0 ''\\0\{1,3}''', + \ ' \*- fffff (float): 0', + \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.watches ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_SetVariableValue_Balloon() + let fn = 'testdata/cpp/simple/struct.cpp' + call s:StartDebugging( #{ fn: fn, line: 24, col: 1, launch: #{ + \ configuration: 'run-to-breakpoint' + \ } } ) + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) + + " leader is , + xmap d VimspectorBalloonEval + nmap d VimspectorBalloonEval + + "evaluate the prev line + call setpos( '.', [ 0, 24, 8 ] ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) + call feedkeys( ',d', 'xt' ) + + call WaitForAssert( {-> + \ assert_notequal( v:none, g:vimspector_session_windows.eval ) + \ } ) + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '{...}', + \ ' - i: 0', + \ ' - c: 0 ''\\0\{1,3}''', + \ ' - fffff: 0', + \ ' + another_test: ', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.eval ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Move down to the ffff line + + call feedkeys( 'jjj', 'xt' ) + " We can't just fire the keys to the inpit prompt because we use inputsave() + " and inputrestore(), so mock that out and fire away. + " Note: mapleder is , + py3 <\100\", "xt" )' ) +EOF + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '{...}', + \ ' - i: 0', + \ ' - c: 0 ''\\0\{1,3}''', + \ ' - fffff: 100', + \ ' + another_test: ', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.eval ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction From edcb057ead0025678cb650a6cd6e07506aa8f584 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 25 Feb 2021 22:23:30 +0000 Subject: [PATCH 538/609] Add to docs --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 00416e3..42d4765 100644 --- a/README.md +++ b/README.md @@ -914,7 +914,8 @@ breakpoint when it is hit. * Current scope shows values of locals. * Use ``, or double-click with left mouse to expand/collapse (+, -). -* Set the value of the variable with `` (control + ``) +* Set the value of the variable with `` (control + ``) or + `` (if `modifyOtherKeys` doesn't work for you) * When changing the stack frame the locals window updates. * While paused, hover to see values @@ -931,7 +932,8 @@ All rules for `Variables and scopes` apply plus the following: and get its result. * Make a normal mode (`nmap`) and visual mode (`xmap`) mapping to `VimspectorBalloonEval` to manually trigger the popup. - * Set the value of the variable with `` (control + ``) + * Set the value of the variable with `` (control + ``) or + `` (if `modifyOtherKeys` doesn't work for you) * Use regular nagivation keys (`j`, `k`) to choose the current selection; `` (or leave the tooltip window) to close the tooltip. @@ -950,7 +952,8 @@ to add a new watch expression. * Alternatively, use `:VimspectorWatch `. Tab-completion for expression is available in some debug adapters. * Expand result with ``, or double-click with left mouse. -* Set the value of the variable with `` (control + ``) +* Set the value of the variable with `` (control + ``) or + `` (if `modifyOtherKeys` doesn't work for you) * Delete with ``. ![watch window](https://puremourning.github.io/vimspector-web/img/vimspector-watch-window.png) @@ -2054,3 +2057,4 @@ hi link jsonComment Comment [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 +[YcmJava]: https://github.com/ycm-core/YouCompleteMe#java-semantic-completion From 5bd83d3e37403d8cc7ad3378a58d58f06a53d318 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 27 Feb 2021 21:02:21 +0000 Subject: [PATCH 539/609] Add debugging instruction --- CONTRIBUTING.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3ba53bf..578fd8f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -184,6 +184,30 @@ They're also run by CI, so please check for lint failures. The canonical definition of the command to run is the command run in CI, i.e. in `.git/workflows/build.yml`. +### Debugging Vimspector + +You can debug vimspector's python code using vimspector! We can use debugpy, +from within Vim's embedded python and connect to it. Here's how: + +1. In one instance of vim, run the following to get debugpy to start listening + for us to connect: `:py3 __import__( 'vimspector', fromlist=[ 'developer' ] + ).developer.SetUpDebugpy()` + +2. In another instance of Vim, set a breakpoint in the vimspector python code + you want to debug and launch vimspector (e.g. ``). Select the `Python: + attach to vim` profile. This will attach to the debugpy running in the other + vim. + +3. Back in the first vim (the debuggee), trigger the vimspector code in + question, e.g. by starting to debug something else. + +4. You'll see it pause, and the 2nd vim (the debugger), you should be able to + step through and inspect as with any other python remote debugging. + +NB. It's also possible to debug the vimscript code using vimspector, but this +requires unreleased vim patches and a fair amount of faff. You can always use +`:debug` (see the help) for this though. + # Code of conduct Please see [code of conduct](CODE_OF_CONDUCT.md). From d2b92b7ce5446bf5aeaea177abeacda129876042 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 2 Mar 2021 10:48:38 +0000 Subject: [PATCH 540/609] Fix crash using STop function --- autoload/vimspector.vim | 4 ++-- tests/mappings.test.vim | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 41d30dd..e94a177 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -195,9 +195,9 @@ function! vimspector#Stop( ... ) abort return endif if a:0 == 0 - options = {} + let options = {} else - options = a:1 + let options = a:1 endif py3 _vimspector_session.Stop( **vim.eval( 'options' ) ) endfunction diff --git a/tests/mappings.test.vim b/tests/mappings.test.vim index 0f9995d..b0e288c 100644 --- a/tests/mappings.test.vim +++ b/tests/mappings.test.vim @@ -114,6 +114,26 @@ function! Test_Use_Mappings_HUMAN() \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 9 ) \ } ) + " Stop + call feedkeys( "\", 'xt' ) + call WaitForAssert( {-> + \ assert_equal( [], + \ getbufline( g:vimspector_session_windows.variables, + \ 1, + \ '$' ) ) + \ } ) + call WaitForAssert( {-> + \ assert_equal( [], + \ getbufline( g:vimspector_session_windows.stack_trace, + \ 1, + \ '$' ) ) + \ } ) + call WaitForAssert( {-> + \ assert_equal( [], + \ getbufline( g:vimspector_session_windows.watches, + \ 1, + \ '$' ) ) + \ } ) call vimspector#test#setup#Reset() From 82db32780baaac99319ffa264d36a002d6bfe4fb Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 4 Mar 2021 13:28:04 +0000 Subject: [PATCH 541/609] Add example of ssh to remote host --- docs/configuration.md | 8 +++-- .../python/simple_python/.vimspector.json | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index f3d0814..e736985 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -634,7 +634,9 @@ Vimspector then orchestrates the various tools to set you up. // %CMD% replaced with the remote-cmdLine configured in the launch // configuration. (mandatory) "runCommand": [ - "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}", + "python", "-m", "debugpy", + "--listen", "0.0.0.0:${port}", + "--wait-for-client", "%CMD%" ] @@ -848,7 +850,9 @@ port. // %CMD% replaced with the remote-cmdLine configured in the launch // configuration. (mandatory) "runCommand": [ - "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}", + "python", "-m", "debugpy", + "--listen", "0.0.0.0:${port}", + "--wait-for-client", "%CMD%" ] diff --git a/support/test/python/simple_python/.vimspector.json b/support/test/python/simple_python/.vimspector.json index 29d7d43..69d37a6 100644 --- a/support/test/python/simple_python/.vimspector.json +++ b/support/test/python/simple_python/.vimspector.json @@ -24,6 +24,24 @@ }, "delay": "5000m" } + }, + "python-remote-ssh": { + "variables": { + "port": "8765" + }, + "port": "${port}", + "host": "${host}", + "launch": { + "remote": { + "host": "${host}", + "account": "${account}", + "runCommand": [ + "python3", "-m", "debugpy", "--listen", "0.0.0.0:${port}", + "--wait-for-client", + "%CMD%" + ] + } + } } }, "configurations": { @@ -164,6 +182,23 @@ "stopOnEntry": false, "console": "integratedTerminal" } + }, + "run - remote host": { + "adapter": "python-remote-ssh", + "remote-cmdLine": [ + "${remoteRoot}/main.py" + ], + "remote-request": "launch", + "configuration": { + "request": "attach", + "redirectOutput": true, + "pathMappings": [ + { + "localRoot": "${workspaceRoot}", + "remoteRoot": "${remoteRoot}" + } + ] + } } } } From 943ae6c7c9d0f256e444c3ddc5e876156335f997 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 5 Mar 2021 19:05:42 +0000 Subject: [PATCH 542/609] Fix error when nothing to expand --- python3/vimspector/variables.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index d225af0..8dcb493 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -473,6 +473,8 @@ class VariablesView( object ): self._DrawWatches() def _GetVariable( self, buf = None, line_num = None ): + none = ( None, None ) + if buf is None: buf = vim.current.buffer @@ -487,10 +489,10 @@ class VariablesView( object ): and buf == self._variable_eval_view.buf ): view = self._variable_eval_view else: - return None + return none if line_num not in view.lines: - return None + return none return view.lines[ line_num ], view From c012f9520ebf07d591f48174129467825fc2b766 Mon Sep 17 00:00:00 2001 From: Tony Dwire Date: Tue, 9 Mar 2021 19:45:27 -0600 Subject: [PATCH 543/609] Updated netcoredbg to 1.2.0-738 --- python3/vimspector/gadgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index d1b6872..f6c3639 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -233,7 +233,7 @@ GADGETS = { 'format': 'tar', }, 'all': { - 'version': '1.2.0-635' + 'version': '1.2.0-738' }, 'macos': { 'file_name': 'netcoredbg-osx.tar.gz', From 38c843219e36ec381b44a14c32a44850d418bc41 Mon Sep 17 00:00:00 2001 From: Tony Dwire Date: Tue, 9 Mar 2021 19:55:38 -0600 Subject: [PATCH 544/609] Updated file name for netcoredbg to reflect new filename. Overrode version for macos since there is no new build for it yet. --- python3/vimspector/gadgets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index f6c3639..ab4c1e4 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -237,11 +237,12 @@ GADGETS = { }, 'macos': { 'file_name': 'netcoredbg-osx.tar.gz', + 'version': '1.2.0-536', 'checksum': '71c773e34d358950f25119bade7e3081c4c2f9d71847bd49027ca5792e918beb', }, 'linux': { - 'file_name': 'netcoredbg-linux-bionic.tar.gz', + 'file_name': 'netcoredbg-linux-bionic-amd64.tar.gz', 'checksum': '', }, 'windows': { From 2a635294d0b549c8ab8f90cf2cacc8f597c2bad8 Mon Sep 17 00:00:00 2001 From: Tony Dwire Date: Tue, 9 Mar 2021 20:11:58 -0600 Subject: [PATCH 545/609] Fix wrong version number for macos version. --- python3/vimspector/gadgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index ab4c1e4..6974367 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -237,7 +237,7 @@ GADGETS = { }, 'macos': { 'file_name': 'netcoredbg-osx.tar.gz', - 'version': '1.2.0-536', + 'version': '1.2.0-635', 'checksum': '71c773e34d358950f25119bade7e3081c4c2f9d71847bd49027ca5792e918beb', }, From 7d2770f3c486ba34acc7eab52dfc1df6af353056 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 8 Mar 2021 23:00:45 +0000 Subject: [PATCH 546/609] Add vertical (i.e. narrow) layout This puts the 3 utility windows at the top, horizontally split, with the code view below. The terminal window is drawn either vertically split (if there's room) or horizontally split otherwise. The output window remains at tht bottom. We add equivalent sizing options too, setting some defauts that roughly work on my macbook pro. We try to guess the best layout to use. In particular we go into 'narrow' mode if there are not enough horizonal columns to fit the sidebar, code and at least the minimum terminal size. We also try to move the terminal to be horizontally spit (i.e. vertically stacked) if we can fit the max number of lines, but only the min number of columns. This is all a little heuristic, but when testing it myself, it feels good and tends to pick a good option by default. Users can always customise the ui mode (g:vimspector_ui_mode and all the various specific width options) --- python3/vimspector/debug_session.py | 91 +++++++ python3/vimspector/settings.py | 19 +- python3/vimspector/terminal.py | 55 ++++- tests/lib/autoload/vimspector/test/setup.vim | 56 +++++ tests/ui.test.vim | 247 +++++++++++++++++++ 5 files changed, 461 insertions(+), 7 deletions(-) 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() From 01ea50cb7062b8a27b8df029f5d1dd13c37b9784 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 11 Mar 2021 21:13:58 +0000 Subject: [PATCH 547/609] use --clean for minimal vimrc --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- CONTRIBUTING.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1788c04..f01a833 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -17,12 +17,12 @@ assignees: '' > Please answer the following questions -* Does your issue reproduce using `vim -Nu /path/to/vimspector/support/minimal_vimrc` ? \[Yes/No] +* Does your issue reproduce using `vim --clean -Nu /path/to/vimspector/support/minimal_vimrc` ? \[Yes/No] * If you are using Neovim, does your issue reproduce using Vim? \[Yes/No] > List of steps to reproduce: -> 1. Run `vim -Nu /path/to/vimspector/support/minimal_vimrc` +> 1. Run `vim ---clean Nu /path/to/vimspector/support/minimal_vimrc` > 2. Open _this project_... > 3. Press _this sequence of keys_ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 578fd8f..1866974 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,7 +83,7 @@ in the issue report. The minimal vimrc is in `support/test/minimal_vimrc` and can be used as follows: ``` -vim -Nu /path/to/vimspector/support/minimal_vimrc +vim --clean -Nu /path/to/vimspector/support/minimal_vimrc ``` ## Pull Requests From 2615cff97ad871de6ced52be93e72c310838543e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 11 Mar 2021 21:14:30 +0000 Subject: [PATCH 548/609] If there isn't enough vertical space, we get vim errors (not enough room), so don't do that --- python3/vimspector/debug_session.py | 14 +++++++++++--- tests/ui.test.vim | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 4a89cdf..16f69be 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -657,8 +657,6 @@ class DebugSession( object ): 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 @@ -670,13 +668,23 @@ class DebugSession( object ): + settings.Int( 'code_minwidth' ) + 1 + settings.Int( 'terminal_minwidth' ) ) + min_height = ( settings.Int( 'code_minheight' ) + 1 + + settings.Int( 'topbar_height' ) + 1 + + settings.Int( 'bottombar_height' ) + 1 + + 2 ) + mode = ( 'vertical' if vim.options[ 'columns' ] < min_width else 'horizontal' ) - self._logger.debug( 'min_width: %s, actual: %s - result: %s', + if vim.options[ 'lines' ] < min_height: + mode = 'horizontal' + + self._logger.debug( 'min_width/height: %s/%s, actual: %s/%s - result: %s', min_width, + min_height, vim.options[ 'columns' ], + vim.options[ 'lines' ], mode ) if mode == 'vertical': diff --git a/tests/ui.test.vim b/tests/ui.test.vim index 1f7789e..5ba2f9c 100644 --- a/tests/ui.test.vim +++ b/tests/ui.test.vim @@ -256,7 +256,7 @@ function! SetUp_Test_AutoLayoutTerminalVertVert() " 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 ) + call vimspector#test#setup#PushOption( 'lines', 50 ) endfunction function! Test_AutoLayoutTerminalVertVert() From 6d3f3e207b84432d7dd0af7c08263c5140bd3dd1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 10 Mar 2021 11:12:28 +0000 Subject: [PATCH 549/609] Fix default variable values having substitutions from the calculus --- python3/vimspector/utils.py | 8 +++++--- tests/python/Test_ExpandReferencesInDict.py | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index e7cb537..d8dd345 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -494,7 +494,6 @@ def _Substitute( template, mapping ): if mo.group( 'braceddefault' ) is not None: named = mo.group( 'defname' ) if named not in mapping: - '' raise MissingSubstitution( named, mo.group( 'default' ).replace( '\\}', '}' ) ) @@ -536,8 +535,11 @@ def ExpandReferencesInString( orig_s, if default_value is None and e.default_value is not None: try: default_value = _Substitute( e.default_value, mapping ) - except MissingSubstitution: - default_value = e.default_value + except MissingSubstitution as e2: + if e2.name in calculus: + default_value = calculus[ e2.name ]() + else: + default_value = e.default_value mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ), default_value ) diff --git a/tests/python/Test_ExpandReferencesInDict.py b/tests/python/Test_ExpandReferencesInDict.py index 4998350..15fb11b 100644 --- a/tests/python/Test_ExpandReferencesInDict.py +++ b/tests/python/Test_ExpandReferencesInDict.py @@ -35,6 +35,7 @@ class TestExpandReferencesInDict( unittest.TestCase ): 'one': '${one}', 'two': '${one} and ${two}', 'three': '${three}', + 'three_with_default': '${three_with_default:${three\\}}', # uses calculus 'four': '${four}', 'five': '${five}', 'list': [ '*${words}' ], @@ -58,6 +59,7 @@ class TestExpandReferencesInDict( unittest.TestCase ): 'one': 'one', 'two': 'one and TWO', 'three': '3', + 'three_with_default': '3', 'four': 'typed text', 'five': '5ive!', 'list': [ 'these', 'are', 'some', 'words' ], From a53d68f04301833b30a9a8e3acf02ba793c49d82 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 10 Mar 2021 12:29:40 +0000 Subject: [PATCH 550/609] Add up and down frame mappings --- autoload/vimspector.vim | 14 ++++ plugin/vimspector.vim | 5 ++ python3/vimspector/debug_session.py | 8 ++ python3/vimspector/stack_trace.py | 40 ++++++++++ tests/stack_trace.test.vim | 114 ++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index e94a177..a547e5c 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -234,6 +234,20 @@ function! vimspector#GoToFrame() abort py3 _vimspector_session.ExpandFrameOrThread() endfunction +function! vimspector#UpFrame() abort + if !s:Enabled() + return + endif + py3 _vimspector_session.UpFrame() +endfunction + +function! vimspector#DownFrame() abort + if !s:Enabled() + return + endif + py3 _vimspector_session.DownFrame() +endfunction + function! vimspector#AddWatch( ... ) abort if !s:Enabled() return diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 53855ef..37509a7 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -67,6 +67,11 @@ nnoremap VimspectorBalloonEval xnoremap VimspectorBalloonEval \ :call vimspector#ShowEvalBalloon( 1 ) +nnoremap VimspectorUpFrame + \ :call vimspector#UpFrame() +nnoremap VimspectorDownFrame + \ :call vimspector#DownFrame() + if s:mappings ==# 'VISUAL_STUDIO' nmap VimspectorContinue nmap VimspectorStop diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 16f69be..4d8c816 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -571,6 +571,14 @@ class DebugSession( object ): def ExpandFrameOrThread( self ): self._stackTraceView.ExpandFrameOrThread() + @IfConnected() + def UpFrame( self ): + self._stackTraceView.UpFrame() + + @IfConnected() + def DownFrame( self ): + self._stackTraceView.DownFrame() + def ToggleLog( self ): if self._HasUI(): return self.ShowOutput( 'Vimspector' ) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index c32cead..ae14e68 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -367,6 +367,46 @@ class StackTraceView( object ): self._JumpToFrame( frame ) + + def _GetFrameOffset( self, delta ): + thread: Thread + for thread in self._threads: + if thread.id != self._current_thread: + continue + + if not thread.stacktrace: + return + + frame_idx = None + for index, frame in enumerate( thread.stacktrace ): + if frame == self._current_frame: + frame_idx = index + break + + if frame_idx is not None: + target_idx = frame_idx + delta + if target_idx >= 0 and target_idx < len( thread.stacktrace ): + return thread.stacktrace[ target_idx ] + + break + + + def UpFrame( self ): + frame = self._GetFrameOffset( 1 ) + if not frame: + utils.UserMessage( 'Top of stack' ) + else: + self._JumpToFrame( frame, 'up' ) + + + def DownFrame( self ): + frame = self._GetFrameOffset( -1 ) + if not frame: + utils.UserMessage( 'Bottom of stack' ) + else: + self._JumpToFrame( frame, 'down' ) + + def AnyThreadsRunning( self ): for thread in self._threads: if thread.state != Thread.TERMINATED: diff --git a/tests/stack_trace.test.vim b/tests/stack_trace.test.vim index 569376c..a65ea5e 100644 --- a/tests/stack_trace.test.vim +++ b/tests/stack_trace.test.vim @@ -356,3 +356,117 @@ function! Test_Multiple_Threads_Step() call vimspector#test#setup#Reset() %bwipe! endfunction + +function! Test_UpDownStack() + let fn='../support/test/python/simple_python/main.py' + exe 'edit ' . fn + call setpos( '.', [ 0, 6, 1 ] ) + + call vimspector#SetLineBreakpoint( fn, 15 ) + call vimspector#LaunchWithSettings( { 'configuration': 'run' } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 15, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '- Thread 1: MainThread (paused)', + \ ' 2: DoSomething@main.py:15', + \ ' 3: __init__@main.py:8', + \ ' 4: Main@main.py:23', + \ ' 5: @main.py:29', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#DownFrame() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 15, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '- Thread 1: MainThread (paused)', + \ ' 2: DoSomething@main.py:15', + \ ' 3: __init__@main.py:8', + \ ' 4: Main@main.py:23', + \ ' 5: @main.py:29', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#UpFrame() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 8, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '- Thread 1: MainThread (paused)', + \ ' 2: DoSomething@main.py:15', + \ ' 3: __init__@main.py:8', + \ ' 4: Main@main.py:23', + \ ' 5: @main.py:29', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call feedkeys( "\VimspectorUpFrame", 'x' ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 23, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '- Thread 1: MainThread (paused)', + \ ' 2: DoSomething@main.py:15', + \ ' 3: __init__@main.py:8', + \ ' 4: Main@main.py:23', + \ ' 5: @main.py:29', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call feedkeys( "\VimspectorDownFrame", 'x' ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 8, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '- Thread 1: MainThread (paused)', + \ ' 2: DoSomething@main.py:15', + \ ' 3: __init__@main.py:8', + \ ' 4: Main@main.py:23', + \ ' 5: @main.py:29', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ '$' ) + \ ) + \ } ) + + call vimspector#DownFrame() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 15, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '- Thread 1: MainThread (paused)', + \ ' 2: DoSomething@main.py:15', + \ ' 3: __init__@main.py:8', + \ ' 4: Main@main.py:23', + \ ' 5: @main.py:29', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ '$' ) + \ ) + \ } ) + + + call vimspector#ClearBreakpoints() + call vimspector#test#setup#Reset() + %bwipe! +endfunction From 6b67d165b92d1fe858ebd427507836021e0f0623 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 12 Mar 2021 21:50:37 +0000 Subject: [PATCH 551/609] Update docs for new mappings --- README.md | 84 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 42d4765..2152a9c 100644 --- a/README.md +++ b/README.md @@ -656,18 +656,23 @@ personal and so you should work out what you like and use vim's powerful mapping features to set your own mappings. To that end, Vimspector defines the following `` mappings: -* `VimspectorContinue` -* `VimspectorStop` -* `VimspectorRestart` -* `VimspectorPause` -* `VimspectorToggleBreakpoint` -* `VimspectorToggleConditionalBreakpoint` -* `VimspectorAddFunctionBreakpoint` -* `VimspectorStepOver` -* `VimspectorStepInto` -* `VimspectorStepOut` -* `VimspectorRunToCursor` -* `VimspectorBalloonEval` +| Mapping | Function | API | +| --- | --- | --- | +| `VimspectorContinue` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` | +| `VimspectorStop` | Stop debugging. | `vimspector#Stop()` | +| `VimpectorRestart` | Restart debugging with the same configuration. | `vimspector#Restart()` | +| `VimspectorPause` | Pause debuggee. | `vimspector#Pause()` | +| `VimspectorToggleBreakpoint` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` | +| `VimspectorToggleConditionalBreakpoint` | Toggle conditional line breakpoint on the current line. | `vimspector#ToggleBreakpoint( { trigger expr, hit count expr } )` | +| `VimspectorAddFunctionBreakpoint` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '' )` | +| `VimspectorRunToCursor` | Run to Cursor | `vimspector#RunToCursor()` | +| `VimspectorStepOver` | Step Over | `vimspector#StepOver()` | +| `VimspectorStepInto` | Step Into | `vimspector#StepInto()` | +| `VimspectorStepOut` | Step out of current function scope | `vimspector#StepOut()` | +| `VimspectorUpFrame` | Move up a frame in the current call stack | `vimspector#UpFrame()` | +| `VimspectorDownFrame` | Move down a frame in the current call stack | `vimspector#DownFrame()` | +| `VimspectorBalloonEval` | Evaluate expression under cursor (or visual) in popup | *internal* | + These map roughly 1-1 with the API functions below. @@ -695,17 +700,17 @@ loading vimspector**: let g:vimspector_enable_mappings = 'VISUAL_STUDIO' ``` -| Key | Function | API | -| --- | --- | --- | -| `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` | -| `Shift F5` | Stop debugging. | `vimspector#Stop()` | -| `Ctrl Shift F5` | Restart debugging with the same configuration. | `vimspector#Restart()` | -| `F6` | Pause debuggee. | `vimspector#Pause()` | -| `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` | -| `Shift F9` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '' )` | -| `F10` | Step Over | `vimspector#StepOver()` | -| `F11` | Step Into | `vimspector#StepInto()` | -| `Shift F11` | Step out of current function scope | `vimspector#StepOut()` | +| Key | Mapping | Function +| --- | --- | --- +| `F5` | `VimspectorContinue` | When debugging, continue. Otherwise start debugging. +| `Shift F5` | `VimspectorStop` | Stop debugging. +| `Ctrl Shift F5` | `VimspectorRestart` | Restart debugging with the same configuration. +| `F6` | `VimspectorPause` | Pause debuggee. +| `F9` | `VimspectorToggleBreakpoint` | Toggle line breakpoint on the current line. +| `Shift F9` | `VimspectorAddFunctionBreakpoint` | Add a function breakpoint for the expression under cursor +| `F10` | `VimspectorStepOver` | Step Over +| `F11` | `VimspectorStepInto` | Step Into +| `Shift F11` | `VimspectorStepOut` | Step out of current function scope ## Human Mode @@ -720,19 +725,19 @@ loading vimspector**: let g:vimspector_enable_mappings = 'HUMAN' ``` -| Key | Function | API | -| --- | --- | --- | -| `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` | -| `F3` | Stop debugging. | `vimspector#Stop()` | -| `F4` | Restart debugging with the same configuration. | `vimspector#Restart()` | -| `F6` | Pause debuggee. | `vimspector#Pause()` | -| `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` | -| `F9` | Toggle conditional line breakpoint on the current line. | `vimspector#ToggleBreakpoint( { trigger expr, hit count expr } )` | -| `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '' )` | -| `F8` | Run to Cursor | `vimspector#RunToCursor()` | -| `F10` | Step Over | `vimspector#StepOver()` | -| `F11` | Step Into | `vimspector#StepInto()` | -| `F12` | Step out of current function scope | `vimspector#StepOut()` | +| Key | Mapping | Function +| --- | --- | --- +| `F5` | `VimspectorContinue` | When debugging, continue. Otherwise start debugging. +| `F3` | `VimspectorStop` | Stop debugging. +| `F4` | `VimspectorRestart` | Restart debugging with the same configuration. +| `F6` | `VimspectorPause` | Pause debuggee. +| `F9` | `VimspectorToggleBreakpoint` | Toggle line breakpoint on the current line. +| `F9` | `VimspectorToggleConditionalBreakpoint` | Toggle conditional line breakpoint on the current line. +| `F8` | `VimspectorAddFunctionBreakpoint` | Add a function breakpoint for the expression under cursor +| `F8` | `VimspectorRunToCursor` | Run to Cursor +| `F10` | `VimspectorStepOver` | Step Over +| `F11` | `VimspectorStepInto` | Step Into +| `F12` | `VimspectorStepOut` | Step out of current function scope In addition, I recommend adding a mapping to `VimspectorBalloonEval`, in normal and visual modes, for example: @@ -746,6 +751,13 @@ nmap di VimspectorBalloonEval xmap di VimspectorBalloonEval ``` +You may also wish to add mappings for up/down the stack, for example: + +```viml +nmap VimspectorUpFrame +nmap VimspectorDownFrame +``` + # Usage and API This section defines detailed usage instructions, organised by feature. For most From 6f88b400e1f0771d9f684ae8a2c495989f196e02 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 18 Mar 2021 13:51:32 +0000 Subject: [PATCH 552/609] Only disable mappings in the keyboard-version of the popup --- autoload/vimspector/internal/balloon.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 32d17ab..f7ed9d7 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -69,7 +69,6 @@ function! vimspector#internal#balloon#CreateTooltip( is_hover, ... ) abort \ 'resize': 1, \ 'close': 'button', \ 'callback': 'vimspector#internal#balloon#CloseCallback', - \ 'mapping': 0 \ } " When ambiwidth is single, use prettier characters for the border. This @@ -86,6 +85,7 @@ function! vimspector#internal#balloon#CreateTooltip( is_hover, ... ) abort let config[ 'filter' ] = 'vimspector#internal#balloon#CursorFilter' let config[ 'moved' ] = 'any' let config[ 'cursorline' ] = 1 + let config[ 'mapping' ] = 0 let s:popup_win_id = popup_atcursor( body, config ) endif From b92a89f0d49611e9476d8825c7d65c700505a387 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 26 Feb 2021 19:21:40 +0000 Subject: [PATCH 553/609] Add a way to have adapter specific message handlers --- python3/vimspector/custom/java.py | 34 +++++++++++++++++++ .../vimspector/debug_adapter_connection.py | 32 ++++++++--------- python3/vimspector/debug_session.py | 16 ++++++++- python3/vimspector/gadgets.py | 3 +- support/test/java/test_project/pom.xml | 4 +-- 5 files changed, 67 insertions(+), 22 deletions(-) create mode 100644 python3/vimspector/custom/java.py diff --git a/python3/vimspector/custom/java.py b/python3/vimspector/custom/java.py new file mode 100644 index 0000000..ebfec35 --- /dev/null +++ b/python3/vimspector/custom/java.py @@ -0,0 +1,34 @@ +# vimspector - A multi-language debugging system for Vim +# Copyright 2021 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. + +from vimspector.debug_session import DebugSession +from vimspector import utils + + +class JavaDebugAdapter( object ): + def __init__( self, debug_session: DebugSession ): + self.debug_session = debug_session + + def OnEvent_hotcodereplace( self, message ): + # Hack for java debug server hot-code-replace + if utils.Call( 'confirm', + 'Code has changed, hot reload?', + '&Yes,&No', + 1, + 'Question' ) == 1: + self.debug_session._connection.DoRequest( None, { + 'command': 'redefineClasses', + 'arguments': {}, + } ) diff --git a/python3/vimspector/debug_adapter_connection.py b/python3/vimspector/debug_adapter_connection.py index 283c40c..df2ef13 100644 --- a/python3/vimspector/debug_adapter_connection.py +++ b/python3/vimspector/debug_adapter_connection.py @@ -29,14 +29,14 @@ class PendingRequest( object ): class DebugAdapterConnection( object ): - def __init__( self, handler, send_func ): + def __init__( self, handlers, send_func ): self._logger = logging.getLogger( __name__ ) utils.SetUpLogging( self._logger ) self._Write = send_func self._SetState( 'READ_HEADER' ) self._buffer = bytes() - self._handler = handler + self._handlers = handlers self._next_message_id = 0 self._outstanding_requests = {} @@ -124,7 +124,7 @@ class DebugAdapterConnection( object ): def Reset( self ): self._Write = None - self._handler = None + self._handlers = None while self._outstanding_requests: _, request = self._outstanding_requests.popitem() @@ -237,7 +237,7 @@ class DebugAdapterConnection( object ): def _OnMessageReceived( self, message ): - if not self._handler: + if not self._handlers: return if message[ 'type' ] == 'response': @@ -270,25 +270,21 @@ class DebugAdapterConnection( object ): self._logger.error( 'Request failed: {0}'.format( reason ) ) if request.failure_handler: request.failure_handler( reason, message ) - elif 'OnFailure' in dir( self._handler ): - self._handler.OnFailure( reason, request.msg, message ) else: - utils.UserMessage( 'Request failed: {0}'.format( reason ) ) + for h in self._handlers: + if 'OnFailure' in dir( h ): + h.OnFailure( reason, request.msg, message ) + elif message[ 'type' ] == 'event': method = 'OnEvent_' + message[ 'event' ] - if method in dir( self._handler ): - getattr( self._handler, method )( message ) - else: - utils.UserMessage( 'Unhandled event: {0}'.format( message[ 'event' ] ), - persist = True ) + for h in self._handlers: + if method in dir( h ): + getattr( h, method )( message ) elif message[ 'type' ] == 'request': method = 'OnRequest_' + message[ 'command' ] - if method in dir( self._handler ): - getattr( self._handler, method )( message ) - else: - utils.UserMessage( - 'Unhandled request: {0}'.format( message[ 'command' ] ), - persist = True ) + for h in self._handlers: + if method in dir( h ): + getattr( h, method )( message ) def _KillTimer( request ): diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 4d8c816..31b4342 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -21,6 +21,7 @@ import shlex import subprocess import functools import vim +import importlib from vimspector import ( breakpoints, code, @@ -888,8 +889,21 @@ class DebugSession( object ): self._splash_screen, "Unable to start adapter" ) else: + if 'custom_handler' in self._adapter: + spec = self._adapter[ 'custom_handler' ] + if isinstance( spec, dict ): + module = spec[ 'module' ] + cls = spec[ 'class' ] + else: + module, cls = spec.rsplit( '.', 1 ) + + CustomHandler = getattr( importlib.import_module( module ), cls ) + handlers = [ CustomHandler( self ), self ] + else: + handlers = [ self ] + self._connection = debug_adapter_connection.DebugAdapterConnection( - self, + handlers, lambda msg: utils.Call( "vimspector#internal#{}#Send".format( self._connection_type ), msg ) ) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index d1b6872..5c61074 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -159,7 +159,8 @@ GADGETS = { "port": "${DAPPort}", "configuration": { "cwd": "${workspaceRoot}" - } + }, + 'custom_handler': 'vimspector.custom.java.JavaDebugAdapter' } }, }, diff --git a/support/test/java/test_project/pom.xml b/support/test/java/test_project/pom.xml index 890e7e8..e6dc4d3 100644 --- a/support/test/java/test_project/pom.xml +++ b/support/test/java/test_project/pom.xml @@ -4,7 +4,7 @@ TestApplication 1 - 8 - 8 + 11 + 11 From afb912dd089a6cfbbadfad2a55531ed00226889a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 2 Mar 2021 10:48:55 +0000 Subject: [PATCH 554/609] Print hotcodereplace messages --- python3/vimspector/custom/java.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/python3/vimspector/custom/java.py b/python3/vimspector/custom/java.py index ebfec35..0dfa89c 100644 --- a/python3/vimspector/custom/java.py +++ b/python3/vimspector/custom/java.py @@ -23,12 +23,18 @@ class JavaDebugAdapter( object ): def OnEvent_hotcodereplace( self, message ): # Hack for java debug server hot-code-replace - if utils.Call( 'confirm', - 'Code has changed, hot reload?', - '&Yes,&No', - 1, - 'Question' ) == 1: - self.debug_session._connection.DoRequest( None, { - 'command': 'redefineClasses', - 'arguments': {}, - } ) + body = message.get( 'body' ) or {} + + if body.get( 'type' ) != 'hotcodereplace': + return + + if body.get( 'changeType' ) == 'BUILD_COMPLETE': + if utils.AskForInput( 'Code has changed, hot reload? [Y/N] ', + 'Y' ).upper()[ 0 ] == 'Y': + self.debug_session._connection.DoRequest( None, { + 'command': 'redefineClasses', + 'arguments': {}, + } ) + elif body.get( 'message' ): + utils.UserMessage( 'Hot code replace: ' + body[ 'message' ] ) + From 63fd3165fb48fa0c5c8dd916aa3c3ae54878d304 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 4 Mar 2021 21:44:54 +0000 Subject: [PATCH 555/609] Use popup for confirmations (note these have to be async) --- autoload/vimspector/internal/neopopup.vim | 22 ++++++++ autoload/vimspector/internal/popup.vim | 31 +++++++++++ python3/vimspector/custom/java.py | 17 +++--- python3/vimspector/debug_session.py | 63 ++++++++++++++--------- python3/vimspector/utils.py | 29 ++++++++++- 5 files changed, 129 insertions(+), 33 deletions(-) diff --git a/autoload/vimspector/internal/neopopup.vim b/autoload/vimspector/internal/neopopup.vim index fe5fe05..cc25020 100644 --- a/autoload/vimspector/internal/neopopup.vim +++ b/autoload/vimspector/internal/neopopup.vim @@ -80,6 +80,28 @@ function! vimspector#internal#neopopup#HideSplash( id ) abort unlet s:db[ a:id ] endfunction +function! vimspector#internal#neopopup#Confirm( confirm_id, + \ text, + \ default_value ) abort + let result = confirm( a:text, '&Yes &No &Default', 3 ) + + " Map the results to what popup_menu_filter would return (ok s:ConfirmCallback + " in popup.vim) + if result == 2 + " No is represented as 0 + let result = 0 + elseif result == 0 + " User pressed ESC/ctrl-c + let result = -1 + elseif result == 3 + " Default + let result = a:default_value + endif + + py3 __import__( 'vimspector', fromlist = [ 'utils' ] ).utils.ConfirmCallback( + \ int( vim.eval( 'a:confirm_id' ) ), + \ int( vim.eval( 'result' ) ) ) +endfunction " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/autoload/vimspector/internal/popup.vim b/autoload/vimspector/internal/popup.vim index fc8820b..e8658ab 100644 --- a/autoload/vimspector/internal/popup.vim +++ b/autoload/vimspector/internal/popup.vim @@ -32,6 +32,37 @@ function! vimspector#internal#popup#HideSplash( id ) abort call popup_hide( a:id ) endfunction +function! s:YesNoDefaultFilter( default_value, id, key ) abort + if a:key ==# "\" || a:key ==# 'D' || a:key ==# 'd' + call popup_close( a:id, a:default_value ) + endif + + return popup_filter_yesno( a:id, a:key ) +endfunction + +function! s:ConfirmCallback( confirm_id, id, result ) abort + py3 __import__( 'vimspector', fromlist = [ 'utils' ] ).utils.ConfirmCallback( + \ int( vim.eval( 'a:confirm_id' ) ), + \ int( vim.eval( 'a:result' ) ) ) +endfunction + +function! vimspector#internal#popup#Confirm( + \ confirm_id, + \ text, + \ default_value ) abort + let text = a:text + if type( a:text ) != v:t_list + let text = [ a:text ] + endif + + call extend( text, [ '', '(Y)es (N)o (D)efault' ] ) + + return popup_dialog( text, { + \ 'callback': function( 's:ConfirmCallback', [ a:confirm_id ] ), + \ 'filter': function( 's:YesNoDefaultFilter', [ a:default_value ] ) + \ } ) +endfunction + " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/python3/vimspector/custom/java.py b/python3/vimspector/custom/java.py index 0dfa89c..83705c1 100644 --- a/python3/vimspector/custom/java.py +++ b/python3/vimspector/custom/java.py @@ -29,12 +29,15 @@ class JavaDebugAdapter( object ): return if body.get( 'changeType' ) == 'BUILD_COMPLETE': - if utils.AskForInput( 'Code has changed, hot reload? [Y/N] ', - 'Y' ).upper()[ 0 ] == 'Y': - self.debug_session._connection.DoRequest( None, { - 'command': 'redefineClasses', - 'arguments': {}, - } ) + def handler( result ): + if result == 1: + self.debug_session._connection.DoRequest( None, { + 'command': 'redefineClasses', + 'arguments': {}, + } ) + + utils.Confirm( self.debug_session._api_prefix, + 'Code has changed, hot reload?', + handler ) elif body.get( 'message' ): utils.UserMessage( 'Hot code replace: ' + body[ 'message' ] ) - diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 31b4342..ae16706 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -911,39 +911,52 @@ class DebugSession( object ): self._logger.info( 'Debug Adapter Started' ) def _StopDebugAdapter( self, interactive = False, callback = None ): - self._splash_screen = utils.DisplaySplash( - self._api_prefix, - self._splash_screen, - "Shutting down debug adapter..." ) + def disconnect( arguments = {} ): + self._splash_screen = utils.DisplaySplash( + self._api_prefix, + self._splash_screen, + "Shutting down debug adapter..." ) - def handler( *args ): - self._splash_screen = utils.HideSplash( self._api_prefix, - self._splash_screen ) + def handler( *args ): + self._splash_screen = utils.HideSplash( self._api_prefix, + self._splash_screen ) - if callback: - self._logger.debug( "Setting server exit handler before disconnect" ) - assert not self._run_on_server_exit - self._run_on_server_exit = callback + if callback: + self._logger.debug( "Setting server exit handler before disconnect" ) + assert not self._run_on_server_exit + self._run_on_server_exit = callback - vim.eval( 'vimspector#internal#{}#StopDebugSession()'.format( - self._connection_type ) ) + vim.eval( 'vimspector#internal#{}#StopDebugSession()'.format( + self._connection_type ) ) - arguments = {} - if ( interactive and - self._server_capabilities.get( 'supportTerminateDebuggee' ) ): - if self._stackTraceView.AnyThreadsRunning(): - choice = utils.AskForInput( "Terminate debuggee [Y/N/default]? ", "" ) - if choice == "Y" or choice == "y": + self._connection.DoRequest( handler, { + 'command': 'disconnect', + 'arguments': {}, + }, failure_handler = handler, timeout = 5000 ) + + if not interactive: + disconnect() + elif not self._server_capabilities.get( 'supportTerminateDebuggee' ): + disconnect() + elif not self._stackTraceView.AnyThreadsRunning(): + disconnect() + else: + def handle_choice( choice ): + arguments = {} + if choice == 1: arguments[ 'terminateDebuggee' ] = True - elif choice == "N" or choice == 'n': + elif choice == 0: arguments[ 'terminateDebuggee' ] = False + elif choice == -1: + # Abort + return - self._connection.DoRequest( handler, { - 'command': 'disconnect', - 'arguments': arguments, - }, failure_handler = handler, timeout = 5000 ) + disconnect( arguments ) - # TODO: Use the 'tarminate' request if supportsTerminateRequest set + utils.Confirm( self._api_prefix, + "Terminate debuggee?", + handle_choice, + default_value = 3 ) def _PrepareAttach( self, adapter_config, launch_config ): diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index d8dd345..c65a184 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -375,6 +375,31 @@ def AskForInput( prompt, default_value = None, completion = None ): return None +CONFIRM = {} +CONFIRM_ID = 0 + +def ConfirmCallback( confirm_id, result ): + try: + handler = CONFIRM.pop( confirm_id ) + except KeyError: + UserMessage( f"Internal error: unexpected callback id { confirm_id }", + persist = True, + error = True ) + return + + handler( result ) + + +def Confirm( api_prefix, prompt, handler, default_value = 1 ): + global CONFIRM_ID + CONFIRM_ID += 1 + CONFIRM[ CONFIRM_ID ] = handler + Call( f'vimspector#internal#{ api_prefix }popup#Confirm', + CONFIRM_ID, + prompt, + default_value ) + + def AppendToBuffer( buf, line_or_lines, modified=False ): line = 1 try: @@ -403,8 +428,10 @@ def AppendToBuffer( buf, line_or_lines, modified=False ): -def ClearBuffer( buf ): +def ClearBuffer( buf, modified = False ): buf[ : ] = None + if not modified: + buf.options[ 'modified' ] = False def SetBufferContents( buf, lines, modified=False ): From 85bb8594ab916358a5605c259f89b036a119d6b9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 5 Mar 2021 21:09:39 +0000 Subject: [PATCH 556/609] Allow forcing selection from the menu with F5 --- autoload/vimspector.vim | 4 ++-- plugin/vimspector.vim | 3 +++ python3/vimspector/debug_session.py | 7 ++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index a547e5c..a0b6c42 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -41,11 +41,11 @@ function! s:Enabled() abort return s:enabled endfunction -function! vimspector#Launch() abort +function! vimspector#Launch( ... ) abort if !s:Enabled() return endif - py3 _vimspector_session.Start() + py3 _vimspector_session.Start( *vim.eval( 'a:000' ) ) endfunction function! vimspector#LaunchWithSettings( settings ) abort diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 37509a7..75e2baa 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -35,6 +35,8 @@ let s:mappings = get( g:, 'vimspector_enable_mappings', '' ) nnoremap VimspectorContinue \ :call vimspector#Continue() +nnoremap VimspectorLaunch + \ :call vimspector#Launch( v:true ) nnoremap VimspectorStop \ :call vimspector#Stop() nnoremap VimspectorRestart @@ -84,6 +86,7 @@ if s:mappings ==# 'VISUAL_STUDIO' nmap VimspectorStepOut elseif s:mappings ==# 'HUMAN' nmap VimspectorContinue + nmap VimspectorLaunch nmap VimspectorStop nmap VimspectorRestart nmap VimspectorPause diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index ae16706..12e4563 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -100,7 +100,7 @@ class DebugSession( object ): return launch_config_file, configurations - def Start( self, launch_variables = None ): + def Start( self, force_choose=False, launch_variables = None ): # We mutate launch_variables, so don't mutate the default argument. # https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments if launch_variables is None: @@ -135,6 +135,11 @@ class DebugSession( object ): if 'configuration' in launch_variables: configuration_name = launch_variables.pop( 'configuration' ) + elif force_choose: + # Always display the menu + configuration_name = utils.SelectFromList( + 'Which launch configuration?', + sorted( configurations.keys() ) ) elif ( len( configurations ) == 1 and next( iter( configurations.values() ) ).get( "autoselect", True ) ): configuration_name = next( iter( configurations.keys() ) ) From 6b74e584d53b284a35c72f7ab5e8f1f2095fe7ea Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 6 Mar 2021 20:24:24 +0000 Subject: [PATCH 557/609] Create a messagebox-like interface in vim --- autoload/vimspector/internal/balloon.vim | 6 +- autoload/vimspector/internal/neopopup.vim | 3 + autoload/vimspector/internal/popup.vim | 88 ++++++++++++++++++++--- python3/vimspector/utils.py | 6 +- 4 files changed, 87 insertions(+), 16 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index f7ed9d7..b607e51 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -71,11 +71,7 @@ function! vimspector#internal#balloon#CreateTooltip( is_hover, ... ) abort \ 'callback': 'vimspector#internal#balloon#CloseCallback', \ } - " When ambiwidth is single, use prettier characters for the border. This - " would look silly when ambiwidth is double. - if &ambiwidth ==# 'single' && &encoding ==? 'utf-8' - let config[ 'borderchars' ] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ] - endif + let config = vimspector#internal#popup#SetBorderChars( config ) if a:is_hover let config[ 'filter' ] = 'vimspector#internal#balloon#MouseFilter' diff --git a/autoload/vimspector/internal/neopopup.vim b/autoload/vimspector/internal/neopopup.vim index cc25020..189c78c 100644 --- a/autoload/vimspector/internal/neopopup.vim +++ b/autoload/vimspector/internal/neopopup.vim @@ -83,6 +83,9 @@ endfunction function! vimspector#internal#neopopup#Confirm( confirm_id, \ text, \ default_value ) abort + + " Neovim doesn't have an equivalent of popup_dialog, and it's way too much + " effort to write one, so we just use confirm(). let result = confirm( a:text, '&Yes &No &Default', 3 ) " Map the results to what popup_menu_filter would return (ok s:ConfirmCallback diff --git a/autoload/vimspector/internal/popup.vim b/autoload/vimspector/internal/popup.vim index e8658ab..5978987 100644 --- a/autoload/vimspector/internal/popup.vim +++ b/autoload/vimspector/internal/popup.vim @@ -32,9 +32,32 @@ function! vimspector#internal#popup#HideSplash( id ) abort call popup_hide( a:id ) endfunction +let s:current_selection = 0 +let s:selections = [] +let s:text = [] + +function! s:UpdatePopup( id ) + let buf = copy( s:text ) + call extend( buf, s:DrawButtons() ) + call popup_settext( a:id, buf ) +endfunction + function! s:YesNoDefaultFilter( default_value, id, key ) abort - if a:key ==# "\" || a:key ==# 'D' || a:key ==# 'd' + if a:key ==# "\" + call popup_close( a:id, s:current_selection + 1 ) + return 1 + elseif a:key ==# 'D' || a:key ==# 'd' call popup_close( a:id, a:default_value ) + return 1 + elseif index( [ "\", "\" ], a:key ) >= 0 + let s:current_selection = ( s:current_selection + 1 ) % len( s:selections ) + call s:UpdatePopup( a:id ) + return 1 + elseif index( [ "\", "\" ], a:key ) >= 0 + let s:current_selection = s:current_selection == 0 + \ ? len( s:selections ) - 1: s:current_selection - 1 + call s:UpdatePopup( a:id ) + return 1 endif return popup_filter_yesno( a:id, a:key ) @@ -46,23 +69,68 @@ function! s:ConfirmCallback( confirm_id, id, result ) abort \ int( vim.eval( 'a:result' ) ) ) endfunction +function! s:SelectionPosition( idx ) abort + return a:idx == 0 ? 0 : len( join( s:selections[ : a:idx - 1 ], ' ' ) ) + 1 +endfunction + +function! s:DrawButtons() abort + return [ { + \ 'text': join( s:selections, ' ' ), + \ 'props': [ + \ { + \ 'col': s:SelectionPosition( s:current_selection ) + 1, + \ 'length': len( s:selections[ s:current_selection ] ), + \ 'type': 'VimspectorSelectedItem' + \ }, + \ ] + \ } ] +endfunction + function! vimspector#internal#popup#Confirm( \ confirm_id, \ text, + \ options, \ default_value ) abort - let text = a:text - if type( a:text ) != v:t_list - let text = [ a:text ] + + silent! call prop_type_add( 'VimspectorSelectedItem', { + \ 'highlight': 'PMenuSel' + \ } ) + + let lines = split( a:text, "\n", v:true ) + let buf = [] + for line in lines + call add( buf, { 'text': line, 'props': [] } ) + endfor + + call add( buf, { 'text': '', 'props': [] } ) + + let s:selections = a:options + let s:current_selection = ( a:default_value - 1 ) + + let s:text = copy( buf ) + call extend( buf, s:DrawButtons() ) + + let config = { + \ 'callback': function( 's:ConfirmCallback', [ a:confirm_id ] ), + \ 'filter': function( 's:YesNoDefaultFilter', [ a:default_value ] ), + \ 'mapping': v:false, + \ } + let config = vimspector#internal#popup#SetBorderChars( config ) + + return popup_dialog( buf, config ) +endfunction + +function! vimspector#internal#popup#SetBorderChars( config ) + " When ambiwidth is single, use prettier characters for the border. This + " would look silly when ambiwidth is double. + if &ambiwidth ==# 'single' && &encoding ==? 'utf-8' + let a:config[ 'borderchars' ] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ] endif - call extend( text, [ '', '(Y)es (N)o (D)efault' ] ) - - return popup_dialog( text, { - \ 'callback': function( 's:ConfirmCallback', [ a:confirm_id ] ), - \ 'filter': function( 's:YesNoDefaultFilter', [ a:default_value ] ) - \ } ) + return a:config endfunction + " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index c65a184..ca0b80a 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -390,13 +390,17 @@ def ConfirmCallback( confirm_id, result ): handler( result ) -def Confirm( api_prefix, prompt, handler, default_value = 1 ): +def Confirm( api_prefix, prompt, handler, default_value = 3, options = None ): global CONFIRM_ID + if not options: + options = [ '(Y)es', '(N)o', '(D)efault' ] + CONFIRM_ID += 1 CONFIRM[ CONFIRM_ID ] = handler Call( f'vimspector#internal#{ api_prefix }popup#Confirm', CONFIRM_ID, prompt, + options, default_value ) From 154e727b9693144439b37735b9e4f0aa44d58c16 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 11 Mar 2021 22:38:01 +0000 Subject: [PATCH 558/609] Make confirm dialog take arbitrary keys Confirm now takes the list of options, list of keys to select them and the default value. Returned values are always a 1-based index into the list (like SelectFromList) or -1 to mean esc/ctrl-c. This uses a nice popup dialog in vim and a crappy input on neovim. --- autoload/vimspector/internal/neopopup.vim | 41 ++++++++++++++++++----- autoload/vimspector/internal/popup.vim | 22 ++++++++---- python3/vimspector/custom/java.py | 16 ++++++--- python3/vimspector/debug_session.py | 10 ++++-- python3/vimspector/settings.py | 5 ++- python3/vimspector/utils.py | 16 ++++++--- 6 files changed, 82 insertions(+), 28 deletions(-) diff --git a/autoload/vimspector/internal/neopopup.vim b/autoload/vimspector/internal/neopopup.vim index 189c78c..9f35914 100644 --- a/autoload/vimspector/internal/neopopup.vim +++ b/autoload/vimspector/internal/neopopup.vim @@ -82,29 +82,52 @@ endfunction function! vimspector#internal#neopopup#Confirm( confirm_id, \ text, - \ default_value ) abort + \ options, + \ default_value, + \ keys ) abort " Neovim doesn't have an equivalent of popup_dialog, and it's way too much " effort to write one, so we just use confirm(). - let result = confirm( a:text, '&Yes &No &Default', 3 ) + let prompt = a:text + for opt in a:options + let prompt .= ' ' . opt + endfor + let prompt .= ': ' + + " Annoyingly we can't use confirm() here because for some reason it doesn't + " render properly in a channel callback. So we use input() and mimic dialog + " behaviour. + try + let result = input( prompt, a:keys[ a:default_value - 1 ] ) + catch /.*/ + let result = -1 + endtry " Map the results to what popup_menu_filter would return (ok s:ConfirmCallback " in popup.vim) - if result == 2 - " No is represented as 0 - let result = 0 - elseif result == 0 + if result == '' " User pressed ESC/ctrl-c let result = -1 - elseif result == 3 - " Default - let result = a:default_value + else + let index = 1 + for k in a:keys + if k ==? result + let result = index + break + endif + let index += 2 + endfor + + if index >= len( a:keys ) + let result = -1 + endif endif py3 __import__( 'vimspector', fromlist = [ 'utils' ] ).utils.ConfirmCallback( \ int( vim.eval( 'a:confirm_id' ) ), \ int( vim.eval( 'result' ) ) ) endfunction + " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/autoload/vimspector/internal/popup.vim b/autoload/vimspector/internal/popup.vim index 5978987..db2c2fd 100644 --- a/autoload/vimspector/internal/popup.vim +++ b/autoload/vimspector/internal/popup.vim @@ -42,13 +42,10 @@ function! s:UpdatePopup( id ) call popup_settext( a:id, buf ) endfunction -function! s:YesNoDefaultFilter( default_value, id, key ) abort +function! s:ConfirmKeyFilter( keys, id, key ) abort if a:key ==# "\" call popup_close( a:id, s:current_selection + 1 ) return 1 - elseif a:key ==# 'D' || a:key ==# 'd' - call popup_close( a:id, a:default_value ) - return 1 elseif index( [ "\", "\" ], a:key ) >= 0 let s:current_selection = ( s:current_selection + 1 ) % len( s:selections ) call s:UpdatePopup( a:id ) @@ -58,9 +55,19 @@ function! s:YesNoDefaultFilter( default_value, id, key ) abort \ ? len( s:selections ) - 1: s:current_selection - 1 call s:UpdatePopup( a:id ) return 1 + elseif a:key ==# "\" || a:key ==# "\" + call popup_close( a:id, -1 ) + return 1 endif - return popup_filter_yesno( a:id, a:key ) + let index = 1 + for key in a:keys + if a:key ==? key + call popup_close( a:id, index ) + return 1 + endif + let index += 1 + endfor endfunction function! s:ConfirmCallback( confirm_id, id, result ) abort @@ -90,7 +97,8 @@ function! vimspector#internal#popup#Confirm( \ confirm_id, \ text, \ options, - \ default_value ) abort + \ default_value, + \ keys ) abort silent! call prop_type_add( 'VimspectorSelectedItem', { \ 'highlight': 'PMenuSel' @@ -112,7 +120,7 @@ function! vimspector#internal#popup#Confirm( let config = { \ 'callback': function( 's:ConfirmCallback', [ a:confirm_id ] ), - \ 'filter': function( 's:YesNoDefaultFilter', [ a:default_value ] ), + \ 'filter': function( 's:ConfirmKeyFilter', [ a:keys ] ), \ 'mapping': v:false, \ } let config = vimspector#internal#popup#SetBorderChars( config ) diff --git a/python3/vimspector/custom/java.py b/python3/vimspector/custom/java.py index 83705c1..c2a2264 100644 --- a/python3/vimspector/custom/java.py +++ b/python3/vimspector/custom/java.py @@ -14,7 +14,7 @@ # limitations under the License. from vimspector.debug_session import DebugSession -from vimspector import utils +from vimspector import utils, settings class JavaDebugAdapter( object ): @@ -36,8 +36,16 @@ class JavaDebugAdapter( object ): 'arguments': {}, } ) - utils.Confirm( self.debug_session._api_prefix, - 'Code has changed, hot reload?', - handler ) + mode = settings.Get( 'java_hotcodereplace_mode' ) + if mode == 'ask': + utils.Confirm( self.debug_session._api_prefix, + 'Code has changed, hot reload?', + handler, + default_value = 1 ) + elif mode == 'always': + self.debug_session._connection.DoRequest( None, { + 'command': 'redefineClasses', + 'arguments': {}, + } ) elif body.get( 'message' ): utils.UserMessage( 'Hot code replace: ' + body[ 'message' ] ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 12e4563..255e072 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -949,10 +949,12 @@ class DebugSession( object ): def handle_choice( choice ): arguments = {} if choice == 1: + # yes arguments[ 'terminateDebuggee' ] = True - elif choice == 0: + elif choice == 2: + # no arguments[ 'terminateDebuggee' ] = False - elif choice == -1: + else: # Abort return @@ -961,7 +963,9 @@ class DebugSession( object ): utils.Confirm( self._api_prefix, "Terminate debuggee?", handle_choice, - default_value = 3 ) + default_value = 3, + options = [ '(Y)es', '(N)o', '(D)efault' ], + keys = [ 'y', 'n', 'd' ] ) def _PrepareAttach( self, adapter_config, launch_config ): diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index e9e76ea..a060543 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -59,7 +59,10 @@ DEFAULTS = { 'expand_or_jump': [ '', '<2-LeftMouse>' ], 'focus_thread': [ '' ], } - } + }, + + # Custom + 'java_hotcodereplace_mode': 'ask', } diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index ca0b80a..53ebd3b 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -390,18 +390,26 @@ def ConfirmCallback( confirm_id, result ): handler( result ) -def Confirm( api_prefix, prompt, handler, default_value = 3, options = None ): - global CONFIRM_ID +def Confirm( api_prefix, + prompt, + handler, + default_value = 2, + options: list = None, + keys: list = None ): if not options: - options = [ '(Y)es', '(N)o', '(D)efault' ] + options = [ '(Y)es', '(N)o' ] + if not keys: + keys = [ 'y', 'n' ] + global CONFIRM_ID CONFIRM_ID += 1 CONFIRM[ CONFIRM_ID ] = handler Call( f'vimspector#internal#{ api_prefix }popup#Confirm', CONFIRM_ID, prompt, options, - default_value ) + default_value, + keys ) def AppendToBuffer( buf, line_or_lines, modified=False ): From efc5c7686638efc4c047bf0f58ae58fbff0bd393 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 11 Mar 2021 22:45:59 +0000 Subject: [PATCH 559/609] Fix lints --- autoload/vimspector/internal/popup.vim | 5 +++-- python3/vimspector/utils.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/popup.vim b/autoload/vimspector/internal/popup.vim index db2c2fd..083fdf5 100644 --- a/autoload/vimspector/internal/popup.vim +++ b/autoload/vimspector/internal/popup.vim @@ -12,6 +12,7 @@ " 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. +scriptencoding utf-8 " Boilerplate {{{ @@ -36,7 +37,7 @@ let s:current_selection = 0 let s:selections = [] let s:text = [] -function! s:UpdatePopup( id ) +function! s:UpdatePopup( id ) abort let buf = copy( s:text ) call extend( buf, s:DrawButtons() ) call popup_settext( a:id, buf ) @@ -128,7 +129,7 @@ function! vimspector#internal#popup#Confirm( return popup_dialog( buf, config ) endfunction -function! vimspector#internal#popup#SetBorderChars( config ) +function! vimspector#internal#popup#SetBorderChars( config ) abort " When ambiwidth is single, use prettier characters for the border. This " would look silly when ambiwidth is double. if &ambiwidth ==# 'single' && &encoding ==? 'utf-8' diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 53ebd3b..5f836fc 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -378,6 +378,7 @@ def AskForInput( prompt, default_value = None, completion = None ): CONFIRM = {} CONFIRM_ID = 0 + def ConfirmCallback( confirm_id, result ): try: handler = CONFIRM.pop( confirm_id ) From c05335c79987dc3ab19f30c477ad1b435e1ab9bf Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 16 Mar 2021 19:15:56 +0000 Subject: [PATCH 560/609] Make default option work for terminate debugee --- python3/vimspector/debug_session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 255e072..156cbfd 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -954,9 +954,10 @@ class DebugSession( object ): elif choice == 2: # no arguments[ 'terminateDebuggee' ] = False - else: + elif choice <= 0: # Abort return + # Else, use server default disconnect( arguments ) From a39017dd06869bdb2b38b30ec21f41cb684a8e8b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 19 Mar 2021 18:23:48 +0000 Subject: [PATCH 561/609] Fix actually sending terminateDebugee, and fix neovim 1-based index reporting --- autoload/vimspector/internal/neopopup.vim | 10 ++++++---- python3/vimspector/debug_session.py | 9 +++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/autoload/vimspector/internal/neopopup.vim b/autoload/vimspector/internal/neopopup.vim index 9f35914..eb5a0dc 100644 --- a/autoload/vimspector/internal/neopopup.vim +++ b/autoload/vimspector/internal/neopopup.vim @@ -103,8 +103,10 @@ function! vimspector#internal#neopopup#Confirm( confirm_id, let result = -1 endtry - " Map the results to what popup_menu_filter would return (ok s:ConfirmCallback - " in popup.vim) + " Map the results to what the vim popup stuff would return (s:ConfirmCallback + " in popup.vim), i.e.: + " - 1-based index of selected item, or + " - -1 or 0 for cancellation if result == '' " User pressed ESC/ctrl-c let result = -1 @@ -115,10 +117,10 @@ function! vimspector#internal#neopopup#Confirm( confirm_id, let result = index break endif - let index += 2 + let index += 1 endfor - if index >= len( a:keys ) + if index > len( a:keys ) let result = -1 endif endif diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 156cbfd..2f132f9 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -916,7 +916,9 @@ class DebugSession( object ): self._logger.info( 'Debug Adapter Started' ) def _StopDebugAdapter( self, interactive = False, callback = None ): - def disconnect( arguments = {} ): + arguments = {} + + def disconnect(): self._splash_screen = utils.DisplaySplash( self._api_prefix, self._splash_screen, @@ -936,7 +938,7 @@ class DebugSession( object ): self._connection.DoRequest( handler, { 'command': 'disconnect', - 'arguments': {}, + 'arguments': arguments, }, failure_handler = handler, timeout = 5000 ) if not interactive: @@ -947,7 +949,6 @@ class DebugSession( object ): disconnect() else: def handle_choice( choice ): - arguments = {} if choice == 1: # yes arguments[ 'terminateDebuggee' ] = True @@ -959,7 +960,7 @@ class DebugSession( object ): return # Else, use server default - disconnect( arguments ) + disconnect() utils.Confirm( self._api_prefix, "Terminate debuggee?", From 1414f261a1700515eb67c429aa681cbd84eca4e5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 19 Mar 2021 22:59:27 +0000 Subject: [PATCH 562/609] Documentation for hot code replace --- README.md | 18 +++++++++++++++++- autoload/vimspector/internal/neopopup.vim | 8 ++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2152a9c..952d07f 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ For detailed explanatin of the `.vimspector.json` format, see the * [Console autocompletion](#console-autocompletion) * [Log View](#log-view) * [Closing debugger](#closing-debugger) + * [Terminate debuggee](#terminate-debuggee) * [Debug profile configuration](#debug-profile-configuration) * [C, C , Rust, etc.](#c-c-rust-etc) * [C Remote debugging](#c-remote-debugging) @@ -76,6 +77,7 @@ For detailed explanatin of the `.vimspector.json` format, see the * [Debug cli application](#debug-cli-application) * [JavaScript, TypeScript, etc.](#javascript-typescript-etc) * [Java](#java) + * [Hot code replace](#hot-code-replace) * [Usage with YouCompleteMe](#usage-with-youcompleteme) * [Other LSP clients](#other-lsp-clients) * [Lua](#lua) @@ -91,7 +93,7 @@ For detailed explanatin of the `.vimspector.json` format, see the * [Example](#example) * [FAQ](#faq) - + @@ -1591,6 +1593,20 @@ editor plugin to use Java. I recommend [YouCompleteMe][], which has full support for jdt.ls, and most importantly a trivial way to load the debug adapter and to use it with Vimspector. +### Hot code replace + +When using the [java debug server][java-debug-server], Vimspector supports the +hot code replace custom feature. By default, when the underlying class files +change, vimspector asks the user if they wish to reload these classes at +runtime. + +This behaviour can be customised: + +* `let g:ycm_java_hotcodereplace_mode = 'ask'` - the default, ask the user for + each reload. +* `let g:ycm_java_hotcodereplace_mode = 'always'` - don't ask, always reload +* `let g:ycm_java_hotcodereplace_mode = 'never'` - don't ask, never reload + ### Usage with YouCompleteMe * Set up [YCM for java][YcmJava]. diff --git a/autoload/vimspector/internal/neopopup.vim b/autoload/vimspector/internal/neopopup.vim index eb5a0dc..a734ca3 100644 --- a/autoload/vimspector/internal/neopopup.vim +++ b/autoload/vimspector/internal/neopopup.vim @@ -87,16 +87,16 @@ function! vimspector#internal#neopopup#Confirm( confirm_id, \ keys ) abort " Neovim doesn't have an equivalent of popup_dialog, and it's way too much - " effort to write one, so we just use confirm(). + " effort to write one, so we just use confirm()... + " Annoyingly we can't use confirm() here because for some reason it doesn't + " render properly in a channel callback. So we use input() and mimic dialog + " behaviour. let prompt = a:text for opt in a:options let prompt .= ' ' . opt endfor let prompt .= ': ' - " Annoyingly we can't use confirm() here because for some reason it doesn't - " render properly in a channel callback. So we use input() and mimic dialog - " behaviour. try let result = input( prompt, a:keys[ a:default_value - 1 ] ) catch /.*/ From 842d9fbc2d39718943b5cf768996a0dcf1b04ffe Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 19 Mar 2021 17:50:29 +0000 Subject: [PATCH 563/609] Config for pyright language server --- .ycm_extra_conf.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py index f2e32ae..6f42505 100644 --- a/.ycm_extra_conf.py +++ b/.ycm_extra_conf.py @@ -42,7 +42,17 @@ def Settings( **kwargs ): return { 'sys_path': [ p.join( PATH_TO_THIS_DIR, 'python3' ) - ] + ], + 'ls': { + 'python': { + 'analysis': { + 'extraPaths': [ + p.join( PATH_TO_THIS_DIR, 'python3' ), + ], + 'useLibraryCodeForTypes': True + } + } + } } if IgnoreExtraConf: From 35e5b3d56e193ee6129eabedcd48fd17b8293b2d Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 20 Mar 2021 15:55:22 +0000 Subject: [PATCH 564/609] Add vim doc based on README Use the following to recrate the doc: * clone https://github.com/ycm-core/vim-tools * create a venv and pip install the requierments * cd to vimspector dir * /path/to/vim-tools/html2vimdoc.py -f vimspector README.md > doc/vimspector.txt --- .gitignore | 1 + doc/vimspector.txt | 2394 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2395 insertions(+) create mode 100644 doc/vimspector.txt diff --git a/.gitignore b/.gitignore index 77f22a9..05dc8cd 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ support/test/csharp/*.exe* configurations/ venv/ test-base/ +tags diff --git a/doc/vimspector.txt b/doc/vimspector.txt new file mode 100644 index 0000000..8def87f --- /dev/null +++ b/doc/vimspector.txt @@ -0,0 +1,2394 @@ +*vimspector* vimspector - A multi language graphical debugger for Vim + +=============================================================================== +Contents ~ + + 1. Introduction |vimspector-introduction| + 2. Features and Usage |vimspector-features-usage| + 1. Supported debugging features |vimspector-supported-debugging-features| + 2. Supported languages |vimspector-supported-languages| + 3. Other languages |vimspector-other-languages| + 3. Installation |vimspector-installation| + 1. Quick Start |vimspector-quick-start| + 2. Dependencies |vimspector-dependencies| + 3. Neovim differences |vimspector-neovim-differences| + 4. Windows differences |vimspector-windows-differences| + 5. Trying it out |vimspector-trying-it-out| + 6. Cloning the plugin |vimspector-cloning-plugin| + 7. Install some gadgets |vimspector-install-gadgets| + 1. VimspectorInstall and VimspectorUpdate commands |vimspectorinstall-vimspectorupdate-commands| + 2. install_gadget.py |vimspector-install_gadget.py| + 8. Manual gadget installation |vimspector-manual-gadget-installation| + 1. The gadget directory |vimspector-gadget-directory| + 9. Upgrade |vimspector-upgrade| + 4. About |vimspector-about| + 1. Background |vimspector-background| + 2. Status |vimspector-status| + 1. Experimental |vimspector-experimental| + 3. Motivation |vimspector-motivation| + 4. License |vimspector-license| + 5. Sponsorship |vimspector-sponsorship| + 5. Mappings |vimspector-mappings| + 1. Visual Studio / VSCode |vimspector-visual-studio-vscode| + 2. Human Mode |vimspector-human-mode| + 6. Usage and API |vimspector-usage-api| + 1. Launch and attach by PID: |vimspector-launch-attach-by-pid| + 1. Launch with options |vimspector-launch-with-options| + 2. Debug configuration selection |vimspector-debug-configuration-selection| + 3. Get configurations |vimspector-get-configurations| + 2. Breakpoints |vimspector-breakpoints| + 1. Summary |vimspector-summary| + 2. Line breakpoints |vimspector-line-breakpoints| + 3. Conditional breakpoints |vimspector-conditional-breakpoints| + 4. Exception breakpoints |vimspector-exception-breakpoints| + 5. Clear breakpoints |vimspector-clear-breakpoints| + 6. Run to Cursor |vimspector-run-to-cursor| + 3. Stepping |vimspector-stepping| + 4. Variables and scopes |vimspector-variables-scopes| + 5. Variable or selection hover evaluation |vimspector-variable-or-selection-hover-evaluation| + 6. Watches |vimspector-watches| + 1. Watch autocompletion |vimspector-watch-autocompletion| + 7. Stack Traces |vimspector-stack-traces| + 8. Program Output |vimspector-program-output| + 1. Console |vimspector-console| + 2. Console autocompletion |vimspector-console-autocompletion| + 3. Log View |vimspector-log-view| + 9. Closing debugger |vimspector-closing-debugger| + 10. Terminate debuggee |vimspector-terminate-debuggee| + 7. Debug profile configuration |vimspector-debug-profile-configuration| + 1. C, C++, Rust, etc. |vimspector-c-c-rust-etc.| + 1. C++ Remote debugging |vimspector-c-remote-debugging| + 2. C++ Remote launch and attach |vimspector-c-remote-launch-attach| + 2. Rust |vimspector-rust| + 3. Python |vimspector-python| + 1. Python Remote Debugging |vimspector-python-remote-debugging| + 2. Python Remote launch and attach |vimspector-python-remote-launch-attach| + 3. Legacy: vscode-python |vimspector-legacy-vscode-python| + 4. TCL |vimspector-tcl| + 5. C♯ |vimspector-c| + 6. Go |vimspector-go| + 7. PHP |vimspector-php| + 1. Debug web application |vimspector-debug-web-application| + 2. Debug cli application |vimspector-debug-cli-application| + 8. JavaScript, TypeScript, etc. |vimspector-javascript-typescript-etc.| + 9. Java |vimspector-java| + 1. Usage with YouCompleteMe |vimspector-usage-with-youcompleteme| + 2. Other LSP clients |vimspector-other-lsp-clients| + 10. Lua |vimspector-lua| + 11. Other servers |vimspector-other-servers| + 8. Customisation |vimspector-customisation| + 1. Changing the default signs |vimspector-changing-default-signs| + 2. Sign priority |vimspector-sign-priority| + 3. Changing the default window sizes |vimspector-changing-default-window-sizes| + 4. Changing the terminal size |vimspector-changing-terminal-size| + 5. Custom mappings while debugging |vimspector-custom-mappings-while-debugging| + 6. Advanced UI customisation |vimspector-advanced-ui-customisation| + 7. Customising the WinBar |vimspector-customising-winbar| + 8. Example |vimspector-example| + 9. FAQ |vimspector-faq| + 10. References |vimspector-references| + +=============================================================================== + *vimspector-introduction* +Introduction ~ + +For a tutorial and usage overview, take a look at the Vimspector website [1]. + +For detailed explanatin of the '.vimspector.json' format, see the reference +guide [2]. + +Image: Build (see reference [3]) Image: Gitter [4] + +- Features and Usage + + - Supported debugging features + - Supported languages + - Other languages + +- Installation + + - Quick Start + - Dependencies + - Neovim differences + - Windows differences + - Trying it out + - Cloning the plugin + - Install some gadgets + - VimspectorInstall and VimspectorUpdate commands + - install_gadget.py + - Manual gadget installation + - The gadget directory + - Upgrade + +- About + + - Background + - Status + - Experimental + - Motivation + - License + - Sponsorship + +- Mappings + + - Visual Studio / VSCode + - Human Mode + +- Usage and API + + - Launch and attach by PID: + - Launch with options + - Debug configuration selection + - Get configurations + - Breakpoints + - Summary + - Line breakpoints + - Conditional breakpoints + - Exception breakpoints + - Clear breakpoints + - Run to Cursor + - Stepping + - Variables and scopes + - Variable or selection hover evaluation + - Watches + - Watch autocompletion + - Stack Traces + - Program Output + - Console + - Console autocompletion + - Log View + - Closing debugger + +- Debug profile configuration + + - C, C , Rust, etc. + - C Remote debugging + - C Remote launch and attach + - Rust + - Python + - Python Remote Debugging + - Python Remote launch and attach + - Legacy: vscode-python + - TCL + - C♯ + - Go + - PHP + - Debug web application + - Debug cli application + - JavaScript, TypeScript, etc. + - Java + - Usage with YouCompleteMe + - Other LSP clients + - Lua + - Other servers + +- Customisation + + - Changing the default signs + - Sign priority + - Changing the default window sizes + - Changing the terminal size + - Custom mappings while debugging + - Advanced UI customisation + - Customising the WinBar + - Example + +- FAQ + +=============================================================================== + *vimspector-features-usage* +Features and Usage ~ + +The plugin is a capable Vim graphical debugger for multiple languages. It's +mostly tested for c++, python and TCL, but in theory supports any language that +Visual Studio Code supports (but see caveats). + +The Vimspector website [1] has an overview of the UI, along with basic +instructions for configuration and setup. + +But for now, here's a (rather old) screenshot of Vimspector debugging Vim: + + Image: vimspector-vim-screenshot (see reference [6]) + +And a couple of brief demos: + + Image: asciicast [7] + + Image: asciicast [9] + +------------------------------------------------------------------------------- + *vimspector-supported-debugging-features* +Supported debugging features ~ + +- flexible configuration syntax that can be checked in to source control +- breakpoints (function, line and exception breakpoints) +- conditional breakpoints (function, line) +- step in/out/over/up, stop, restart +- run to cursor +- launch and attach +- remote launch, remote attach +- locals and globals display +- watch expressions with autocompletion +- variable inspection tooltip on hover +- set variable value in locals, watch and hover windows +- call stack display and navigation +- hierarchical variable value display popup (see + 'VimspectorBalloonEval') +- interactive debug console with autocompletion +- launch debuggee within Vim's embedded terminal +- logging/stdout display +- simple stable API for custom tooling (e.g. integrate with language server) + +------------------------------------------------------------------------------- + *vimspector-supported-languages* +Supported languages ~ + +The following table lists the languages that are "built-in" (along with their +runtime dependencies). They are categorised by their level of support: + +- 'Tested' : Fully supported, Vimspector regression tests cover them +- 'Supported' : Fully supported, frequently used and manually tested +- 'Experimental': Working, but not frequently used and rarely tested +- 'Legacy': No longer supported, please migrate your config + +======================================================================================================================================================== +| _Language_ | _Status_ | _Switch (for 'install_gadget.py')_ | _Adapter (for ':VimspectorInstall')_ | _Dependencies_ | +======================================================================================================================================================== +| C, C++, Rust etc. | Tested | '--all' or '--enable-c' (or cpp) | vscode-cpptools | mono-core | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| Rust, C, C++, etc. | Supported | '--force-enable-rust' | CodeLLDB | Python 3 | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| Python | Tested | '--all' or '--enable-python' | debugpy | Python 2.7 or Python 3 | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| Go | Tested | '--enable-go' | vscode-go | Go, Delve [11] | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| TCL | Supported | '--all' or '--enable-tcl' | tclpro | TCL 8.5 | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| Bourne Shell | Supported | '--all' or '--enable-bash' | vscode-bash-debug | Bash v?? | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| Lua | Supported | '--all' or '--enable-lua' | local-lua-debugger-vscode | Node >=12.13.0, Npm, Lua interpreter | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| Node.js | Supported | '--force-enable-node' | vscode-node-debug2 | 6 < Node < 12, Npm | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| Javascript | Supported | '--force-enable-chrome' | debugger-for-chrome | Chrome | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| Java | Supported | '--force-enable-java' | vscode-java-debug | Compatible LSP plugin (see later) | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| C# (dotnet core) | Experimental | '--force-enable-csharp' | netcoredbg | DotNet core | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| C# (mono) | Experimental | '--force-enable-csharp' | vscode-mono-debug | Mono | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| F#, VB, etc. | Experimental | '--force-enable-fsharp' (or vbnet) | netcoredbg | DotNet core | +-------------------------------------------------------------------------------------------------------------------------------------------------------- +| Python.legacy | Legacy | '--force-enable-python.legacy' | vscode-python | Node 10, Python 2.7 or Python 3 | +-------------------------------------------------------------------------------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- + *vimspector-other-languages* +Other languages ~ + +Vimspector should work for any debug adapter that works in Visual Studio Code. + +To use Vimspector with a language that's not "built-in", see this wiki page +[12]. + +=============================================================================== + *vimspector-installation* +Installation ~ + +------------------------------------------------------------------------------- + *vimspector-quick-start* +Quick Start ~ + +There are 2 installation methods: + +- Using a release tarball and vim packages +- Using a clone of the repo (e.g. package manager) + +Release tarballs come with debug adapters for the default languages +pre-packaged. To use a release tarball: + +1. Check the dependencies +2. Untar the release tarball for your OS into '$HOME/.vim/pack': +> + $ mkdir -p $HOME/.vim/pack + $ curl -L | tar -C $HOME/.vim/pack zxvf - +< +1. Add 'packadd! vimspector' to you '.vimrc' + +2. (optionally) Enable the default set of mappings: +> + let g:vimspector_enable_mappings = 'HUMAN' +< +1. Configure your project's debug profiles (create '.vimspector.json') + +Alternatively, you can clone the repo and select which gadgets are installed: + +1. Check the dependencies +2. Install the plugin as a Vim package. See ':help packages'. +3. Add 'packadd! vimspector' to you '.vimrc' +4. Install some 'gadgets' (debug adapters) - see ':VimspectorInstall ...' +5. Configure your project's debug profiles (create '.vimspector.json') + +If you prefer to use a plugin manager, see the plugin manager's docs. For +Vundle, use: +> + Plugin 'puremourning/vimspector' +< +The following sections expand on the above brief overview. + +------------------------------------------------------------------------------- + *vimspector-dependencies* +Dependencies ~ + +Vimspector requires: + +- One of: +- Vim 8.2 Huge build compiled with Python 3.6 or later +- Neovim 0.4.3 with Python 3.6 or later (experimental) +- One of the following operating systems: +- Linux +- macOS Mojave or later +- Windows (experimental) + +Why such a new vim ? Well 2 reasons: + +1. Because vimspector uses a lot of new Vim features +2. Because there are Vim bugs that vimspector triggers that will frustrate + you if you hit them. + +Why is neovim experimental? Because the author doesn't use neovim regularly, +and there are no regression tests for vimspector in neovim, so it may break +occasionally. Issue reports are handled on best-efforts basis, and PRs are +welcome to fix bugs. See also the next section descibing differences for neovim +vs vim. + +Why Windows support experimental? Because it's effort and it's not a priority +for the author. PRs are welcome to fix bugs. Windows will not be regularly +tested. + +Which Linux versions? I only test on Ubuntu 18.04 and later and RHEL 7. + +------------------------------------------------------------------------------- + *vimspector-neovim-differences* +Neovim differences ~ + +neovim doesn't implement some features Vimspector relies on: + +- WinBar - used for the buttons at the top of the code window and for + changing the output window's current output. + +- Prompt Buffers - used to send commands in the Console and add Watches. + (_Note_: prompt buffers are available in neovim nightly) + +- Balloons - this allows for the variable evaluation popup to be displayed + when hovering the mouse. See below for how to create a keyboard mapping + instead. + +Workarounds are in place as follows: + +- WinBar - There are mappings, ':VimspectorShowOutput' and ':VimspectorReset' + +- Prompt Buffers - There are ':VimspectorEval' and ':VimspectorWatch' + +- Balloons - There is the 'VimspectorBalloonEval' mapping. There is no + default mapping for this, so I recommend something like this to get + variable display in a popup: +> + " mnemonic 'di' = 'debug inspect' (pick your own, if you prefer!) + + " for normal mode - the word under the cursor + nmap di VimspectorBalloonEval + " for visual mode, the visually selected text + xmap di VimspectorBalloonEval +< +------------------------------------------------------------------------------- + *vimspector-windows-differences* +Windows differences ~ + +The following features are not implemented for Windows: + +- Tailing the vimspector log in the Output Window. + +------------------------------------------------------------------------------- + *vimspector-trying-it-out* +Trying it out ~ + +If you just want to try out vimspector without changing your vim config, there +are example projects for a number of languages in 'support/test', including: + +- Python ('support/test/python/simple_python') +- Go ('support/test/go/hello_world') +- Nodejs ('support/test/node/simple') +- Chrome ('support/test/chrome/') +- etc. + +To test one of these out, cd to the directory and run: +> + vim -Nu /path/to/vimspector/tests/vimrc --cmd "let g:vimspector_enable_mappings='HUMAN'" +< +Then press ''. + +There's also a C++ project in 'tests/testdata/cpp/simple/' with a 'Makefile' +which can be used to check everything is working. This is used by the +regression tests in CI so should always work, and is a good way to check if the +problem is your configuration rather than a bug. + +------------------------------------------------------------------------------- + *vimspector-cloning-plugin* +Cloning the plugin ~ + +If you're not using a release tarball, you'll need to clone this repo to the +appropriate place. + +1. Clone the plugin + +There are many Vim plugin managers, and I'm not going to state a particular +preference, so if you choose to use one, follow the plugin manager's +documentation. For example, for Vundle, use: +> + Plugin 'puremourning/vimspector' +< +If you don't use a plugin manager already, install vimspector as a Vim package +by cloning this repository into your package path, like this: +> + $ git clone https://github.com/puremourning/vimspector ~/.vim/pack/vimspector/opt/vimspector +< +1. Configure vimspector in your '.vimrc', for example to enable the standard + mapings: +> + let g:vimspector_enable_mappings = 'HUMAN' +< +1. Load vimspector at runtime. This can also be added to your '.vimrc' after + configuring vimspector: +> + packadd! vimspector +< +See support/doc/example_vimrc.vim for a minimal example. + +------------------------------------------------------------------------------- + *vimspector-install-gadgets* +Install some gadgets ~ + +Vimspector is a generic client for Debug Adapters. Debug Adapters (referred to +as 'gadgets' or 'adapters') are what actually do the work of talking to the +real debuggers. + +In order for Vimspector to be useful, you need to have some adapters installed. + +There are a few ways to do this: + +- If you downloaded a tarball, gadgets for main supported languages are + already installed for you. + +- Using ':VimspectorInstall ' (use TAB 'wildmenu' to see + the options, also accepts any 'install_gadget.py' option) + +- Using 'python3 install_gadget.py ' (use '--help' to see all options) + +- Attempting to launch a debug configuration; if the configured adapter can't + be found, vimspector will suggest installing one. + +- Using ':VimspectorUpdate' to install the latest supported versions of the + gadgets. + +Here's a demo of doing some installs and an upgrade: + + Image: asciicast [13] + +Both 'install_gadget.py' and ':VimspectorInstall' do the same set of things, +though the default behaviours are slightly different. For supported languages, +they will: + +- Download the relevant debug adapter at a version that's been tested from + the internet, either as a 'vsix' (Visusal Studio plugin), or clone from + GitHub. If you're in a corporate environment and this is a problem, you may + need to install the gadgets manually. + +- Perform any necessary post-installation actions, such as: + +- Building any binary components + +- Ensuring scripts are executable, because the VSIX packages are usually + broken in this regard. + +- Set up the 'gadgetDir' symlinks for the platform. + +For example, to install the tested debug adapter for a language, run: + +======================================================================================================================================== +| _To install_ | _Script_ | _Command_ | +======================================================================================================================================== +| '' | ':VimspectorInstall ' | +----------------------------------------------------------------------------------------- +| '', '', ... | ':VimspectorInstall ...' | +----------------------------------------------------------------------------------------- +| '' | './install_gadget.py --enable- ...' | ':VimspectorInstall --enable- ...' | +---------------------------------------------------------------------------------------------------------------------------------------- +| Supported adapters | './install_gadget.py --all' | ':VimspectorInstall --all' | +---------------------------------------------------------------------------------------------------------------------------------------- +| Supported adapters, but not TCL | './install_gadget.py --all --disable-tcl' | ':VimspectorInstall --all --disable-tcl' | +---------------------------------------------------------------------------------------------------------------------------------------- +| Supported and experimental adapters | './install_gadget.py --all --force-all' | ':VimspectorInstall --all' | +---------------------------------------------------------------------------------------------------------------------------------------- +| Adapter for specific debug config | Suggested by Vimspector when starting debugging | +----------------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- + *vimspectorinstall-vimspectorupdate-commands* +VimspectorInstall and VimspectorUpdate commands ~ + +':VimspectorInstall' runs 'install_gadget.py' in the background with some of +the options defaulted. + +':VimspectorUpdate' runs 'install_gadget.py' to re-install (i.e. update) any +gadgets already installed in your '.gadgets.json'. + +The output is minimal, to see the full output add '--verbose' to the command, +as in ':VimspectorInstall --verbose ...' or ':VimspectorUpdate --verbose ...'. + +If the installation is successful, the output window is closed (and the output +lost forever). Use a '!' to keep it open (e.g. ':VimspectorInstall! --verbose +--all' or ':VimspectorUpdate!' (etc.). + +If you know in advance which gadgets you want to install, for example so that +you can reproduce your config from source control, you can set +'g:vimspector_install_gadgets' to a list of gadgets. This will be used when: + +- Running ':VimspectorInstall' with no arguments, or +- Running ':VimspectorUpdate' + +For example: +> + let g:vimspector_install_gadgets = [ 'debugpy', 'vscode-cpptools', 'CodeLLDB' ] +< +------------------------------------------------------------------------------- + *vimspector-install_gadget.py* +install_gadget.py ~ + +By default 'install_gadget.py' will overwrite your '.gadgets.json' with the set +of adapters just installed, whereas ':VimspectorInstall' will _update_ it, +overwriting only newly changed or installed adapters. + +If you want to just add a new adapter using the script without destroying the +existing ones, add '--update-gadget-config', as in: +> + $ ./install_gadget.py --enable-tcl + $ ./install_gadget.py --enable-rust --update-gadget-config + $ ./install_gadget.py --enable-java --update-gadget-config +< +If you want to maintain 'configurations' outside of the vimspector repository +(this can be useful if you have custom gadgets or global configurations), you +can tell the installer to use a different basedir, then set +'g:vimspector_base_dir' to point to that directory, for example: +> + $ ./install_gadget.py --basedir $HOME/.vim/vimspector-config --all --force-all +< +Then add this to your '.vimrc': +> + let g:vimspector_base_dir=expand( '$HOME/.vim/vimspector-config' ) +< +When usnig ':VimspectorInstall', the 'g:vimspector_base_dir' setting is +respected unless '--basedir' is manually added (not recommended). + +See '--help' for more info on the various options. + +------------------------------------------------------------------------------- + *vimspector-manual-gadget-installation* +Manual gadget installation ~ + +If the language you want to debug is not in the supported list above, you can +probably still make it work, but it's more effort. + +You essentially need to get a working installation of the debug adapter, find +out how to start it, and configure that in an 'adapters' entry in either your +'.vimspector.json' or in '.gadgets.json'. + +The simplest way in practice is to install or start Visual Studio Code and use +its extension manager to install the relevant extension. You can then configure +the adapter manually in the 'adapters' section of your '.vimspector.json' or in +a 'gadgets.json'. + +PRs are always welcome to add supported languages (which roughly translates to +updating 'python/vimspector/gadgets.py' and testing it). + +------------------------------------------------------------------------------- + *vimspector-gadget-directory* +The gadget directory ~ + +Vimspector uses the following directory by default to look for a file named +'.gadgets.json': '/gadgets/'. + +This path is exposed as the vimspector _variable_ '${gadgetDir}'. This is +useful for configuring gadget command lines. + +Where os is one of: + +- 'macos' +- 'linux' +- 'windows' (though note: Windows is not supported) + +The format is the same as '.vimspector.json', but only the 'adapters' key is +used: + +Example: +> + { + "adapters": { + "lldb-vscode": { + "variables": { + "LLVM": { + "shell": "brew --prefix llvm" + } + }, + "attach": { + "pidProperty": "pid", + "pidSelect": "ask" + }, + "command": [ + "${LLVM}/bin/lldb-vscode" + ], + "env": { + "LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY": "YES" + }, + "name": "lldb" + }, + "vscode-cpptools": { + "attach": { + "pidProperty": "processId", + "pidSelect": "ask" + }, + "command": [ + "${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7" + ], + "name": "cppdbg" + }, + "vscode-python": { + "command": [ + "node", + "${gadgetDir}/vscode-python/out/client/debugger/debugAdapter/main.js" + ], + "name": "vscode-python" + } + } + } +< +The gadget file is automatically written by 'install_gadget.py' (or +':VimspectorInstall'). + +Vimspector will also load any fies matching: +'/gadgets//.gadgets.d/*.json'. These have the same +format as '.gadgets.json' but are not overwritten when running +'install_gadget.py'. + +------------------------------------------------------------------------------- + *vimspector-upgrade* +Upgrade ~ + +After updating the Vimspector code (either via 'git pull' or whatever package +manager), run ':VimspectorUpdate' to update any already-installed gadgets. + +=============================================================================== + *vimspector-about* +About ~ + +------------------------------------------------------------------------------- + *vimspector-background* +Background ~ + +The motivation is that debugging in Vim is a pretty horrible experience, +particularly if you use multiple languages. With pyclewn no more and the +built-in termdebug plugin limited to gdb, I wanted to explore options. + +While Language Server Protocol is well known, the Debug Adapter Protocol is +less well known, but achieves a similar goal: language agnostic API abstracting +debuggers from clients. + +The aim of this project is to provide a simple but effective debugging +experience in Vim for multiple languages, by leveraging the debug adapters that +are being built for Visual Studio Code. + +The ability to do remote debugging is a must. This is key to my workflow, so +baking it in to the debugging experience is a top bill goal for the project. So +vimspector has first-class support for executing programs remotely and +attaching to them. This support is unique to vimspector and on top of +(complementary to) any such support in actual debug adapters. + +------------------------------------------------------------------------------- + *vimspector-status* +Status ~ + +Vimspector is a work in progress, and any feedback/contributions are more than +welcome. + +The backlog can be viewed on Trello [15]. + +------------------------------------------------------------------------------- + *vimspector-experimental* +Experimental ~ + +The plugin is currently _experimental_. That means that any part of it can (and +probably will) change, including things like: + +- breaking changes to the configuration +- keys, layout, functionality of the UI + +However, I commit to only doing this in the most extreme cases and to annouce +such changes on Gitter well in advance. There's nothing more annoying than +stuff just breaking on you. I get that. + +------------------------------------------------------------------------------- + *vimspector-motivation* +Motivation ~ + +A message from the author about the motivation for this plugin: + +Many development environments have a built-in debugger. I spend an inordinate +amount of my time in Vim. I do all my development in Vim and I have even +customised my workflows for building code, running tests etc. + +For many years I have observed myself, friends and colleagues have been writing +'printf', 'puts', 'print', etc. debugging statements in all sorts of files +simply because there is no _easy_ way to run a debugger for _whatever_ language +we happen to be developing in. + +I truly believe that interactive, graphical debugging environments are the best +way to understand and reason about both unfamiliar and familiar code, and that +the lack of ready, simple access to a debugger is a huge hidden productivity +hole for many. + +Don't get me wrong, I know there are literally millions of developers out there +that are more than competent at developing without a graphical debugger, but I +maintain that if they had the ability to _just press a key_ and jump into the +debugger, it would be faster and more enjoyable that just cerebral code +comprehension. + +I created Vimspector because I find changing tools frustrating. 'gdb' for c++, +'pdb' for python, etc. Each has its own syntax. Each its own lexicon. Each its +own foibles. + +I designed the configuration system in such a way that the configuration can be +committed to source control so that it _just works_ for any of your colleagues, +friends, collaborators or complete strangers. + +I made remote debugging a first-class feature because that's a primary use case +for me in my job. + +With Vimspector I can _just hit ''_ in all of the languages I develop in +and debug locally or remotely using the exact same workflow, mappings and UI. I +have integrated this with my Vim in such a way that I can hit a button and _run +the test under the cursor in Vimspector_. This kind of integration has +massively improved my workflow and productivity. It's even made the process of +learning a new codebase... fun. + +- Ben Jackson, Creator. + +------------------------------------------------------------------------------- + *vimspector-license* +License ~ + +Apache 2.0 [16] + +Copyright © 2018 Ben Jackson + +------------------------------------------------------------------------------- + *vimspector-sponsorship* +Sponsorship ~ + +If you like Vimspector so much that you're wiling to part with your hard-earned +cash, please consider donating to one of the following charities, which are +meaningful to the author of Vimspector (in order of preference): + +- Greyhound Rescue Wales [17] +- Cancer Research UK [18] +- ICCF Holland [19] +- Any charity of your choosing. + +=============================================================================== + *vimspector-mappings* +Mappings ~ + +By default, vimspector does not change any of your mappings. Mappings are very +personal and so you should work out what you like and use vim's powerful +mapping features to set your own mappings. To that end, Vimspector defines the +following '' mappings: + +================================================================================================================================================================================= +| _Mapping_ | _Function_ | _API_ | +================================================================================================================================================================================= +| 'VimspectorContinue' | When debugging, continue. Otherwise start debugging. | 'vimspector#Continue()' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorStop' | Stop debugging. | 'vimspector#Stop()' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimpectorRestart' | Restart debugging with the same configuration. | 'vimspector#Restart()' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorPause' | Pause debuggee. | 'vimspector#Pause()' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorToggleBreakpoint' | Toggle line breakpoint on the current line. | 'vimspector#ToggleBreakpoint()' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorToggleConditionalBreakpoint' | Toggle conditional line breakpoint on the current line. | 'vimspector#ToggleBreakpoint( { trigger expr, hit count expr } )' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorAddFunctionBreakpoint' | Add a function breakpoint for the expression under cursor | "vimspector#AddFunctionBreakpoint( '' )" | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorRunToCursor' | Run to Cursor | 'vimspector#RunToCursor()' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorStepOver' | Step Over | 'vimspector#StepOver()' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorStepInto' | Step Into | 'vimspector#StepInto()' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorStepOut' | Step out of current function scope | 'vimspector#StepOut()' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorUpFrame' | Move up a frame in the current call stack | 'vimspector#UpFrame()' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorDownFrame' | Move down a frame in the current call stack | 'vimspector#DownFrame()' | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| 'VimspectorBalloonEval' | Evaluate expression under cursor (or visual) in popup | _internal_ | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + +These map roughly 1-1 with the API functions below. + +For example, if you want '' to start/continue debugging, add this to some +appropriate place, such as your 'vimrc' (hint: run ':e $MYVIMRC'). +> + nmap VimspectorContinue +< +In addition, many users probably want to only enable certain Vimspector +mappings while debugging is active. This is also possible, though it requires +writing some vimscipt. + +That said, many people are familiar with particular debuggers, so the following +mappings can be enabled by setting 'g:vimspector_enable_mappings' to the +specified value. + +------------------------------------------------------------------------------- + *vimspector-visual-studio-vscode* +Visual Studio / VSCode ~ + +To use Visual Studio-like mappings, add the following to your 'vimrc' **before +loading vimspector**: +> + let g:vimspector_enable_mappings = 'VISUAL_STUDIO' +< +========================================================================================================================= +| _Key_ | _Mapping_ | _Function_ | +========================================================================================================================= +| 'F5' | 'VimspectorContinue' | When debugging, continue. Otherwise start debugging. | +------------------------------------------------------------------------------------------------------------------------- +| 'Shift F5' | 'VimspectorStop' | Stop debugging. | +------------------------------------------------------------------------------------------------------------------------- +| 'Ctrl Shift F5' | 'VimspectorRestart' | Restart debugging with the same configuration. | +------------------------------------------------------------------------------------------------------------------------- +| 'F6' | 'VimspectorPause' | Pause debuggee. | +------------------------------------------------------------------------------------------------------------------------- +| 'F9' | 'VimspectorToggleBreakpoint' | Toggle line breakpoint on the current line. | +------------------------------------------------------------------------------------------------------------------------- +| 'Shift F9' | 'VimspectorAddFunctionBreakpoint' | Add a function breakpoint for the expression under cursor | +------------------------------------------------------------------------------------------------------------------------- +| 'F10' | 'VimspectorStepOver' | Step Over | +------------------------------------------------------------------------------------------------------------------------- +| 'F11' | 'VimspectorStepInto' | Step Into | +------------------------------------------------------------------------------------------------------------------------- +| 'Shift F11' | 'VimspectorStepOut' | Step out of current function scope | +------------------------------------------------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- + *vimspector-human-mode* +Human Mode ~ + +If, like me, you only have 2 hands and 10 fingers, you probably don't like +Ctrl-Shift-F keys. Also, if you're running in a terminal, there's a real +possibility of terminfo being wrong for shifted-F-keys, particularly if your +'TERM' is 'screen-256color'. If these issues (number of hands, 'TERM' +variables) are unfixable, try the following mappings, by adding the following +**before loading vimspector**: +> + let g:vimspector_enable_mappings = 'HUMAN' +< +============================================================================================================================ +| _Key_ | _Mapping_ | _Function_ | +============================================================================================================================ +| 'F5' | 'VimspectorContinue' | When debugging, continue. Otherwise start debugging. | +---------------------------------------------------------------------------------------------------------------------------- +| 'F3' | 'VimspectorStop' | Stop debugging. | +---------------------------------------------------------------------------------------------------------------------------- +| 'F4' | 'VimspectorRestart' | Restart debugging with the same configuration. | +---------------------------------------------------------------------------------------------------------------------------- +| 'F6' | 'VimspectorPause' | Pause debuggee. | +---------------------------------------------------------------------------------------------------------------------------- +| 'F9' | 'VimspectorToggleBreakpoint' | Toggle line breakpoint on the current line. | +---------------------------------------------------------------------------------------------------------------------------- +| 'F9' | 'VimspectorToggleConditionalBreakpoint' | Toggle conditional line breakpoint on the current line. | +---------------------------------------------------------------------------------------------------------------------------- +| 'F8' | 'VimspectorAddFunctionBreakpoint' | Add a function breakpoint for the expression under cursor | +---------------------------------------------------------------------------------------------------------------------------- +| 'F8' | 'VimspectorRunToCursor' | Run to Cursor | +---------------------------------------------------------------------------------------------------------------------------- +| 'F10' | 'VimspectorStepOver' | Step Over | +---------------------------------------------------------------------------------------------------------------------------- +| 'F11' | 'VimspectorStepInto' | Step Into | +---------------------------------------------------------------------------------------------------------------------------- +| 'F12' | 'VimspectorStepOut' | Step out of current function scope | +---------------------------------------------------------------------------------------------------------------------------- + + +In addition, I recommend adding a mapping to 'VimspectorBalloonEval', in +normal and visual modes, for example: +> + " mnemonic 'di' = 'debug inspect' (pick your own, if you prefer!) + + " for normal mode - the word under the cursor + nmap di VimspectorBalloonEval + " for visual mode, the visually selected text + xmap di VimspectorBalloonEval +< +You may also wish to add mappings for up/down the stack, for example: +> + nmap VimspectorUpFrame + nmap VimspectorDownFrame +< +=============================================================================== + *vimspector-usage-api* +Usage and API ~ + +This section defines detailed usage instructions, organised by feature. For +most users, the mappings section contains the most common commands and default +usage. This section can be used as a reference to create your own mappings or +custom behaviours. + +------------------------------------------------------------------------------- + *vimspector-launch-attach-by-pid* +Launch and attach by PID: ~ + +- Create '.vimspector.json'. See below. +- ':call vimspector#Launch()' and select a configuration. + + Image: debug session (see reference [6]) + +------------------------------------------------------------------------------- + *vimspector-launch-with-options* +Launch with options ~ + +To launch a specific debug configuration, or specify replacement variables [20] +for the launch, you can use: + +- ':call vimspector#LaunchWithSettings( dict )' + +The argument is a 'dict' with the following keys: + +- 'configuration': (optional) Name of the debug configuration to launch +- '': (optional) Name of a variable to set + +This allows for some integration and automation. For example, if you have a +configuration named 'Run Test' that contains a replacement variable [20] named +'${Test}' you could write a mapping which ultimately executes: +> + vimspector#LaunchWithSettings( #{ configuration: 'Run Test' + \ Test: 'Name of the test' } ) +< +This would start the 'Run Test' configuration with '${Test}' set to "'Name of +the test'" and Vimspector would _not_ prompt the user to enter or confirm these +things. + +See our YouCompleteMe integration guide for another example where it can be +used to specify the port to connect the java debugger + +------------------------------------------------------------------------------- + *vimspector-debug-configuration-selection* +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 [21] for details. + +------------------------------------------------------------------------------- + *vimspector-get-configurations* +Get configurations ~ + +- Use 'vimspector#GetConfigurations()' to get a list of configurations + +For example, to get an array of configurations and fuzzy matching on the result +> + :call matchfuzzy(vimspector#GetConfigurations(), "test::case_1") +< +------------------------------------------------------------------------------- + *vimspector-breakpoints* +Breakpoints ~ + +See the mappings [22] section for the default mappings for working with +breakpoints. This section describes the full API in vimscript functions. + +------------------------------------------------------------------------------- + *vimspector-summary* +Summary ~ + +- Use 'vimspector#ToggleBreakpoint( { options dict } )' to set/disable/delete + a line breakpoint. The argument is optional (see below). + +- Use "vimspector#AddFunctionBreakpoint( '', { options dict} )" to add + a function breakpoint. The second argument is optional (see below). + +- Use 'vimspector#SetLineBreakpoint( file_name, line_num, { options dict } )' + to set a breakpoint at a specific file/line. The last argument is optional + (see below) + +- Use 'vimspector#ClearLineBreakpoint( file_name, line_num )' to remove a + breakpoint at a specific file/line + +- Use 'vimspector#ClearBreakpoints()' to clear all breakpoints + +Examples: + +- 'call vimspector#ToggleBreakpoint()' - toggle breakpoint on current line + +- "call vimspector#SetLineBreakpoint( 'some_file.py', 10 )" - set a + breakpoint on 'some_filepy:10' + +- "call vimspector#AddFunctionBreakpoint( 'main' )" - add a function + breakpoint on the 'main' function + +- "call vimspector#ToggleBreakpoint( { 'condition': 'i > 5' } )" - add a + breakpoint on the current line that triggers only when 'i > 5' is 'true' + +- "call vimspector#SetLineBreakpoint( 'some_file.py', 10, { 'condition': 'i > + 5' } )" - add a breakpoint at 'some_file.py:10' that triggers only when 'i + > 5' is 'true' + +- "call vimspector#ClearLineBreakpoint( 'some_file.py', 10 )" - delete the + breakpoint at 'some_file.py:10' + +- 'call vimspector#ClearBreakpoints()' - clear all breakpoints + +------------------------------------------------------------------------------- + *vimspector-line-breakpoints* +Line breakpoints ~ + +The simplest and most common form of breakpoint is a line breakpoint. Execution +is paused when the specified line is executed. + +For most debugging scenarios, users will just hit '' to create a line +breakpoint on the current line and '' to launch the application. + +------------------------------------------------------------------------------- + *vimspector-conditional-breakpoints* +Conditional breakpoints ~ + +Some debug adapters support conditional breakpoints. Note that vimspector does +not tell you if the debugger doesn't support conditional breakpoints (yet). A +conditional breakpoint is a breakpoint which only triggers if some expression +evaluates to true, or has some other constraints met. + +Some of these functions above take a single optional argument which is a +dictionary of options. The dictionary can have the following keys: + +- 'condition': An optional expression evaluated to determine if the + breakpoint should fire. Not supported by all debug adapters. For example, + to break when 'abc' is '10', enter something like 'abc == 10', depending on + the language. + +- 'hitCondition': An optional expression evaluated to determine a number of + times the breakpoint should be ignored. Should (probably?) not be used in + combination with 'condition'. Not supported by all debug adapters. For + example, to break on the 3rd time hitting this line, enter '3'. + +In both cases, the expression is evaluated by the debugger, so should be in +whatever dialect the debugger understands when evaluating expressions. + +When using the '' mapping, the user is prompted to enter these +expressions in a command line (with history). + +------------------------------------------------------------------------------- + *vimspector-exception-breakpoints* +Exception breakpoints ~ + +Exception breakpoints typically fire when an exception is throw or other error +condition occurs. Depending on the debugger, when starting debugging, you may +be asked a few questions about how to handle exceptions. These are "exception +breakpoints" and vimspector remembers your choices while Vim is still running. + +Typically you can accept the defaults (just keep pressing ''!) as most +debug adapter defaults are sane, but if you want to break on, say 'uncaught +exception' then answer 'Y' to that (for example). + +You can configure your choices in the '.vimspector.json'. See the configuration +guide [23] for details on that. + +------------------------------------------------------------------------------- + *vimspector-clear-breakpoints* +Clear breakpoints ~ + +Use 'vimspector#ClearBreakpoints()' to clear all breakpoints including the +memory of exception breakpoint choices. + +------------------------------------------------------------------------------- + *vimspector-run-to-cursor* +Run to Cursor ~ + +Use 'vimspector#RunToCursor' or '': this creates a temporary +breakpoint on the current line, then continues execution, clearing the +breakpoint when it is hit. + +------------------------------------------------------------------------------- + *vimspector-stepping* +Stepping ~ + +- Step in/out, finish, continue, pause etc. using the WinBar, or mappings. +- If you really want to, the API is 'vimspector#StepInto()' etc. + + Image: code window (see reference [24]) + +------------------------------------------------------------------------------- + *vimspector-variables-scopes* +Variables and scopes ~ + +- Current scope shows values of locals. +- Use '', or double-click with left mouse to expand/collapse (+, -). +- Set the value of the variable with '' (control + '') or + '' (if 'modifyOtherKeys' doesn't work for you) +- When changing the stack frame the locals window updates. +- While paused, hover to see values + + Image: locals window (see reference [25]) + +Scopes and variables are represented by the buffer 'vimspector.Variables'. + +------------------------------------------------------------------------------- + *vimspector-variable-or-selection-hover-evaluation* +Variable or selection hover evaluation ~ + +All rules for 'Variables and scopes' apply plus the following: + +- With mouse enabled, hover over a variable and get the value it evaluates + to. + +- Use your mouse to perform a visual selection of an expression (e.g. 'a + + b') and get its result. + +- Make a normal mode ('nmap') and visual mode ('xmap') mapping to + 'VimspectorBalloonEval' to manually trigger the popup. + +- Set the value of the variable with '' (control + '') or + '' (if 'modifyOtherKeys' doesn't work for you) + +- Use regular nagivation keys ('j', 'k') to choose the current selection; + '' (or leave the tooltip window) to close the tooltip. + + Image: variable eval hover (see reference [26]) + +------------------------------------------------------------------------------- + *vimspector-watches* +Watches ~ + +The watch window is used to inspect variables and expressions. Expressions are +evaluated in the selected stack frame which is "focussed" + +The watches window is a prompt buffer, where that's available. Enter insert +mode to add a new watch expression. + +- Add watches to the variables window by entering insert mode and typing the + expression. Commit with ''. + +- Alternatively, use ':VimspectorWatch '. Tab-completion for + expression is available in some debug adapters. + +- Expand result with '', or double-click with left mouse. + +- Set the value of the variable with '' (control + '') or + '' (if 'modifyOtherKeys' doesn't work for you) + +- Delete with ''. + + Image: watch window (see reference [27]) + +The watches are represented by the buffer 'vimspector.StackTrace'. + +------------------------------------------------------------------------------- + *vimspector-watch-autocompletion* +Watch autocompletion ~ + +The watch prompt buffer has its 'omnifunc' set to a function that will +calculate completion for the current expression. This is trivially used with +'' (see ':help ins-completion'), or integrated with your +favourite completion system. The filetype in the buffer is set to +'VimspectorPrompt'. + +For YouCompleteMe, the following config works well: +> + let g:ycm_semantic_triggers = { + \ 'VimspectorPrompt': [ '.', '->', ':', '<' ] + } +< +------------------------------------------------------------------------------- + *vimspector-stack-traces* +Stack Traces ~ + +The stack trace window shows the state of each program thread. Threads which +are stopped can be expanded to show the stack trace of that thread. + +Often, but not always, all threads are stopped when a breakpoint is hit. The +status of a thread is show in parentheses after the thread's name. Where +supported by the underlying debugger, threads can be paused and continued +individually from within the Stack Trace window. + +A particular thread, highlighted with the 'CursorLine' highlight group is the +"focussed" thread. This is the thread that receives commands like "Stop In", +"Stop Out", "Continue" and "Pause" in the code window. The focussed thread can +be changed manually to "switch to" that thread. + +- Use '', or double-click with left mouse to expand/collapse a thread + stack trace, or use the WinBar button. + +- Use '', or double-click with left mouse on a stack frame to jump to it. + +- Use the WinBar or 'vimspector#PauseContinueThread()' to individually pause + or continue the selected thread. + +- Use the "Focus" WinBar button, '' or + 'vimspector#SetCurrentThread()' to set the "focussed" thread to the + currently selected one. If the selected line is a stack frame, set the + focussed thread to the thread of that frame and jump to that frame in the + code window. + + Image: stack trace (see reference [28]) + +The stack trace is represented by the buffer 'vimspector.StackTrace'. + +------------------------------------------------------------------------------- + *vimspector-program-output* +Program Output ~ + +- In the outputs window, use the WinBar to select the output channel. +- Alternatively, use ':VimspectorShowOutput '. Use command-line + completion to see the categories. +- The debuggee prints to the stdout channel. +- Other channels may be useful for debugging. + + Image: output window (see reference [29]) + +If the output window is closed, a new one can be opened with +':VimspectorShowOutput ' (use tab-completion - 'wildmenu' to see the +options). + +------------------------------------------------------------------------------- + *vimspector-console* +Console ~ + +The console window is a prompt buffer, where that's available, and can be used +as an interactive CLI for the debug adapter. Support for this varies amongst +adapters. + +- Enter insert mode to enter a command to evaluate. +- Alternatively, ':VimspectorEval '. Completion is available with + some debug adapters. +- Commit the request with '' +- The request and subsequent result are printed. + +NOTE: See also Watches above. + +If the output window is closed, a new one can be opened with +':VimspectorShowOutput Console'. + +------------------------------------------------------------------------------- + *vimspector-console-autocompletion* +Console autocompletion ~ + +The console prompt buffer has its 'omnifunc' set to a function that will +calculate completion for the current command/expression. This is trivially used +with '' (see ':help ins-completion'), or integrated with your +favourite completion system. The filetype in the buffer is set to +'VimspectorPrompt'. + +For YouCompleteMe, the following config works well: +> + let g:ycm_semantic_triggers = { + \ 'VimspectorPrompt': [ '.', '->', ':', '<' ] + } +< +------------------------------------------------------------------------------- + *vimspector-log-view* +Log View ~ + +The Vimspector log file contains a full trace of the communication between +Vimspector and the debug adapter. This is the primary source of diagnostic +information when something goes wrong that's not a Vim traceback. + +If you just want to see the Vimspector log file, use ':VimspectorToggleLog', +which will tail it in a little window (doesn't work on Windows). + +------------------------------------------------------------------------------- + *vimspector-closing-debugger* +Closing debugger ~ + +To close the debugger, use: + +- 'Reset' WinBar button +- ':VimspectorReset' when the WinBar is not available. +- 'call vimspector#Reset()' + +------------------------------------------------------------------------------- + *vimspector-terminate-debuggee* +Terminate debuggee ~ + +If the debuggee is still running when stopping or resetting, then some debug +adapters allow you to specify what should happen to it when finishing +debugging. Typically, the default behaviour is sensible, and this is what +happens most of the time. These are the defaults according to DAP: + +- If the request was 'launch': terminate the debuggee +- If the request was 'attach': don't terminate the debuggee + +Some debug adapters allow you to choose what to do when disconnecting. If you +wish to control this behaviour, use ':VimspectorReset' or call +"vimspector#Reset( { 'interactive': v:true } )". If the debug adapter offers a +choice as to whether or not to terminate the debuggee, you will be prompted to +choose. The same applies for 'vimspector#Stop()' which can take an argument: +"vimspector#Stop( { 'interactive': v:true } )". + +=============================================================================== + *vimspector-debug-profile-configuration* +Debug profile configuration ~ + +For an introduction to the configuration of '.vimspector.json', take a look at +the Getting Started section of the Vimspector website [1]. + +For full explanation, including how to use variables, substitutions and how to +specify exception breakpoints, see the docs [2]. + +The JSON configuration file allows C-style comments: + +- '// comment to end of line ...' +- '/* inline comment ... */' + +Current tested with the following debug adapters. + +------------------------------------------------------------------------------- + *vimspector-c-c-rust-etc.* +C, C++, Rust, etc. ~ + +- vscode-cpptools [30] + +- On macOS, I _strongly_ recommend using CodeLLDB instead for C and C++ + projects. It's really excellent, has fewer dependencies and doesn't open + console apps in another Terminal window. + +Example '.vimspector.json' (works with both 'vscode-cpptools' and +'lldb-vscode'. For 'lldb-vscode' replace the name of the adapter with +'lldb-vscode': + +- vscode-cpptools Linux/MacOS: +> + { + "configurations": { + "Launch": { + "adapter": "vscode-cpptools", + "configuration": { + "request": "launch", + "program": "", + "args": [ ... ], + "cwd": "", + "environment": [ ... ], + "externalConsole": true, + "MIMode": "" + } + }, + "Attach": { + "adapter": "vscode-cpptools", + "configuration": { + "request": "attach", + "program": "", + "MIMode": "" + } + } + ... + } + } +< +- vscode-cpptools Windows + +**_NOTE FOR WINDOWS USERS:_** You need to install 'gdb.exe'. I recommend using +'scoop install gdb'. Vimspector cannot use the visual studio debugger due to +licensing. +> + { + "configurations": { + "Launch": { + "adapter": "vscode-cpptools", + "configuration": { + "request": "launch", + "program": "", + "stopAtEntry": true + } + } + } + } +< +------------------------------------------------------------------------------- + *vimspector-c-remote-debugging* +C++ Remote debugging ~ + +The cpptools documentation describes how to attach cpptools to gdbserver using +'miDebuggerAddress'. Note that when doing this you should use the '"request": +"attach"'. + +------------------------------------------------------------------------------- + *vimspector-c-remote-launch-attach* +C++ Remote launch and attach ~ + +If you're feeling fancy, checkout the reference guide [31] for an example of +getting Vimspector to remotely launch and attach. + +- CodeLLDB (MacOS) + +CodeLLDB is superior to vscode-cpptools in a number of ways on macOS at least. + +See Rust. + +- lldb-vscode (MacOS) + +An alternative is to to use 'lldb-vscode', which comes with llvm. Here's how: + +- Install llvm (e.g. with HomeBrew: 'brew install llvm') +- Create a file named + '/path/to/vimspector/gadgets/macos/.gadgets.d/lldb-vscode.json': +> + { + "adapters": { + "lldb-vscode": { + "variables": { + "LLVM": { + "shell": "brew --prefix llvm" + } + }, + "attach": { + "pidProperty": "pid", + "pidSelect": "ask" + }, + "command": [ + "${LLVM}/bin/lldb-vscode" + ], + "env": { + "LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY": "YES" + }, + "name": "lldb" + } + } + } +< +------------------------------------------------------------------------------- + *vimspector-rust* +Rust ~ + +Rust is supported with any gdb/lldb-based debugger. So it works fine with +'vscode-cpptools' and 'lldb-vscode' above. However, support for rust is best in +'CodeLLDB' [32]. + +- './install_gadget.py --force-enable-rust' or ':VimspectorInstall CodeLLDB' +- Example: 'support/test/rust/vimspector_test' +> + { + "configurations": { + "launch": { + "adapter": "CodeLLDB", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/target/debug/vimspector_test" + } + } + } + } +< +- Docs: https://github.com/vadimcn/vscode-lldb/blob/master/MANUAL.md + +------------------------------------------------------------------------------- + *vimspector-python* +Python ~ + +- Python: debugpy [33] + +- Install with 'install_gadget.py --enable-python' or ':VimspectorInstall + debugpy', ideally requires a working compiler and the python development + headers/libs to build a C python extension for performance. + +- Full options: + https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings + +**Migrating from 'vscode-python'**: change '"adapter": "vscode-python"' to +'"adapter": "debugpy"'. +> + { + "configurations": { + ": Launch": { + "adapter": "debugpy", + "configuration": { + "name": ": Launch", + "type": "python", + "request": "launch", + "cwd": "", + "python": "/path/to/python/interpreter/to/use", + "stopOnEntry": true, + "console": "externalTerminal", + "debugOptions": [], + "program": "" + } + } + ... + } + } +< +------------------------------------------------------------------------------- + *vimspector-python-remote-debugging* +Python Remote Debugging ~ + +In order to use remote debugging with debugpy, you have to connect Vimspector +directly to the application that is being debugged. This is easy, but it's a +little different from how we normally configure things. Specifically, you need +to: + +- Start your application with debugpy, specifying the '--listen' argument. + See the debugpy documentation [34] for details. + +- Use the built-in "multi-session" adapter. This just asks for the host/port + to connect to. For example: +> + { + "configurations": { + "Python Attach": { + "adapter": "multi-session", + "configuration": { + "request": "attach", + "pathMappings": [ + // mappings here (optional) + ] + } + } + } + } +< +See details of the launch configuration [35] for explanation of things like +'pathMappings'. + +Additional documentation, including how to do this when the remote machine can +only be contacted via SSH are provided by debugpy [36]. + +------------------------------------------------------------------------------- + *vimspector-python-remote-launch-attach* +Python Remote launch and attach ~ + +If you're feeling fancy, checkout the reference guide [31] for an example of +getting Vimspector to remotely launch and attach. + +------------------------------------------------------------------------------- + *vimspector-legacy-vscode-python* +Legacy: vscode-python ~ + +- No longer installed by default - please pass '--force-enable-python.legacy' + if you just want to continue using your working setup. +- vscode-python [37] +- NOTE: You must be running 'node' 10. See this issue [38] +> + { + "configurations": { + ": Launch": { + "adapter": "vscode-python", + "configuration": { + "name": ": Launch", + "type": "python", + "request": "launch", + "cwd": "", + "stopOnEntry": true, + "console": "externalTerminal", + "debugOptions": [], + "program": "" + } + } + ... + } + } +< +------------------------------------------------------------------------------- + *vimspector-tcl* +TCL ~ + +- TCL (TclProDebug) + +See my fork of TclProDebug [39] for instructions. + +------------------------------------------------------------------------------- + *vimspector-c* +C♯ ~ + +- C# - dotnet core + +Install with 'install_gadget.py --force-enable-csharp' or ':VimspectorInstall +netcoredbg' +> + { + "configurations": { + "launch - netcoredbg": { + "adapter": "netcoredbg", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll", + "args": [], + "stopAtEntry": true, + "cwd": "${workspaceRoot}" + } + } + } + } +< +- C# - mono + +Install with 'install_gadget.py --force-enable-csharp' or ':VimspectorInstall +vscode-mono-debug'. + +**_Known not to work._** +> + { + "configurations": { + "launch - mono": { + "adapter": "vscode-mono-debug", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll", + "args": [], + "cwd": "${workspaceRoot}", + "runtimeExecutable": "mono", + "runtimeArgs": [], + "env": [], + "externalConsole": false, + "console": "integratedTerminal" + } + } + } + } +< +------------------------------------------------------------------------------- + *vimspector-go* +Go ~ + +- Go + +Requires: + +- 'install_gadget.py --enable-go' or ':VimspectorInstall vscode-go' +- Delve [40] installed, e.g. 'go get -u github.com/go-delve/delve/cmd/dlv' +- Delve to be in your PATH, or specify the 'dlvToolPath' launch option +> + { + "configurations": { + "run": { + "adapter": "vscode-go", + "configuration": { + "request": "launch", + "program": "${fileDirname}", + "mode": "debug", + "dlvToolPath": "$HOME/go/bin/dlv" + } + } + } + } +< +See the vscode-go docs for troubleshooting information [41] + +------------------------------------------------------------------------------- + *vimspector-php* +PHP ~ + +This uses the php-debug, see +https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug + +Requires: + +- (optional) Xdebug helper for chrome https://chrome.google.com/webstore/deta + il/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc + +- 'install_gadget.py --force-enable-php' or ':VimspectorInstall + vscode-php-debug' + +- configured php xdebug extension +> + zend_extension=xdebug.so + xdebug.remote_enable=on + xdebug.remote_handler=dbgp + xdebug.remote_host=localhost + xdebug.remote_port=9000 +< + replace 'localhost' with the ip of your workstation. + +lazy alternative +> + zend_extension=xdebug.so + xdebug.remote_enable=on + xdebug.remote_handler=dbgp + xdebug.remote_connect_back=true + xdebug.remote_port=9000 +< +- .vimspector.json +> + { + "configurations": { + "Listen for XDebug": { + "adapter": "vscode-php-debug", + "configuration": { + "name": "Listen for XDebug", + "type": "php", + "request": "launch", + "port": 9000, + "stopOnEntry": false, + "pathMappings": { + "/var/www/html": "${workspaceRoot}" + } + } + }, + "Launch currently open script": { + "adapter": "vscode-php-debug", + "configuration": { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 9000 + } + } + } + } +< +------------------------------------------------------------------------------- + *vimspector-debug-web-application* +Debug web application ~ + +append 'XDEBUG_SESSION_START=xdebug' to your query string +> + curl "http://localhost?XDEBUG_SESSION_START=xdebug" +< +or use the previously mentioned Xdebug Helper extension (which sets a +'XDEBUG_SESSION' cookie) + +------------------------------------------------------------------------------- + *vimspector-debug-cli-application* +Debug cli application ~ +> + export XDEBUG_CONFIG="idekey=xdebug" + php +< +------------------------------------------------------------------------------- + *vimspector-javascript-typescript-etc.* +JavaScript, TypeScript, etc. ~ + +- Node.js + +Requires: + +- 'install_gadget.py --force-enable-node' + +- For installation, a Node.js environment that is < node 12. I believe this + is an incompatibility with gulp. Advice, use [nvm][] with 'nvm install + --lts 10; nvm use --lts 10; ./install_gadget.py --force-enable-node ...' + +- Options described here: + https://code.visualstudio.com/docs/nodejs/nodejs-debugging + +- Example: 'support/test/node/simple' +> + { + "configurations": { + "run": { + "adapter": "vscode-node", + "configuration": { + "request": "launch", + "protocol": "auto", + "stopOnEntry": true, + "console": "integratedTerminal", + "program": "${workspaceRoot}/simple.js", + "cwd": "${workspaceRoot}" + } + } + } + } +< +- Chrome + +This uses the chrome debugger, see https://marketplace.visualstudio.com/items?i +temName=msjsdiag.debugger-for-chrome. + +It allows you to debug scripts running inside chrome from within Vim. + +- './install_gadget.py --force-enable-chrome' or ':VimspectorInstall + debugger-for-chrome' +- Example: 'support/test/chrome' +> + { + "configurations": { + "launch": { + "adapter": "chrome", + "configuration": { + "request": "launch", + "url": "http://localhost:1234/", + "webRoot": "${workspaceRoot}/www" + } + } + } + } +< +------------------------------------------------------------------------------- + *vimspector-java* +Java ~ + +Vimspector works well with the java debug server [42], which runs as a jdt.ls +(Java Language Server) plugin, rather than a standalone debug adapter. + +Vimspector is not in the business of running language servers, only debug +adapters, so this means that you need a compatible Language Server Protocol +editor plugin to use Java. I recommend YouCompleteMe [43], which has full +support for jdt.ls, and most importantly a trivial way to load the debug +adapter and to use it with Vimspector. + +------------------------------------------------------------------------------- + *vimspector-usage-with-youcompleteme* +Usage with YouCompleteMe ~ + +- Set up YCM for java [43]. + +- Get Vimspector to download the java debug plugin: 'install_gadget.py + --force-enable-java ' or ':VimspectorInstall + java-debug-adapter' + +- Configure Vimspector for your project using the 'vscode-java' adapter, + e.g.: +> + { + "configurations": { + "Java Attach": { + "adapter": "vscode-java", + "configuration": { + "request": "attach", + "hostName": "${host}", + "port": "${port}", + "sourcePaths": [ + "${workspaceRoot}/src/main/java", + "${workspaceRoot}/src/test/java" + ] + } + } + } + } +< +- Tell YCM to load the debugger plugin. This should be the 'gadgets/' + directory, not any specific adapter. e.g. in '.vimrc' +> + " Tell YCM where to find the plugin. Add to any existing values. + let g:ycm_java_jdtls_extension_path = [ + \ '' + \ ] +< +- Create a mapping, such as '' to start the debug server and + launch vimspector, e.g. in '~/.vim/ftplugin/java.vim': +> + let s:jdt_ls_debugger_port = 0 + function! s:StartDebugging() + if s:jdt_ls_debugger_port <= 0 + " Get the DAP port + let s:jdt_ls_debugger_port = youcompleteme#GetCommandResponse( + \ 'ExecuteCommand', + \ 'vscode.java.startDebugSession' ) + + if s:jdt_ls_debugger_port == '' + echom "Unable to get DAP port - is JDT.LS initialized?" + let s:jdt_ls_debugger_port = 0 + return + endif + endif + + " Start debugging with the DAP port + call vimspector#LaunchWithSettings( { 'DAPPort': s:jdt_ls_debugger_port } ) + endfunction + + nnoremap :call StartDebugging() +< +You can then use '' to start debugging rather than just ''. + +If you see "Unable to get DAP port - is JDT.LS initialized?", try running +':YcmCompleter ExecuteCommand vscode.java.startDebugSession' and note the +output. If you see an error like 'ResponseFailedException: Request failed: +-32601: No delegateCommandHandler for vscode.java.startDebugSession', make sure +that: _Your YCM jdt.ls is actually working, see the YCM docs [44] for +troubleshooting_ The YCM jdt.ls has had time to initialize before you start the +debugger * That 'g:ycm_java_jdtls_extension_path' is set in '.vimrc' or prior +to YCM starting + +For the launch arguments, see the vscode document [45]. + +------------------------------------------------------------------------------- + *vimspector-other-lsp-clients* +Other LSP clients ~ + +See this issue [46] for more background. + +------------------------------------------------------------------------------- + *vimspector-lua* +Lua ~ + +Lua is supported through local-lua-debugger-vscode [47]. This debugger uses +stdio to communicate with the running process, so calls to 'io.read' will cause +problems. + +- './install_gadget.py --enable-lua' or ':VimspectorInstall + local-lua-debugger-vscode' +- Examples: 'support/test/lua/simple' and 'support/test/lua/love' +> + { + "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#", + "configurations": { + "lua": { + "adapter": "lua-local", + "configuration": { + "request": "launch", + "type": "lua-local", + "cwd": "${workspaceFolder}", + "program": { + "lua": "lua", + "file": "${file}" + } + } + }, + "luajit": { + "adapter": "lua-local", + "configuration": { + "request": "launch", + "type": "lua-local", + "cwd": "${workspaceFolder}", + "program": { + "lua": "luajit", + "file": "${file}" + } + } + }, + "love": { + "adapter": "lua-local", + "configuration": { + "request": "launch", + "type": "lua-local", + "cwd": "${workspaceFolder}", + "program": { + "command": "love" + }, + "args": ["${workspaceFolder}"] + } + } + } + } +< +------------------------------------------------------------------------------- + *vimspector-other-servers* +Other servers ~ + +- Java - vscode-javac. This works, but is not as functional as Java Debug + Server. Take a look at this comment [48] for instructions. + +=============================================================================== + *vimspector-customisation* +Customisation ~ + +There is very limited support for customisation of the UI. + +------------------------------------------------------------------------------- + *vimspector-changing-default-signs* +Changing the default signs ~ + +Vimsector uses the following signs internally. If they are defined before +Vimsector uses them, they will not be replaced. So to customise the signs, +define them in your 'vimrc'. + +============================================================================= +| _Sign_ | _Description_ | _Priority_ | +============================================================================= +| 'vimspectorBP' | Line breakpoint | 9 | +----------------------------------------------------------------------------- +| 'vimspectorBPCond' | Conditional line breakpoint | 9 | +----------------------------------------------------------------------------- +| 'vimspectorBPDisabled' | Disabled breakpoint | 9 | +----------------------------------------------------------------------------- +| 'vimspectorPC' | Program counter (i.e. current line) | 200 | +----------------------------------------------------------------------------- +| 'vimspectorPCBP' | Program counter and breakpoint | 200 | +----------------------------------------------------------------------------- + + +The default symbols are the equivalent of something like the following: +> + sign define vimspectorBP text=\ ● texthl=WarningMsg + sign define vimspectorBPCond text=\ ◆ texthl=WarningMsg + sign define vimspectorBPDisabled text=\ ● texthl=LineNr + sign define vimspectorPC text=\ ▶ texthl=MatchParen linehl=CursorLine + sign define vimspectorPCBP text=●▶ texthl=MatchParen linehl=CursorLine +< +If the signs don't display properly, your font probably doesn't contain these +glyphs. You can easily change them by defining the sign in your vimrc. For +example, you could put this in your 'vimrc' to use some simple ASCII symbols: +> + sign define vimspectorBP text=o texthl=WarningMsg + sign define vimspectorBPCond text=o? texthl=WarningMsg + sign define vimspectorBPDisabled text=o! texthl=LineNr + sign define vimspectorPC text=\ > texthl=MatchParen + sign define vimspectorPCBP text=o> texthl=MatchParen +< +------------------------------------------------------------------------------- + *vimspector-sign-priority* +Sign priority ~ + +Many different plugins provide signs for various purposes. Examples include +diagnostic signs for code errors, etc. Vim provides only a single priority to +determine which sign should be displayed when multiple signs are placed at a +single line. If you are finding that other signs are interfering with +vimspector's (or vice-versa), you can customise the priority used by vimspector +by setting the following dictionary: +> + let g:vimspector_sign_priority = { + \ '': , + \ } +< +For example: +> + let g:vimspector_sign_priority = { + \ 'vimspectorBP': 3, + \ 'vimspectorBPCond': 2, + \ 'vimspectorBPDisabled': 1, + \ 'vimspectorPC': 999, + \ } +< +All keys are optional. If a sign is not customised, the default priority it +used (as shown above). + +See ':help sign-priority'. The default priority is 10, larger numbers override +smaller ones. + +------------------------------------------------------------------------------- + *vimspector-changing-default-window-sizes* +Changing the default window sizes ~ + +**_Please Note_**: This customisation API is **_unstable_**, meaning that it +may change at any time. I will endeavour to reduce the impact of this and +announce 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: +> + let g:vimspector_sidebar_width = 75 + let g:vimspector_bottombar_height = 15 +< +------------------------------------------------------------------------------- + *vimspector-changing-terminal-size* +Changing the terminal size ~ + +The terminal is typically created as a vertical split to the right 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 constraints. + +Example: +> + let g:vimspector_code_minwidth = 90 + let g:vimspector_terminal_maxwidth = 75 + let g:vimspector_terminal_minwidth = 20 +< +------------------------------------------------------------------------------- + *vimspector-custom-mappings-while-debugging* +Custom mappings while debugging ~ + +It's useful to be able to define mappings only while debugging and remove those +mappings when debugging is complete. For this purpose, Vimspector provides 2 +'User' autocommands: + +- 'VimspectorJumpedToFrame' - triggered whenever a 'break' event happens, or + when selecting a stack from to jump to. This can be used to create (for + example) buffer-local mappings for any files opened in the code window. + +- 'VimspectorDebugEnded' - triggered when the debug session is terminated + (actually when Vimspector is fully reset) + +An example way to use this is included in 'support/custom_ui_vimrc'. In there, +these autocommands are used to create buffer-local mappings for any files +visited while debugging and to clear them when completing debugging. This is +particularly useful for commands like 'VimspectorBalloonEval' which only +make sense while debugging (and only in the code window). Check the commented +section 'Custom mappings while debugging'. + +NOTE: This is a fairly advanced feature requiring some nontrivial vimscript. +It's possible that this feature will be incorporated into Vimspector in future +as it is a common requirement. + +------------------------------------------------------------------------------- + *vimspector-advanced-ui-customisation* +Advanced UI customisation ~ + +**_Please Note_**: This customisation API is **_unstable_**, meaning that it +may change at any time. I will endeavour to reduce the impact of this and +announce 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, + containing 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 + +------------------------------------------------------------------------------- + *vimspector-customising-winbar* +Customising the WinBar ~ + +You can even customise the WinBar buttons by simply running the usual 'menu' +(and 'unmenu') commands. + +By default, Vimspector uses something a bit like this: +> + nnoremenu WinBar.■\ Stop :call vimspector#Stop( { 'interactive': v:false } ) + nnoremenu WinBar.▶\ Cont :call vimspector#Continue() + nnoremenu WinBar.▷\ Pause :call vimspector#Pause() + nnoremenu WinBar.↷\ Next :call vimspector#StepOver() + nnoremenu WinBar.→\ Step :call vimspector#StepInto() + nnoremenu WinBar.←\ Out :call vimspector#StepOut() + nnoremenu WinBar.⟲: :call vimspector#Restart() + nnoremenu WinBar.✕ :call vimspector#Reset( { 'interactive': v:false } ) +< +If you prefer a different layout or if the unicode symbols don't render +correctly in your font, you can customise this in the 'VimspectorUICreated' +autocommand, for example: +> + func! CustomiseUI() + call win_gotoid( g:vimspector_session_windows.code ) + " Clear the existing WinBar created by Vimspector + nunmenu WinBar + " Cretae our own WinBar + nnoremenu WinBar.Kill :call vimspector#Stop( { 'interactive': v:true } ) + nnoremenu WinBar.Continue :call vimspector#Continue() + nnoremenu WinBar.Pause :call vimspector#Pause() + nnoremenu WinBar.Step\ Over :call vimspector#StepOver() + nnoremenu WinBar.Step\ In :call vimspector#StepInto() + nnoremenu WinBar.Step\ Out :call vimspector#StepOut() + nnoremenu WinBar.Restart :call vimspector#Restart() + nnoremenu WinBar.Exit :call vimspector#Reset() + endfunction + + augroup MyVimspectorUICustomistaion + autocmd! + autocmd User VimspectorUICreated call s:CustomiseUI() + augroup END +< +------------------------------------------------------------------------------- + *vimspector-example* +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' function 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'): +> + " 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 +< +=============================================================================== + *vimspector-faq* +FAQ ~ + +1. Q: Does it work? A: Yeah. It's a bit unpolished. + +2. Q: Does it work with _this_ language? A: Probably, but it won't + necessarily be easy to work out what to put in the '.vimspector.json'. As + you can see above, some of the servers aren't really editor agnostic, and + require very-specific unique handling. + +3. How do I stop it starting a new Terminal.app on macOS? See this comment + [49] + +4. Can I specify answers to the annoying questions about exception + breakpoints in my '.vimspector.json' ? Yes, see here [23]. + +5. Do I have to specify the file to execute in '.vimspector.json', or could + it be the current vim file? You don't need to. You can specify $file for + the current active file. See here [20] for complete list of replacements + in the configuration file. + +6. You allow comments in '.vimspector.json', but Vim highlights these as + errors, do you know how to make this not-an-error? Yes, put this in + '~/.vim/after/syntax/json.vim': +> + syn region jsonComment start="/\*" end="\*/" + hi link jsonCommentError Comment + hi link jsonComment Comment +< +1. What is the difference between a 'gadget' and an 'adapter'? A gadget is + something you install with ':VimspectorInstall' or 'install_gadget.py', + an 'adapter' is something that Vimspector talks to (actually it's the + Vimspector config describing that thing). These are _usually_ one-to-one, + but in theory a single gadget can supply multiple 'adapter' configs. + Typically this happens when a 'gadget' supplies different 'adapter' + config for, say remote debugging, or debugging in a container, etc. + +2. The signs and winbar display funny symbols. How do I fix them? See this + and this + +3. What's this telemetry stuff all about? Are you sending my data to evil + companies? Debug adapters (for some reason) send telemetry data to + clients. Vimspector simply displays this information in the output + window. It _does not_ and _will not ever_ collect, use, forward or + otherwise share any data with any third parties. + +=============================================================================== + *vimspector-references* +References ~ + +[1] https://puremourning.github.io/vimspector-web/ +[2] https://puremourning.github.io/vimspector/configuration.html +[3] https://github.com/puremourning/vimspector/workflows/Build/badge.svg?branch=master +[4] https://gitter.im/vimspector/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge +[5] https://badges.gitter.im/vimspector/Lobby.svg +[6] https://puremourning.github.io/vimspector-web/img/vimspector-overview.png +[7] https://asciinema.org/a/VmptWmFHTNLPfK3DVsrR2bv8S +[8] https://asciinema.org/a/VmptWmFHTNLPfK3DVsrR2bv8S.svg +[9] https://asciinema.org/a/1wZJSoCgs3AvjkhKwetJOJhDh +[10] https://asciinema.org/a/1wZJSoCgs3AvjkhKwetJOJhDh.svg +[11] https://github.com/go-delve/delve +[12] https://github.com/puremourning/vimspector/wiki/languages +[13] https://asciinema.org/a/Hfu4ZvuyTZun8THNen9FQbTay +[14] https://asciinema.org/a/Hfu4ZvuyTZun8THNen9FQbTay.svg +[15] https://trello.com/b/yvAKK0rD/vimspector +[16] http://www.apache.org/licenses/LICENSE-2.0 +[17] https://greyhoundrescuewales.co.uk +[18] https://www.cancerresearchuk.org +[19] https://iccf.nl +[20] https://puremourning.github.io/vimspector/configuration.html#replacements-and-variables +[21] https://puremourning.github.io/vimspector/configuration.html#configuration-selection +[22] €mappings +[23] https://puremourning.github.io/vimspector/configuration.html#exception-breakpoints +[24] https://puremourning.github.io/vimspector-web/img/vimspector-code-window.png +[25] https://puremourning.github.io/vimspector-web/img/vimspector-locals-window.png +[26] https://puremourning.github.io/vimspector-web/img/vimspector-variable-eval-hover.png +[27] https://puremourning.github.io/vimspector-web/img/vimspector-watch-window.png +[28] https://puremourning.github.io/vimspector-web/img/vimspector-callstack-window.png +[29] https://puremourning.github.io/vimspector-web/img/vimspector-output-window.png +[30] https://github.com/Microsoft/vscode-cpptools +[31] https://puremourning.github.io/vimspector/configuration.html#remote-debugging-support +[32] https://github.com/vadimcn/vscode-lldb#features +[33] https://github.com/microsoft/debugpy +[34] https://github.com/microsoft/debugpy#debugpy-cli-usage +[35] https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings +[36] https://github.com/microsoft/debugpy/wiki/Debugging-over-SSH +[37] https://github.com/Microsoft/vscode-python +[38] https://github.com/puremourning/vimspector/issues/105 +[39] https://github.com/puremourning/TclProDebug +[40] https://github.com/go-delve/delve/tree/master/Documentation/installation +[41] https://github.com/golang/vscode-go/blob/master/docs/debugging.md#troubleshooting +[42] https://github.com/Microsoft/java-debug +[43] https://github.com/ycm-core/YouCompleteMe#java-semantic-completion +[44] https://github.com/ycm-core/YouCompleteMe#troubleshooting +[45] https://code.visualstudio.com/docs/java/java-debugging +[46] https://github.com/puremourning/vimspector/issues/3 +[47] https://github.com/tomblind/local-lua-debugger-vscode +[48] https://github.com/puremourning/vimspector/issues/3#issuecomment-576916076 +[49] https://github.com/puremourning/vimspector/issues/90#issuecomment-577857322 + +vim: ft=help From 244d7d8fdfb073c8befbc777836b675459aae002 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 22 Mar 2021 14:45:21 +0000 Subject: [PATCH 565/609] Add the reference material to the vim doc too --- README.md | 2 +- doc/vimspector-ref.txt | 1085 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1086 insertions(+), 1 deletion(-) create mode 100644 doc/vimspector-ref.txt diff --git a/README.md b/README.md index 952d07f..6979e44 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ runtime dependencies). They are categorised by their level of support: | C, C++, Rust etc. | Tested | `--all` or `--enable-c` (or cpp) | vscode-cpptools | mono-core | | Rust, C, C++, etc. | Supported | `--force-enable-rust` | CodeLLDB | Python 3 | | Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | -| Go | Tested | `--enable-go` | vscode-go | Go, [Delve][] | +| Go | Tested | `--enable-go` | vscode-go | Node, Go, [Delve][] | | TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | | Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | | Lua | Supported | `--all` or `--enable-lua` | local-lua-debugger-vscode | Node >=12.13.0, Npm, Lua interpreter | diff --git a/doc/vimspector-ref.txt b/doc/vimspector-ref.txt new file mode 100644 index 0000000..367d409 --- /dev/null +++ b/doc/vimspector-ref.txt @@ -0,0 +1,1085 @@ +*vimspector-ref* + +=============================================================================== +Contents ~ + + 1. Introduction |vimspector-ref-introduction| + 2. title: Configuration |vimspector-ref-title-configuration| + 3. Concepts |vimspector-ref-concepts| + 1. Debug adapter configuration |vimspector-ref-debug-adapter-configuration| + 2. Debug profile configuration |vimspector-ref-debug-profile-configuration| + 3. Replacements and variables |vimspector-ref-replacements-variables| + 1. The splat operator |vimspector-ref-splat-operator| + 2. Default values |vimspector-ref-default-values| + 3. Coercing Types |vimspector-ref-coercing-types| + 4. Configuration Format |vimspector-ref-configuration-format| + 5. Files and locations |vimspector-ref-files-locations| + 6. Adapter configurations |vimspector-ref-adapter-configurations| + 7. Debug configurations |vimspector-ref-debug-configurations| + 1. Configuration selection |vimspector-ref-configuration-selection| + 1. Specifying a default configuration |vimspector-ref-specifying-default-configuration| + 2. Preventing automatic selection |vimspector-ref-preventing-automatic-selection| + 2. Exception Breakpoints |vimspector-ref-exception-breakpoints| + 8. Predefined Variables |vimspector-ref-predefined-variables| + 9. Remote Debugging Support |vimspector-ref-remote-debugging-support| + 1. Python (debugpy) Example |vimspector-ref-python-example| + 2. C-family (gdbserver) Example |vimspector-ref-c-family-example| + 3. Docker Example |vimspector-ref-docker-example| + 10. Appendix: Configuration file format |vimspector-ref-appendix-configuration-file-format| + 11. Appendix: Editor configuration |vimspector-ref-appendix-editor-configuration| + 12. References |vimspector-ref-references| + +=============================================================================== + *vimspector-ref-introduction* +Introduction ~ + +=============================================================================== + *vimspector-ref-title-configuration* +title: Configuration ~ + +This document defines the supported format for project and adapter +configuration for Vimspector. + +- Concepts + + - Debug adapter configuration + - Debug profile configuration + - Replacements and variables + - The splat operator + - Default values + - Coercing Types + +- Configuration Format +- Files and locations +- Adapter configurations +- Debug configurations + + - Configuration selection + + - Specifying a default configuration + - Preventing automatic selection + + - Exception Breakpoints + +- Predefined Variables +- Remote Debugging Support + + - Python (debugpy) Example + - C-family (gdbserver) Example + - Docker Example + +- Appendix: Configuration file format +- Appendix: Editor configuration + +=============================================================================== + *vimspector-ref-concepts* +Concepts ~ + +As Vimspector supports debugging arbitrary projects, you need to tell it a few +details about what you want to debug, and how to go about doing that. + +In order to debug things, Vimspector requires a Debug Adapter which bridges +between Vimspector and the actual debugger tool. Vimspector can be used with +any debug adapter that implements the Debug Adapter Protocol [1]. + +For each debugging session, you provide a _debug configuration_ which includes +things like: + +- The debug adapter to use (and possibly how to launch and configure it). +- How to connect to the remote host, if remote debugging. +- How to launch or attach to your process. + +Along with optional additional configuration for things like: + +- Exception breakpoints + +------------------------------------------------------------------------------- + *vimspector-ref-debug-adapter-configuration* +Debug adapter configuration ~ + +The adapter to use for a particular debug session can be specified inline +within the _debug configuration_, but more usually the debug adapter is defined +separately and just referenced from the _debug configuration_. + +The adapter configuration includes things like: + +- How to launch or connect to the debug adapter +- How to configure it for PID attachment +- How to set up remote debugging, such as how to launch the process remotely + (for example, under 'gdbserver', 'ptvsd', etc.) + +------------------------------------------------------------------------------- + *vimspector-ref-debug-profile-configuration* +Debug profile configuration ~ + +Projects can have many different debug profiles. For example you might have all +of the following, for a given source tree: + +- Remotely launch c++ the process, and break on 'main' +- Locally Python test and break exception +- Remotely attach to a c++ process +- Locally launch a bash script +- Attach to a JVM listening on a port + +Each of these represents a different use case and a different _debug +configuration_. As mentioned above, a _debug configuration_ is essentially: + +- The adapter to use + +- The type of session (launch or attach), and whether or not to do it + remotely + +- The configuration to pass to the adapter in order to launch or attach to + the process. + +The bulk of the configuration is the last of these, which comprises +adapter-specific options, as the Debug Adapter Protocol does not specify any +standard for launch or attach configuration. + +------------------------------------------------------------------------------- + *vimspector-ref-replacements-variables* +Replacements and variables ~ + +Vimspector _debug configuration_ is intended to be as general as possible, and +to be committed to source control so that debugging your applications becomes a +simple, quick and pain-free habit (e.g. answering questions like "what happens +if..." with "just hit F5 and step through!"). + +Therefore it's important to abstract certain details, like runtime and +build-time paths, and to parameterise the _debug configuration_. Vimspector +provides a simple mechanism to do this with '${replacement}' style +replacements. + +The values available within the '${...}' are defined below, but in summary the +following are supported: + +- Environment variables, such as '${PATH}' +- Predefined variables, such as '${workspaceRoot}', '${file}' etc. +- Configuration-defined variables, either provided by the adapter + configuration or debug configuration, or from running a simple shell + command. +- Anything else you like - the user will be asked to provide a value. + +If the latter 2 are confusing, for now, suffice to say that they are how +Vimspector allows parameterisation of debug sessions. The [Vimspector +website][website-getting-started] has a good example of where this sort of +thing is useful: accepting the name of a test to run. + +But for now, consider the following example snippet: +> + { + "configurations": { + "example-debug-configuration": { + // This is a single-line comment explaining the purpose + "adapter": "example-adapter-name", + "variables": { + "SecretToken": { // Variables should start with upper-case letters + "shell" : [ "cat", "${HOME}/.secret_token" ] + } + }, + "configuration": { + "request": "launch" /* or it could be "attach" */, + "program": [ + "${fileBasenameNoExtension}", + "-c", "configuration_file.cfg", + "-u", "${USER}", + "--test-identifier", "${TestIdentifier}", + "--secret-token", "${SecretToken}" + ] + }, + "breakpoints": { + "exception": { + "caught": "", + "uncaught": "Y" + } + } + } + } + } +< +In this (fictitious) example the 'program' launch configuration item contains +the following variable substitutions: + +- '${fileBasenameNoExtension}' - this is a Predefined Variable, set by + Vimspector to the base name of the file that's opened in Vim, with its + extension removed ('/path/to/xyz.cc' -> 'xyz'). + +- '${USER}' - this refers to the Environment Variable 'USER'. + +- '${TestIdentifier}' - this variable is not defined, so the user is asked to + provide a value interactively when starting debugging. Vimspector remembers + what they said and provides it as the default should they debug again. + +- '${SecretToken}' - this variable is provided by the configuration's + 'variables' block. Its value is taken from the 'strip''d result of running + the shell command. Note these variables can be supplied by both the debug + and adapter configurations and can be either static strings or shell + commands. + +------------------------------------------------------------------------------- + *vimspector-ref-splat-operator* +The splat operator ~ + +Often we want to create a single '.vimspector.json' entry which encompasses +many use cases, as it is tedious to write every use case/start up option in +JSON. This is why we have the replacement variables after all. + +Frequently debug adapters request command arguments as a JSON array, for +example: +> + "args": [ "one", "two three", "four" ], +< +To help with this sort of case, Vimspector supports a 'splat' operator for +replacement variables operating within lists. The syntax is: '"*${var}', which +means roughly "splice the contents of '${var}' into the list at this position". +'${var}' is parsed like a shell command (using python's 'shlex' parser) and +each word is added as a list item. + +For example: +> + "args": [ "*${CommandLineArgs}" ] +< +This would: + +- Ask the user to provide the variable 'CommandLineArgs'. Let's say they + entered 'one "two three" four' +- Split 'CommandLineArgs' like shell arguments: 'one', 'two three' and 'four' +- Set 'args' in the settings dict to: '[ "one", "two three", "four" ]' + +You can also combine with static values: +> + "args": [ "First", "*${CommandLineArgs}", "Last" ] +< +This would yield the intuitive result: '[ "First", "one", "two three", "four", +"Last" ]' + +------------------------------------------------------------------------------- + *vimspector-ref-default-values* +Default values ~ + +You can specify replacements with default values. In this case if the user has +not specified a value, they are prompted but with the default value +pre-populated, allowing them to just press return to accept the default. + +The syntax is '${variableName:default value}'. The default value can contain +any character, but to include a '}' you must escape it with a backslash. To +include a backslash in the JSON you must write '\\', as in: +> + { "key": "${value:default {\\} stuff}" } +< +The default value can also be a replacement variable. However, this _must_ be a +variable that's already defined, such as one of the predefined variables, or +one specified in a 'variables' block. In order to reference them, you _must_ +use '${var}' syntax and you _must_ escape the closing '}'. For example, the is +a common and useful case: +> + { + "configuration": { + "program": "${script:${file\\}}" + } + } +< +This will prompt the user to specify 'script', but it will default to the path +to the current file. + +------------------------------------------------------------------------------- + *vimspector-ref-coercing-types* +Coercing Types ~ + +Sometimes, you want to provide an option for a boolean parameter, or want to +allow the user to specify more than just strings. Vimspector allows you to do +this, ensuring that the resulting JSON is valid. This is done by interpreting a +value as a JSON string and substituting the resulting JSON value in its place. + +This is easier to explain with an example. Let's say we want to offer the +ability to break on entry, as an option for the user. The launch configuration +requires 'stopOnEntry' to be a bool. This doesn't work: +> + "stopOnEntry": "${StopOnEntry}" +< +The reason is that if the user types 'true', the resulting object is: +> + "stopOnEntry": "true" +< +The problem being that is a string, not a boolean. So Vimspector allows you to +re-interpret the string as a JSON value and use that instead. To do this, add +'#json' to the key's name. You can even add a default, like this: +> + "stopOnEntry#json": "${stopOnEntry:true}" +< +If the user accepts the default, the resulting string '"true"' is coerced to a +JSON value 'true', and the suffix is stripped fom the key, resulting in the +following: +> + "stopOnEntry#json": true +< +Which is what we need. + +If you happen to have a key that already ends in '#json' (unlikely!), then you +can force Vimspector to treat the value as a string by appending '#s', as in: +> + "unlikelyKeyName#json#s": "this is a string, not JSON data" +< +**_Advanced usage:_** + +The most common usage for this is for number and bool types, but it works for +objects too. If you want to be able to specify a whole object (e.g. a whole +'env' dict), then you can do that too: +> + "env#json": "${Environment:{\\}}" +< +The default value here is '{}' (note the '}' must be escaped!). The user can +then enter something like '{ "MYVAR": "MyValue", "OTHER": "Other" }' and the +resulting object would be: +> + "env": { + "MYVAR": "MyValue", + "OTHER": "Other" + } +< +It also works for lists, though the splat operator is usually more convenient +for that. + +=============================================================================== + *vimspector-ref-configuration-format* +Configuration Format ~ + +All Vimspector configuration is defined in a JSON object. The complete +specification of this object is available in the JSON Schema [2], but the basic +format for the configuration object is: +> + { + "adapters": { }, + "configurations": { } + } +< +The 'adapters' key is actually optional, as '' can be +embedded within '', though this is not recommended usage. + +=============================================================================== + *vimspector-ref-files-locations* +Files and locations ~ + +The above configuration object is constructed from a number of configuration +files, by merging objects in a specified order. + +In a minimal sense, the only file required is a '.vimspector.json' file in the +root of your project which defines the full configuration object [2], but it is +usually useful to split the 'adapters' configuration into a separate file (or +indeed one file per debug adapter). + +The following sections describe the files that are read and use the following +abbreviations: + +- '' means the path to the Vimspector installation (such as + '$HOME/.vim/pack/vimspector/start/vimspector') + +- '' is either 'macos' or 'linux' depending on the host operating system. + +- '' is the Vim filetype. Where multiple filetypes are in effect, + typically all filetypes are checked. + +=============================================================================== + *vimspector-ref-adapter-configurations* +Adapter configurations ~ + +Vimspector reads a series of files to build the 'adapters' object. The +'adapters' objects are merged in such a way that a definition for an adapter +named 'example-adapter' in a later file _completely replaces_ a previous +definition. + +- '/gadgets//.gadgets.json' - the file written by + 'install_gadget.py' and not usually edited by users. + +- '/gadgets//.gadgets.d/*.json' (sorted alphabetically). + These files are user-supplied and override the above. + +- The first such '.gadgets.json' file found in all parent directories of the + file open in Vim. + +- The '.vimspector.json' and any filetype-specific configurations (see below) + +In all cases, the required format is: +> + { + "$schema": "https://puremourning.github.io/vimspector/schema/gadgets.schema.json#", + "adapters": { + "": { + + } + } + } +< +Each adapters block can define any number of adapters. As mentioned, if the +same adapter name exists in multiple files, the last one read takes precedence +and _completely replaces_ the previous configuration. In particular that means +you can't just override one option, you have to override the whole block. + +Adapter configurations are re-read at the start of each debug session. + +The specification for the gadget object is defined in the [gadget schema][]. + +=============================================================================== + *vimspector-ref-debug-configurations* +Debug configurations ~ + +There are two locations for debug configurations for a project: + +- '/configurations///*.json' +- '.vimspector.json' in the project source + +Typically, the debug configurations are read from '.vimspector.json'. The file +is found (like '.gadgets.json' above) by recursively searching up the directory +hierarchy from the directory of the file open in Vim. The first file found is +read and no further searching is done. + +Only a single '.vimspector.json' is read. If one is found, the location of this +file is used for '${workspaceRoot}' and other workspace-relative paths. + +In addition, users can create filetype-specific configurations in the +Vimspector installation directory. This can be useful where the parameters for +the debug session for a particular filetype are always known in advance, or can +always be entered by the user. This allows for debugging to "just work" without +any modification to the project source (no need to add a '.vimspector.json'). +In this case, the '${workspaceRoot}' and workspace-relative paths are +interpreted relative to the file open in Vim. This isn't ideal, but there is no +other obvious way to default this variable. + +As with gadgets, any debug configurations appearing within '.vimspector.json' +override any that appear in the common configuration dir. + +Debug configurations are re-read at the start of each debug session, so +modifications are picked up without any restarts of Vim. + +The specification for the gadget object is defined in the schema [2], but a +typical example looks like this: +> + { + "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#", + "configurations": { + "": { + "adapter": "", + "configuration": { + "request": "", + + } + } + } + } +< +------------------------------------------------------------------------------- + *vimspector-ref-configuration-selection* +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'. + +------------------------------------------------------------------------------- + *vimspector-ref-specifying-default-configuration* +Specifying a default configuration ~ + +As noted, you can specify a default configuration with '"default": true': +> + { + "configurations": { + "use this one": { + "default": true, + "adapter": " ... ", + "configuation": { + // ... + } + }, + "don't use this one": { + // ... + } + } + } +< +If multiple configurations are found with 'default' set to 'true', then the +user is prompted anyway. + +------------------------------------------------------------------------------- + *vimspector-ref-preventing-automatic-selection* +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: +> + "configurations": { + "Don't use this by default!": { + "autoselect": false, + "adapter": " ... ", + "configuation": { + // ... + } + } + } +< +Setting 'autoselect' to 'false' overrides setting 'default' to 'true'. + +------------------------------------------------------------------------------- + *vimspector-ref-exception-breakpoints* +Exception Breakpoints ~ + +Debug adapters have arbitrary configuration for exception breakpoints. Normally +this is presented as a series of question to the user on starting the debug +session. The question includes the name of the exception breakpoint option, the +default and the list of valid responses (usually 'Y' or 'N'). + +You can pre-configure the answers to these questions in the 'breakpoints' +section of the debug configuration. For each question, take the name provided +and configure the response 'exception' mapping in the 'breakpoints' mapping. If +the configured response is empty string, the debug adapter default will be +used. + +Referring to the above example, the following tells the debug adapter to use +the default value for 'caught' exceptions and to break on 'uncaught' exception: +> + { + "configurations": { + "example-debug-configuration": { + "adapter": "example-adapter-name", + "breakpoints": { + "exception": { + "caught": "", + "uncaught": "Y" + } + }, + ... +< +The keys in the 'exception' mapping are what Vimspector includes in the prompt. +For example, when prompted with the following: +> + cpp_throw: Break on C++: on throw (Y/N/default: Y)? +< +The exception breakpoint "type" is 'cpp_throw' and the default is 'Y'. + +Similarly: +> + cpp_catch: Break on C++: on catch (Y/N/default: N)? +< +The exception breakpoint "type" is 'cpp_catch' and the default is 'N'. + +Use the following to set the values in configuration and not get asked: +> + "configurations": { + "example-debug-configuration": { + "adapter": "example-adapter-name", + "breakpoints": { + "exception": { + "cpp_throw": "Y", + "cpp_catch": "Y" + } + }, +< +To just accept the defaults for these exception breakpoint types, don't specify +a value, as in : +> + "configurations": { + "example-debug-configuration": { + "adapter": "example-adapter-name", + "breakpoints": { + "exception": { + "cpp_throw": "", + "cpp_catch": "" + } + }, +< +=============================================================================== + *vimspector-ref-predefined-variables* +Predefined Variables ~ + +The following variables are provided: + +- '${dollar}' - has the value '$', can be used to enter a literal dollar +- '$$' - a literal dollar +- '${workspaceRoot}' - the path of the folder where '.vimspector.json' was + found +- '${workspaceFolder}' - the path of the folder where '.vimspector.json' was + found +- '${gadgetDir}' - path to the OS-specific gadget dir ('/gadgets/') +- '${file}' - the current opened file +- '${relativeFile}' - the current opened file relative to 'workspaceRoot' +- '${fileBasename}' - the current opened file's 'basename' +- '${fileBasenameNoExtension}' - the current opened file's 'basename' with no + file extension +- '${fileDirname}' - the current opened file's 'dirname' +- '${fileExtname}' - the current opened file's extension +- '${cwd}' - the current working directory of the active window on launch +- '${unusedLocalPort}' - an unused local TCP port + +=============================================================================== + *vimspector-ref-remote-debugging-support* +Remote Debugging Support ~ + +Vimspector has in-built support for executing remote debuggers (such as +'gdbserver', 'debugpy', 'llvm-server' etc.). This is useful for environments +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 paswordless SSH between the local +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 familiar with ssh or such, you might need to independently +research that. + +Vimspector's tools are intended to automate your existing process for setting +this up rather than to offer batteries-included approach. Ultimately, all +Vimspector is going to do is run your commands over SSH, or docker, and +co-ordinate with the adapter. + +------------------------------------------------------------------------------- + *vimspector-ref-python-example* +Python (debugpy) Example ~ + +Here is some examples using the Vimspector built-in remote support (using SSH) +to remotely launch and attach a python application and connect to it using +debugpy. + +The usage pattern is to hit '', enter 'host' (the host where your app +runs), 'account' (the account it runs under), and 'port' (a port that will be +opened on the remote host). Vimspector also supports exec'ing into Docker run +containers with 'container' (the container name or id your app is running in). +Vimspector then orchestrates the various tools to set you up. +> + { + "adapters": { + "python-remote": { + "port": "${port}", + "host": "${host}", + "launch": { + "remote": { + "host": "${host}", // Remote host to ssh to (mandatory if not using container) + "account": "${account}", // User to connect as (optional) + + // Optional.... Manual additional arguments for ssh + // "ssh": { + // "args": [ "-o", "StrictHostKeyChecking=no" ] + // }, + + // Command to launch the debugee and attach the debugger; + // %CMD% replaced with the remote-cmdLine configured in the launch + // configuration. (mandatory) + "runCommand": [ + "python", "-m", "debugpy", + "--listen", "0.0.0.0:${port}", + "--wait-for-client", + "%CMD%" + ] + + // Optional alternative to runCommand (if you need to run multiple + // commands) + // "runCommands": [ + // [ /* first command */ ], + // [ /* second command */ ] + // ] + + } + + // optional delay to wait after running runCommand(s). This is often + // needed because of the way docker handles TCP, or if you're using some + // wrapper (e.g. to start the JVM) + // "delay": "1000m" // format as per :help sleep + }, + "attach": { + "remote": { + "host": "${host}", // Remote host to ssh to (mandatory if not using container) + "account": "${account}", // User to connect as (optional) + // Command to get the PID of the process to attach (mandatory) + "pidCommand": [ + // + // Remember taht you can use ${var} to ask for input. I use this to + // call a custom command to returm the PID for a named service, so + // here's an examle: + // + "/path/to/secret/script/GetPIDForService", "${ServiceName}" + ], + + // Command to attach the debugger; %PID% replaced with output of + // pidCommand above (mandatory) + "attachCommand": [ + "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}", + "--pid", "%PID%" + ] + + // Optional alternative to attachCommand (if you need to run multiple + // commands) + // "attachCommands": [ + // [ /* first command */ ], + // [ /* second command */ ] + // ], + + // Optional.... useful with buggy gdbservers to kill -TRAP %PID% + // "initCompleteCommand": [ + // /* optional command to run after initialized */ + // ] + + // Optional.... Manual additional arguments for ssh + // "ssh": { + // "args": [ "-o", "StrictHostKeyChecking=no" ] + // }, + } + // optional delay to wait after running runCommand(s). This is often + // needed because of the way docker handles TCP, or if you're using some + // wrapper (e.g. to start the JVM) + // "delay": "1000m" // format as per :help sleep + } + } + }, + "configurations": { + "remote-launch": { + "adapter": "python-remote", + + "remote-request": "launch", + "remote-cmdLine": [ + "${RemoteRoot}/${fileBasename}", "*${args}" + ], + + "configuration": { + "request": "attach", + "pathMappings": [ + { + "localRoot": "${workspaceRoot}", + "remoteRoot": "${RemoteRoot}" + } + ] + } + }, + "remote-attach": { + "variables": { + // Just an example of how to specify a variable manually rather than + // vimspector asking for input from the user + "ServiceName": "${fileBasenameNoExtention}" + }, + + "adapter": "python-remote", + "remote-request": "attach", + + "configuration": { + "request": "attach", + "pathMappings": [ + { + "localRoot": "${workspaceRoot}", + "remoteRoot": "${RemoteRoot}" + } + ] + } + } + } + } +< +------------------------------------------------------------------------------- + *vimspector-ref-c-family-example* +C-family (gdbserver) Example ~ + +This example uses Vimspector to remotely launch or attach to a binary using +'gdbserver' and then instructs vscode-cpptools to attach to that 'gdbserver'. + +The approach is very similar to the above for python, just that we use +gdbserver and have to tell cpptools a few more options. +> + { + "adapters": { + "cpptools-remote": { + "command": [ + "${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7" + ], + "name": "cppdbg", + "configuration": { + "type": "cppdbg" + }, + "launch": { + "remote": { + "host": "${host}", + "account": "${account}", + "runCommand": [ + "gdbserver", + "--once", + "--no-startup-with-shell", + "--disable-randomisation", + "0.0.0.0:${port}", + "%CMD%" + } + }, + "attach": { + "remote": { + "host": "${host}", + "account": "${account}", + "pidCommand": [ + "/path/to/secret/script/GetPIDForService", "${ServiceName}" + ], + "attachCommand": [ + "gdbserver", + "--once", + "--attach", + "0.0.0.0:${port}", + "%PID%" + ], + // + // If your application is started by a wrapper script, then you might + // need the followin. GDB can't pause an application because it only + // sends the signal to the process group leader. Or something. + // Basically, if you find that everything just hangs and the + // application never attaches, try using the following to manually + // force the trap signal. + // + "initCompleteCommand": [ + "kill", + "-TRAP", + "%PID%" + ] + } + } + } + }, + "configurations": { + "remote launch": { + "adapter": "cpptools-remote", + "remote-cmdLine": [ "/path/to/the/remote/executable", "args..." ], + "remote-request": "launch", + "configuration": { + "request": "attach", // yes, attach! + + "program": "/path/to/the/local/executable", + "MIMode": "gdb", + "miDebuggerAddress": "${host}:${port}" + } + }, + "remote attach": { + "adapter": "cpptools-remote", + "remote-request": "attach", + "configuration": { + "request": "attach", + + "program": "/path/to/the/local/executable", + "MIMode": "gdb", + "miDebuggerAddress": "${host}:${port}" + } + } + } +< +------------------------------------------------------------------------------- + *vimspector-ref-docker-example* +Docker Example ~ + +This example uses Vimspector to remotely launch or attach to a docker container +port. +> + { + "adapters": { + "python-remote": { + "port": "${port}", + "launch": { + "remote": { + "container": "${container}", // Docker container id or name to exec into to. + + // Command to launch the debugee and attach the debugger; + // %CMD% replaced with the remote-cmdLine configured in the launch + // configuration. (mandatory) + "runCommand": [ + "python", "-m", "debugpy", + "--listen", "0.0.0.0:${port}", + "--wait-for-client", + "%CMD%" + ] + + // Optional alternative to runCommand (if you need to run multiple + // commands) + // "runCommands": [ + // [ /* first command */ ], + // [ /* second command */ ] + // ] + + } + + // optional delay to wait after running runCommand(s). This is often + // needed because of the way docker handles TCP + "delay": "1000m" // format as per :help sleep + }, + "attach": { + "remote": { + "container": "${container}", // Docker container id or name to exec into. + // Command to get the PID of the process to attach (mandatory) + // This command gets appended to "docker exec ${container}" + "pidCommand": [ + // + // Remember taht you can use ${var} to ask for input. I use this to + // call a custom command to returm the PID for a named service, so + // here's an examle: + // + "sh", "-c", "pgrep", "-f", "${filename}" + ], + + // Command to attach the debugger; %PID% replaced with output of + // pidCommand above (mandatory) + "attachCommand": [ + "sh", "-c", "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}", + "--pid", "%PID%" + ] + + // Optional alternative to attachCommand (if you need to run multiple + // commands) + // "attachCommands": [ + // [ /* first command */ ], + // [ /* second command */ ] + // ], + + // Optional.... useful with buggy gdbservers to kill -TRAP %PID% + // "initCompleteCommand": [ + // /* optional command to run after initialized */ + // ] + + } + + // optional delay to wait after running runCommand(s). This is often + // needed because of the way docker handles TCP, or if you're using some + // wrapper (e.g. to start the JVM) + "delay": "1000m" // format as per :help sleep + } + } + }, + "configurations": { + "remote-launch": { + "adapter": "python-remote", + + "remote-request": "launch", + "remote-cmdLine": [ + "${RemoteRoot}/${fileBasename}", "*${args}" + ], + + "configuration": { + "request": "attach", + "pathMappings": [ + { + "localRoot": "${workspaceRoot}", + "remoteRoot": "${RemoteRoot}" + } + ] + } + }, + "remote-attach": { + "variables": { + // Just an example of how to specify a variable manually rather than + // vimspector asking for input from the user + "FileName": "${fileName}" + }, + + "adapter": "python-remote", + "remote-request": "attach", + + "configuration": { + "request": "attach", + "pathMappings": [ + { + "localRoot": "${workspaceRoot}", + "remoteRoot": "${RemoteRoot}" + } + ] + } + } + } + } +< +=============================================================================== + *vimspector-ref-appendix-configuration-file-format* +Appendix: Configuration file format ~ + +The configuration files are text files which must be UTF-8 encoded. They +contain a single JSON object, along with optional comments. + +Comments are "c-style", i.e.: + +- '// single line comment ...' +- '/* inline comment */' + +There is much debate about whether JSON files should contain comments. I have +added them because they are useful in the context of configuration files. +Unforutnately this may mean your editor doesn't like them (they are strictly +invalid JSON) so it's up to you if you use them. + +Technically, Vimspector uses JSON minify [3] to strip comments before parsing +the JSON. + +=============================================================================== + *vimspector-ref-appendix-editor-configuration* +Appendix: Editor configuration ~ + +If you would like some assistance with writing the JSON files, and your editor +of choice has a way to use a language server, you can use the VSCode JSON +language server [4]. + +It is recommended to include the '$schema' declaration as in the above +examples, but if that isn't present, the following JSON language server +configuration [5] is recommened to load the schema from the Internet: +> + { + "json": { + "schemas": [ + { + "fileMatch": [ ".vimspector.json" ], + "url": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json" + }, + { + "fileMatch": [ ".gadgets.json", ".gadgets.d/*.json" ], + "url": "https://puremourning.github.io/vimspector/schema/gadgets.schema.json" + } + ] + } + } +< +If your language server client of choice happens to be YouCompleteMe [6], then +the following '.ycm_extra_conf.py' is good enough to get you going, after +following the instructions in the lsp-examples [7] repo to get the server set +up: +> + VIMSPECTOR_HOME = '/path/to/vimspector' # TODO: Change this + + def Settings( **kwargs ): + if kwargs[ 'language' ] == 'json': + return { + 'ls': { + 'json': { + 'schemas': [ + { + 'fileMatch': [ '.vimspector.json' ], + 'url': f'file://{VIMSPECTOR_HOME}/docs/schema/vimspector.schema.json' + }, + { + 'fileMatch': [ '.gadgets.json', '.gadgets.d/*.json' ], + 'url': f'file://{VIMSPECTOR_HOME}/docs/schema/gadgets.schema.json' + } + ] + } + } + } + + return None # Or your existing Settings definition.... +< +This configuration can be adapted to any other LSP-based editor configuration +and is provided just as an example. + +=============================================================================== + *vimspector-ref-references* +References ~ + +[1] https://microsoft.github.io/debug-adapter-protocol/ +[2] http://puremourning.github.io/vimspector/schema/vimspector.schema.json +[3] https://github.com/getify/JSON.minify +[4] https://github.com/vscode-langservers/vscode-json-languageserver +[5] https://github.com/vscode-langservers/vscode-json-languageserver#settings +[6] https://github.com/ycm-core/YouCompleteMe +[7] https://github.com/ycm-core/lsp-examples + +vim: ft=help From 054ea35428e5e8d6abe12b6a535a3b0426d4874e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 22 Mar 2021 15:13:58 +0000 Subject: [PATCH 566/609] Add a way to test with dlv directly. Currrently not ready for the big time --- support/test/go/hello_world/.vimspector.json | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/support/test/go/hello_world/.vimspector.json b/support/test/go/hello_world/.vimspector.json index 8ab5091..4613b98 100644 --- a/support/test/go/hello_world/.vimspector.json +++ b/support/test/go/hello_world/.vimspector.json @@ -1,4 +1,18 @@ { + "adapters": { + "dlv-dap": { + "variables": { + "port": "${unusedLocalPort}" + }, + "command": [ + "$HOME/go/bin/dlv", + "dap", + "--listen", + "127.0.0.1:${port}" + ], + "port": "${port}" + } + }, "configurations": { "run": { "adapter": "vscode-go", @@ -12,6 +26,21 @@ "env": { "GO111MODULE": "off" } } }, + "run-dap": { + "adapter": "dlv-dap", + "configuration": { + "request": "launch", + "env": { "GO111MODULE": "off" }, + + "mode": "debug", // debug|test + "program": "${workspaceRoot}/hello-world.go" + + // "args": [], + // "buildFlags": ... + // "stackTraceDepth": ..., + // "showGlobalVariables": true, + } + }, "run-exec": { // NOTE: To use this you _must_ disable optimistaion: // go build -o hello_world -gcflags="all=-N -l" From 0d9e7835a85d6b0e53faeac982d1a4599aaf6fd6 Mon Sep 17 00:00:00 2001 From: puh <-> Date: Mon, 29 Mar 2021 23:11:26 +0300 Subject: [PATCH 567/609] Update CodeLLDB --- python3/vimspector/gadgets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 528f60c..d2c280b 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -453,12 +453,12 @@ GADGETS = { '${version}/${file_name}', }, 'all': { - 'version': 'v1.5.3', + 'version': 'v1.6.1', }, 'macos': { 'file_name': 'codelldb-x86_64-darwin.vsix', 'checksum': - '7505bc1cdfcfd1cb981e2996aec62d63577440709bac31dcadb41a3b4b44631a', + 'b1c998e7421beea9f3ba21aa5706210bb2249eba93c99b809247ee831075262f', 'make_executable': [ 'adapter/codelldb', 'lldb/bin/debugserver', @@ -469,7 +469,7 @@ GADGETS = { 'linux': { 'file_name': 'codelldb-x86_64-linux.vsix', 'checksum': - 'ce7efc3e94d775368e5942a02bf5c326b6809a0b4c389f79ffa6a8f6f6b72139', + 'f2a36cb6971fd95a467cf1a7620e160914e8f11bf82929932ee0aa5afbf6ae6a', 'make_executable': [ 'adapter/codelldb', 'lldb/bin/lldb', @@ -480,7 +480,7 @@ GADGETS = { 'windows': { 'file_name': 'codelldb-x86_64-windows.vsix', 'checksum': - '', + 'ca6a6525bf7719dc95265dc630b3cc817a8c0393b756fd242b710805ffdfb940', 'make_executable': [] }, 'adapters': { From d70d51a614a11f03a64294a48aca134634053713 Mon Sep 17 00:00:00 2001 From: Tony Dwire Date: Wed, 7 Apr 2021 11:50:26 -0500 Subject: [PATCH 568/609] Updated netcoredbg to 1.2.0-761 to enable mac support of async/await --- python3/vimspector/gadgets.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index d2c280b..8be7db7 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -234,11 +234,10 @@ GADGETS = { 'format': 'tar', }, 'all': { - 'version': '1.2.0-738' + 'version': '1.2.0-761' }, 'macos': { 'file_name': 'netcoredbg-osx.tar.gz', - 'version': '1.2.0-635', 'checksum': '71c773e34d358950f25119bade7e3081c4c2f9d71847bd49027ca5792e918beb', }, From 7d83419a4f813aee826eee994b8e419b6ff102b0 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 7 Apr 2021 22:45:58 +0100 Subject: [PATCH 569/609] Update docs bundles --- docs/Gemfile.lock | 50 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index d2eb55f..6a18520 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.3.4) + activesupport (6.0.3.6) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -16,7 +16,7 @@ GEM colorator (1.1.0) commonmarker (0.17.13) ruby-enum (~> 0.5) - concurrent-ruby (1.1.7) + concurrent-ruby (1.1.8) dnsruby (1.61.5) simpleidn (~> 0.1) em-websocket (0.5.2) @@ -30,12 +30,12 @@ GEM faraday-net_http (~> 1.0) multipart-post (>= 1.2, < 3) ruby2_keywords - faraday-net_http (1.0.0) - ffi (1.14.2) + faraday-net_http (1.0.1) + ffi (1.15.0) forwardable-extended (2.6.0) gemoji (3.0.1) - github-pages (209) - github-pages-health-check (= 1.16.1) + github-pages (214) + github-pages-health-check (= 1.17.0) jekyll (= 3.9.0) jekyll-avatar (= 0.7.0) jekyll-coffeescript (= 1.1.1) @@ -50,9 +50,9 @@ GEM jekyll-readme-index (= 0.3.0) jekyll-redirect-from (= 0.16.0) jekyll-relative-links (= 0.6.1) - jekyll-remote-theme (= 0.4.2) + jekyll-remote-theme (= 0.4.3) jekyll-sass-converter (= 1.5.2) - jekyll-seo-tag (= 2.6.1) + jekyll-seo-tag (= 2.7.1) jekyll-sitemap (= 1.4.0) jekyll-swiss (= 1.0.0) jekyll-theme-architect (= 0.1.1) @@ -70,19 +70,19 @@ GEM jekyll-theme-time-machine (= 0.1.1) jekyll-titles-from-headings (= 0.5.3) jemoji (= 0.12.0) - kramdown (= 2.3.0) + kramdown (= 2.3.1) kramdown-parser-gfm (= 1.1.0) liquid (= 4.0.3) mercenary (~> 0.3) minima (= 2.5.1) nokogiri (>= 1.10.4, < 2.0) - rouge (= 3.23.0) + rouge (= 3.26.0) terminal-table (~> 1.4) - github-pages-health-check (1.16.1) + github-pages-health-check (1.17.0) addressable (~> 2.3) dnsruby (~> 1.60) octokit (~> 4.0) - public_suffix (~> 3.0) + public_suffix (>= 2.0.2, < 5.0) typhoeus (~> 1.3) html-pipeline (2.14.0) activesupport (>= 2) @@ -136,15 +136,15 @@ GEM jekyll (>= 3.3, < 5.0) jekyll-relative-links (0.6.1) jekyll (>= 3.3, < 5.0) - jekyll-remote-theme (0.4.2) + jekyll-remote-theme (0.4.3) addressable (~> 2.0) jekyll (>= 3.5, < 5.0) jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) rubyzip (>= 1.3.0, < 3.0) jekyll-sass-converter (1.5.2) sass (~> 3.4) - jekyll-seo-tag (2.6.1) - jekyll (>= 3.3, < 5.0) + jekyll-seo-tag (2.7.1) + jekyll (>= 3.8, < 5.0) jekyll-sitemap (1.4.0) jekyll (>= 3.7, < 5.0) jekyll-swiss (1.0.0) @@ -196,12 +196,12 @@ GEM gemoji (~> 3.0) html-pipeline (~> 2.2) jekyll (>= 3.0, < 5.0) - kramdown (2.3.0) + kramdown (2.3.1) rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) liquid (4.0.3) - listen (3.4.0) + listen (3.5.1) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) @@ -210,9 +210,9 @@ GEM jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.14.3) + minitest (5.14.4) multipart-post (2.1.1) - nokogiri (1.11.1) + nokogiri (1.11.3) mini_portile2 (~> 2.5.0) racc (~> 1.4) octokit (4.20.0) @@ -220,16 +220,16 @@ GEM sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) forwardable-extended (~> 2.6) - public_suffix (3.1.1) + public_suffix (4.0.6) racc (1.5.2) rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) - rexml (3.2.4) - rouge (3.23.0) - ruby-enum (0.8.0) + rexml (3.2.5) + rouge (3.26.0) + ruby-enum (0.9.0) i18n - ruby2_keywords (0.0.2) + ruby2_keywords (0.0.4) rubyzip (2.3.0) safe_yaml (1.0.5) sass (3.7.4) @@ -240,7 +240,7 @@ GEM sawyer (0.8.2) addressable (>= 2.3.5) faraday (> 0.8, < 2.0) - simpleidn (0.1.1) + simpleidn (0.2.1) unf (~> 0.1.4) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) From 13a5a1b947825f710a0aacc0fd8167bd3d42822c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 9 Apr 2021 16:54:38 +0100 Subject: [PATCH 570/609] Fix traceback when +python3 is not availble --- autoload/vimspector.vim | 3 +++ docs/configuration.md | 2 +- plugin/vimspector.vim | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index a0b6c42..1219661 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -13,6 +13,9 @@ " See the License for the specific language governing permissions and " limitations under the License. +if !has( 'python3' ) + finish +endif " Boilerplate {{{ let s:save_cpo = &cpoptions diff --git a/docs/configuration.md b/docs/configuration.md index e736985..a2864b1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -271,7 +271,7 @@ JSON value `true`, and the suffix is stripped fom the key, resulting in the following: ```json - "stopOnEntry#json": true + "stopOnEntry": true ``` Which is what we need. diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 75e2baa..27ce473 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -13,6 +13,13 @@ " See the License for the specific language governing permissions and " limitations under the License. +if !has( 'python3' ) + echohl WarningMsg + echom 'Vimspector unavailable: Requires Vim compiled with +python3' + echohl None + finish +endif + " Boilerplate {{{ let s:save_cpo = &cpoptions set cpoptions&vim From 278fc3cd8c285e3e2f6abff824592a18186912bd Mon Sep 17 00:00:00 2001 From: Tony Dwire Date: Fri, 9 Apr 2021 11:29:17 -0500 Subject: [PATCH 571/609] Update sha256sum of netcoredbg --- python3/vimspector/gadgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 8be7db7..7b807b6 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -239,7 +239,7 @@ GADGETS = { 'macos': { 'file_name': 'netcoredbg-osx.tar.gz', 'checksum': - '71c773e34d358950f25119bade7e3081c4c2f9d71847bd49027ca5792e918beb', + '994e0d001f53af058c94468336dfd1f166a3f3540c9e0de4f9f45f59e6c969fe', }, 'linux': { 'file_name': 'netcoredbg-linux-bionic-amd64.tar.gz', From fa92c2a8d525972bcc97cba9579d9adfca3c859a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 11 Apr 2021 19:23:47 +0100 Subject: [PATCH 572/609] Add a mode for debugging the extremely flaky netcoredbg --- support/test/csharp/.vimspector.json | 65 ++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/support/test/csharp/.vimspector.json b/support/test/csharp/.vimspector.json index 524ae1a..8b2c499 100644 --- a/support/test/csharp/.vimspector.json +++ b/support/test/csharp/.vimspector.json @@ -1,25 +1,52 @@ { - "configurations": { - "launch - netcoredbg": { - "adapter": "netcoredbg", - "configuration": { - "request": "launch", - "program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll", - "args": [], - "stopAtEntry": true - } + "adapters": { + "netcoredbg-debuglog": { + "attach": { + "pidProperty": "processId", + "pidSelect": "ask" }, - "launch - mono": { - "adapter": "vscode-mono-debug", - "configuration": { - "request": "launch", - "program": "${workspaceRoot}/Program.exe", - "console": "integratedTerminal", - "cwd": "${workspaceRoot}", - "args": [], - "env": {} - } + "command": [ + "${gadgetDir}/netcoredbg/netcoredbg", + "--interpreter=vscode", + "--engineLogging=${workspaceRoot}/netcoredbg.engine.log", + "--log=${workspaceRoot}/netcoredbg.log" + ], + "configuration": { + "cwd": "${workspaceRoot}" + }, + "name": "netcoredbg" + } + }, + "configurations": { + "launch - netcoredbg": { + "adapter": "netcoredbg", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll", + "args": [], + "stopAtEntry": true + } + }, + "launch - netcoredbg - with debug log": { + "adapter": "netcoredbg-debuglog", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll", + "args": [], + "stopAtEntry": true + } + }, + "launch - mono": { + "adapter": "vscode-mono-debug", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/Program.exe", + "console": "integratedTerminal", + "cwd": "${workspaceRoot}", + "args": [], + "env": {} } } + } } From dd88e051a408e828af1d54ce8c98e18f41c64b5d Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 13 Apr 2021 17:34:51 +0100 Subject: [PATCH 573/609] Attempt to recover from broken messages --- python3/vimspector/debug_adapter_connection.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/debug_adapter_connection.py b/python3/vimspector/debug_adapter_connection.py index df2ef13..206938a 100644 --- a/python3/vimspector/debug_adapter_connection.py +++ b/python3/vimspector/debug_adapter_connection.py @@ -226,7 +226,12 @@ class DebugAdapterConnection( object ): # self._logger.debug( 'Message received (raw): %s', payload ) - message = json.loads( payload ) + try: + message = json.loads( payload, strict = False ) + except Exception: + self._logger.exception( "Invalid message received: %s", payload ) + self._SetState( 'READ_HEADER' ) + raise self._logger.debug( 'Message received: {0}'.format( message ) ) From a41db89523723dbdde6b3f1381e4de321e67b78c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 13 Apr 2021 18:01:10 +0100 Subject: [PATCH 574/609] Fix get_configurations test --- support/test/csharp/.vimspector.json | 5 +++++ tests/get_configurations.test.vim | 1 + 2 files changed, 6 insertions(+) diff --git a/support/test/csharp/.vimspector.json b/support/test/csharp/.vimspector.json index 8b2c499..ae796e5 100644 --- a/support/test/csharp/.vimspector.json +++ b/support/test/csharp/.vimspector.json @@ -18,6 +18,11 @@ } }, "configurations": { + // + // NOTE: + // If you add to this, you must update tests/get_configurations.test.vim + // + "launch - netcoredbg": { "adapter": "netcoredbg", "configuration": { diff --git a/tests/get_configurations.test.vim b/tests/get_configurations.test.vim index 4a37d01..33e6577 100644 --- a/tests/get_configurations.test.vim +++ b/tests/get_configurations.test.vim @@ -12,6 +12,7 @@ function Test_Get_Configurations() let configs = vimspector#GetConfigurations() call assert_equal([ \ 'launch - netcoredbg', + \ 'launch - netcoredbg - with debug log', \ 'launch - mono', \ ], configs) From 6ad9101cf27f6d07d2f68fe04762e055869ec2d7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 15 Apr 2021 15:02:46 +0100 Subject: [PATCH 575/609] Add FAQ about json files in each project. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 6979e44..ae0ef46 100644 --- a/README.md +++ b/README.md @@ -2070,6 +2070,10 @@ hi link jsonComment Comment Debug adapters (for some reason) send telemetry data to clients. Vimspector simply displays this information in the output window. It *does not* and *will not ever* collect, use, forward or otherwise share any data with any third parties. +10. Do I _have_ to put a `.vimspector.json` in the root of every project? No, you + can put all of your adapter and debug configs in a [single directory](https://puremourning.github.io/vimspector/configuration.html#debug-configurations) if you want to, but note + the caveat that `${workspaceRoot}` won't be calculated correctly in that case. + The vimsepctor author uses this [a lot](https://github.com/puremourning/.vim-mac/tree/master/vimspector-conf). [ycmd]: https://github.com/Valloric/ycmd From b4195eee93846da6c73241520751b20a25bdbab7 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 15 Apr 2021 18:02:55 +0100 Subject: [PATCH 576/609] Upgrade to netcoredbg v1.2.0-782 Also upgrade the test project to LTS 3.1 dotnet framework, as MS is making it very difficult to get framework 2.2 anymore. Also remove some object files which apparently were included in the repo. --- python3/vimspector/gadgets.py | 4 +- support/test/csharp/.gitignore | 1 + support/test/csharp/.vimspector.json | 4 +- support/test/csharp/csharp.csproj | 2 +- .../test/csharp/obj/csharp.csproj.nuget.cache | 5 - .../csharp/obj/csharp.csproj.nuget.g.props | 21 - .../csharp/obj/csharp.csproj.nuget.g.targets | 10 - support/test/csharp/obj/project.assets.json | 748 ------------------ 8 files changed, 6 insertions(+), 789 deletions(-) delete mode 100644 support/test/csharp/obj/csharp.csproj.nuget.cache delete mode 100644 support/test/csharp/obj/csharp.csproj.nuget.g.props delete mode 100644 support/test/csharp/obj/csharp.csproj.nuget.g.targets delete mode 100644 support/test/csharp/obj/project.assets.json diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 7b807b6..6b28095 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -234,12 +234,12 @@ GADGETS = { 'format': 'tar', }, 'all': { - 'version': '1.2.0-761' + 'version': '1.2.0-782' }, 'macos': { 'file_name': 'netcoredbg-osx.tar.gz', 'checksum': - '994e0d001f53af058c94468336dfd1f166a3f3540c9e0de4f9f45f59e6c969fe', + '', }, 'linux': { 'file_name': 'netcoredbg-linux-bionic-amd64.tar.gz', diff --git a/support/test/csharp/.gitignore b/support/test/csharp/.gitignore index b7d74e4..03cd7d8 100644 --- a/support/test/csharp/.gitignore +++ b/support/test/csharp/.gitignore @@ -1,2 +1,3 @@ bin/ obj/Debug +obj/ diff --git a/support/test/csharp/.vimspector.json b/support/test/csharp/.vimspector.json index ae796e5..3bfb0f3 100644 --- a/support/test/csharp/.vimspector.json +++ b/support/test/csharp/.vimspector.json @@ -27,7 +27,7 @@ "adapter": "netcoredbg", "configuration": { "request": "launch", - "program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll", + "program": "${workspaceRoot}/bin/Debug/netcoreapp3.1/csharp.dll", "args": [], "stopAtEntry": true } @@ -36,7 +36,7 @@ "adapter": "netcoredbg-debuglog", "configuration": { "request": "launch", - "program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll", + "program": "${workspaceRoot}/bin/Debug/netcoreapp3.1/csharp.dll", "args": [], "stopAtEntry": true } diff --git a/support/test/csharp/csharp.csproj b/support/test/csharp/csharp.csproj index 01d5113..d453e9a 100644 --- a/support/test/csharp/csharp.csproj +++ b/support/test/csharp/csharp.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.1 diff --git a/support/test/csharp/obj/csharp.csproj.nuget.cache b/support/test/csharp/obj/csharp.csproj.nuget.cache deleted file mode 100644 index 3ac8d84..0000000 --- a/support/test/csharp/obj/csharp.csproj.nuget.cache +++ /dev/null @@ -1,5 +0,0 @@ -{ - "version": 1, - "dgSpecHash": "6/vdr7YprlSIoQecv/nNuLNflFpO0X7eN7jHUinZTsgian9nYpmHMWirsDWMi5l+29TH+Qy8O/QfaB/48QtjRQ==", - "success": true -} \ No newline at end of file diff --git a/support/test/csharp/obj/csharp.csproj.nuget.g.props b/support/test/csharp/obj/csharp.csproj.nuget.g.props deleted file mode 100644 index c71f0e6..0000000 --- a/support/test/csharp/obj/csharp.csproj.nuget.g.props +++ /dev/null @@ -1,21 +0,0 @@ - - - - True - NuGet - $(MSBuildThisFileDirectory)project.assets.json - /Users/ben/.nuget/packages/ - /Users/ben/.nuget/packages/;/usr/local/share/dotnet/sdk/NuGetFallbackFolder - PackageReference - 5.7.0 - - - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - \ No newline at end of file diff --git a/support/test/csharp/obj/csharp.csproj.nuget.g.targets b/support/test/csharp/obj/csharp.csproj.nuget.g.targets deleted file mode 100644 index 099158b..0000000 --- a/support/test/csharp/obj/csharp.csproj.nuget.g.targets +++ /dev/null @@ -1,10 +0,0 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - - \ No newline at end of file diff --git a/support/test/csharp/obj/project.assets.json b/support/test/csharp/obj/project.assets.json deleted file mode 100644 index bd6c0fc..0000000 --- a/support/test/csharp/obj/project.assets.json +++ /dev/null @@ -1,748 +0,0 @@ -{ - "version": 3, - "targets": { - ".NETCoreApp,Version=v2.2": { - "Microsoft.NETCore.App/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.DotNetHostPolicy": "2.2.0", - "Microsoft.NETCore.Platforms": "2.2.0", - "Microsoft.NETCore.Targets": "2.0.0", - "NETStandard.Library": "2.0.3" - }, - "compile": { - "ref/netcoreapp2.2/Microsoft.CSharp.dll": {}, - "ref/netcoreapp2.2/Microsoft.VisualBasic.dll": {}, - "ref/netcoreapp2.2/Microsoft.Win32.Primitives.dll": {}, - "ref/netcoreapp2.2/System.AppContext.dll": {}, - "ref/netcoreapp2.2/System.Buffers.dll": {}, - "ref/netcoreapp2.2/System.Collections.Concurrent.dll": {}, - "ref/netcoreapp2.2/System.Collections.Immutable.dll": {}, - "ref/netcoreapp2.2/System.Collections.NonGeneric.dll": {}, - "ref/netcoreapp2.2/System.Collections.Specialized.dll": {}, - "ref/netcoreapp2.2/System.Collections.dll": {}, - "ref/netcoreapp2.2/System.ComponentModel.Annotations.dll": {}, - "ref/netcoreapp2.2/System.ComponentModel.DataAnnotations.dll": {}, - "ref/netcoreapp2.2/System.ComponentModel.EventBasedAsync.dll": {}, - "ref/netcoreapp2.2/System.ComponentModel.Primitives.dll": {}, - "ref/netcoreapp2.2/System.ComponentModel.TypeConverter.dll": {}, - "ref/netcoreapp2.2/System.ComponentModel.dll": {}, - "ref/netcoreapp2.2/System.Configuration.dll": {}, - "ref/netcoreapp2.2/System.Console.dll": {}, - "ref/netcoreapp2.2/System.Core.dll": {}, - "ref/netcoreapp2.2/System.Data.Common.dll": {}, - "ref/netcoreapp2.2/System.Data.dll": {}, - "ref/netcoreapp2.2/System.Diagnostics.Contracts.dll": {}, - "ref/netcoreapp2.2/System.Diagnostics.Debug.dll": {}, - "ref/netcoreapp2.2/System.Diagnostics.DiagnosticSource.dll": {}, - "ref/netcoreapp2.2/System.Diagnostics.FileVersionInfo.dll": {}, - "ref/netcoreapp2.2/System.Diagnostics.Process.dll": {}, - "ref/netcoreapp2.2/System.Diagnostics.StackTrace.dll": {}, - "ref/netcoreapp2.2/System.Diagnostics.TextWriterTraceListener.dll": {}, - "ref/netcoreapp2.2/System.Diagnostics.Tools.dll": {}, - "ref/netcoreapp2.2/System.Diagnostics.TraceSource.dll": {}, - "ref/netcoreapp2.2/System.Diagnostics.Tracing.dll": {}, - "ref/netcoreapp2.2/System.Drawing.Primitives.dll": {}, - "ref/netcoreapp2.2/System.Drawing.dll": {}, - "ref/netcoreapp2.2/System.Dynamic.Runtime.dll": {}, - "ref/netcoreapp2.2/System.Globalization.Calendars.dll": {}, - "ref/netcoreapp2.2/System.Globalization.Extensions.dll": {}, - "ref/netcoreapp2.2/System.Globalization.dll": {}, - "ref/netcoreapp2.2/System.IO.Compression.Brotli.dll": {}, - "ref/netcoreapp2.2/System.IO.Compression.FileSystem.dll": {}, - "ref/netcoreapp2.2/System.IO.Compression.ZipFile.dll": {}, - "ref/netcoreapp2.2/System.IO.Compression.dll": {}, - "ref/netcoreapp2.2/System.IO.FileSystem.DriveInfo.dll": {}, - "ref/netcoreapp2.2/System.IO.FileSystem.Primitives.dll": {}, - "ref/netcoreapp2.2/System.IO.FileSystem.Watcher.dll": {}, - "ref/netcoreapp2.2/System.IO.FileSystem.dll": {}, - "ref/netcoreapp2.2/System.IO.IsolatedStorage.dll": {}, - "ref/netcoreapp2.2/System.IO.MemoryMappedFiles.dll": {}, - "ref/netcoreapp2.2/System.IO.Pipes.dll": {}, - "ref/netcoreapp2.2/System.IO.UnmanagedMemoryStream.dll": {}, - "ref/netcoreapp2.2/System.IO.dll": {}, - "ref/netcoreapp2.2/System.Linq.Expressions.dll": {}, - "ref/netcoreapp2.2/System.Linq.Parallel.dll": {}, - "ref/netcoreapp2.2/System.Linq.Queryable.dll": {}, - "ref/netcoreapp2.2/System.Linq.dll": {}, - "ref/netcoreapp2.2/System.Memory.dll": {}, - "ref/netcoreapp2.2/System.Net.Http.dll": {}, - "ref/netcoreapp2.2/System.Net.HttpListener.dll": {}, - "ref/netcoreapp2.2/System.Net.Mail.dll": {}, - "ref/netcoreapp2.2/System.Net.NameResolution.dll": {}, - "ref/netcoreapp2.2/System.Net.NetworkInformation.dll": {}, - "ref/netcoreapp2.2/System.Net.Ping.dll": {}, - "ref/netcoreapp2.2/System.Net.Primitives.dll": {}, - "ref/netcoreapp2.2/System.Net.Requests.dll": {}, - "ref/netcoreapp2.2/System.Net.Security.dll": {}, - "ref/netcoreapp2.2/System.Net.ServicePoint.dll": {}, - "ref/netcoreapp2.2/System.Net.Sockets.dll": {}, - "ref/netcoreapp2.2/System.Net.WebClient.dll": {}, - "ref/netcoreapp2.2/System.Net.WebHeaderCollection.dll": {}, - "ref/netcoreapp2.2/System.Net.WebProxy.dll": {}, - "ref/netcoreapp2.2/System.Net.WebSockets.Client.dll": {}, - "ref/netcoreapp2.2/System.Net.WebSockets.dll": {}, - "ref/netcoreapp2.2/System.Net.dll": {}, - "ref/netcoreapp2.2/System.Numerics.Vectors.dll": {}, - "ref/netcoreapp2.2/System.Numerics.dll": {}, - "ref/netcoreapp2.2/System.ObjectModel.dll": {}, - "ref/netcoreapp2.2/System.Reflection.DispatchProxy.dll": {}, - "ref/netcoreapp2.2/System.Reflection.Emit.ILGeneration.dll": {}, - "ref/netcoreapp2.2/System.Reflection.Emit.Lightweight.dll": {}, - "ref/netcoreapp2.2/System.Reflection.Emit.dll": {}, - "ref/netcoreapp2.2/System.Reflection.Extensions.dll": {}, - "ref/netcoreapp2.2/System.Reflection.Metadata.dll": {}, - "ref/netcoreapp2.2/System.Reflection.Primitives.dll": {}, - "ref/netcoreapp2.2/System.Reflection.TypeExtensions.dll": {}, - "ref/netcoreapp2.2/System.Reflection.dll": {}, - "ref/netcoreapp2.2/System.Resources.Reader.dll": {}, - "ref/netcoreapp2.2/System.Resources.ResourceManager.dll": {}, - "ref/netcoreapp2.2/System.Resources.Writer.dll": {}, - "ref/netcoreapp2.2/System.Runtime.CompilerServices.VisualC.dll": {}, - "ref/netcoreapp2.2/System.Runtime.Extensions.dll": {}, - "ref/netcoreapp2.2/System.Runtime.Handles.dll": {}, - "ref/netcoreapp2.2/System.Runtime.InteropServices.RuntimeInformation.dll": {}, - "ref/netcoreapp2.2/System.Runtime.InteropServices.WindowsRuntime.dll": {}, - "ref/netcoreapp2.2/System.Runtime.InteropServices.dll": {}, - "ref/netcoreapp2.2/System.Runtime.Loader.dll": {}, - "ref/netcoreapp2.2/System.Runtime.Numerics.dll": {}, - "ref/netcoreapp2.2/System.Runtime.Serialization.Formatters.dll": {}, - "ref/netcoreapp2.2/System.Runtime.Serialization.Json.dll": {}, - "ref/netcoreapp2.2/System.Runtime.Serialization.Primitives.dll": {}, - "ref/netcoreapp2.2/System.Runtime.Serialization.Xml.dll": {}, - "ref/netcoreapp2.2/System.Runtime.Serialization.dll": {}, - "ref/netcoreapp2.2/System.Runtime.dll": {}, - "ref/netcoreapp2.2/System.Security.Claims.dll": {}, - "ref/netcoreapp2.2/System.Security.Cryptography.Algorithms.dll": {}, - "ref/netcoreapp2.2/System.Security.Cryptography.Csp.dll": {}, - "ref/netcoreapp2.2/System.Security.Cryptography.Encoding.dll": {}, - "ref/netcoreapp2.2/System.Security.Cryptography.Primitives.dll": {}, - "ref/netcoreapp2.2/System.Security.Cryptography.X509Certificates.dll": {}, - "ref/netcoreapp2.2/System.Security.Principal.dll": {}, - "ref/netcoreapp2.2/System.Security.SecureString.dll": {}, - "ref/netcoreapp2.2/System.Security.dll": {}, - "ref/netcoreapp2.2/System.ServiceModel.Web.dll": {}, - "ref/netcoreapp2.2/System.ServiceProcess.dll": {}, - "ref/netcoreapp2.2/System.Text.Encoding.Extensions.dll": {}, - "ref/netcoreapp2.2/System.Text.Encoding.dll": {}, - "ref/netcoreapp2.2/System.Text.RegularExpressions.dll": {}, - "ref/netcoreapp2.2/System.Threading.Overlapped.dll": {}, - "ref/netcoreapp2.2/System.Threading.Tasks.Dataflow.dll": {}, - "ref/netcoreapp2.2/System.Threading.Tasks.Extensions.dll": {}, - "ref/netcoreapp2.2/System.Threading.Tasks.Parallel.dll": {}, - "ref/netcoreapp2.2/System.Threading.Tasks.dll": {}, - "ref/netcoreapp2.2/System.Threading.Thread.dll": {}, - "ref/netcoreapp2.2/System.Threading.ThreadPool.dll": {}, - "ref/netcoreapp2.2/System.Threading.Timer.dll": {}, - "ref/netcoreapp2.2/System.Threading.dll": {}, - "ref/netcoreapp2.2/System.Transactions.Local.dll": {}, - "ref/netcoreapp2.2/System.Transactions.dll": {}, - "ref/netcoreapp2.2/System.ValueTuple.dll": {}, - "ref/netcoreapp2.2/System.Web.HttpUtility.dll": {}, - "ref/netcoreapp2.2/System.Web.dll": {}, - "ref/netcoreapp2.2/System.Windows.dll": {}, - "ref/netcoreapp2.2/System.Xml.Linq.dll": {}, - "ref/netcoreapp2.2/System.Xml.ReaderWriter.dll": {}, - "ref/netcoreapp2.2/System.Xml.Serialization.dll": {}, - "ref/netcoreapp2.2/System.Xml.XDocument.dll": {}, - "ref/netcoreapp2.2/System.Xml.XPath.XDocument.dll": {}, - "ref/netcoreapp2.2/System.Xml.XPath.dll": {}, - "ref/netcoreapp2.2/System.Xml.XmlDocument.dll": {}, - "ref/netcoreapp2.2/System.Xml.XmlSerializer.dll": {}, - "ref/netcoreapp2.2/System.Xml.dll": {}, - "ref/netcoreapp2.2/System.dll": {}, - "ref/netcoreapp2.2/WindowsBase.dll": {}, - "ref/netcoreapp2.2/mscorlib.dll": {}, - "ref/netcoreapp2.2/netstandard.dll": {} - }, - "build": { - "build/netcoreapp2.2/Microsoft.NETCore.App.props": {}, - "build/netcoreapp2.2/Microsoft.NETCore.App.targets": {} - } - }, - "Microsoft.NETCore.DotNetAppHost/2.2.0": { - "type": "package" - }, - "Microsoft.NETCore.DotNetHostPolicy/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.DotNetHostResolver": "2.2.0" - } - }, - "Microsoft.NETCore.DotNetHostResolver/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.DotNetAppHost": "2.2.0" - } - }, - "Microsoft.NETCore.Platforms/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "Microsoft.NETCore.Targets/2.0.0": { - "type": "package", - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "NETStandard.Library/2.0.3": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - }, - "build": { - "build/netstandard2.0/NETStandard.Library.targets": {} - } - } - } - }, - "libraries": { - "Microsoft.NETCore.App/2.2.0": { - "sha512": "7z5l8Jp324S8bU8+yyWeYHXUFYvKyiI5lqS1dXgTzOx1H69Qbf6df12kCKlNX45LpMfCMd4U3M6p7Rl5Zk7SLA==", - "type": "package", - "path": "microsoft.netcore.app/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "Microsoft.NETCore.App.versions.txt", - "THIRD-PARTY-NOTICES.TXT", - "build/netcoreapp2.2/Microsoft.NETCore.App.PlatformManifest.txt", - "build/netcoreapp2.2/Microsoft.NETCore.App.props", - "build/netcoreapp2.2/Microsoft.NETCore.App.targets", - "microsoft.netcore.app.2.2.0.nupkg.sha512", - "microsoft.netcore.app.nuspec", - "ref/netcoreapp2.2/Microsoft.CSharp.dll", - "ref/netcoreapp2.2/Microsoft.CSharp.xml", - "ref/netcoreapp2.2/Microsoft.VisualBasic.dll", - "ref/netcoreapp2.2/Microsoft.VisualBasic.xml", - "ref/netcoreapp2.2/Microsoft.Win32.Primitives.dll", - "ref/netcoreapp2.2/Microsoft.Win32.Primitives.xml", - "ref/netcoreapp2.2/System.AppContext.dll", - "ref/netcoreapp2.2/System.Buffers.dll", - "ref/netcoreapp2.2/System.Buffers.xml", - "ref/netcoreapp2.2/System.Collections.Concurrent.dll", - "ref/netcoreapp2.2/System.Collections.Concurrent.xml", - "ref/netcoreapp2.2/System.Collections.Immutable.dll", - "ref/netcoreapp2.2/System.Collections.Immutable.xml", - "ref/netcoreapp2.2/System.Collections.NonGeneric.dll", - "ref/netcoreapp2.2/System.Collections.NonGeneric.xml", - "ref/netcoreapp2.2/System.Collections.Specialized.dll", - "ref/netcoreapp2.2/System.Collections.Specialized.xml", - "ref/netcoreapp2.2/System.Collections.dll", - "ref/netcoreapp2.2/System.Collections.xml", - "ref/netcoreapp2.2/System.ComponentModel.Annotations.dll", - "ref/netcoreapp2.2/System.ComponentModel.Annotations.xml", - "ref/netcoreapp2.2/System.ComponentModel.DataAnnotations.dll", - "ref/netcoreapp2.2/System.ComponentModel.EventBasedAsync.dll", - "ref/netcoreapp2.2/System.ComponentModel.EventBasedAsync.xml", - "ref/netcoreapp2.2/System.ComponentModel.Primitives.dll", - "ref/netcoreapp2.2/System.ComponentModel.Primitives.xml", - "ref/netcoreapp2.2/System.ComponentModel.TypeConverter.dll", - "ref/netcoreapp2.2/System.ComponentModel.TypeConverter.xml", - "ref/netcoreapp2.2/System.ComponentModel.dll", - "ref/netcoreapp2.2/System.ComponentModel.xml", - "ref/netcoreapp2.2/System.Configuration.dll", - "ref/netcoreapp2.2/System.Console.dll", - "ref/netcoreapp2.2/System.Console.xml", - "ref/netcoreapp2.2/System.Core.dll", - "ref/netcoreapp2.2/System.Data.Common.dll", - "ref/netcoreapp2.2/System.Data.Common.xml", - "ref/netcoreapp2.2/System.Data.dll", - "ref/netcoreapp2.2/System.Diagnostics.Contracts.dll", - "ref/netcoreapp2.2/System.Diagnostics.Contracts.xml", - "ref/netcoreapp2.2/System.Diagnostics.Debug.dll", - "ref/netcoreapp2.2/System.Diagnostics.Debug.xml", - "ref/netcoreapp2.2/System.Diagnostics.DiagnosticSource.dll", - "ref/netcoreapp2.2/System.Diagnostics.DiagnosticSource.xml", - "ref/netcoreapp2.2/System.Diagnostics.FileVersionInfo.dll", - "ref/netcoreapp2.2/System.Diagnostics.FileVersionInfo.xml", - "ref/netcoreapp2.2/System.Diagnostics.Process.dll", - "ref/netcoreapp2.2/System.Diagnostics.Process.xml", - "ref/netcoreapp2.2/System.Diagnostics.StackTrace.dll", - "ref/netcoreapp2.2/System.Diagnostics.StackTrace.xml", - "ref/netcoreapp2.2/System.Diagnostics.TextWriterTraceListener.dll", - "ref/netcoreapp2.2/System.Diagnostics.TextWriterTraceListener.xml", - "ref/netcoreapp2.2/System.Diagnostics.Tools.dll", - "ref/netcoreapp2.2/System.Diagnostics.Tools.xml", - "ref/netcoreapp2.2/System.Diagnostics.TraceSource.dll", - "ref/netcoreapp2.2/System.Diagnostics.TraceSource.xml", - "ref/netcoreapp2.2/System.Diagnostics.Tracing.dll", - "ref/netcoreapp2.2/System.Diagnostics.Tracing.xml", - "ref/netcoreapp2.2/System.Drawing.Primitives.dll", - "ref/netcoreapp2.2/System.Drawing.Primitives.xml", - "ref/netcoreapp2.2/System.Drawing.dll", - "ref/netcoreapp2.2/System.Dynamic.Runtime.dll", - "ref/netcoreapp2.2/System.Globalization.Calendars.dll", - "ref/netcoreapp2.2/System.Globalization.Extensions.dll", - "ref/netcoreapp2.2/System.Globalization.dll", - "ref/netcoreapp2.2/System.IO.Compression.Brotli.dll", - "ref/netcoreapp2.2/System.IO.Compression.FileSystem.dll", - "ref/netcoreapp2.2/System.IO.Compression.ZipFile.dll", - "ref/netcoreapp2.2/System.IO.Compression.ZipFile.xml", - "ref/netcoreapp2.2/System.IO.Compression.dll", - "ref/netcoreapp2.2/System.IO.Compression.xml", - "ref/netcoreapp2.2/System.IO.FileSystem.DriveInfo.dll", - "ref/netcoreapp2.2/System.IO.FileSystem.DriveInfo.xml", - "ref/netcoreapp2.2/System.IO.FileSystem.Primitives.dll", - "ref/netcoreapp2.2/System.IO.FileSystem.Watcher.dll", - "ref/netcoreapp2.2/System.IO.FileSystem.Watcher.xml", - "ref/netcoreapp2.2/System.IO.FileSystem.dll", - "ref/netcoreapp2.2/System.IO.FileSystem.xml", - "ref/netcoreapp2.2/System.IO.IsolatedStorage.dll", - "ref/netcoreapp2.2/System.IO.IsolatedStorage.xml", - "ref/netcoreapp2.2/System.IO.MemoryMappedFiles.dll", - "ref/netcoreapp2.2/System.IO.MemoryMappedFiles.xml", - "ref/netcoreapp2.2/System.IO.Pipes.dll", - "ref/netcoreapp2.2/System.IO.Pipes.xml", - "ref/netcoreapp2.2/System.IO.UnmanagedMemoryStream.dll", - "ref/netcoreapp2.2/System.IO.dll", - "ref/netcoreapp2.2/System.Linq.Expressions.dll", - "ref/netcoreapp2.2/System.Linq.Expressions.xml", - "ref/netcoreapp2.2/System.Linq.Parallel.dll", - "ref/netcoreapp2.2/System.Linq.Parallel.xml", - "ref/netcoreapp2.2/System.Linq.Queryable.dll", - "ref/netcoreapp2.2/System.Linq.Queryable.xml", - "ref/netcoreapp2.2/System.Linq.dll", - "ref/netcoreapp2.2/System.Linq.xml", - "ref/netcoreapp2.2/System.Memory.dll", - "ref/netcoreapp2.2/System.Memory.xml", - "ref/netcoreapp2.2/System.Net.Http.dll", - "ref/netcoreapp2.2/System.Net.Http.xml", - "ref/netcoreapp2.2/System.Net.HttpListener.dll", - "ref/netcoreapp2.2/System.Net.HttpListener.xml", - "ref/netcoreapp2.2/System.Net.Mail.dll", - "ref/netcoreapp2.2/System.Net.Mail.xml", - "ref/netcoreapp2.2/System.Net.NameResolution.dll", - "ref/netcoreapp2.2/System.Net.NameResolution.xml", - "ref/netcoreapp2.2/System.Net.NetworkInformation.dll", - "ref/netcoreapp2.2/System.Net.NetworkInformation.xml", - "ref/netcoreapp2.2/System.Net.Ping.dll", - "ref/netcoreapp2.2/System.Net.Ping.xml", - "ref/netcoreapp2.2/System.Net.Primitives.dll", - "ref/netcoreapp2.2/System.Net.Primitives.xml", - "ref/netcoreapp2.2/System.Net.Requests.dll", - "ref/netcoreapp2.2/System.Net.Requests.xml", - "ref/netcoreapp2.2/System.Net.Security.dll", - "ref/netcoreapp2.2/System.Net.Security.xml", - "ref/netcoreapp2.2/System.Net.ServicePoint.dll", - "ref/netcoreapp2.2/System.Net.ServicePoint.xml", - "ref/netcoreapp2.2/System.Net.Sockets.dll", - "ref/netcoreapp2.2/System.Net.Sockets.xml", - "ref/netcoreapp2.2/System.Net.WebClient.dll", - "ref/netcoreapp2.2/System.Net.WebClient.xml", - "ref/netcoreapp2.2/System.Net.WebHeaderCollection.dll", - "ref/netcoreapp2.2/System.Net.WebHeaderCollection.xml", - "ref/netcoreapp2.2/System.Net.WebProxy.dll", - "ref/netcoreapp2.2/System.Net.WebProxy.xml", - "ref/netcoreapp2.2/System.Net.WebSockets.Client.dll", - "ref/netcoreapp2.2/System.Net.WebSockets.Client.xml", - "ref/netcoreapp2.2/System.Net.WebSockets.dll", - "ref/netcoreapp2.2/System.Net.WebSockets.xml", - "ref/netcoreapp2.2/System.Net.dll", - "ref/netcoreapp2.2/System.Numerics.Vectors.dll", - "ref/netcoreapp2.2/System.Numerics.Vectors.xml", - "ref/netcoreapp2.2/System.Numerics.dll", - "ref/netcoreapp2.2/System.ObjectModel.dll", - "ref/netcoreapp2.2/System.ObjectModel.xml", - "ref/netcoreapp2.2/System.Reflection.DispatchProxy.dll", - "ref/netcoreapp2.2/System.Reflection.DispatchProxy.xml", - "ref/netcoreapp2.2/System.Reflection.Emit.ILGeneration.dll", - "ref/netcoreapp2.2/System.Reflection.Emit.ILGeneration.xml", - "ref/netcoreapp2.2/System.Reflection.Emit.Lightweight.dll", - "ref/netcoreapp2.2/System.Reflection.Emit.Lightweight.xml", - "ref/netcoreapp2.2/System.Reflection.Emit.dll", - "ref/netcoreapp2.2/System.Reflection.Emit.xml", - "ref/netcoreapp2.2/System.Reflection.Extensions.dll", - "ref/netcoreapp2.2/System.Reflection.Metadata.dll", - "ref/netcoreapp2.2/System.Reflection.Metadata.xml", - "ref/netcoreapp2.2/System.Reflection.Primitives.dll", - "ref/netcoreapp2.2/System.Reflection.Primitives.xml", - "ref/netcoreapp2.2/System.Reflection.TypeExtensions.dll", - "ref/netcoreapp2.2/System.Reflection.TypeExtensions.xml", - "ref/netcoreapp2.2/System.Reflection.dll", - "ref/netcoreapp2.2/System.Resources.Reader.dll", - "ref/netcoreapp2.2/System.Resources.ResourceManager.dll", - "ref/netcoreapp2.2/System.Resources.ResourceManager.xml", - "ref/netcoreapp2.2/System.Resources.Writer.dll", - "ref/netcoreapp2.2/System.Resources.Writer.xml", - "ref/netcoreapp2.2/System.Runtime.CompilerServices.VisualC.dll", - "ref/netcoreapp2.2/System.Runtime.CompilerServices.VisualC.xml", - "ref/netcoreapp2.2/System.Runtime.Extensions.dll", - "ref/netcoreapp2.2/System.Runtime.Extensions.xml", - "ref/netcoreapp2.2/System.Runtime.Handles.dll", - "ref/netcoreapp2.2/System.Runtime.InteropServices.RuntimeInformation.dll", - "ref/netcoreapp2.2/System.Runtime.InteropServices.RuntimeInformation.xml", - "ref/netcoreapp2.2/System.Runtime.InteropServices.WindowsRuntime.dll", - "ref/netcoreapp2.2/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcoreapp2.2/System.Runtime.InteropServices.dll", - "ref/netcoreapp2.2/System.Runtime.InteropServices.xml", - "ref/netcoreapp2.2/System.Runtime.Loader.dll", - "ref/netcoreapp2.2/System.Runtime.Loader.xml", - "ref/netcoreapp2.2/System.Runtime.Numerics.dll", - "ref/netcoreapp2.2/System.Runtime.Numerics.xml", - "ref/netcoreapp2.2/System.Runtime.Serialization.Formatters.dll", - "ref/netcoreapp2.2/System.Runtime.Serialization.Formatters.xml", - "ref/netcoreapp2.2/System.Runtime.Serialization.Json.dll", - "ref/netcoreapp2.2/System.Runtime.Serialization.Json.xml", - "ref/netcoreapp2.2/System.Runtime.Serialization.Primitives.dll", - "ref/netcoreapp2.2/System.Runtime.Serialization.Primitives.xml", - "ref/netcoreapp2.2/System.Runtime.Serialization.Xml.dll", - "ref/netcoreapp2.2/System.Runtime.Serialization.Xml.xml", - "ref/netcoreapp2.2/System.Runtime.Serialization.dll", - "ref/netcoreapp2.2/System.Runtime.dll", - "ref/netcoreapp2.2/System.Runtime.xml", - "ref/netcoreapp2.2/System.Security.Claims.dll", - "ref/netcoreapp2.2/System.Security.Claims.xml", - "ref/netcoreapp2.2/System.Security.Cryptography.Algorithms.dll", - "ref/netcoreapp2.2/System.Security.Cryptography.Algorithms.xml", - "ref/netcoreapp2.2/System.Security.Cryptography.Csp.dll", - "ref/netcoreapp2.2/System.Security.Cryptography.Csp.xml", - "ref/netcoreapp2.2/System.Security.Cryptography.Encoding.dll", - "ref/netcoreapp2.2/System.Security.Cryptography.Encoding.xml", - "ref/netcoreapp2.2/System.Security.Cryptography.Primitives.dll", - "ref/netcoreapp2.2/System.Security.Cryptography.Primitives.xml", - "ref/netcoreapp2.2/System.Security.Cryptography.X509Certificates.dll", - "ref/netcoreapp2.2/System.Security.Cryptography.X509Certificates.xml", - "ref/netcoreapp2.2/System.Security.Principal.dll", - "ref/netcoreapp2.2/System.Security.Principal.xml", - "ref/netcoreapp2.2/System.Security.SecureString.dll", - "ref/netcoreapp2.2/System.Security.dll", - "ref/netcoreapp2.2/System.ServiceModel.Web.dll", - "ref/netcoreapp2.2/System.ServiceProcess.dll", - "ref/netcoreapp2.2/System.Text.Encoding.Extensions.dll", - "ref/netcoreapp2.2/System.Text.Encoding.Extensions.xml", - "ref/netcoreapp2.2/System.Text.Encoding.dll", - "ref/netcoreapp2.2/System.Text.RegularExpressions.dll", - "ref/netcoreapp2.2/System.Text.RegularExpressions.xml", - "ref/netcoreapp2.2/System.Threading.Overlapped.dll", - "ref/netcoreapp2.2/System.Threading.Overlapped.xml", - "ref/netcoreapp2.2/System.Threading.Tasks.Dataflow.dll", - "ref/netcoreapp2.2/System.Threading.Tasks.Dataflow.xml", - "ref/netcoreapp2.2/System.Threading.Tasks.Extensions.dll", - "ref/netcoreapp2.2/System.Threading.Tasks.Extensions.xml", - "ref/netcoreapp2.2/System.Threading.Tasks.Parallel.dll", - "ref/netcoreapp2.2/System.Threading.Tasks.Parallel.xml", - "ref/netcoreapp2.2/System.Threading.Tasks.dll", - "ref/netcoreapp2.2/System.Threading.Tasks.xml", - "ref/netcoreapp2.2/System.Threading.Thread.dll", - "ref/netcoreapp2.2/System.Threading.Thread.xml", - "ref/netcoreapp2.2/System.Threading.ThreadPool.dll", - "ref/netcoreapp2.2/System.Threading.ThreadPool.xml", - "ref/netcoreapp2.2/System.Threading.Timer.dll", - "ref/netcoreapp2.2/System.Threading.Timer.xml", - "ref/netcoreapp2.2/System.Threading.dll", - "ref/netcoreapp2.2/System.Threading.xml", - "ref/netcoreapp2.2/System.Transactions.Local.dll", - "ref/netcoreapp2.2/System.Transactions.Local.xml", - "ref/netcoreapp2.2/System.Transactions.dll", - "ref/netcoreapp2.2/System.ValueTuple.dll", - "ref/netcoreapp2.2/System.Web.HttpUtility.dll", - "ref/netcoreapp2.2/System.Web.HttpUtility.xml", - "ref/netcoreapp2.2/System.Web.dll", - "ref/netcoreapp2.2/System.Windows.dll", - "ref/netcoreapp2.2/System.Xml.Linq.dll", - "ref/netcoreapp2.2/System.Xml.ReaderWriter.dll", - "ref/netcoreapp2.2/System.Xml.ReaderWriter.xml", - "ref/netcoreapp2.2/System.Xml.Serialization.dll", - "ref/netcoreapp2.2/System.Xml.XDocument.dll", - "ref/netcoreapp2.2/System.Xml.XDocument.xml", - "ref/netcoreapp2.2/System.Xml.XPath.XDocument.dll", - "ref/netcoreapp2.2/System.Xml.XPath.XDocument.xml", - "ref/netcoreapp2.2/System.Xml.XPath.dll", - "ref/netcoreapp2.2/System.Xml.XPath.xml", - "ref/netcoreapp2.2/System.Xml.XmlDocument.dll", - "ref/netcoreapp2.2/System.Xml.XmlSerializer.dll", - "ref/netcoreapp2.2/System.Xml.XmlSerializer.xml", - "ref/netcoreapp2.2/System.Xml.dll", - "ref/netcoreapp2.2/System.dll", - "ref/netcoreapp2.2/WindowsBase.dll", - "ref/netcoreapp2.2/mscorlib.dll", - "ref/netcoreapp2.2/netstandard.dll", - "runtime.json" - ] - }, - "Microsoft.NETCore.DotNetAppHost/2.2.0": { - "sha512": "DrhaKInRKKvN6Ns2VNIlC7ZffLOp9THf8cO6X4fytPRJovJUbF49/zzx4WfgX9E44FMsw9hT8hrKiIqDSHvGvA==", - "type": "package", - "path": "microsoft.netcore.dotnetapphost/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "microsoft.netcore.dotnetapphost.2.2.0.nupkg.sha512", - "microsoft.netcore.dotnetapphost.nuspec", - "runtime.json" - ] - }, - "Microsoft.NETCore.DotNetHostPolicy/2.2.0": { - "sha512": "FJie7IoPZFaPgNDxhZGmDBQP/Bs5vPdfca/G2Wf9gd6LIvMYkZcibtmJwB4tcf4KXkaOYfIOo4Cl9sEPMsSzkw==", - "type": "package", - "path": "microsoft.netcore.dotnethostpolicy/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "microsoft.netcore.dotnethostpolicy.2.2.0.nupkg.sha512", - "microsoft.netcore.dotnethostpolicy.nuspec", - "runtime.json" - ] - }, - "Microsoft.NETCore.DotNetHostResolver/2.2.0": { - "sha512": "spDm3AJYmebthDNhzY17YLPtvbc+Y1lCLVeiIH1uLJ/hZaM+40pBiPefFR8J1u66Ndkqi8ipR2tEbqPnYnjRhw==", - "type": "package", - "path": "microsoft.netcore.dotnethostresolver/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "microsoft.netcore.dotnethostresolver.2.2.0.nupkg.sha512", - "microsoft.netcore.dotnethostresolver.nuspec", - "runtime.json" - ] - }, - "Microsoft.NETCore.Platforms/2.2.0": { - "sha512": "T/J+XZo+YheFTJh8/4uoeJDdz5qOmOMkjg6/VL8mHJ9AnP8+fmV/kcbxeXsob0irRNiChf+V0ig1MCRLp/+Kog==", - "type": "package", - "path": "microsoft.netcore.platforms/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/_._", - "microsoft.netcore.platforms.2.2.0.nupkg.sha512", - "microsoft.netcore.platforms.nuspec", - "runtime.json", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "Microsoft.NETCore.Targets/2.0.0": { - "sha512": "odP/tJj1z6GylFpNo7pMtbd/xQgTC3Ex2If63dRTL38bBNMwsBnJ+RceUIyHdRBC0oik/3NehYT+oECwBhIM3Q==", - "type": "package", - "path": "microsoft.netcore.targets/2.0.0", - "files": [ - ".nupkg.metadata", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/_._", - "microsoft.netcore.targets.2.0.0.nupkg.sha512", - "microsoft.netcore.targets.nuspec", - "runtime.json", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "NETStandard.Library/2.0.3": { - "sha512": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "type": "package", - "path": "netstandard.library/2.0.3", - "files": [ - ".nupkg.metadata", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "build/netstandard2.0/NETStandard.Library.targets", - "build/netstandard2.0/ref/Microsoft.Win32.Primitives.dll", - "build/netstandard2.0/ref/System.AppContext.dll", - "build/netstandard2.0/ref/System.Collections.Concurrent.dll", - "build/netstandard2.0/ref/System.Collections.NonGeneric.dll", - "build/netstandard2.0/ref/System.Collections.Specialized.dll", - "build/netstandard2.0/ref/System.Collections.dll", - "build/netstandard2.0/ref/System.ComponentModel.Composition.dll", - "build/netstandard2.0/ref/System.ComponentModel.EventBasedAsync.dll", - "build/netstandard2.0/ref/System.ComponentModel.Primitives.dll", - "build/netstandard2.0/ref/System.ComponentModel.TypeConverter.dll", - "build/netstandard2.0/ref/System.ComponentModel.dll", - "build/netstandard2.0/ref/System.Console.dll", - "build/netstandard2.0/ref/System.Core.dll", - "build/netstandard2.0/ref/System.Data.Common.dll", - "build/netstandard2.0/ref/System.Data.dll", - "build/netstandard2.0/ref/System.Diagnostics.Contracts.dll", - "build/netstandard2.0/ref/System.Diagnostics.Debug.dll", - "build/netstandard2.0/ref/System.Diagnostics.FileVersionInfo.dll", - "build/netstandard2.0/ref/System.Diagnostics.Process.dll", - "build/netstandard2.0/ref/System.Diagnostics.StackTrace.dll", - "build/netstandard2.0/ref/System.Diagnostics.TextWriterTraceListener.dll", - "build/netstandard2.0/ref/System.Diagnostics.Tools.dll", - "build/netstandard2.0/ref/System.Diagnostics.TraceSource.dll", - "build/netstandard2.0/ref/System.Diagnostics.Tracing.dll", - "build/netstandard2.0/ref/System.Drawing.Primitives.dll", - "build/netstandard2.0/ref/System.Drawing.dll", - "build/netstandard2.0/ref/System.Dynamic.Runtime.dll", - "build/netstandard2.0/ref/System.Globalization.Calendars.dll", - "build/netstandard2.0/ref/System.Globalization.Extensions.dll", - "build/netstandard2.0/ref/System.Globalization.dll", - "build/netstandard2.0/ref/System.IO.Compression.FileSystem.dll", - "build/netstandard2.0/ref/System.IO.Compression.ZipFile.dll", - "build/netstandard2.0/ref/System.IO.Compression.dll", - "build/netstandard2.0/ref/System.IO.FileSystem.DriveInfo.dll", - "build/netstandard2.0/ref/System.IO.FileSystem.Primitives.dll", - "build/netstandard2.0/ref/System.IO.FileSystem.Watcher.dll", - "build/netstandard2.0/ref/System.IO.FileSystem.dll", - "build/netstandard2.0/ref/System.IO.IsolatedStorage.dll", - "build/netstandard2.0/ref/System.IO.MemoryMappedFiles.dll", - "build/netstandard2.0/ref/System.IO.Pipes.dll", - "build/netstandard2.0/ref/System.IO.UnmanagedMemoryStream.dll", - "build/netstandard2.0/ref/System.IO.dll", - "build/netstandard2.0/ref/System.Linq.Expressions.dll", - "build/netstandard2.0/ref/System.Linq.Parallel.dll", - "build/netstandard2.0/ref/System.Linq.Queryable.dll", - "build/netstandard2.0/ref/System.Linq.dll", - "build/netstandard2.0/ref/System.Net.Http.dll", - "build/netstandard2.0/ref/System.Net.NameResolution.dll", - "build/netstandard2.0/ref/System.Net.NetworkInformation.dll", - "build/netstandard2.0/ref/System.Net.Ping.dll", - "build/netstandard2.0/ref/System.Net.Primitives.dll", - "build/netstandard2.0/ref/System.Net.Requests.dll", - "build/netstandard2.0/ref/System.Net.Security.dll", - "build/netstandard2.0/ref/System.Net.Sockets.dll", - "build/netstandard2.0/ref/System.Net.WebHeaderCollection.dll", - "build/netstandard2.0/ref/System.Net.WebSockets.Client.dll", - "build/netstandard2.0/ref/System.Net.WebSockets.dll", - "build/netstandard2.0/ref/System.Net.dll", - "build/netstandard2.0/ref/System.Numerics.dll", - "build/netstandard2.0/ref/System.ObjectModel.dll", - "build/netstandard2.0/ref/System.Reflection.Extensions.dll", - "build/netstandard2.0/ref/System.Reflection.Primitives.dll", - "build/netstandard2.0/ref/System.Reflection.dll", - "build/netstandard2.0/ref/System.Resources.Reader.dll", - "build/netstandard2.0/ref/System.Resources.ResourceManager.dll", - "build/netstandard2.0/ref/System.Resources.Writer.dll", - "build/netstandard2.0/ref/System.Runtime.CompilerServices.VisualC.dll", - "build/netstandard2.0/ref/System.Runtime.Extensions.dll", - "build/netstandard2.0/ref/System.Runtime.Handles.dll", - "build/netstandard2.0/ref/System.Runtime.InteropServices.RuntimeInformation.dll", - "build/netstandard2.0/ref/System.Runtime.InteropServices.dll", - "build/netstandard2.0/ref/System.Runtime.Numerics.dll", - "build/netstandard2.0/ref/System.Runtime.Serialization.Formatters.dll", - "build/netstandard2.0/ref/System.Runtime.Serialization.Json.dll", - "build/netstandard2.0/ref/System.Runtime.Serialization.Primitives.dll", - "build/netstandard2.0/ref/System.Runtime.Serialization.Xml.dll", - "build/netstandard2.0/ref/System.Runtime.Serialization.dll", - "build/netstandard2.0/ref/System.Runtime.dll", - "build/netstandard2.0/ref/System.Security.Claims.dll", - "build/netstandard2.0/ref/System.Security.Cryptography.Algorithms.dll", - "build/netstandard2.0/ref/System.Security.Cryptography.Csp.dll", - "build/netstandard2.0/ref/System.Security.Cryptography.Encoding.dll", - "build/netstandard2.0/ref/System.Security.Cryptography.Primitives.dll", - "build/netstandard2.0/ref/System.Security.Cryptography.X509Certificates.dll", - "build/netstandard2.0/ref/System.Security.Principal.dll", - "build/netstandard2.0/ref/System.Security.SecureString.dll", - "build/netstandard2.0/ref/System.ServiceModel.Web.dll", - "build/netstandard2.0/ref/System.Text.Encoding.Extensions.dll", - "build/netstandard2.0/ref/System.Text.Encoding.dll", - "build/netstandard2.0/ref/System.Text.RegularExpressions.dll", - "build/netstandard2.0/ref/System.Threading.Overlapped.dll", - "build/netstandard2.0/ref/System.Threading.Tasks.Parallel.dll", - "build/netstandard2.0/ref/System.Threading.Tasks.dll", - "build/netstandard2.0/ref/System.Threading.Thread.dll", - "build/netstandard2.0/ref/System.Threading.ThreadPool.dll", - "build/netstandard2.0/ref/System.Threading.Timer.dll", - "build/netstandard2.0/ref/System.Threading.dll", - "build/netstandard2.0/ref/System.Transactions.dll", - "build/netstandard2.0/ref/System.ValueTuple.dll", - "build/netstandard2.0/ref/System.Web.dll", - "build/netstandard2.0/ref/System.Windows.dll", - "build/netstandard2.0/ref/System.Xml.Linq.dll", - "build/netstandard2.0/ref/System.Xml.ReaderWriter.dll", - "build/netstandard2.0/ref/System.Xml.Serialization.dll", - "build/netstandard2.0/ref/System.Xml.XDocument.dll", - "build/netstandard2.0/ref/System.Xml.XPath.XDocument.dll", - "build/netstandard2.0/ref/System.Xml.XPath.dll", - "build/netstandard2.0/ref/System.Xml.XmlDocument.dll", - "build/netstandard2.0/ref/System.Xml.XmlSerializer.dll", - "build/netstandard2.0/ref/System.Xml.dll", - "build/netstandard2.0/ref/System.dll", - "build/netstandard2.0/ref/mscorlib.dll", - "build/netstandard2.0/ref/netstandard.dll", - "build/netstandard2.0/ref/netstandard.xml", - "lib/netstandard1.0/_._", - "netstandard.library.2.0.3.nupkg.sha512", - "netstandard.library.nuspec" - ] - } - }, - "projectFileDependencyGroups": { - ".NETCoreApp,Version=v2.2": [ - "Microsoft.NETCore.App >= 2.2.0" - ] - }, - "packageFolders": { - "/Users/ben/.nuget/packages/": {}, - "/usr/local/share/dotnet/sdk/NuGetFallbackFolder": {} - }, - "project": { - "version": "1.0.0", - "restore": { - "projectUniqueName": "/Users/ben/.vim/bundle/vimspector/support/test/csharp/csharp.csproj", - "projectName": "csharp", - "projectPath": "/Users/ben/.vim/bundle/vimspector/support/test/csharp/csharp.csproj", - "packagesPath": "/Users/ben/.nuget/packages/", - "outputPath": "/Users/ben/.vim/bundle/vimspector/support/test/csharp/obj/", - "projectStyle": "PackageReference", - "fallbackFolders": [ - "/usr/local/share/dotnet/sdk/NuGetFallbackFolder" - ], - "configFilePaths": [ - "/Users/ben/.nuget/NuGet/NuGet.Config" - ], - "originalTargetFrameworks": [ - "netcoreapp2.2" - ], - "sources": { - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "netcoreapp2.2": { - "projectReferences": {} - } - }, - "warningProperties": { - "warnAsError": [ - "NU1605" - ] - } - }, - "frameworks": { - "netcoreapp2.2": { - "dependencies": { - "Microsoft.NETCore.App": { - "suppressParent": "All", - "target": "Package", - "version": "[2.2.0, )", - "autoReferenced": true - } - }, - "imports": [ - "net461", - "net462", - "net47", - "net471", - "net472", - "net48" - ], - "assetTargetFallback": true, - "warn": true, - "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/3.1.402/RuntimeIdentifierGraph.json" - } - } - } -} \ No newline at end of file From f389d65a24e02137b62c939264ffeb37952c522a Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 15 Apr 2021 19:12:24 +0100 Subject: [PATCH 577/609] Add a test for csharp, run in CI --- .github/workflows/build.yaml | 5 +++ run_tests | 8 +++- support/test/csharp/.vimspector.json | 4 +- support/test/csharp/csharp.sln | 16 +++++++ tests/ci/image/Dockerfile | 6 +++ tests/language_csharp.test.vim | 65 ++++++++++++++++++++++++++++ 6 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 tests/language_csharp.test.vim diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 17f9601..f4ec9bf 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -111,6 +111,11 @@ jobs: path: gadgets/macos/download name: Cache gadgets + - name: 'Install .NET Core SDK 3.1' + uses: actions/setup-dotnet@v1.7.2 + with: + dotnet-version: 3.1 + - run: vim --version name: 'Print vim version information' diff --git a/run_tests b/run_tests index 39dc7ec..441acb0 100755 --- a/run_tests +++ b/run_tests @@ -91,7 +91,8 @@ if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "script" ]; then if ! python3 $(dirname $0)/install_gadget.py \ --basedir ${BASEDIR} \ ${INSTALLER_ARGS} \ - --all; then + --all \ + --force-enable-csharp; then echo "Script installation reported errors" >&2 exit 1 fi @@ -102,7 +103,7 @@ if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "vim" ]; then --cmd "${BASEDIR_CMD}" \ -c 'autocmd User VimspectorInstallSuccess qa!' \ -c 'autocmd User VimspectorInstallFailed cquit!' \ - -c "VimspectorInstall --all"; then + -c "VimspectorInstall --all netcoredbg"; then echo "Vim installation reported errors" >&2 exit 1 fi @@ -144,6 +145,9 @@ set -e pushd tests/testdata/cpp/simple make clean all popd + pushd support/test/csharp + dotnet build + popd set +e echo "%DONE - built test programs" diff --git a/support/test/csharp/.vimspector.json b/support/test/csharp/.vimspector.json index 3bfb0f3..326739b 100644 --- a/support/test/csharp/.vimspector.json +++ b/support/test/csharp/.vimspector.json @@ -29,7 +29,7 @@ "request": "launch", "program": "${workspaceRoot}/bin/Debug/netcoreapp3.1/csharp.dll", "args": [], - "stopAtEntry": true + "stopAtEntry": false } }, "launch - netcoredbg - with debug log": { @@ -38,7 +38,7 @@ "request": "launch", "program": "${workspaceRoot}/bin/Debug/netcoreapp3.1/csharp.dll", "args": [], - "stopAtEntry": true + "stopAtEntry": false } }, "launch - mono": { diff --git a/support/test/csharp/csharp.sln b/support/test/csharp/csharp.sln index bba50e0..91f59bf 100644 --- a/support/test/csharp/csharp.sln +++ b/support/test/csharp/csharp.sln @@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp", "csharp.csproj", "{91DB205F-E422-430B-BBB8-955110C7B3B6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,4 +17,18 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|x64.ActiveCfg = Debug|Any CPU + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|x64.Build.0 = Debug|Any CPU + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|x86.ActiveCfg = Debug|Any CPU + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Debug|x86.Build.0 = Debug|Any CPU + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|Any CPU.Build.0 = Release|Any CPU + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|x64.ActiveCfg = Release|Any CPU + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|x64.Build.0 = Release|Any CPU + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|x86.ActiveCfg = Release|Any CPU + {91DB205F-E422-430B-BBB8-955110C7B3B6}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection EndGlobal diff --git a/tests/ci/image/Dockerfile b/tests/ci/image/Dockerfile index a25febc..164a5a7 100644 --- a/tests/ci/image/Dockerfile +++ b/tests/ci/image/Dockerfile @@ -70,6 +70,12 @@ RUN mkdir -p /home/linuxbrew/.linuxbrew &&\ RUN /home/linuxbrew/.linuxbrew/bin/brew install golang +# dotnet +RUN curl -sSL https://dot.net/v1/dotnet-install.sh \ + | bash /dev/stdin --channel LTS --install-dir /usr/share/dotnet && \ + update-alternatives --install /usr/bin/dotnet dotnet \ + /usr/share/dotnet/dotnet 1 + # clean up RUN /home/linuxbrew/.linuxbrew/bin/brew cleanup && \ rm -rf ~/.cache && \ diff --git a/tests/language_csharp.test.vim b/tests/language_csharp.test.vim new file mode 100644 index 0000000..64cf954 --- /dev/null +++ b/tests/language_csharp.test.vim @@ -0,0 +1,65 @@ +function! SetUp() + call vimspector#test#setup#SetUpWithMappings( v:none ) +endfunction + +function! ClearDown() + call vimspector#test#setup#ClearDown() +endfunction + +function! SetUp_Test_Go_Simple() + let g:vimspector_enable_mappings = 'HUMAN' +endfunction + +function! Test_CSharp_Simple() + let fn='Program.cs' + lcd ../support/test/csharp + exe 'edit ' . fn + + call vimspector#SetLineBreakpoint( fn, 31 ) + call vimspector#LaunchWithSettings( { + \ 'configuration': 'launch - netcoredbg' + \ } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 31, 7 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 31 ) + \ } ) + + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 32, 12 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 32 ) + \ } ) + + call vimspector#test#setup#Reset() + + lcd - + %bwipeout! +endfunction + + +function! Test_Run_To_Cursor() + let fn='Program.cs' + lcd ../support/test/csharp + exe 'edit ' . fn + + call vimspector#SetLineBreakpoint( fn, 31 ) + call vimspector#LaunchWithSettings( { + \ 'configuration': 'launch - netcoredbg' + \ } ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 31, 7 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 31 ) + \ } ) + + call cursor( 33, 1 ) + call vimspector#RunToCursor() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 33, 1 ) + call WaitForAssert( {-> + \ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 33 ) + \ } ) + + call vimspector#test#setup#Reset() + lcd - + %bwipeout! +endfunction + From bc15c9451375909c6a52cf98c9d2ce8d2a834f41 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 15 Apr 2021 21:39:38 +0100 Subject: [PATCH 578/609] Retire mono debug - never worked and seems abandoned, and vscode-python as it is serious legacy now --- README.md | 102 ++++++---------------------------- python3/vimspector/gadgets.py | 58 ------------------- 2 files changed, 18 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index ae0ef46..5e05500 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,6 @@ For detailed explanatin of the `.vimspector.json` format, see the * [Python](#python) * [Python Remote Debugging](#python-remote-debugging) * [Python Remote launch and attach](#python-remote-launch-and-attach) - * [Legacy: vscode-python](#legacy-vscode-python) * [TCL](#tcl) * [C♯](#c) * [Go](#go) @@ -145,23 +144,24 @@ runtime dependencies). They are categorised by their level of support: * `Supported` : Fully supported, frequently used and manually tested * `Experimental`: Working, but not frequently used and rarely tested * `Legacy`: No longer supported, please migrate your config +* `Retired`: No longer included or supported. -| Language | Status | Switch (for `install_gadget.py`) | Adapter (for `:VimspectorInstall`) | Dependencies | -|--------------------|--------------|------------------------------------|------------------------------------|--------------------------------------------| -| C, C++, Rust etc. | Tested | `--all` or `--enable-c` (or cpp) | vscode-cpptools | mono-core | -| Rust, C, C++, etc. | Supported | `--force-enable-rust` | CodeLLDB | Python 3 | -| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | -| Go | Tested | `--enable-go` | vscode-go | Node, Go, [Delve][] | -| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | -| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | -| Lua | Supported | `--all` or `--enable-lua` | local-lua-debugger-vscode | Node >=12.13.0, Npm, Lua interpreter | -| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | -| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | -| Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | -| C# (dotnet core) | Experimental | `--force-enable-csharp` | netcoredbg | DotNet core | -| C# (mono) | Experimental | `--force-enable-csharp` | vscode-mono-debug | Mono | -| F#, VB, etc. | Experimental | `--force-enable-fsharp` (or vbnet) | netcoredbg | DotNet core | -| Python.legacy | Legacy | `--force-enable-python.legacy` | vscode-python | Node 10, Python 2.7 or Python 3 | +| Language | Status | Switch (for `install_gadget.py`) | Adapter (for `:VimspectorInstall`) | Dependencies | +|--------------------|-----------|----------------------------------|------------------------------------|--------------------------------------------| +| C, C++, Rust etc. | Tested | `--all` or `--enable-c` (or cpp) | vscode-cpptools | mono-core | +| Rust, C, C++, etc. | Supported | `--force-enable-rust` | CodeLLDB | Python 3 | +| Python | Tested | `--all` or `--enable-python` | debugpy | Python 2.7 or Python 3 | +| Go | Tested | `--enable-go` | vscode-go | Node, Go, [Delve][] | +| TCL | Supported | `--all` or `--enable-tcl` | tclpro | TCL 8.5 | +| Bourne Shell | Supported | `--all` or `--enable-bash` | vscode-bash-debug | Bash v?? | +| Lua | Supported | `--all` or `--enable-lua` | local-lua-debugger-vscode | Node >=12.13.0, Npm, Lua interpreter | +| Node.js | Supported | `--force-enable-node` | vscode-node-debug2 | 6 < Node < 12, Npm | +| Javascript | Supported | `--force-enable-chrome` | debugger-for-chrome | Chrome | +| Java | Supported | `--force-enable-java ` | vscode-java-debug | Compatible LSP plugin (see [later](#java)) | +| C# (dotnet core) | Tested | `--force-enable-csharp` | netcoredbg | DotNet core | +| F#, VB, etc. | Supported | `--force-enable-[fsharp,vbnet]` | `, `--force-enable-vbnet` | netcoredbg | DotNet core | +| C# (mono) | _Retired_ | N/A | N/A | N/A | +| Python.legacy | _Retired_ | N/A | N/A | N/A | ## Other languages @@ -526,13 +526,6 @@ Example: "${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7" ], "name": "cppdbg" - }, - "vscode-python": { - "command": [ - "node", - "${gadgetDir}/vscode-python/out/client/debugger/debugAdapter/main.js" - ], - "name": "vscode-python" } } } @@ -1264,10 +1257,6 @@ Rust is supported with any gdb/lldb-based debugger. So it works fine with headers/libs to build a C python extension for performance. * Full options: https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings - -**Migrating from `vscode-python`**: change `"adapter": "vscode-python"` to -`"adapter": "debugpy"`. - ```json { "configurations": { @@ -1334,34 +1323,6 @@ debugpy](https://github.com/microsoft/debugpy/wiki/Debugging-over-SSH). If you're feeling fancy, checkout the [reference guide][remote-debugging] for an example of getting Vimspector to remotely launch and attach. -### Legacy: vscode-python - -* No longer installed by default - please pass `--force-enable-python.legacy` if - you just want to continue using your working setup. -* [vscode-python](https://github.com/Microsoft/vscode-python) -* NOTE: You must be running `node` 10. See [this issue](https://github.com/puremourning/vimspector/issues/105) - -```json -{ - "configurations": { - ": Launch": { - "adapter": "vscode-python", - "configuration": { - "name": ": Launch", - "type": "python", - "request": "launch", - "cwd": "", - "stopOnEntry": true, - "console": "externalTerminal", - "debugOptions": [], - "program": "" - } - } - ... - } -} -``` - ## TCL * TCL (TclProDebug) @@ -1385,35 +1346,8 @@ netcoredbg` "program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll", "args": [], "stopAtEntry": true, - "cwd": "${workspaceRoot}" - } - } - } -} -``` - -* C# - mono - -Install with `install_gadget.py --force-enable-csharp` or `:VimspectorInstall -vscode-mono-debug`. - -***Known not to work.*** - -```json -{ - "configurations": { - "launch - mono": { - "adapter": "vscode-mono-debug", - "configuration": { - "request": "launch", - "program": "${workspaceRoot}/bin/Debug/netcoreapp2.2/csharp.dll", - "args": [], "cwd": "${workspaceRoot}", - "runtimeExecutable": "mono", - "runtimeArgs": [], - "env": [], - "externalConsole": false, - "console": "integratedTerminal" + "env": {} } } } diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 6b28095..2c60a1a 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -86,29 +86,6 @@ GADGETS = { }, }, }, - 'vscode-python': { - 'language': 'python.legacy', - 'enabled': False, - 'download': { - 'url': 'https://github.com/Microsoft/vscode-python/releases/download/' - '${version}/${file_name}', - }, - 'all': { - 'version': '2019.11.50794', - 'file_name': 'ms-python-release.vsix', - 'checksum': - '6a9edf9ecabed14aac424e6007858068204a3638bf3bb4f235bd6035d823acc6', - }, - 'adapters': { - "vscode-python": { - "name": "vscode-python", - "command": [ - "node", - "${gadgetDir}/vscode-python/out/client/debugger/debugAdapter/main.js", - ], - } - }, - }, 'debugpy': { 'language': 'python', 'download': { @@ -269,41 +246,6 @@ GADGETS = { }, } }, - 'vscode-mono-debug': { - 'language': 'csharp', - 'enabled': False, - 'download': { - 'url': 'https://marketplace.visualstudio.com/_apis/public/gallery/' - 'publishers/ms-vscode/vsextensions/mono-debug/${version}/' - 'vspackage', - 'target': 'vscode-mono-debug.vsix.gz', - 'format': 'zip.gz', - }, - 'all': { - 'file_name': 'vscode-mono-debug.vsix', - 'version': '0.16.2', - 'checksum': - '121eca297d83daeeb1e6e1d791305d1827998dbd595c330086b3b94d33dba3b9', - }, - 'adapters': { - 'vscode-mono-debug': { - "name": "mono-debug", - "command": [ - "mono", - "${gadgetDir}/vscode-mono-debug/bin/Release/mono-debug.exe" - ], - "attach": { - "pidSelect": "none" - }, - "configuration": { - "cwd": "${workspaceRoot}", - "console": "integratedTerminal", - "args": [], - "env": {} - } - }, - } - }, 'vscode-bash-debug': { 'language': 'bash', 'download': { From 1e25313cb5efc014b66a79d84d5c92c9bded6ebf Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 15 Apr 2021 22:52:58 +0100 Subject: [PATCH 579/609] Switch to xcode 11 which apparently works with coreclr debugging https://github.com/dotnet/runtime/issues/42311#issuecomment-700718025 --- .github/workflows/build.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f4ec9bf..14f5979 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -116,6 +116,11 @@ jobs: with: dotnet-version: 3.1 + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: ^11 + name: "Switch to xcode 11 because of .NET debugging bug" + - run: vim --version name: 'Print vim version information' @@ -148,7 +153,7 @@ jobs: # if: failure() # with: # NGROK_AUTH_TOKEN: ${{ secrets.NGROK_AUTH_TOKEN }} - # SSH_PASS: ${{ secrets.SSH_PASS }} + # SSH_PASS: ${{ secrets.SSH_PASS }} # [V]imspector PublishRelease: runs-on: 'ubuntu-16.04' From 297c0bea56fd3afce5209f47f330880d759c8698 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 20 Apr 2021 20:52:47 +0100 Subject: [PATCH 580/609] Ensure linux tests pass on PR --- .mergify.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.mergify.yml b/.mergify.yml index 09d3d83..0f02002 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -9,9 +9,8 @@ pull_request_rules: # CI https://doc.mergify.io/conditions.html#github-actions - status-success=PythonLint - status-success=VimscriptLint + - status-success=Linux - status-success=MacOS - - actions: &merge-actions merge: method: merge From 026ac22280cc19b7dd7bb3f976968264b6b15aab Mon Sep 17 00:00:00 2001 From: Jade Date: Thu, 22 Apr 2021 03:33:02 -0700 Subject: [PATCH 581/609] Fix some typos in the readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5e05500..e3a5ad5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ For a tutorial and usage overview, take a look at the [Vimspector website][website]. -For detailed explanatin of the `.vimspector.json` format, see the +For detailed explanation of the `.vimspector.json` format, see the [reference guide][vimspector-ref]. ![Build](https://github.com/puremourning/vimspector/workflows/Build/badge.svg?branch=master) [![Gitter](https://badges.gitter.im/vimspector/Lobby.svg)](https://gitter.im/vimspector/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) @@ -99,7 +99,7 @@ For detailed explanatin of the `.vimspector.json` format, see the # Features and Usage The plugin is a capable Vim graphical debugger for multiple languages. -It's mostly tested for c++, python and TCL, but in theory supports any +It's mostly tested for C++, Python and TCL, but in theory supports any language that Visual Studio Code supports (but see caveats). The [Vimspector website][website] has an overview of the UI, along with basic From 0c88cc8badeeee74f9cafbf461b72769b06a15d5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Thu, 29 Apr 2021 10:02:23 +0100 Subject: [PATCH 582/609] Fix traceback when opening popup in neovim with log visible * We enter the popup window * some log data is received, so we update the log buffer and jump to the log window to scroll it * meanwhile, the WinLeave auto command on the popup window fires and closes the popup window * Having scrolled the log window, we try to jump back to the popup window, which by now has been closed. Fixes #390 --- autoload/vimspector/internal/neojob.vim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/autoload/vimspector/internal/neojob.vim b/autoload/vimspector/internal/neojob.vim index 0cefc63..a398afe 100644 --- a/autoload/vimspector/internal/neojob.vim +++ b/autoload/vimspector/internal/neojob.vim @@ -159,15 +159,16 @@ function! s:_OnCommandEvent( category, id, data, event ) abort call setbufvar( buffer, '&modified', 0 ) endtry - " if the buffer is visible, scroll it + " if the buffer is visible, scroll it, but don't allow autocommands to fire, + " as this may close the current window! let w = bufwinnr( buffer ) if w > 0 let cw = winnr() try - execute w . 'wincmd w' - normal! Gz- + noautocmd execute w . 'wincmd w' + noautocmd normal! Gz- finally - execute cw . 'wincmd w' + noautocmd execute cw . 'wincmd w' endtry endif elseif a:event ==# 'exit' From 2b844394133343a8868e7b4044ed286d5202fc99 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 3 May 2021 14:09:14 +0100 Subject: [PATCH 583/609] Lock closed issues after 60 days --- .github/workflows/lock_old_issues.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/lock_old_issues.yaml diff --git a/.github/workflows/lock_old_issues.yaml b/.github/workflows/lock_old_issues.yaml new file mode 100644 index 0000000..d9c3bca --- /dev/null +++ b/.github/workflows/lock_old_issues.yaml @@ -0,0 +1,26 @@ +name: "Lock Old Issues" + +on: + schedule: + - cron: '0 0 * * *' + +jobs: + lock: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v2 + with: + github-token: ${{ github.token }} + issue-lock-inactive-days: '60' + # issue-exclude-created-before: '' + # issue-exclude-labels: '' + # issue-lock-labels: '' + # issue-lock-comment: '' + # issue-lock-reason: 'resolved' + # pr-lock-inactive-days: '365' + # pr-exclude-created-before: '' + # pr-exclude-labels: '' + # pr-lock-labels: '' + # pr-lock-comment: '' + # pr-lock-reason: 'resolved' + process-only: 'issues' From a7e8e93920cbdf41eb9334b4998c0688a00b8ef8 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 3 May 2021 14:09:56 +0100 Subject: [PATCH 584/609] Allow manual running of locker --- .github/workflows/lock_old_issues.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lock_old_issues.yaml b/.github/workflows/lock_old_issues.yaml index d9c3bca..25d65e0 100644 --- a/.github/workflows/lock_old_issues.yaml +++ b/.github/workflows/lock_old_issues.yaml @@ -3,6 +3,7 @@ name: "Lock Old Issues" on: schedule: - cron: '0 0 * * *' + workflow_dispatch: jobs: lock: From 4e04a862cb37105acebac8b6ac5b275dc7865815 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 3 May 2021 15:02:50 +0100 Subject: [PATCH 585/609] Add note about existing github issues --- CONTRIBUTING.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1866974..adec034 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,6 +46,23 @@ The GitHub issue tracker is for *bug reports* and *features requests* for the Vimspector project, and on-topic comments and follow-ups to them. It is not for general discussion, general support or for any other purpose. +Please **search the issue tracker for similar issues** before creating a new +one. There's no point in duplication; if an existing open issue addresses your +problem, please comment there instead of creating a duplicate. However, if the +issue you found is **closed as resolved** (e.g. with a PR or the original user's +problem was resolved), raise a **new issue**, because you've found a new +problem. Reference the original issue if you think that's useful information. + +Closed issues which have been inactive for 60 days will be locked, this helps to +keep discussions focussed. If you believe you are still experiencing an issue +which has been closed, please raise a new issue, completing the issue template. + +If you do find a similar _open_ issue, **don't just post 'me too' or similar** +responses. This almost never helps resolve the issue, and just causes noise for +the maintainers. Only post if it will aid the maintainers in solving the issue; +if there are existing diagnostics requested in the thread, perform +them and post the results. + Please do not be offended if your Issue or comment is closed or hidden, for any of the following reasons: @@ -53,6 +70,7 @@ of the following reasons: * The issue or comment is off-topic * The issue does not represent a Vimspector bug or feature request * The issue cannot be reasonably reproduced using the minimal vimrc +* The issue is a duplicate of an existing issue * etc. Issue titles are important. It's not usually helpful to write a title like From 08679d1c3ec46edb693e934a3a0f8326280e5893 Mon Sep 17 00:00:00 2001 From: przepompownia Date: Tue, 11 May 2021 00:55:35 +0200 Subject: [PATCH 586/609] Upgrade php-debug to v1.15.1 --- python3/vimspector/gadgets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 2c60a1a..62321ae 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -323,10 +323,10 @@ GADGETS = { '${version}/${file_name}', }, 'all': { - 'version': 'v1.14.9', - 'file_name': 'php-debug.vsix', + 'version': 'v1.15.1', + 'file_name': 'php-debug-1.15.1.vsix', 'checksum': - '0c5709cbbffe26b12aa63a88142195a9a045a5d8fca7fe63d62c789fe601630d', + '10222655d4179c7d109b1f951d88034eba772b45bf6141dcdb4e9b4477d2e2ab', }, 'adapters': { 'vscode-php-debug': { From 9113dbb6989ff9e289a2d1b8d740774cc33ee024 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 4 May 2021 21:59:44 +0100 Subject: [PATCH 587/609] stack_trace: Add cursorline highlight and sign for current frame --- README.md | 42 +++++---- python3/vimspector/settings.py | 3 +- python3/vimspector/stack_trace.py | 41 +++++++-- .../cpp/simple_c_program/.ycm_extra_conf.py | 2 +- tests/stack_trace.test.vim | 89 +++++++++++++++++++ 5 files changed, 150 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index e3a5ad5..bf84d2f 100644 --- a/README.md +++ b/README.md @@ -1007,6 +1007,8 @@ be changed manually to "switch to" that thread. to set the "focussed" thread to the currently selected one. If the selected line is a stack frame, set the focussed thread to the thread of that frame and jump to that frame in the code window. +* The current frame when a breakpoint is hit or if manuall jumping is also + highlighted. ![stack trace](https://puremourning.github.io/vimspector-web/img/vimspector-callstack-window.png) @@ -1700,22 +1702,26 @@ Vimsector uses them, they will not be replaced. So to customise the signs, define them in your `vimrc`. -| Sign | Description | Priority | -|------------------------|-------------------------------------|----------| -| `vimspectorBP` | Line breakpoint | 9 | -| `vimspectorBPCond` | Conditional line breakpoint | 9 | -| `vimspectorBPDisabled` | Disabled breakpoint | 9 | -| `vimspectorPC` | Program counter (i.e. current line) | 200 | -| `vimspectorPCBP` | Program counter and breakpoint | 200 | +| Sign | Description | Priority | +|---------------------------|-----------------------------------------|----------| +| `vimspectorBP` | Line breakpoint | 9 | +| `vimspectorBPCond` | Conditional line breakpoint | 9 | +| `vimspectorBPDisabled` | Disabled breakpoint | 9 | +| `vimspectorPC` | Program counter (i.e. current line) | 200 | +| `vimspectorPCBP` | Program counter and breakpoint | 200 | +| `vimspectorCurrentThread` | Focussed thread in stack trace view | 200 | +| `vimspectorCurrentFrame` | Current stack frame in stack trace view | 200 | The default symbols are the equivalent of something like the following: ```viml -sign define vimspectorBP text=\ ● texthl=WarningMsg -sign define vimspectorBPCond text=\ ◆ texthl=WarningMsg -sign define vimspectorBPDisabled text=\ ● texthl=LineNr -sign define vimspectorPC text=\ ▶ texthl=MatchParen linehl=CursorLine -sign define vimspectorPCBP text=●▶ texthl=MatchParen linehl=CursorLine +sign define vimspectorBP text=\ ● texthl=WarningMsg +sign define vimspectorBPCond text=\ ◆ texthl=WarningMsg +sign define vimspectorBPDisabled text=\ ● texthl=LineNr +sign define vimspectorPC text=\ ▶ texthl=MatchParen linehl=CursorLine +sign define vimspectorPCBP text=●▶ texthl=MatchParen linehl=CursorLine +sign define vimspectorCurrentThread text=▶ texthl=MatchParen linehl=CursorLine +sign define vimspectorCurrentFrame text=▶ texthl=Special linehl=CursorLine ``` If the signs don't display properly, your font probably doesn't contain these @@ -1723,11 +1729,13 @@ glyphs. You can easily change them by defining the sign in your vimrc. For example, you could put this in your `vimrc` to use some simple ASCII symbols: ```viml -sign define vimspectorBP text=o texthl=WarningMsg -sign define vimspectorBPCond text=o? texthl=WarningMsg -sign define vimspectorBPDisabled text=o! texthl=LineNr -sign define vimspectorPC text=\ > texthl=MatchParen -sign define vimspectorPCBP text=o> texthl=MatchParen +sign define vimspectorBP text=o texthl=WarningMsg +sign define vimspectorBPCond text=o? texthl=WarningMsg +sign define vimspectorBPDisabled text=o! texthl=LineNr +sign define vimspectorPC text=\ > texthl=MatchParen +sign define vimspectorPCBP text=o> texthl=MatchParen +sign define vimspectorCurrentThread text=> texthl=MatchParen +sign define vimspectorCurrentFrame text=> texthl=Special ``` ## Sign priority diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index a060543..89378af 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -42,7 +42,8 @@ DEFAULTS = { 'vimspectorBP': 9, 'vimspectorBPCond': 9, 'vimspectorBPDisabled': 9, - 'vimspectorCurrentThread': 200 + 'vimspectorCurrentThread': 200, + 'vimspectorCurrentFrame': 200, }, # Installer diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index ae14e68..f9540ba 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -102,7 +102,8 @@ class StackTraceView( object ): self._scratch_buffers = [] # FIXME: This ID is by group, so should be module scope - self._next_sign_id = 1 + self._current_thread_sign_id = 0 # 1 when used + self._current_frame_sign_id = 0 # 2 when used utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' ) utils.SetUpUIWindow( win ) @@ -127,6 +128,7 @@ class StackTraceView( object ): ':call vimspector#SetCurrentThread()' ) win.options[ 'cursorline' ] = False + win.options[ 'signcolumn' ] = 'auto' if not signs.SignDefined( 'vimspectorCurrentThread' ): @@ -136,6 +138,13 @@ class StackTraceView( object ): texthl = 'MatchParen', linehl = 'CursorLine' ) + if not signs.SignDefined( 'vimspectorCurrentFrame' ): + signs.DefineSign( 'vimspectorCurrentFrame', + text = '▶ ', + double_text = '▶', + texthl = 'Special', + linehl = 'CursorLine' ) + self._line_to_frame = {} self._line_to_thread = {} @@ -157,9 +166,12 @@ class StackTraceView( object ): self._sources = {} self._requesting_threads = StackTraceView.ThreadRequestState.NO self._pending_thread_request = None - if self._next_sign_id: - signs.UnplaceSign( self._next_sign_id, 'VimspectorStackTrace' ) - self._next_sign_id = 0 + if self._current_thread_sign_id: + signs.UnplaceSign( self._current_thread_sign_id, 'VimspectorStackTrace' ) + self._current_thread_sign_id = 0 + if self._current_frame_sign_id: + signs.UnplaceSign( self._current_frame_sign_id, 'VimspectorStackTrace' ) + self._current_frame_sign_id = 0 with utils.ModifiableScratchBuffer( self._buf ): utils.ClearBuffer( self._buf ) @@ -273,10 +285,10 @@ class StackTraceView( object ): self._line_to_frame.clear() self._line_to_thread.clear() - if self._next_sign_id: - signs.UnplaceSign( self._next_sign_id, 'VimspectorStackTrace' ) + if self._current_thread_sign_id: + signs.UnplaceSign( self._current_thread_sign_id, 'VimspectorStackTrace' ) else: - self._next_sign_id = 1 + self._current_thread_sign_id = 1 with utils.ModifiableScratchBuffer( self._buf ): with utils.RestoreCursorPosition(): @@ -290,7 +302,7 @@ class StackTraceView( object ): f'({thread.State()})' ) if self._current_thread == thread.id: - signs.PlaceSign( self._next_sign_id, + signs.PlaceSign( self._current_thread_sign_id, 'VimspectorStackTrace', 'vimspectorCurrentThread', self._buf.name, @@ -421,6 +433,7 @@ class StackTraceView( object ): # Should this set the current _Thread_ too ? If i jump to a frame in # Thread 2, should that become the focussed thread ? self._current_frame = frame + self._DrawThreads() return self._session.SetCurrentFrame( self._current_frame, reason ) return False @@ -518,6 +531,11 @@ class StackTraceView( object ): if not thread.IsExpanded(): return + if self._current_frame_sign_id: + signs.UnplaceSign( self._current_frame_sign_id, 'VimspectorStackTrace' ) + else: + self._current_frame_sign_id = 2 + for frame in thread.stacktrace: if frame.get( 'source' ): source = frame[ 'source' ] @@ -542,6 +560,13 @@ class StackTraceView( object ): source[ 'name' ], frame[ 'line' ] ) ) + if self._current_frame[ 'id' ] == frame[ 'id' ]: + signs.PlaceSign( self._current_frame_sign_id, + 'VimspectorStackTrace', + 'vimspectorCurrentFrame', + self._buf.name, + line ) + self._line_to_frame[ line ] = ( thread, frame ) def _ResolveSource( self, source, and_then ): diff --git a/support/test/cpp/simple_c_program/.ycm_extra_conf.py b/support/test/cpp/simple_c_program/.ycm_extra_conf.py index 0d17586..4203b36 100644 --- a/support/test/cpp/simple_c_program/.ycm_extra_conf.py +++ b/support/test/cpp/simple_c_program/.ycm_extra_conf.py @@ -1,4 +1,4 @@ def Settings( **kwargs ): return { - 'flags': [ '-x', 'c++', '-Wall', '-Wextra' ] + 'flags': [ '-x', 'c++', '-Wall', '-Wextra', '-std=c++17' ] } diff --git a/tests/stack_trace.test.vim b/tests/stack_trace.test.vim index a65ea5e..f191201 100644 --- a/tests/stack_trace.test.vim +++ b/tests/stack_trace.test.vim @@ -379,6 +379,20 @@ function! Test_UpDownStack() \ '$' ) \ ) \ } ) + call win_gotoid( g:vimspector_session_windows.stack_trace ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 1, + \ 'vimspectorCurrentThread', + \ 200 ) } ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 2, + \ 'vimspectorCurrentFrame', + \ 200 ) } ) + wincmd w call vimspector#DownFrame() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 15, 1 ) @@ -396,6 +410,21 @@ function! Test_UpDownStack() \ '$' ) \ ) \ } ) + call win_gotoid( g:vimspector_session_windows.stack_trace ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 1, + \ 'vimspectorCurrentThread', + \ 200 ) } ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 2, + \ 'vimspectorCurrentFrame', + \ 200 ) } ) + wincmd w + call vimspector#UpFrame() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 8, 1 ) @@ -413,6 +442,21 @@ function! Test_UpDownStack() \ '$' ) \ ) \ } ) + call win_gotoid( g:vimspector_session_windows.stack_trace ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 1, + \ 'vimspectorCurrentThread', + \ 200 ) } ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 3, + \ 'vimspectorCurrentFrame', + \ 200 ) } ) + wincmd w + call feedkeys( "\VimspectorUpFrame", 'x' ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 23, 1 ) @@ -430,6 +474,21 @@ function! Test_UpDownStack() \ '$' ) \ ) \ } ) + call win_gotoid( g:vimspector_session_windows.stack_trace ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 1, + \ 'vimspectorCurrentThread', + \ 200 ) } ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 4, + \ 'vimspectorCurrentFrame', + \ 200 ) } ) + wincmd w + call feedkeys( "\VimspectorDownFrame", 'x' ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 8, 1 ) @@ -447,6 +506,21 @@ function! Test_UpDownStack() \ '$' ) \ ) \ } ) + call win_gotoid( g:vimspector_session_windows.stack_trace ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 1, + \ 'vimspectorCurrentThread', + \ 200 ) } ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 3, + \ 'vimspectorCurrentFrame', + \ 200 ) } ) + wincmd w + call vimspector#DownFrame() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 15, 1 ) @@ -464,6 +538,21 @@ function! Test_UpDownStack() \ '$' ) \ ) \ } ) + call win_gotoid( g:vimspector_session_windows.stack_trace ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 1, + \ 'vimspectorCurrentThread', + \ 200 ) } ) + call WaitForAssert( { -> + \ vimspector#test#signs#AssertSignGroupSingletonAtLine( + \ 'VimspectorStackTrace', + \ 2, + \ 'vimspectorCurrentFrame', + \ 200 ) } ) + wincmd w + call vimspector#ClearBreakpoints() From 0e9497ce8f12d481974397e7c1c7f8378b9bce69 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 15 May 2021 15:50:59 +0100 Subject: [PATCH 588/609] Add cpptools to cpp test proj --- support/test/cpp/simple_c_program/.vimspector.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/support/test/cpp/simple_c_program/.vimspector.json b/support/test/cpp/simple_c_program/.vimspector.json index 7b8c53a..fb4c958 100644 --- a/support/test/cpp/simple_c_program/.vimspector.json +++ b/support/test/cpp/simple_c_program/.vimspector.json @@ -15,6 +15,15 @@ "program": "${workspaceRoot}/test", "stopAtEntry": true } + }, + "cpptools": { + "adapter": "vscode-cpptools", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/test", + "stopOnEntry": true, + "MIMode": "lldb" + } } } } From aacd62f09feed377c35930790514b2739fa08e51 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 15 May 2021 16:03:02 +0100 Subject: [PATCH 589/609] Rename AssertMatchist -> AssertMatchList --- tests/lib/plugin/shared.vim | 2 +- tests/stack_trace.test.vim | 56 ++++++++++++++++++------------------- tests/variables.test.vim | 44 ++++++++++++++--------------- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/tests/lib/plugin/shared.vim b/tests/lib/plugin/shared.vim index 70e297e..f98b8e9 100644 --- a/tests/lib/plugin/shared.vim +++ b/tests/lib/plugin/shared.vim @@ -99,7 +99,7 @@ function! ThisTestIsFlaky() let g:test_is_flaky = v:true endfunction -function! AssertMatchist( expected, actual ) abort +function! AssertMatchList( 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 diff --git a/tests/stack_trace.test.vim b/tests/stack_trace.test.vim index f191201..b5ed795 100644 --- a/tests/stack_trace.test.vim +++ b/tests/stack_trace.test.vim @@ -30,7 +30,7 @@ function! Test_Multiple_Threads_Continue() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) call cursor( 1, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread [0-9]\+: .* (paused)', \ ' .*: .*@threads.cpp:' . string( thread_l ) @@ -45,7 +45,7 @@ function! Test_Multiple_Threads_Continue() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) call cursor( 1, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread [0-9]\+: .* (paused)', \ ' .*: .*@threads.cpp:' . string( thread_l ) @@ -56,7 +56,7 @@ function! Test_Multiple_Threads_Continue() \ ) \ } ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ ], @@ -70,7 +70,7 @@ function! Test_Multiple_Threads_Continue() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) call cursor( 1, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread [0-9]\+: .* (paused)', \ ' .*: .*@threads.cpp:' . string( thread_l ) @@ -81,7 +81,7 @@ function! Test_Multiple_Threads_Continue() \ ) \ } ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ ], @@ -95,7 +95,7 @@ function! Test_Multiple_Threads_Continue() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) call cursor( 1, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread [0-9]\+: .* (paused)', \ ' .*: .*@threads.cpp:' . string( thread_l ) @@ -106,7 +106,7 @@ function! Test_Multiple_Threads_Continue() \ ) \ } ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ ], @@ -121,7 +121,7 @@ function! Test_Multiple_Threads_Continue() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) call cursor( 1, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread [0-9]\+: .* (paused)', \ ' .*: .*@threads.cpp:' . string( thread_l ) @@ -132,7 +132,7 @@ function! Test_Multiple_Threads_Continue() \ ) \ } ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ ], @@ -146,7 +146,7 @@ function! Test_Multiple_Threads_Continue() " So we break out of the loop call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, notify_l, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread [0-9]\+: .* (paused)', \ ' .*: .*@threads.cpp:' . string( notify_l ) @@ -157,7 +157,7 @@ function! Test_Multiple_Threads_Continue() \ ) \ } ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ ], @@ -192,7 +192,7 @@ function! Test_Multiple_Threads_Step() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread [0-9]\+: .* (paused)', \ ' .*: .*@threads.cpp:' . string( thread_l ) @@ -205,7 +205,7 @@ function! Test_Multiple_Threads_Step() call vimspector#StepOver() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ ], @@ -218,7 +218,7 @@ function! Test_Multiple_Threads_Step() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ ], @@ -230,7 +230,7 @@ function! Test_Multiple_Threads_Step() call vimspector#StepOver() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ '+ Thread [0-9]\+: .* (paused)', @@ -244,7 +244,7 @@ function! Test_Multiple_Threads_Step() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ '+ Thread [0-9]\+: .* (paused)', @@ -257,7 +257,7 @@ function! Test_Multiple_Threads_Step() call vimspector#StepOver() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ '+ Thread [0-9]\+: .* (paused)', @@ -273,7 +273,7 @@ function! Test_Multiple_Threads_Step() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ '+ Thread [0-9]\+: .* (paused)', @@ -287,7 +287,7 @@ function! Test_Multiple_Threads_Step() call vimspector#StepOver() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ '+ Thread [0-9]\+: .* (paused)', @@ -304,7 +304,7 @@ function! Test_Multiple_Threads_Step() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ '+ Thread [0-9]\+: .* (paused)', @@ -319,7 +319,7 @@ function! Test_Multiple_Threads_Step() call vimspector#StepOver() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ '+ Thread [0-9]\+: .* (paused)', @@ -338,7 +338,7 @@ function! Test_Multiple_Threads_Step() " So we break out of the loop call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, notify_l, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '+ Thread [0-9]\+: .* (paused)', \ '+ Thread [0-9]\+: .* (paused)', @@ -366,7 +366,7 @@ function! Test_UpDownStack() call vimspector#LaunchWithSettings( { 'configuration': 'run' } ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 15, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread 1: MainThread (paused)', \ ' 2: DoSomething@main.py:15', @@ -397,7 +397,7 @@ function! Test_UpDownStack() call vimspector#DownFrame() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 15, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread 1: MainThread (paused)', \ ' 2: DoSomething@main.py:15', @@ -429,7 +429,7 @@ function! Test_UpDownStack() call vimspector#UpFrame() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 8, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread 1: MainThread (paused)', \ ' 2: DoSomething@main.py:15', @@ -461,7 +461,7 @@ function! Test_UpDownStack() call feedkeys( "\VimspectorUpFrame", 'x' ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 23, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread 1: MainThread (paused)', \ ' 2: DoSomething@main.py:15', @@ -493,7 +493,7 @@ function! Test_UpDownStack() call feedkeys( "\VimspectorDownFrame", 'x' ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 8, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread 1: MainThread (paused)', \ ' 2: DoSomething@main.py:15', @@ -525,7 +525,7 @@ function! Test_UpDownStack() call vimspector#DownFrame() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 15, 1 ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Thread 1: MainThread (paused)', \ ' 2: DoSomething@main.py:15', diff --git a/tests/variables.test.vim b/tests/variables.test.vim index c7d373b..59ca2c0 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -211,7 +211,7 @@ function! Test_ExpandVariables() call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Scope: Locals', \ ' \*- t (Test): {...}', @@ -229,7 +229,7 @@ function! Test_ExpandVariables() " Step - stays expanded call vimspector#StepOver() call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Scope: Locals', \ ' - t (Test): {...}', @@ -278,7 +278,7 @@ function! Test_ExpandVariables() call setpos( '.', [ 0, 2, 1 ] ) call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Scope: Locals', \ ' - t (Test): {...}', @@ -378,7 +378,7 @@ function! Test_ExpandWatch() call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ 'Watches: ----', \ 'Expression: t', @@ -397,7 +397,7 @@ function! Test_ExpandWatch() " Step - stays expanded call vimspector#StepOver() call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ 'Watches: ----', \ 'Expression: t', @@ -449,7 +449,7 @@ function! Test_ExpandWatch() call setpos( '.', [ 0, 3, 1 ] ) call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ 'Watches: ----', \ 'Expression: t', @@ -607,7 +607,7 @@ function! Test_EvaluateFailure() " Add a wtch call vimspector#AddWatch( 'test' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ 'Watches: ----', \ 'Expression: test', @@ -658,7 +658,7 @@ function! Test_VariableEval() \ } ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '{...}', \ ' - i: 0', @@ -690,7 +690,7 @@ function! Test_VariableEval() \ } ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '{...}', \ ' - i: 0', @@ -724,7 +724,7 @@ function! Test_VariableEval() \ } ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ 'Evaluation error', \ ], @@ -768,7 +768,7 @@ function! Test_VariableEvalExpand() \ } ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '{...}', \ ' - i: 0', @@ -786,7 +786,7 @@ function! Test_VariableEvalExpand() call feedkeys( "jjjj\", 'xt' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '{...}', \ ' - i: 0', @@ -806,7 +806,7 @@ function! Test_VariableEvalExpand() call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '{...}', \ ' - i: 0', @@ -863,7 +863,7 @@ function! Test_SetVariableValue_Local() call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Scope: Locals', \ ' \*- t (Test): {...}', @@ -889,7 +889,7 @@ with mock.patch( 'vimspector.utils.InputSave' ): EOF call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Scope: Locals', \ ' \*- t (Test): {...}', @@ -908,7 +908,7 @@ EOF call vimspector#SetVariableValue( '1234' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Scope: Locals', \ ' \*- t (Test): {...}', @@ -927,7 +927,7 @@ EOF call vimspector#SetVariableValue( 'this is invalid' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '- Scope: Locals', \ ' \*- t (Test): {...}', @@ -983,7 +983,7 @@ function! Test_SetVariableValue_Watch() call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ 'Watches: ----', \ 'Expression: t', @@ -1012,7 +1012,7 @@ EOF call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ 'Watches: ----', \ 'Expression: t', @@ -1032,7 +1032,7 @@ EOF call vimspector#SetVariableValue( '1234' ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ 'Watches: ----', \ 'Expression: t', @@ -1075,7 +1075,7 @@ function! Test_SetVariableValue_Balloon() \ } ) call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '{...}', \ ' - i: 0', @@ -1102,7 +1102,7 @@ with mock.patch( 'vimspector.utils.InputSave' ): EOF call WaitForAssert( {-> - \ AssertMatchist( + \ AssertMatchList( \ [ \ '{...}', \ ' - i: 0', From d43904eb57c2771c6ba05b48c47da5ff2bbb9735 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 15 May 2021 17:55:01 +0100 Subject: [PATCH 590/609] Add basic VimspectorDebugInfo command --- .github/ISSUE_TEMPLATE/bug_report.md | 6 ++++++ README.md | 2 ++ autoload/vimspector.vim | 8 +++++++ plugin/vimspector.vim | 3 +++ python3/vimspector/debug_session.py | 31 ++++++++++++++++++++++++++++ python3/vimspector/output.py | 26 ++++++++++++++++++----- 6 files changed, 71 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index f01a833..828ec18 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -57,6 +57,12 @@ discussing on Gitter rather than raising an issue. * Version of Vimspector: (e.g. output of `git rev-parse HEAD` if cloned or the name of the tarball used to install otherwise) +* Output of `:VimspectorDebugInfo` + +``` +paste here +``` + * Output of `vim --version` or `nvim --version` ``` diff --git a/README.md b/README.md index bf84d2f..f8db3aa 100644 --- a/README.md +++ b/README.md @@ -1070,6 +1070,8 @@ information when something goes wrong that's not a Vim traceback. If you just want to see the Vimspector log file, use `:VimspectorToggleLog`, which will tail it in a little window (doesn't work on Windows). +You can see some debugging info with `:VimspectorDebugInfo` + ## Closing debugger To close the debugger, use: diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 1219661..78c7c1b 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -557,6 +557,14 @@ function! vimspector#ShowEvalBalloon( is_visual ) abort \ . '", 0 )' ) endfunction +function! vimspector#PrintDebugInfo() abort + if !s:Enabled() + return + endif + + py3 _vimspector_session.PrintDebugInfo() +endfunction + " Boilerplate {{{ let &cpoptions=s:save_cpo diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 27ce473..2668bf1 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -115,6 +115,9 @@ command! -bar -nargs=? -complete=custom,vimspector#CompleteOutput command! -bar \ VimspectorToggleLog \ call vimspector#ToggleLog() +command! -bar + \ VimspectorDebugInfo + \ call vimspector#PrintDebugInfo() command! -nargs=1 -complete=custom,vimspector#CompleteExpr \ VimspectorEval \ call vimspector#Evaluate( ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 2f132f9..36ad62b 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -1270,6 +1270,37 @@ class DebugSession( object ): self._stackTraceView.LoadThreads( True ) + @IfConnected() + @RequiresUI() + def PrintDebugInfo( self ): + def Line(): + return ( "--------------------------------------------------------------" + "------------------" ) + + def Pretty( obj ): + if obj is None: + return [ "None" ] + return [ Line() ] + json.dumps( obj, indent=2 ).splitlines() + [ Line() ] + + + debugInfo = [ + "Vimspector Debug Info", + Line(), + f"ConnectionType: { self._connection_type }", + "Adapter: " ] + Pretty( self._adapter ) + [ + "Configuration: " ] + Pretty( self._configuration ) + [ + f"API Prefix: { self._api_prefix }", + f"Launch/Init: { self._launch_complete } / { self._init_complete }", + f"Workspace Root: { self._workspace_root }", + "Launch Config: " ] + Pretty( self._launch_config ) + [ + "Server Capabilities: " ] + Pretty( self._server_capabilities ) + [ + ] + + self._outputView.ClearCategory( 'DebugInfo' ) + self._outputView.Print( "DebugInfo", debugInfo ) + self.ShowOutput( "DebugInfo" ) + + def OnEvent_loadedSource( self, msg ): pass diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index c453417..8c94b44 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -64,8 +64,11 @@ class OutputView( object ): self._api_prefix = api_prefix VIEWS.add( self ) - def Print( self, categroy, text ): - self._Print( 'server', text.splitlines() ) + def Print( self, category, text: typing.Union[ str, list ] ): + if not isinstance( text, list ): + text = text.splitlines() + + self._Print( category, text ) def OnOutput( self, event ): category = CategoryToBuffer( event.get( 'category' ) or 'output' ) @@ -104,13 +107,26 @@ class OutputView( object ): def Clear( self ): for category, tab_buffer in self._buffers.items(): - if tab_buffer.is_job: - utils.CleanUpCommand( category, self._api_prefix ) - utils.CleanUpHiddenBuffer( tab_buffer.buf ) + self._CleanUpBuffer( category, tab_buffer ) # FIXME: nunmenu the WinBar ? self._buffers = {} + + def ClearCategory( self, category: str ): + if category not in self._buffers: + return + + self._CleanUpBuffer( category, self._buffers[ category ] ) + + + def _CleanUpBuffer( self, category: str, tab_buffer: TabBuffer ): + if tab_buffer.is_job: + utils.CleanUpCommand( category, self._api_prefix ) + + utils.CleanUpHiddenBuffer( tab_buffer.buf ) + + def WindowIsValid( self ): return self._window.valid From daa8865feab2ec32a60ab2fb11b6f33a0cdeadb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 May 2021 10:56:04 +0000 Subject: [PATCH 591/609] Bump nokogiri from 1.11.3 to 1.11.5 in /docs Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.11.3 to 1.11.5. - [Release notes](https://github.com/sparklemotion/nokogiri/releases) - [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md) - [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.11.3...v1.11.5) Signed-off-by: dependabot[bot] --- docs/Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 6a18520..0fa7776 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -205,14 +205,14 @@ GEM rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) - mini_portile2 (2.5.0) + mini_portile2 (2.5.1) minima (2.5.1) jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) minitest (5.14.4) multipart-post (2.1.1) - nokogiri (1.11.3) + nokogiri (1.11.5) mini_portile2 (~> 2.5.0) racc (~> 1.4) octokit (4.20.0) From a51b8b23c9b7e12c2899e423037458d46dbe196e Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 25 May 2021 14:53:29 +0100 Subject: [PATCH 592/609] Fix traceback if the current frame isn't set --- python3/vimspector/stack_trace.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index f9540ba..8b1d848 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -560,7 +560,8 @@ class StackTraceView( object ): source[ 'name' ], frame[ 'line' ] ) ) - if self._current_frame[ 'id' ] == frame[ 'id' ]: + if ( self._current_frame is not None and + self._current_frame[ 'id' ] == frame[ 'id' ] ): signs.PlaceSign( self._current_frame_sign_id, 'VimspectorStackTrace', 'vimspectorCurrentFrame', From 5ea1f0d9d49011259ecedb70df94c6ff799ead62 Mon Sep 17 00:00:00 2001 From: przepompownia Date: Mon, 7 Jun 2021 14:10:44 +0200 Subject: [PATCH 593/609] Upgrade vscode-php-debug to 1.16.0 --- python3/vimspector/gadgets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 62321ae..23a298c 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -323,10 +323,10 @@ GADGETS = { '${version}/${file_name}', }, 'all': { - 'version': 'v1.15.1', - 'file_name': 'php-debug-1.15.1.vsix', + 'version': 'v1.16.0', + 'file_name': 'php-debug-1.16.0.vsix', 'checksum': - '10222655d4179c7d109b1f951d88034eba772b45bf6141dcdb4e9b4477d2e2ab', + '62d210f7b87b21315c37ea10a1a5dbae376ff9f963b8f8cf33361e01413731be', }, 'adapters': { 'vscode-php-debug': { From 0500e41429bde17105e931d9920b3c6dbe5eabdb Mon Sep 17 00:00:00 2001 From: Paulo Date: Wed, 9 Jun 2021 19:24:37 +0200 Subject: [PATCH 594/609] FIx typo in configuration.md A small typo that wastes time for people that copy and modify the config file --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index a2864b1..3d524bf 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -722,7 +722,7 @@ Vimspector then orchestrates the various tools to set you up. "variables": { // Just an example of how to specify a variable manually rather than // vimspector asking for input from the user - "ServiceName": "${fileBasenameNoExtention}" + "ServiceName": "${fileBasenameNoExtension}" }, "adapter": "python-remote", From 5075f3a11aba4e31cb7994746862a4342c8546ae Mon Sep 17 00:00:00 2001 From: Simon Drake Date: Thu, 24 Jun 2021 14:40:40 +0100 Subject: [PATCH 595/609] Add a runnable Go example --- README.md | 2 +- .../name-starts-with-vowel/.vimspector.json | 29 ++++++++++++++++ .../test/go/name-starts-with-vowel/README.md | 33 +++++++++++++++++++ .../cmd/namestartswithvowel/main.go | 20 +++++++++++ support/test/go/name-starts-with-vowel/go.mod | 3 ++ .../internal/vowels/vowels.go | 9 +++++ .../internal/vowels/vowels_test.go | 30 +++++++++++++++++ 7 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 support/test/go/name-starts-with-vowel/.vimspector.json create mode 100644 support/test/go/name-starts-with-vowel/README.md create mode 100644 support/test/go/name-starts-with-vowel/cmd/namestartswithvowel/main.go create mode 100644 support/test/go/name-starts-with-vowel/go.mod create mode 100644 support/test/go/name-starts-with-vowel/internal/vowels/vowels.go create mode 100644 support/test/go/name-starts-with-vowel/internal/vowels/vowels_test.go diff --git a/README.md b/README.md index f8db3aa..66f2720 100644 --- a/README.md +++ b/README.md @@ -290,7 +290,7 @@ If you just want to try out vimspector without changing your vim config, there are example projects for a number of languages in `support/test`, including: * Python (`support/test/python/simple_python`) -* Go (`support/test/go/hello_world`) +* Go (`support/test/go/hello_world` and `support/test/go/name-starts-with-vowel`) * Nodejs (`support/test/node/simple`) * Chrome (`support/test/chrome/`) * etc. diff --git a/support/test/go/name-starts-with-vowel/.vimspector.json b/support/test/go/name-starts-with-vowel/.vimspector.json new file mode 100644 index 0000000..ffcfc93 --- /dev/null +++ b/support/test/go/name-starts-with-vowel/.vimspector.json @@ -0,0 +1,29 @@ +{ + "configurations": { + "run-cmd": { + "adapter": "vscode-go", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/cmd/namestartswithvowel/main.go", + "mode": "debug", + "dlvToolPath": "$HOME/go/bin/dlv", + "dlvLoadConfig": { + "maxArrayValues": 1000, + "maxStringLen": 1000 + } + } + }, + "test-current-file": { + "adapter": "vscode-go", + "configuration": { + "request": "launch", + "mode": "test", + "program": "${fileDirname}", + "cwd": "${fileDirname}", + "dlvToolPath": "$GOPATH/bin/dlv", + "env": {}, + "args": [] + } + } + } +} diff --git a/support/test/go/name-starts-with-vowel/README.md b/support/test/go/name-starts-with-vowel/README.md new file mode 100644 index 0000000..fec967e --- /dev/null +++ b/support/test/go/name-starts-with-vowel/README.md @@ -0,0 +1,33 @@ +# Purpose + +This example comes with two example vimspector configs for the Go programming language. + +1) `run-cmd` will launch the main programme under `cmd/namestartswithvowel`. +1) `test-current-file` will run the tests in the current file in debug mode. + +## Example use-cases + +### run-cmd + +* Open `cmd/namestartswithvowel/main.go` +* Add a breakpoint somewhere within the programme +* Start the debugger (`:call vimspector#Continue()` or your relevant keymapping) +* Select the first launch configuration (`1: run-cmd`) + +### test-current-file + +* Open `internal/vowels/vowels_test.go` +* Add a breakpoint somewhere within the test +* Start the debugger (`:call vimspector#Continue()` or your relevant keymapping) +* Select the second launch configuration (`2: test-current-file`) + +## Additional Configuration + +There are two additional configuration options specified under `run-cmd`; these parameters configure the maximum string/array size to be shown while debugging. + +``` +"dlvLoadConfig": { + "maxArrayValues": 1000, + "maxStringLen": 1000 +} +``` diff --git a/support/test/go/name-starts-with-vowel/cmd/namestartswithvowel/main.go b/support/test/go/name-starts-with-vowel/cmd/namestartswithvowel/main.go new file mode 100644 index 0000000..c160aea --- /dev/null +++ b/support/test/go/name-starts-with-vowel/cmd/namestartswithvowel/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + + "example.com/internal/vowels" +) + +func main() { + names := []string{"Simon", "Bob", "Jennifer", "Amy", "Duke", "Elizabeth"} + + for _, n := range names { + if vowels.NameStartsWithVowel(n) { + fmt.Printf("%s starts with a vowel!\n", n) + continue + } + + fmt.Printf("%s does not start with a vowel!\n", n) + } +} diff --git a/support/test/go/name-starts-with-vowel/go.mod b/support/test/go/name-starts-with-vowel/go.mod new file mode 100644 index 0000000..3070734 --- /dev/null +++ b/support/test/go/name-starts-with-vowel/go.mod @@ -0,0 +1,3 @@ +module example.com + +go 1.16 diff --git a/support/test/go/name-starts-with-vowel/internal/vowels/vowels.go b/support/test/go/name-starts-with-vowel/internal/vowels/vowels.go new file mode 100644 index 0000000..4e76480 --- /dev/null +++ b/support/test/go/name-starts-with-vowel/internal/vowels/vowels.go @@ -0,0 +1,9 @@ +package vowels + +import "strings" + +func NameStartsWithVowel(name string) bool { + s := strings.Split(strings.ToLower(name), "") + + return s[0] == "a" || s[0] == "e" || s[0] == "i" || s[0] == "o" || s[0] == "u" +} diff --git a/support/test/go/name-starts-with-vowel/internal/vowels/vowels_test.go b/support/test/go/name-starts-with-vowel/internal/vowels/vowels_test.go new file mode 100644 index 0000000..e0d5773 --- /dev/null +++ b/support/test/go/name-starts-with-vowel/internal/vowels/vowels_test.go @@ -0,0 +1,30 @@ +package vowels + +import ( + "fmt" + "testing" +) + +func TestNameStartsWithVowel(t *testing.T) { + testCases := []struct { + input string + expectedOutput bool + }{ + { + input: "Simon", + expectedOutput: false, + }, + { + input: "Andy", + expectedOutput: true, + }, + } + for _, tt := range testCases { + t.Run(fmt.Sprintf("%s should product %t", tt.input, tt.expectedOutput), func(t *testing.T) { + out := NameStartsWithVowel(tt.input) + if out != tt.expectedOutput { + t.Errorf("%s produced %t, when %t was expected", tt.input, out, tt.expectedOutput) + } + }) + } +} From 21ebb22fd44c586f6b22ef393012863b6502a010 Mon Sep 17 00:00:00 2001 From: przepompownia Date: Sun, 4 Jul 2021 23:00:32 +0200 Subject: [PATCH 596/609] Upgrade vscode-php-debug to 1.16.1 --- python3/vimspector/gadgets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 23a298c..240fc68 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -323,10 +323,10 @@ GADGETS = { '${version}/${file_name}', }, 'all': { - 'version': 'v1.16.0', - 'file_name': 'php-debug-1.16.0.vsix', + 'version': 'v1.16.1', + 'file_name': 'php-debug-1.16.1.vsix', 'checksum': - '62d210f7b87b21315c37ea10a1a5dbae376ff9f963b8f8cf33361e01413731be', + '2eb6ff1100b6b3d2d160f243858f3524e269078b8154e108d015882e2c0d52c4', }, 'adapters': { 'vscode-php-debug': { From 3af97f192841247a2891c021d935a2e4f15db7c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jul 2021 05:43:17 +0000 Subject: [PATCH 597/609] Bump addressable from 2.7.0 to 2.8.0 in /docs Bumps [addressable](https://github.com/sporkmonger/addressable) from 2.7.0 to 2.8.0. - [Release notes](https://github.com/sporkmonger/addressable/releases) - [Changelog](https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md) - [Commits](https://github.com/sporkmonger/addressable/compare/addressable-2.7.0...addressable-2.8.0) --- updated-dependencies: - dependency-name: addressable dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 0fa7776..acf20f2 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -7,7 +7,7 @@ GEM minitest (~> 5.1) tzinfo (~> 1.1) zeitwerk (~> 2.2, >= 2.2.2) - addressable (2.7.0) + addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) coffee-script (2.4.1) coffee-script-source From 59c9cd10ab9073b91a11ccd9b21c34d8331fa9cb Mon Sep 17 00:00:00 2001 From: przepompownia Date: Mon, 2 Aug 2021 16:11:04 +0200 Subject: [PATCH 598/609] Upgrade vscode-php-debug to 1.17.0 --- python3/vimspector/gadgets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 240fc68..170956a 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -323,10 +323,10 @@ GADGETS = { '${version}/${file_name}', }, 'all': { - 'version': 'v1.16.1', - 'file_name': 'php-debug-1.16.1.vsix', + 'version': 'v1.17.0', + 'file_name': 'php-debug-1.17.0.vsix', 'checksum': - '2eb6ff1100b6b3d2d160f243858f3524e269078b8154e108d015882e2c0d52c4', + 'd0fff272503414b6696cc737bc2e18e060fdd5e5dc4bcaf38ae7373afd8d8bc9', }, 'adapters': { 'vscode-php-debug': { From f1e2c12e5bfbb0b9abd1dec75ad8d2810ecc4fc9 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Mon, 2 Aug 2021 16:01:47 +0100 Subject: [PATCH 599/609] Update codelldb Add a way to checksum downloads and a little guide to updating gadgets --- python3/vimspector/gadgets.py | 8 ++++---- run_tests | 4 ++-- support/gadget_upgrade/README.md | 8 ++++++++ support/gadget_upgrade/checksum.py | 13 +++++++++++++ 4 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 support/gadget_upgrade/README.md create mode 100755 support/gadget_upgrade/checksum.py diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 170956a..72f13c8 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -394,12 +394,12 @@ GADGETS = { '${version}/${file_name}', }, 'all': { - 'version': 'v1.6.1', + 'version': 'v1.6.5', }, 'macos': { 'file_name': 'codelldb-x86_64-darwin.vsix', 'checksum': - 'b1c998e7421beea9f3ba21aa5706210bb2249eba93c99b809247ee831075262f', + 'e7d9f4f8ec3c3774af6d1dbf11f0568db1417c4d51038927228cd07028725594', 'make_executable': [ 'adapter/codelldb', 'lldb/bin/debugserver', @@ -410,7 +410,7 @@ GADGETS = { 'linux': { 'file_name': 'codelldb-x86_64-linux.vsix', 'checksum': - 'f2a36cb6971fd95a467cf1a7620e160914e8f11bf82929932ee0aa5afbf6ae6a', + 'eda2cd9b3089dcc0524c273e91ffb5875fe08c930bf643739a2cd1846e1f98d6', 'make_executable': [ 'adapter/codelldb', 'lldb/bin/lldb', @@ -421,7 +421,7 @@ GADGETS = { 'windows': { 'file_name': 'codelldb-x86_64-windows.vsix', 'checksum': - 'ca6a6525bf7719dc95265dc630b3cc817a8c0393b756fd242b710805ffdfb940', + '8ddebe8381a3d22dc3d95139c3797fda06b5cc34aadf300e13b1c516b9da95fe', 'make_executable': [] }, 'adapters': { diff --git a/run_tests b/run_tests index 441acb0..201ec1b 100755 --- a/run_tests +++ b/run_tests @@ -21,7 +21,7 @@ out_fd=1 while [ -n "$1" ]; do case "$1" in - "--basedir") + "--basedir"|"--base-dir"|"--test-base") shift SetBaseDir $1 shift @@ -36,7 +36,7 @@ while [ -n "$1" ]; do INSTALL=$1 shift ;; - "--update") + "--update"|"--upgrade") UPDATE=1 shift ;; diff --git a/support/gadget_upgrade/README.md b/support/gadget_upgrade/README.md new file mode 100644 index 0000000..9ae3d7f --- /dev/null +++ b/support/gadget_upgrade/README.md @@ -0,0 +1,8 @@ +# Manually updating shipped gadgets + +Download the gadget files manuall from their official source into this dir. +Run `./checksum.py ` to get the checksums. + +Update ../../python3/vimspector/gadgets.py with the new version and the +checksums. + diff --git a/support/gadget_upgrade/checksum.py b/support/gadget_upgrade/checksum.py new file mode 100755 index 0000000..d0c1404 --- /dev/null +++ b/support/gadget_upgrade/checksum.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +import hashlib +import sys + + +def GetChecksumSHA254( file_path ): + with open( file_path, 'rb' ) as existing_file: + return hashlib.sha256( existing_file.read() ).hexdigest() + + +for arg in sys.argv[ 1: ]: + print( f"{ arg } = { GetChecksumSHA254( arg ) }" ) From 57ce0992803fcf22c0557550fff45e3fe869f707 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 3 Aug 2021 17:29:55 +0100 Subject: [PATCH 600/609] SHow the output in the console, as this is where codelldb puts it --- python3/vimspector/output.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python3/vimspector/output.py b/python3/vimspector/output.py index 8c94b44..3f0da1e 100644 --- a/python3/vimspector/output.py +++ b/python3/vimspector/output.py @@ -32,6 +32,7 @@ class TabBuffer( object ): BUFFER_MAP = { 'console': 'Console', 'stdout': 'Console', + 'output': 'Console', 'stderr': 'stderr', 'telemetry': None, } From 7c7e3f9c3f63de7ef91e5d579662a4e16be95315 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 20 Aug 2021 11:17:05 +0100 Subject: [PATCH 601/609] Add config for bash to tests --- support/test/bash/.vimspector.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 support/test/bash/.vimspector.json diff --git a/support/test/bash/.vimspector.json b/support/test/bash/.vimspector.json new file mode 100644 index 0000000..a1be1b9 --- /dev/null +++ b/support/test/bash/.vimspector.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json", + "configurations": { + "Run Current Script": { + "adapter": "vscode-bash", + "autoselect": false, + "configuration": { + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "args": [ "*${args}" ] + } + } + } +} From a720d0e1d56743bb1f335b2a4bcb963491e9b421 Mon Sep 17 00:00:00 2001 From: roachsinai Date: Sat, 21 Aug 2021 00:57:27 +0800 Subject: [PATCH 602/609] Fix error: E806: using Float as a String. --- support/custom_ui_vimrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/custom_ui_vimrc b/support/custom_ui_vimrc index a8812cb..e76c6ee 100644 --- a/support/custom_ui_vimrc +++ b/support/custom_ui_vimrc @@ -54,7 +54,7 @@ function s:SetUpTerminal() let padding = 4 let cols = max( [ min( [ &columns - left_bar - code - padding, 80 ] ), 10 ] ) call win_gotoid( terminal_win ) - execute cols . 'wincmd |' + execute string(cols) . 'wincmd |' endfunction function! s:CustomiseWinBar() From 51d78fce5f95fb10ba84b8ab66e97d44d75df010 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 7 Sep 2021 17:00:04 +0100 Subject: [PATCH 603/609] Note on using legacy vscode-dap --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 66f2720..3420d56 100644 --- a/README.md +++ b/README.md @@ -1368,6 +1368,8 @@ Requires: * [Delve][delve-install] installed, e.g. `go get -u github.com/go-delve/delve/cmd/dlv` * Delve to be in your PATH, or specify the `dlvToolPath` launch option +NOTE: Vimspector uses the ["legacy" vscode-go debug adapter](https://github.com/golang/vscode-go/blob/master/docs/debugging-legacy.md) rather than the "built-in" DAP support in Delve. You can track https://github.com/puremourning/vimspector/issues/186 for that. + ```json { "configurations": { @@ -1385,7 +1387,7 @@ Requires: ``` See the vscode-go docs for -[troubleshooting information](https://github.com/golang/vscode-go/blob/master/docs/debugging.md#troubleshooting) +[troubleshooting information](https://github.com/golang/vscode-go/blob/master/docs/debugging-legacy.md#troubleshooting) ## PHP From 46cfdc678dbdbb162f8cb8ab4384897e1a3d3bd0 Mon Sep 17 00:00:00 2001 From: Sebastian Goth Date: Wed, 8 Sep 2021 12:19:51 +0200 Subject: [PATCH 604/609] Update vscode-cpptools from 0.27.0 to 1.6.0 --- python3/vimspector/gadgets.py | 10 +++++----- python3/vimspector/installer.py | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index 72f13c8..dda962d 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -30,12 +30,12 @@ GADGETS = { root, gadget ), 'all': { - 'version': '0.27.0', + 'version': '1.6.0', "adapters": { "vscode-cpptools": { "name": "cppdbg", "command": [ - "${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7" + "${gadgetDir}/vscode-cpptools/debugAdapters/bin/OpenDebugAD7" ], "attach": { "pidProperty": "processId", @@ -53,17 +53,17 @@ GADGETS = { 'linux': { 'file_name': 'cpptools-linux.vsix', 'checksum': - '3695202e1e75a03de18049323b66d868165123f26151f8c974a480eaf0205435', + 'c25299bcfb46b22d41aa3f125df7184e6282a35ff9fb69c47def744cb4778f55', }, 'macos': { 'file_name': 'cpptools-osx.vsix', 'checksum': - 'cb061e3acd7559a539e5586f8d3f535101c4ec4e8a48195856d1d39380b5cf3c', + 'ae21cde361335b350402904991cf9f746fec685449ca9bd5d50227c3dec3719b', }, 'windows': { 'file_name': 'cpptools-win32.vsix', 'checksum': - 'aa294368ed16d48c59e49c8000e146eae5a19ad07b654efed5db8ec93b24229e', + 'ef7ac5831874a3c7dbf0feb826bfda2be579aff9b6d990622fff1d0d4ede00d1', "adapters": { "vscode-cpptools": { "name": "cppdbg", diff --git a/python3/vimspector/installer.py b/python3/vimspector/installer.py index f0f85a4..a81db8f 100644 --- a/python3/vimspector/installer.py +++ b/python3/vimspector/installer.py @@ -358,7 +358,8 @@ def InstallCppTools( name, root, gadget ): # It's hilarious, but the execute bits aren't set in the vsix. So they # actually have javascript code which does this. It's just a horrible horrible # hack that really is not funny. - MakeExecutable( os.path.join( extension, 'debugAdapters', 'OpenDebugAD7' ) ) + MakeExecutable( + os.path.join( extension, 'debugAdapters', 'bin', 'OpenDebugAD7' ) ) with open( os.path.join( extension, 'package.json' ) ) as f: package = json.load( f ) runtime_dependencies = package[ 'runtimeDependencies' ] From 561a5b9aa2c0c0a9f801b12af3cd7ef861fad962 Mon Sep 17 00:00:00 2001 From: Sebastian Goth Date: Wed, 8 Sep 2021 21:36:37 +0200 Subject: [PATCH 605/609] Update variables tests to expect register scope vscode-cpptools 1.6.0 now reports an additional scope 'Registers' after 'Locals'. --- tests/variables.test.vim | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index 59ca2c0..c00fb7f 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -194,6 +194,7 @@ function! Test_ExpandVariables() \ [ \ '- Scope: Locals', \ ' *+ t (Test): {...}', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -219,6 +220,7 @@ function! Test_ExpandVariables() \ ' \*- c (char): 0 ''\\0\{1,3}''', \ ' \*- fffff (float): 0', \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -237,6 +239,7 @@ function! Test_ExpandVariables() \ ' - c (char): 0 ''\\0\{1,3}''', \ ' - fffff (float): 0', \ ' + another_test (AnotherTest):\( {...}\)\?', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -253,6 +256,7 @@ function! Test_ExpandVariables() \ [ \ '- Scope: Locals', \ ' + t (Test): {...}', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -267,6 +271,7 @@ function! Test_ExpandVariables() \ [ \ '- Scope: Locals', \ ' + t (Test): {...}', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -286,6 +291,7 @@ function! Test_ExpandVariables() \ ' \*- c (char): 99 ''c''', \ ' \*- fffff (float): 0', \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -302,6 +308,7 @@ function! Test_ExpandVariables() \ assert_equal( \ [ \ '+ Scope: Locals', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -316,6 +323,7 @@ function! Test_ExpandVariables() \ assert_equal( \ [ \ '+ Scope: Locals', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -331,6 +339,7 @@ function! Test_ExpandVariables() \ assert_equal( \ [ \ '+ Scope: Locals', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -846,6 +855,7 @@ function! Test_SetVariableValue_Local() \ [ \ '- Scope: Locals', \ ' *+ t (Test): {...}', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -871,6 +881,7 @@ function! Test_SetVariableValue_Local() \ ' \*- c (char): 0 ''\\0\{1,3}''', \ ' \*- fffff (float): 0', \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -897,6 +908,7 @@ EOF \ ' \*- c (char): 0 ''\\0\{1,3}''', \ ' \*- fffff (float): 0', \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -916,6 +928,7 @@ EOF \ ' \*- c (char): 0 ''\\0\{1,3}''', \ ' \*- fffff (float): 0', \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, @@ -935,6 +948,7 @@ EOF \ ' \*- c (char): 0 ''\\0\{1,3}''', \ ' \*- fffff (float): 0', \ ' \*+ another_test (AnotherTest):\( {...}\)\?', + \ '+ Scope: Registers', \ ], \ getbufline( winbufnr( g:vimspector_session_windows.variables ), \ 1, From 17ca1522f8a0cca53c8ab75a680f27ea58b50de3 Mon Sep 17 00:00:00 2001 From: Sebastian Goth Date: Wed, 8 Sep 2021 23:16:47 +0200 Subject: [PATCH 606/609] Use correct spelling of MIMode in tests --- tests/testdata/cpp/simple/.vimspector.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/testdata/cpp/simple/.vimspector.json b/tests/testdata/cpp/simple/.vimspector.json index 0dca061..48ce801 100644 --- a/tests/testdata/cpp/simple/.vimspector.json +++ b/tests/testdata/cpp/simple/.vimspector.json @@ -12,7 +12,7 @@ "externalConsole": false, "stopAtEntry": true, "stopOnEntry": true, - "MImode": "${VIMSPECTOR_MIMODE}" + "MIMode": "${VIMSPECTOR_MIMODE}" }, "breakpoints": { "exception": { @@ -33,7 +33,7 @@ "externalConsole": false, "stopAtEntry": false, "stopOnEntry": false, - "MImode": "${VIMSPECTOR_MIMODE}" + "MIMode": "${VIMSPECTOR_MIMODE}" }, "breakpoints": { "exception": { @@ -55,7 +55,7 @@ "externalConsole": false, "stopAtEntry": false, "stopOnEntry": false, - "MImode": "${VIMSPECTOR_MIMODE}" + "MIMode": "${VIMSPECTOR_MIMODE}" }, "breakpoints": { "exception": { @@ -82,7 +82,7 @@ "configuration": { "request": "launch", "program": "${workspaceRoot}/${fileBasenameNoExtension}", - "MImode": "${VIMSPECTOR_MIMODE}", + "MIMode": "${VIMSPECTOR_MIMODE}", "externalConsole": false, "args": [ "CALCULATED_LIST", "${CALCULATED_LIST}", From db5ed8e80228fbb5366e6fc7629e430f886d2b34 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 8 Sep 2021 22:20:33 +0100 Subject: [PATCH 607/609] Update to ubuntu 18.04 as 16.04 is deprecated --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 14f5979..f186f5d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -13,7 +13,7 @@ defaults: jobs: PythonLint: - runs-on: ubuntu-16.04 + runs-on: ubuntu-18.04 container: 'puremourning/vimspector:test' steps: - uses: actions/checkout@v2 @@ -22,7 +22,7 @@ jobs: - name: 'Run flake8' run: '$HOME/.local/bin/flake8 python3/ *.py' VimscriptLint: - runs-on: 'ubuntu-16.04' + runs-on: 'ubuntu-18.04' container: 'puremourning/vimspector:test' steps: - uses: actions/checkout@v2 @@ -32,7 +32,7 @@ jobs: run: $HOME/.local/bin/vint autoload/ compiler/ plugin/ tests/ syntax/ Linux: - runs-on: 'ubuntu-16.04' + runs-on: 'ubuntu-18.04' container: image: 'puremourning/vimspector:test' options: --cap-add=SYS_PTRACE --security-opt seccomp=unconfined @@ -156,7 +156,7 @@ jobs: # SSH_PASS: ${{ secrets.SSH_PASS }} # [V]imspector PublishRelease: - runs-on: 'ubuntu-16.04' + runs-on: 'ubuntu-18.04' needs: - Linux - MacOS From dc862fe565a74a9810f37edaa8d0e1bb7bbdd79d Mon Sep 17 00:00:00 2001 From: Sebastian Goth Date: Thu, 9 Sep 2021 16:42:49 +0200 Subject: [PATCH 608/609] Readme: pretty printing with vscode-cpptools / gdb --- README.md | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3420d56..d198292 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,10 @@ For detailed explanation of the `.vimspector.json` format, see the * [Closing debugger](#closing-debugger) * [Terminate debuggee](#terminate-debuggee) * [Debug profile configuration](#debug-profile-configuration) - * [C, C , Rust, etc.](#c-c-rust-etc) - * [C Remote debugging](#c-remote-debugging) - * [C Remote launch and attach](#c-remote-launch-and-attach) + * [C, C++, Rust, etc.](#c-c-rust-etc) + * [Data visualization / pretty printing](#data-visualization--pretty-printing) + * [C++ Remote debugging](#c-remote-debugging) + * [C++ Remote launch and attach](#c-remote-launch-and-attach) * [Rust](#rust) * [Python](#python) * [Python Remote Debugging](#python-remote-debugging) @@ -1176,6 +1177,38 @@ licensing. } ``` +### Data visualization / pretty printing + +Depending on the backend you need to enable pretty printing of complex types manually. + +* LLDB: Pretty printing is enabled by default + +* GDB: To enable gdb pretty printers, consider the snippet below. + It is not enough to have `set print pretty on` in your .gdbinit! + +``` +{ + "configurations": { + "Launch": { + "adapter": "vscode-cpptools", + "configuration": { + "request": "launch", + "program": "", + ... + "MIMode": "gdb" + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + } + } + } +} +``` + ### C++ Remote debugging The cpptools documentation describes how to attach cpptools to gdbserver using From 7c12519b9d87f261abfff62f4f197c693a0ffb4f Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 10 Sep 2021 10:30:44 -0600 Subject: [PATCH 609/609] Modify for mac m1 --- python3/vimspector/gadgets.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python3/vimspector/gadgets.py b/python3/vimspector/gadgets.py index dda962d..02eb0e7 100644 --- a/python3/vimspector/gadgets.py +++ b/python3/vimspector/gadgets.py @@ -56,9 +56,9 @@ GADGETS = { 'c25299bcfb46b22d41aa3f125df7184e6282a35ff9fb69c47def744cb4778f55', }, 'macos': { - 'file_name': 'cpptools-osx.vsix', + 'file_name': 'cpptools-osx-arm64.vsix', 'checksum': - 'ae21cde361335b350402904991cf9f746fec685449ca9bd5d50227c3dec3719b', + 'ceb3e8cdaa2b5bb45af50913ddd8402089969748af8d70f5d46480408287ba6f', }, 'windows': { 'file_name': 'cpptools-win32.vsix', @@ -394,12 +394,12 @@ GADGETS = { '${version}/${file_name}', }, 'all': { - 'version': 'v1.6.5', + 'version': 'v1.6.6', }, 'macos': { - 'file_name': 'codelldb-x86_64-darwin.vsix', + 'file_name': 'codelldb-aarch64-darwin.vsix', 'checksum': - 'e7d9f4f8ec3c3774af6d1dbf11f0568db1417c4d51038927228cd07028725594', + '5adc3b9139eabdafd825bd5efc55df4424a203fb2b6087b425cd434956e7ec58', 'make_executable': [ 'adapter/codelldb', 'lldb/bin/debugserver',