def SetCurrentFrame(self, frame): """Returns True if the code window was updated with the frame, False otherwise. False means either the frame is junk, we couldn't find the file (or don't have the data) or the code window no longer exits.""" if not frame or not frame.get('source'): self._UndisplayPC() return False if 'path' not in frame['source']: self._UndisplayPC() return False self._current_frame = frame if not self._window.valid: return False utils.JumpToWindow(self._window) try: utils.OpenFileInCurrentWindow(frame['source']['path']) except vim.error: self._logger.exception( 'Unexpected vim error opening file {}'.format( frame['source']['path'])) return False # SIC: column is 0-based, line is 1-based in vim. Why? Nobody knows. # Note: max() with 0 because some debug adapters (go) return 0 for the # column. try: self._window.cursor = (frame['line'], max(frame['column'] - 1, 0)) except vim.error: self._logger.exception( "Unable to jump to %s:%s in %s, maybe the file " "doesn't exist", frame['line'], frame['column'], frame['source']['path']) return False self.current_syntax = utils.ToUnicode( vim.current.buffer.options['syntax']) self.filetype = '.'.join(utils.GetBufferFiletypes(vim.current.buffer)) self.ShowBreakpoints() return True
def GetConfigurations(self): current_file = utils.GetBufferFilepath(vim.current.buffer) filetypes = utils.GetBufferFiletypes(vim.current.buffer) configurations = {} 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())) configurations.update(database.get('configurations' or {})) return launch_config_file, configurations
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)
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)