Merge pull request #215 from puremourning/variables-default-value

Add ability to specify defaults for variables
This commit is contained in:
mergify[bot] 2020-07-31 21:30:13 +00:00 • committed by GitHub
commit 1f270ed423
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 171 additions and 26 deletions

View file

@ -188,11 +188,15 @@ function! vimspector#EvaluateConsole( expr ) abort
py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) ) py3 _vimspector_session.EvaluateConsole( vim.eval( 'a:expr' ) )
endfunction endfunction
function! vimspector#ShowOutput( category ) abort function! vimspector#ShowOutput( ... ) abort
if !s:enabled if !s:enabled
return return
endif 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 endfunction
function! vimspector#ShowOutputInWindow( win_id, category ) abort function! vimspector#ShowOutputInWindow( win_id, category ) abort

View file

@ -12,6 +12,7 @@ for Vimspector.
* [Debug profile configuration](#debug-profile-configuration) * [Debug profile configuration](#debug-profile-configuration)
* [Replacements and variables](#replacements-and-variables) * [Replacements and variables](#replacements-and-variables)
* [The splat operator](#the-splat-operator) * [The splat operator](#the-splat-operator)
* [Default values](#default-values)
* [Configuration Format](#configuration-format) * [Configuration Format](#configuration-format)
* [Files and locations](#files-and-locations) * [Files and locations](#files-and-locations)
* [Adapter configurations](#adapter-configurations) * [Adapter configurations](#adapter-configurations)
@ -28,7 +29,7 @@ for Vimspector.
* [Appendix: Configuration file format](#appendix-configuration-file-format) * [Appendix: Configuration file format](#appendix-configuration-file-format)
* [Appendix: Editor configuration](#appendix-editor-configuration) * [Appendix: Editor configuration](#appendix-editor-configuration)
<!-- Added by: ben, at: Sun 12 Jul 2020 16:46:18 BST --> <!-- Added by: ben, at: Fri 31 Jul 2020 22:13:39 BST -->
<!--te--> <!--te-->
@ -204,6 +205,37 @@ You can also combine with static values:
This would yield the intuitive result: This would yield the intuitive result:
`[ "First", "one", "two three", "four", "Last" ]` `[ "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 ## Configuration Format
All Vimspector configuration is defined in a JSON object. The complete All Vimspector configuration is defined in a JSON object. The complete

View file

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

View file

@ -88,7 +88,7 @@ endif
command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr
\ VimspectorWatch \ VimspectorWatch
\ call vimspector#AddWatch( <f-args> ) \ call vimspector#AddWatch( <f-args> )
command! -bar -nargs=1 -complete=custom,vimspector#CompleteOutput command! -bar -nargs=? -complete=custom,vimspector#CompleteOutput
\ VimspectorShowOutput \ VimspectorShowOutput
\ call vimspector#ShowOutput( <f-args> ) \ call vimspector#ShowOutput( <f-args> )
command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr

View file

@ -53,13 +53,13 @@ class ProjectBreakpoints( object ):
self._next_sign_id = 1 self._next_sign_id = 1
if not utils.SignDefined( 'vimspectorBP' ): 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' ): 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' ): 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 ): def ConnectionUp( self, connection ):

View file

@ -41,17 +41,17 @@ class CodeView( object ):
} }
with utils.LetCurrentWindow( self._window ): with utils.LetCurrentWindow( self._window ):
vim.command( 'nnoremenu WinBar.Continue :call vimspector#Continue()<CR>' ) vim.command( 'nnoremenu WinBar.■\\ Stop :call vimspector#Stop()<CR>' )
vim.command( 'nnoremenu WinBar.Next :call vimspector#StepOver()<CR>' ) vim.command( 'nnoremenu WinBar.▶\\ Cont :call vimspector#Continue()<CR>' )
vim.command( 'nnoremenu WinBar.Step :call vimspector#StepInto()<CR>' ) vim.command( 'nnoremenu WinBar.▷\\ Pause :call vimspector#Pause()<CR>' )
vim.command( 'nnoremenu WinBar.Finish :call vimspector#StepOut()<CR>' ) vim.command( 'nnoremenu WinBar.↷\\ Next :call vimspector#StepOver()<CR>' )
vim.command( 'nnoremenu WinBar.Pause :call vimspector#Pause()<CR>' ) vim.command( 'nnoremenu WinBar.→\\ Step :call vimspector#StepInto()<CR>' )
vim.command( 'nnoremenu WinBar.Stop :call vimspector#Stop()<CR>' ) vim.command( 'nnoremenu WinBar.←\\ Out :call vimspector#StepOut()<CR>' )
vim.command( 'nnoremenu WinBar.Restart :call vimspector#Restart()<CR>' ) vim.command( 'nnoremenu WinBar.⟲: :call vimspector#Restart()<CR>' )
vim.command( 'nnoremenu WinBar.Reset :call vimspector#Reset()<CR>' ) vim.command( 'nnoremenu WinBar.✕ :call vimspector#Reset()<CR>' )
if not utils.SignDefined( 'vimspectorPC' ): 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 ): def SetCurrentFrame( self, frame ):

View file

@ -17,7 +17,7 @@
import sys import sys
import os import os
from vimspector import install, utils from vimspector import install, utils, installer
def SetUpDebugpy( wait=False, port=5678 ): def SetUpDebugpy( wait=False, port=5678 ):
@ -32,7 +32,7 @@ def SetUpDebugpy( wait=False, port=5678 ):
exe = sys.executable exe = sys.executable
try: try:
# debugpy uses sys.executable (which is `vim`, so we hack it) # debugpy uses sys.executable (which is `vim`, so we hack it)
sys.executable = 'python3' sys.executable = installer.PathToAnyWorkingPython3()
debugpy.listen( port ) debugpy.listen( port )
finally: finally:
sys.executable = exe sys.executable = exe

View file

@ -19,11 +19,11 @@ import os
import contextlib import contextlib
import vim import vim
import json import json
import string
import functools import functools
import subprocess import subprocess
import shlex import shlex
import collections import collections
import re
LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log' ) ) LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log' ) )
@ -435,6 +435,60 @@ def ExpandReferencesInObject( obj, mapping, calculus, user_choices ):
return obj 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<escaped>\$) | # Another doller = escaped
(?P<named>[_a-z][_a-z0-9]*) | # or An identifier - named param
{(?P<braced>[_a-z][_a-z0-9]*)} | # or An {identifier} - braced param
{(?P<braceddefault> # or An {id:default} - default param, as
(?P<defname>[_a-z][a-z0-9]*) # an ID
: # then a colon
(?P<default>(?:[^}]|\})*) # then anything up to }, or a \}
)} | #
(?P<invalid>) # 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, def ExpandReferencesInString( orig_s,
mapping, mapping,
calculus, calculus,
@ -449,17 +503,23 @@ def ExpandReferencesInString( orig_s,
++bug_catcher ++bug_catcher
try: try:
s = string.Template( s ).substitute( mapping ) s = _Substitute( s, mapping )
break break
except KeyError as e: except MissingSubstitution as e:
# HACK: This is seemingly the only way to get the key. str( e ) returns key = e.name
# the key surrounded by '' for unknowable reasons.
key = e.args[ 0 ]
if key in calculus: if key in calculus:
mapping[ key ] = calculus[ key ]() mapping[ key ] = calculus[ key ]()
else: 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 ), mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ),
default_value ) default_value )

View file

@ -106,6 +106,7 @@ class Watch:
"""Holds a user watch expression (DAP request) and the result (WatchResult)""" """Holds a user watch expression (DAP request) and the result (WatchResult)"""
def __init__( self, expression: dict ): def __init__( self, expression: dict ):
self.result: WatchResult self.result: WatchResult
self.line = None
self.expression = expression self.expression = expression
self.result = None self.result = None

View file

@ -3,6 +3,7 @@
BASEDIR=$(dirname $0) BASEDIR=$(dirname $0)
INSTALL=0 INSTALL=0
UPDATE=0 UPDATE=0
INSTALLER_ARGS=''
RUN_VIM="vim -N --clean --not-a-term" RUN_VIM="vim -N --clean --not-a-term"
RUN_TEST="${RUN_VIM} -S lib/run_test.vim" RUN_TEST="${RUN_VIM} -S lib/run_test.vim"
BASEDIR_CMD='py3 pass' BASEDIR_CMD='py3 pass'
@ -48,6 +49,7 @@ while [ -n "$1" ]; do
# old on macOS # old on macOS
out_fd=3 out_fd=3
exec 3>/dev/null exec 3>/dev/null
INSTALLER_ARGS="${INSTALLER_ARGS} --quiet"
;; ;;
"--") "--")
shift shift
@ -82,7 +84,10 @@ if [ "${out_fd}" = "1" ]; then
fi fi
if [ "$INSTALL" = "1" ] || [ "$INSTALL" = "script" ]; then 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 echo "Script installation reported errors" >&2
exit 1 exit 1
fi fi

View file

@ -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": { "run - main.py": {
"adapter": "debugpy", "adapter": "debugpy",
"configuration": { "configuration": {