Beispiel #1
0
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(), {}))

        if 'download' in gadget:
            if 'file_name' not in v:
                raise RuntimeError("Unsupported OS {} for gadget {}".format(
                    install.GetOS(), name))

            destination = os.path.join(
                install.GetGadgetDir(options.vimspector_base), '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(
                install.GetGadgetDir(options.vimspector_base), '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', {}))

        succeeded.append(name)
        print(f" - Done installing {name}")
    except Exception as e:
        if not options.quiet:
            traceback.print_exc()
        failed.append(name)
        print(f" - FAILED installing {name}: {e}".format(name, e))
Beispiel #2
0
    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))

        self._uiTab = None
        self._logView = None
        self._stackTraceView = None
        self._variablesView = None
        self._outputView = None
        self._breakpoints = breakpoints.ProjectBreakpoints()
        self._splash_screen = None
        self._remote_term = None

        self._run_on_server_exit = None

        self._configuration = None
        self._adapter = None

        self._ResetServerState()
Beispiel #3
0
def SetUpDebugpy(wait=False, port=5678):
    sys.path.insert(
        1,
        os.path.join(install.GetGadgetDir(utils.GetVimspectorBase()),
                     'debugpy', 'build', 'lib'))
    import debugpy

    exe = sys.executable
    try:
        # debugpy uses sys.executable (which is `vim`, so we hack it)
        sys.executable = installer.PathToAnyWorkingPython3()
        debugpy.listen(port)
    finally:
        sys.executable = exe

    if wait:
        debugpy.wait_for_client()
Beispiel #4
0
    def __init__(self):
        self._logger = logging.getLogger(__name__)
        utils.SetUpLogging(self._logger)

        self._logger.info('VIMSPECTOR_HOME = %s', VIMSPECTOR_HOME)
        self._logger.info(
            'gadgetDir = %s',
            install.GetGadgetDir(VIMSPECTOR_HOME, install.GetOS()))

        self._uiTab = None
        self._stackTraceView = None
        self._variablesView = None
        self._outputView = None
        self._breakpoints = breakpoints.ProjectBreakpoints()

        self._run_on_server_exit = None

        self._ResetServerState()
Beispiel #5
0
def MakeSymlink(link, pointing_to, in_folder=None):
    if not in_folder:
        in_folder = install.GetGadgetDir(options.vimspector_base)

    RemoveIfExists(os.path.join(in_folder, link))

    in_folder = os.path.abspath(in_folder)
    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)
        CheckCall(['cmd.exe', '/c', 'mklink', '/J', link_path, pointing_to])
    else:
        os.symlink(pointing_to_relative, link_path)
    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
        self._adapter = None

        current_file = utils.GetBufferFilepath(vim.current.buffer)
        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 not gadget_config_file or not os.path.exists(
                    gadget_config_file):
                continue

            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')
        elif len(configurations) == 1:
            configuration_name = next(iter(configurations.keys()))
        else:
            configuration_name = utils.SelectFromList(
                'Which launch configuration?', sorted(configurations.keys()))

        if not configuration_name or configuration_name not in configurations:
            return

        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')
        if isinstance(adapter, str):
            adapter = adapters.get(adapter)

        # 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
        # ${workspaceFolderBasename} - the name of the folder opened in VS Code
        #                              without any slashes (/)
        # ${file} - the current opened file
        # ${relativeFile} - the current opened file relative to workspaceFolder
        # ${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 task runner's current working directory on startup
        # ${lineNumber} - the current selected line number in the active file
        # ${selectedText} - the current selected text in the active file
        # ${execPath} - the path to the running VS Code executable

        def relpath(p, relative_to):
            if not p:
                return ''
            return os.path.relpath(p, relative_to)

        def splitext(p):
            if not p:
                return ['', '']
            return os.path.splitext(p)

        self._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),
            'fileBasenameNoExtension':
            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(),
        }

        # 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,
                                 USER_CHOICES))
        self._variables.update(
            utils.ParseVariables(configuration.get('variables', {}),
                                 self._variables, USER_CHOICES))

        utils.ExpandReferencesInDict(configuration, self._variables,
                                     USER_CHOICES)
        utils.ExpandReferencesInDict(adapter, self._variables, USER_CHOICES)

        if not adapter:
            utils.UserMessage(
                'No adapter configured for {}'.format(configuration_name),
                persist=True)
            return

        self._StartWithConfiguration(configuration, adapter)
