Merge pull request #70 from puremourning/working-dir

[READY]: Document config format and add some usability improvements
This commit is contained in:
mergify[bot] 2019-11-06 11:58:36 +00:00 committed by GitHub
commit 105e6d4460
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 741 additions and 21 deletions

35
.ycm_extra_conf.py Normal file
View file

@ -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

318
docs/CONFIGURATION.md Normal file
View file

@ -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": { <object mapping name to <adapter configuration> },
"configurations": { <object mapping name to <debug configuration> }
}
```
The `adapters` key is actually optional, as `<adapter configuration>` can be
embedded within `<debug configuration>`, 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:
* `<vimspector home>` means the path to the Vimspector installation (such as
`$HOME/.vim/pack/vimspector/start/vimspector`)
* `<OS>` 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.
* `<vimspector home>/gadgets/<OS>/.gadgets.json` - the file written by
`install_gadget.py` and not usually edited by users.
* `<vimspector home>/gadgets/<OS>/.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": {
"<adapter name>": {
<adapter configuration>
}
}
}
```
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": {
"<configuation name>": {
"adapter": "<adapter name>",
"configuration": {
"request": "<launch or attach>",
<debug configutation>
}
}
}
}
```
# 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 (`<vimspector
home>/gadgets/<OS>`)
* `${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

17
docs/index.md Normal file
View file

@ -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/)

View file

@ -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" }
}
}
}

19
docs/schema/index.md Normal file
View file

@ -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

View file

@ -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"
}
]
}
}
}
}
}
}
]
}
}
}
}

View file

@ -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' ] )

View file

@ -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,
@ -73,11 +74,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:
@ -86,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 {} )
@ -134,8 +141,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 +163,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(
@ -192,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
@ -655,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',
@ -735,6 +741,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 ):
@ -854,3 +862,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 ) )

View file

@ -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' )

View file

@ -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 ):

View file

@ -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": ""
}
}
}
}