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": {