Beispiel #7
0
            gadget['do'](name, root, v)
        else:
            installer.MakeExtensionSymlink(vimspector_base, 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(vimspector_base, OS)

print('OS = ' + OS)
print('gadget_dir = ' + gadget_dir)

parser = argparse.ArgumentParser(
    formatter_class=argparse.RawDescriptionHelpFormatter,
    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:
Beispiel #8
0
    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
        self._adapter = None

        current_file = utils.GetBufferFilepath(vim.current.buffer)
        filetypes = utils.GetBufferFiletypes(vim.current.buffer)
        configurations = {}
        adapters = {}

        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}')
            if not gadget_config_file or not os.path.exists(
                    gadget_config_file):
                continue

            with open(gadget_config_file, 'r') as f:
                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 next(iter(configurations.values())).get("autoselect", True)):
            configuration_name = next(iter(configurations.keys()))
        else:
            # 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

        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')
        if isinstance(adapter, str):
            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,
                            False,  # Don't leave open
                            *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'?",
                    persist=True,
                    error=True)
                return

            adapter = adapter_dict

        # Additional vars as defined by VSCode:
        #
        # ${workspaceFolder} - the path of the folder opened in VS Code
        # ${workspaceFolderBasename} - the name of the folder opened in VS Code
        #                              without any slashes (/)
        # ${file} - the current opened file
        # ${relativeFile} - the current opened file relative to workspaceFolder
        # ${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 task runner's current working directory on startup
        # ${lineNumber} - the current selected line number in the active file
        # ${selectedText} - the current selected text in the active file
        # ${execPath} - the path to the running VS Code executable

        def relpath(p, relative_to):
            if not p:
                return ''
            return os.path.relpath(p, relative_to)

        def splitext(p):
            if not p:
                return ['', '']
            return os.path.splitext(p)

        variables = {
            'dollar': '$',  # HACK. Hote '$$' also works.
            'workspaceRoot': self._workspace_root,
            'workspaceFolder': self._workspace_root,
            'gadgetDir': install.GetGadgetDir(VIMSPECTOR_HOME),
            'file': current_file,
        }

        calculus = {
            'relativeFile':
            lambda: relpath(current_file, self._workspace_root),
            'fileBasename':
            lambda: os.path.basename(current_file),
            'fileBasenameNoExtension':
            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,
            '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)
        variables.update(launch_variables)

        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)
        except KeyboardInterrupt:
            self._Reset()
            return

        if not adapter:
            utils.UserMessage(
                'No adapter configured for {}'.format(configuration_name),
                persist=True)
            return

        self._StartWithConfiguration(configuration, adapter)
Beispiel #9
0
def InstallGagdet(name: str, gadget: dict, manifest: Manifest, succeeded: list,
                  failed: list, all_adapters: dict):

    try:
        # 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 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, spec['version'])

            url = string.Template(gadget['download']['url']).substitute(spec)

            file_path = DownloadFileTo(
                url,
                destination,
                file_name=gadget['download'].get('target'),
                checksum=spec.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(spec)
            ref = string.Template(gadget['repo']['ref']).substitute(spec)

            print(f"Installing {name}@{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), 'download',
                name)
            CloneRepoTo(url, ref, destination)
            root = destination

        if 'do' in gadget:
            gadget['do'](name, root, spec)
        else:
            InstallGeneric(name, root, spec)

        save_adapters()
        manifest.Update(name, spec)
        succeeded.append(name)
        print(f" - Done installing {name}")
    except Exception as e:
        if not options.quiet:
            traceback.print_exc()
        failed.append(name)
        print(f" - FAILED installing {name}: {e}".format(name, e))
Beispiel #10
0
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)
    subprocess.check_call(['git', 'clone', url, destination])
    subprocess.check_call(['git', '-C', destination, 'checkout', ref])


OS = install.GetOS()
gadget_dir = install.GetGadgetDir(os.path.dirname(__file__), OS)

print('OS = ' + OS)
print('gadget_dir = ' + gadget_dir)

parser = argparse.ArgumentParser()
parser.add_argument('--all', action='store_true', help='Enable all completers')

done_languages = set()
for name, gadget in GADGETS.items():
    lang = gadget['language']
    if lang in done_languages:
        continue

    done_languages.add(lang)
    if not gadget.get('enabled', True):
