Merge pull request #70 from puremourning/working-dir
[READY]: Document config format and add some usability improvements
This commit is contained in:
commit
105e6d4460
11 changed files with 741 additions and 21 deletions
35
.ycm_extra_conf.py
Normal file
35
.ycm_extra_conf.py
Normal 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
318
docs/CONFIGURATION.md
Normal 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
17
docs/index.md
Normal 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/)
|
||||
11
docs/schema/gadgets.schema.json
Normal file
11
docs/schema/gadgets.schema.json
Normal 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
19
docs/schema/index.md
Normal 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
|
||||
256
docs/schema/vimspector.schema.json
Normal file
256
docs/schema/vimspector.schema.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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' ] )
|
||||
|
|
|
|||
|
|
@ -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 ) )
|
||||
|
|
|
|||
|
|
@ -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' )
|
||||
|
|
|
|||
|
|
@ -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 ):
|
||||
|
|
|
|||
11
tests/testdata/cpp/simple/.vimspector.json
vendored
11
tests/testdata/cpp/simple/.vimspector.json
vendored
|
|
@ -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": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue