def __init__(self, window: sublime.Window): super().__init__() # ensure we are being run inside a sublime project # if not prompt the user to create one project_name = window.project_file_name() while not project_name: r = sublime.ok_cancel_dialog("Debugger requires a sublime project. Would you like to create a new sublime project?", "Save Project As...") if r: window.run_command('save_project_and_workspace_as') else: raise core.Error("Debugger must be run inside a sublime project") project_name = window.project_file_name() self.name = project_name self.window = window self.on_updated: core.Event[None] = core.Event() self.tasks: List[Task] = [] self.compounds: List[ConfigurationCompound] = [] self.configurations: List[Configuration] = [] self.configuration_or_compound: Optional[Union[Configuration, ConfigurationCompound]] = None self.external_terminal_kind = 'platform' self.ui_scale = 12 self.bring_window_to_front_on_pause = False # add the empty debugger configurations settings if needed data = window.project_data() or {} data.setdefault('debugger_configurations', []) window.set_project_data(data) self.disposables += Settings.updated.add(self.reload) self.reload()
def select_configuration( window: sublime.Window, index: int) -> core.awaitable[Optional[Configuration]]: try: configs = all_configurations(window) except Exception as e: core.display(e) show_settings(window) done = core.main_loop.create_future() names = list(map(lambda x: x.name, configs)) + ["-" ] + ["Add configuration"] index = yield from core.sublime_show_quick_panel_async( window, names, index) if index < 0: return None if index >= len(configs): project = window.project_file_name() if project: sublime.run_command("new_window") window = sublime.active_window() window.open_file(project) else: window.run_command( 'edit_settings', {"base_file": "${packages}/sublime_db/debug.sublime-settings"}) return None return configs[index]
async def _insert_snippet(window: sublime.Window, snippet: dict[str, Any]): for (key, value) in snippet.items(): if isinstance(value, str) and value.startswith('^"') and value.endswith('"'): snippet[key] = value[2:-1] content = json.dumps(snippet, indent="\t") content = content.replace('\\\\', '\\') # remove json encoded \ ... content = content.replace('${workspaceFolder}', '${folder}') try: project = window.project_file_name() if not project: raise core.Error('Expected project file in window') view = await core.sublime_open_file_async(window, project) region = view.find(r'''"\s*debugger_configurations\s*"\s*:\s*\[''', 0) if not region: raise core.Error('Unable to find debugger_configurations') view.sel().clear() view.sel().add(sublime.Region(region.b, region.b)) view.run_command('insert', { 'characters': '\n' }) view.run_command('insert_snippet', { 'contents': content + ',' }) except core.Error as e: sublime.set_clipboard(content) core.display('Unable to insert configuration into sublime-project file: Copied to clipboard instead')
async def _insert_snippet(window: sublime.Window, snippet: dict): content = json.dumps(snippet, indent="\t") content = content.replace('\\\\', '\\') # remove json encoded \ ... project = window.project_file_name() if project: view = await core.sublime_open_file_async(window, project) region = view.find('''"\s*debug.configurations\s*"\s*:\s*\[''', 0) view.sel().clear() view.sel().add(sublime.Region(region.b, region.b)) view.run_command('insert', {'characters': '\n'}) view.run_command('insert_snippet', {'contents': content + ','}) else: sublime.set_clipboard(content) core.display( 'Unable to insert configuration into sublime-project file: Copied to clipboard instead' )
def __init__(self, window: sublime.Window) -> None: # ensure we are being run inside a sublime project # if not prompt the user to create one while True: data = window.project_data() project_name = window.project_file_name() while not data or not project_name: r = sublime.ok_cancel_dialog("Debugger requires a sublime project. Would you like to create a new sublime project?", "Save Project As...") if r: window.run_command('save_project_and_workspace_as') else: raise core.Error("Debugger must be run inside a sublime project") # ensure we have debug configurations added to the project file data.setdefault('settings', {}).setdefault('debug.configurations', []) window.set_project_data(data) break self.project = DebuggerProject(window) autocomplete = Autocomplete.create_for_window(window) self.window = window self.disposeables = [] #type: List[Any] self.breakpoints = Breakpoints(); self.callstack_panel = CallStackPanel() self.breakpoints_panel = BreakpointsPanel(self.breakpoints) self.debugger_panel = DebuggerPanel(self, self.breakpoints_panel) def on_state_changed(state: int) -> None: if state == DebuggerStateful.stopped: self.debugger_panel.setState(STOPPED) self.show_console_panel() elif state == DebuggerStateful.running: self.debugger_panel.setState(RUNNING) elif state == DebuggerStateful.paused: self.debugger_panel.setState(PAUSED) if get_setting(self.view, 'bring_window_to_front_on_pause', False): # is there a better way to bring sublime to the front?? # this probably doesn't work for most people. subl needs to be in PATH file = self.window.active_view().file_name() if file: # ignore any errors try: subprocess.call(["subl", file]) except Exception: pass self.show_call_stack_panel() elif state == DebuggerStateful.stopping or state == DebuggerStateful.starting: self.debugger_panel.setState(LOADING) def on_scopes(scopes: List[dap.Scope]) -> None: self.variables_panel.set_scopes(scopes) def on_selected_frame(thread: Optional[dap.Thread], frame: Optional[dap.StackFrame]) -> None: if frame and thread and frame.source: self.source_provider.select(frame.source, frame.line, thread.stopped_text) else: self.source_provider.clear() def on_output(event: dap.OutputEvent) -> None: self.terminal.program_output(self.debugger.adapter, event) def on_threads_stateful(threads: Any): self.callstack_panel.update(self.debugger, threads) from .diff import DiffCollection from .terminal import Terminal def on_terminal_added(terminal: Terminal): component = TerminalComponent(terminal) panel = TabbedPanelItem(id(terminal), component, terminal.name(), 0, component.action_buttons()) def on_modified(): self.panels.modified(panel) terminal.on_updated.add(on_modified) self.panels.add([panel]) def on_terminal_removed(terminal: Terminal): self.panels.remove(id(terminal)) terminals = DiffCollection(on_terminal_added, on_terminal_removed) def on_terminals(list: Any): terminals.update(list) self.debugger = DebuggerStateful( self.breakpoints, on_state_changed=on_state_changed, on_scopes=on_scopes, on_output=on_output, on_selected_frame=on_selected_frame, on_threads_stateful=on_threads_stateful, on_terminals=on_terminals) self.variables_panel = VariablesPanel(self.breakpoints, self.debugger.watch) self.panel = OutputPhantomsPanel(window, 'Debugger') self.panel.show() self.view = self.panel.view #type: sublime.View self.persistance = PersistedData(project_name) self.load_data() self.load_settings_and_configurations() self.disposeables.extend([ self.panel, ]) self.disposeables.append(WindowSettingsCallback(self.window, self.on_settings_updated)) phantom_location = self.panel.phantom_location() phantom_view = self.panel.phantom_view() self.disposeables.extend([ ui.Phantom(self.debugger_panel, phantom_view, sublime.Region(phantom_location, phantom_location + 0), sublime.LAYOUT_INLINE), ]) callstack_panel_item = TabbedPanelItem(id(self.callstack_panel), self.callstack_panel, "Call Stack", 0) variables_panel_item = TabbedPanelItem(id(self.variables_panel), self.variables_panel, "Variables", 1) self.terminal = DebuggerTerminal( on_run_command=self.on_run_command, on_clicked_source=self.on_navigate_to_source ) terminal_component = TerminalComponent(self.terminal) terminal_panel_item = TabbedPanelItem(id(self.terminal), terminal_component, self.terminal.name(), 0) modules_panel = TabbedPanelItem(id(self.debugger.modules), ModulesView(self.debugger.modules), "Modules", 1) sources_panel = TabbedPanelItem(id(self.debugger.sources), SourcesView(self.debugger.sources), "Sources", 1) self.terminal.log_info('Opened In Workspace: {}'.format(os.path.dirname(project_name))) self.panels = Panels(phantom_view, phantom_location + 1, 3) self.panels.add([ callstack_panel_item, variables_panel_item, terminal_panel_item, modules_panel, sources_panel ]) view_hover = ViewHoverProvider(self.project, self.debugger) self.disposeables.append(view_hover) self.source_provider = ViewSelectedSourceProvider(self.project, self.debugger) self.disposeables.append(self.source_provider) self.breakpoints_provider = BreakpointCommandsProvider(self.project, self.debugger, self.breakpoints) self.disposeables.append(self.breakpoints_provider)
def __init__(self, window: sublime.Window) -> None: print('new Main for window', window.id()) self.window = window self.disposeables = [] #type: List[Any] self.breakpoints = Breakpoints() self.view = None #type: Optional[sublime.View] self.eventLog = EventLogComponent() self.variablesComponent = VariablesComponent() self.callstackComponent = CallStackComponent() self.debuggerComponent = DebuggerComponent(self.breakpoints, self) self.debugAdapterClient = None #type: Optional[DebugAdapterClient] self.selectedFrameComponent = None #type: Optional[ui.Phantom] self.breakpointInformation = None #type: Optional[ui.Phantom] self.pausedWithError = False self.process = None #type: Optional[Process] self.disconnecting = False self.project_name = window.project_file_name() or "user" data = config.persisted_for_project(self.project_name) config_name = data.get('config_name') config_maybe_at_index = data.get('config_maybe_at_index') for bp in data.get('breakpoints', []): self.breakpoints.add(Breakpoint.from_json(bp)) if config_name: self.configuration = get_configuration_for_name(window, config_name, config_maybe_at_index) else: self.configuration = None if self.configuration: self.debuggerComponent.set_name(self.configuration.name) else: self.debuggerComponent.set_name('select config') self.stopped_reason = '' self.path = window.project_file_name() if self.path: self.path = os.path.dirname(self.path) self.eventLog.Add('Opened In Workspace: {}'.format(self.path)) else: self.eventLog.AddStderr('warning: debugger opened in a window that is not part of a project') print('Creating a window: h') self.disposeables.extend([ ui.view_gutter_hovered.add(self.on_gutter_hovered), ui.view_text_hovered.add(self.on_text_hovered), ui.view_drag_select.add(self.on_drag_select), ]) mode = get_setting(window.active_view(), 'display') if mode == 'window': sublime.run_command("new_window") new_window = sublime.active_window() output = new_window.new_file() new_window.set_minimap_visible(False) new_window.set_sidebar_visible(False) new_window.set_tabs_visible(False) new_window.set_menu_visible(False) new_window.set_status_bar_visible(False) elif mode == 'view': output = self.window.new_file() elif mode == 'output': output = self.window.create_output_panel('debugger') self.window.run_command('show_panel', { 'panel': 'output.debugger' }) else: core.display('expected setting "mode" to be one of "window", "view" or "output", found "{}"'.format(mode)) return output.run_command('insert', { 'characters': " " }) output.set_scratch(True) output.set_read_only(True) output.set_name('Debugger') view_settings = output.settings() view_settings.set("is_widget", True) view_settings.set("gutter", False) view_settings.set("margin", 0) view_settings.set("always_show_minimap_viewport", False) self.view = output self.disposeables.extend([ ui.Phantom(self.debuggerComponent, output, sublime.Region(1, 1), sublime.LAYOUT_INLINE), ui.Phantom(self.callstackComponent, output, sublime.Region(1, 2), sublime.LAYOUT_INLINE), ui.Phantom(self.variablesComponent, output, sublime.Region(1, 3), sublime.LAYOUT_INLINE), ui.Phantom(self.eventLog, output, sublime.Region(1, 4), sublime.LAYOUT_INLINE) ]) self.breakpoints.onRemovedBreakpoint.add(lambda b: self.clearBreakpointInformation()) self.breakpoints.onChangedBreakpoint.add(self.onChangedBreakpoint) self.breakpoints.onChangedFilter.add(self.onChangedFilter) self.breakpoints.onSelectedBreakpoint.add(self.onSelectedBreakpoint)
def __init__(self, window: sublime.Window) -> None: data = window.project_data() project_name = window.project_file_name() if not data or not project_name: sublime.error_message( "Debugger must be run inside a sublime project") return # ensure we have debug configurations added to the project file data.setdefault('settings', {}).setdefault('debug.configurations', []) window.set_project_data(data) ui.set_create_input_handler(window, self.create_input_handler) self.input_open = False self.window = window self.disposeables = [] #type: List[Any] self.breakpoints = Breakpoints() self.console_panel = ConsolePanel(self.open_repl_console) self.variables_panel = VariablesPanel() self.callstack_panel = CallStackPanel() self.breakpoints_panel = BreakpointsComponent( self.breakpoints, self.onSelectedBreakpoint) self.pages_panel = TabbedPanel([ ("Breakpoints", self.breakpoints_panel, None), ("Call Stack", self.callstack_panel, None), ("Console", self.console_panel, self.console_panel.change_filter), ], 0) self.debugger_panel = DebuggerPanel(self.breakpoints, self) self.selected_frame_line = None #type: Optional[SelectedLine] self.selected_frame_generated_view = None #type: Optional[sublime.View] self.breakpointInformation = None #type: Optional[Any] def on_state_changed(state: int) -> None: if state == DebuggerState.stopped: self.breakpoints.clear_breakpoint_results() self.debugger_panel.setState(STOPPED) self.show_console_panel() elif state == DebuggerState.running: self.debugger_panel.setState(RUNNING) elif state == DebuggerState.paused: self.debugger_panel.setState(PAUSED) if get_setting(self.view, 'bring_window_to_front_on_pause', False): # is there a better way to bring sublime to the front?? # this probably doesn't work for most people. subl needs to be in PATH file = self.window.active_view().file_name() if file: # ignore any errors try: subprocess.call(["subl", file]) except Exception: pass self.show_call_stack_panel() elif state == DebuggerState.stopping or state == DebuggerState.starting: self.debugger_panel.setState(LOADING) def on_threads(threads: List[Thread]) -> None: self.callstack_panel.update(self.debugger, threads) def on_scopes(scopes: List[Scope]) -> None: self.variables_panel.set_scopes(scopes) def on_selected_frame(thread: Optional[Thread], frame: Optional[StackFrame]) -> None: if frame and thread: self.run_async(self.navigate_to_frame(thread, frame)) else: self.dispose_selected_frame() def on_output(event: OutputEvent) -> None: category = event.category msg = event.text variablesReference = event.variablesReference if variablesReference and self.debugger.adapter: # this seems to be what vscode does it ignores the actual message here. # Some of the messages are junk like "output" that we probably don't want to display @core. async def appendVariabble() -> core.awaitable[None]: variables = yield from self.debugger.adapter.GetVariables( variablesReference) for variable in variables: variable.name = "" # this is what vs code does? self.console_panel.AddVariable(variable) self.pages_panel.modified(2) # this could make variable messages appear out of order. Do we care?? self.run_async(appendVariabble()) elif category == "stdout": self.console_panel.AddStdout(msg) self.pages_panel.modified(2) elif category == "stderr": self.console_panel.AddStderr(msg) self.pages_panel.modified(2) elif category == "telemetry": pass elif category == "output": self.console_panel.AddStdout(msg) self.pages_panel.modified(2) elif category == "error": self.console_panel.AddStderr(msg) self.pages_panel.modified(2) elif category == "info": self.console_panel.Add(msg) self.pages_panel.modified(2) else: self.console_panel.AddOutputOther(msg) self.pages_panel.modified(2) self.debugger = DebuggerState(on_state_changed=on_state_changed, on_threads=on_threads, on_scopes=on_scopes, on_output=on_output, on_selected_frame=on_selected_frame) self.panel = OutputPhantomsPanel(window, 'Debugger') self.panel.show() self.view = self.panel.view #type: sublime.View self.persistance = PersistedData(project_name) for breakpoint in self.persistance.load_breakpoints(): self.breakpoints.add(breakpoint) self.load_configurations() self.stopped_reason = '' self.path = window.project_file_name() if self.path: self.path = os.path.dirname(self.path) self.console_panel.Add('Opened In Workspace: {}'.format(self.path)) else: self.console_panel.AddStderr( 'warning: debugger opened in a window that is not part of a project' ) print('Creating a window: h') self.disposeables.extend([ self.panel, ui.view_gutter_hovered.add(self.on_gutter_hovered), ui.view_text_hovered.add(self.on_text_hovered), ui.view_drag_select.add(self.on_drag_select), ]) offset = self.panel.phantom_location() self.disposeables.extend([ ui.Phantom(self.debugger_panel, self.view, sublime.Region(offset, offset + 0), sublime.LAYOUT_INLINE), ui.Phantom(self.pages_panel, self.view, sublime.Region(offset, offset + 1), sublime.LAYOUT_INLINE), ui.Phantom(self.variables_panel, self.view, sublime.Region(offset, offset + 2), sublime.LAYOUT_INLINE), ]) self.breakpoints.onRemovedBreakpoint.add( lambda b: self.clearBreakpointInformation()) self.breakpoints.onChangedBreakpoint.add(self.onChangedBreakpoint) self.breakpoints.onChangedFilter.add(self.onChangedFilter) self.breakpoints.onSelectedBreakpoint.add(self.onSelectedBreakpoint) active_view = self.window.active_view() if active_view: self.disposeables.append( register_on_changed_setting(active_view, self.on_settings_updated)) else: print('Failed to find active view to listen for settings changes')
def __init__(self, window: sublime.Window): super().__init__() # ensure we are being run inside a sublime project # if not prompt the user to create one while True: data = window.project_data() project_name = window.project_file_name() while not project_name: r = sublime.ok_cancel_dialog( "Debugger requires a sublime project. Would you like to create a new sublime project?", "Save Project As...") if r: window.run_command('save_project_and_workspace_as') else: raise core.Error( "Debugger must be run inside a sublime project") # ensure we have debug configurations added to the project file data.setdefault('settings', {}).setdefault('debug.configurations', []) window.set_project_data(data) break self.name = project_name self.window = window self.on_updated: core.Event[None] = core.Event() self.compounds: List[ConfigurationCompound] = [] self.configurations: List[Configuration] = [] self.external_terminal_kind = 'platform' self.ui_scale = 12 self.bring_window_to_front_on_pause = False self.panel = self.window.create_output_panel("Debugger") self.panel_show() # we need enough space to place our phantoms in increasing regions (1, 1), (1, 2)... etc # otherwise they will get reordered when one of them gets redrawn # we use new lines so we don't have extra space on the rhs self.panel.run_command('insert', {'characters': _phantom_text}) settings = self.panel.settings() settings.set("margin", 0) settings.set('line_padding_top', 1) settings.set('gutter', False) settings.set('word_wrap', False) settings.set('line_spacing', 0) settings.set('context_menu', 'Widget Debug.sublime-menu') self.panel.sel().clear() # hack to get view to not freak out when clicking the edge of the window self.panel.set_viewport_position((_panel_position, 0), animate=False) async def later(): await core.sleep(0) self.panel.set_viewport_position((_panel_position, 0), animate=False) core.run(later()) self.reload()