Beispiel #11
0
    def Start(self, launch_variables={}):
        self._configuration = None
        self._adapter = None

        launch_config_file = utils.PathToConfigFile('.vimspector.json')

        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')
        adapters = {}

        for gadget_config_file in [
                install.GetGadgetConfigFile(VIMSPECTOR_HOME),
                utils.PathToConfigFile('.gadgets.json')
        ]:
            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 {})

        adapters.update(database.get('adapters') or {})

        if len(configurations) == 1:
            configuration_name = next(iter(configurations.keys()))
        else:
            configuration_name = utils.SelectFromList(
                'Which launch configuration?',
                sorted(list(configurations.keys())))

        if not configuration_name or configuration_name not in configurations:
            return

        self._workspace_root = os.path.dirname(launch_config_file)

        configuration = configurations[configuration_name]
        adapter = configuration.get('adapter')
        if isinstance(adapter, str):
            adapter = adapters.get(adapter)

        # 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
        self._variables = {
            'dollar': '$',  # HACK. Hote '$$' also works.
            'workspaceRoot': self._workspace_root,
            'gadgetDir': install.GetGadgetDir(VIMSPECTOR_HOME, install.GetOS())
        }
        self._variables.update(
            utils.ParseVariables(adapter.get('variables', {})))
        self._variables.update(
            utils.ParseVariables(configuration.get('variables', {})))
        self._variables.update(launch_variables)

        utils.ExpandReferencesInDict(configuration, self._variables)
        utils.ExpandReferencesInDict(adapter, self._variables)

        if not adapter:
            utils.UserMessage(
                'No adapter configured for {}'.format(configuration_name),
                persist=True)
            return

        self._StartWithConfiguration(configuration, adapter)
Beispiel #12
0
def MakeExtensionSymlink( vimspector_base, name, root ):
  MakeSymlink( install.GetGadgetDir( vimspector_base,
                                     install.GetOS() ),
               name,
               os.path.join( root, 'extension' ) ),
Beispiel #13
0
  def Start( self, launch_variables = {} ):
    self._logger.info( "User requested start debug session with %s",
                       launch_variables )
    self._configuration = None
    self._adapter = None

    launch_config_file = utils.PathToConfigFile( '.vimspector.json' )

    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' )
    adapters = {}

    for gadget_config_file in [ install.GetGadgetConfigFile( VIMSPECTOR_HOME ),
                                utils.PathToConfigFile( '.gadgets.json' ) ]:
      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 {} )

    adapters.update( database.get( 'adapters' ) or {} )

    if len( configurations ) == 1:
      configuration_name = next( iter( configurations.keys() ) )
    else:
      configuration_name = utils.SelectFromList(
        'Which launch configuration?',
        sorted( list( configurations.keys() ) ) )

    if not configuration_name or configuration_name not in configurations:
      return

    self._workspace_root = os.path.dirname( launch_config_file )

    configuration = configurations[ configuration_name ]
    adapter = configuration.get( 'adapter' )
    if isinstance( adapter, str ):
      adapter = adapters.get( adapter )

    # 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
    # ${workspaceFolderBasename} - the name of the folder opened in VS Code
    #                              without any slashes (/)
    # ${file} - the current opened file
    # ${relativeFile} - the current opened file relative to workspaceFolder
    # ${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 task runner's current working directory on startup
    # ${lineNumber} - the current selected line number in the active file
    # ${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 )

    self._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': os.path.relpath( current_file, self._workspace_root ),
      'fileBasename': os.path.basename( current_file ),
      'fileBasenameNoExtension':
        os.path.splitext( os.path.basename( current_file ) )[ 0 ],
      'fileDirname': os.path.dirname( current_file ),
      'fileExtname':
        os.path.splitext( os.path.basename( current_file ) )[ 1 ],
      'cwd': os.getcwd(),
    }
    self._variables.update(
      utils.ParseVariables( adapter.get( 'variables', {} ),
                            self._variables ) )
    self._variables.update(
      utils.ParseVariables( configuration.get( 'variables', {} ),
                            self._variables ) )
    self._variables.update( launch_variables )

    utils.ExpandReferencesInDict( configuration, self._variables )
    utils.ExpandReferencesInDict( adapter, self._variables )

    if not adapter:
      utils.UserMessage( 'No adapter configured for {}'.format(
        configuration_name ), persist=True )
      return

    self._StartWithConfiguration( configuration, adapter )