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]
def forWindow(window: sublime.Window, create: bool = False) -> 'Optional[Main]': instance = Main.instances.get(window.id()) if not instance and create: main = Main(window) Main.instances[window.id()] = main return main return instance
def run_build(window: sublime.Window, on_output_callback, args) -> core.awaitable[int]: global on_finished_futures global id id += 1 future = core.create_future() on_finished_futures[id] = future on_output_callbacks[id] = on_output_callback window.run_command("debugger_build_exec", { "id": id, "args": args, }) try: exit_code = yield from future return exit_code except core.CancelledError as e: core.log_info("Cancel build") window.run_command("debugger_build_exec", { "id": id, "args": { "kill": True }, }) raise e
def __init__(self, window: sublime.Window, on_output, args): global debugger_exec_for_id global id id += 1 debugger_exec_for_id[id] = self self.future = core.create_future() self.on_output_callback = on_output window.run_command("debugger_exec", { "id": id, "args": args, }) async def kill_if_canceled(): try: await self.future except core.CancelledError as e: core.log_info("Cancel task") window.run_command("debugger_exec", { "id": id, "args": { "kill": True }, }) raise e core.run(kill_if_canceled())
def open_file(window: sublime.Window, file_path: str, flags: int = 0, group: int = -1) -> Promise[Optional[sublime.View]]: """Open a file asynchronously. It is only safe to call this function from the UI thread.""" # window.open_file brings the file to focus if it's already opened, which we don't want. # So we first check if there's already a view for that file. view = window.find_open_file(file_path) if view: return Promise.resolve(view) view = window.open_file(file_path, flags, group) if not view.is_loading(): # It's already loaded. Possibly already open in a tab. return Promise.resolve(view) # Is the view opening right now? Then return the associated unresolved promise for fn, value in opening_files.items(): if fn == file_path or os.path.samefile(fn, file_path): # Return the unresolved promise. A future on_load event will resolve the promise. return value[0] # Prepare a new promise to be resolved by a future on_load event (see the event listener in main.py) def fullfill(resolve: ResolveFunc[Optional[sublime.View]]) -> None: global opening_files # Save the promise in the first element of the tuple -- except we cannot yet do that here opening_files[file_path] = (None, resolve) # type: ignore promise = Promise(fullfill) tup = opening_files[file_path] # Save the promise in the first element of the tuple so that the for-loop above can return it opening_files[file_path] = (promise, tup[1]) return promise
def open_location(window: sublime.Window, location: Tuple[str, str, Tuple[int, int]]) -> None: fname, file_path_and_row_col, rowcol = location row, col = rowcol debug("opening location", file_path_and_row_col) window.open_file(file_path_and_row_col, sublime.ENCODED_POSITION | sublime.FORCE_GROUP)
def update_diagnostics_panel(window: sublime.Window): assert window, "missing window!" base_dir = get_project_path(window) panel = ensure_diagnostics_panel(window) assert panel, "must have a panel now!" diagnostics_by_file = get_window_diagnostics(window) if diagnostics_by_file is not None: active_panel = window.active_panel() is_active_panel = (active_panel == "output.diagnostics") panel.settings().set("result_base_dir", base_dir) panel.set_read_only(False) if diagnostics_by_file: to_render = [] for file_path, source_diagnostics in diagnostics_by_file.items(): relative_file_path = os.path.relpath( file_path, base_dir) if base_dir else file_path if source_diagnostics: to_render.append( format_diagnostics(relative_file_path, source_diagnostics)) panel.run_command("lsp_update_panel", {"characters": "\n".join(to_render)}) if settings.auto_show_diagnostics_panel and not active_panel: window.run_command("show_panel", {"panel": "output.diagnostics"}) else: panel.run_command("lsp_clear_panel") if settings.auto_show_diagnostics_panel and is_active_panel: window.run_command("hide_panel", {"panel": "output.diagnostics"}) panel.set_read_only(True)
def get_project_path(window: sublime.Window) -> 'Optional[str]': """ Returns the common root of all open folders in the window """ if len(window.folders()): folder_paths = window.folders() return folder_paths[0] else: view = window.active_view() if view: filename = view.file_name() if filename: project_path = os.path.dirname(filename) debug( "Couldn't determine project directory since no folders are open!", "Using", project_path, "as a fallback.") return project_path else: debug( "Couldn't determine project directory since no folders are open", "and the current file isn't saved on the disk.") return None else: debug("No view is active in current window") return None # https://github.com/tomv564/LSP/issues/219
def forWindow(window: sublime.Window, create: bool = False) -> 'Optional[DebuggerInterface]': instance = DebuggerInterface.instances.get(window.id()) if not instance and create: main = DebuggerInterface(window) DebuggerInterface.instances[window.id()] = main return main return instance
def sublime_show_quick_panel_async(window: sublime.Window, items: List[str], selected_index: int) -> awaitable[int]: done = main_loop.create_future() window.show_quick_panel(items, lambda index: done.set_result(index), selected_index=selected_index) r = yield from done return r
def send_terminus_repl(window: sublime.Window, code_block: str): # ensure code block ends with newline, so that it will be executed in REPL if not code_block.endswith("\n"): code_block += "\n" window.run_command("terminus_send_string", { "string": code_block, "tag": JULIA_REPL_TAG })
def select_entry(window: sublime.Window, locations: List[Tuple[str, str, Tuple[int, int]]], idx: int, orig_view: sublime.View) -> None: if idx >= 0: open_location(window, locations[idx]) else: if orig_view: window.focus_view(orig_view)
def sublime_show_quick_panel_async(window: sublime.Window, items: List[str], selected_index: int) -> awaitable[int]: done = create_future() window.show_quick_panel( items, lambda index: call_soon_threadsafe(done.set_result, index), selected_index=selected_index) r = yield from done return r
def destroy_output_panels(window: sublime.Window) -> None: for field in filter(lambda a: not a.startswith('__'), PanelName.__dict__.keys()): panel_name = getattr(PanelName, field) panel = window.find_output_panel(panel_name) if panel and panel.is_valid(): panel.settings().set("syntax", "Packages/Text/Plain text.tmLanguage") window.destroy_output_panel(panel_name)
def start_window_config(window: sublime.Window, project_path: str, config: ClientConfig, on_created: 'Callable'): args, env = get_window_env(window, config) config.binary_args = args session = create_session(config, project_path, env, settings, on_created=on_created, on_ended=lambda: on_session_ended(window, config.name)) clients_by_window.setdefault(window.id(), {})[config.name] = session debug("{} client registered for window {}".format(config.name, window.id()))
def highlight_entry(window: sublime.Window, locations: List[Tuple[str, str, Tuple[int, int]]], idx: int) -> None: fname, file_path_and_row_col, rowcol = locations[idx] row, col = rowcol window.open_file(file_path_and_row_col, group=window.active_group(), flags=sublime.TRANSIENT | sublime.ENCODED_POSITION | sublime.FORCE_GROUP)
def start_client(window: sublime.Window, config: ClientConfig): project_path = get_project_path(window) if project_path is None: return None if settings.show_status_messages: window.status_message("Starting " + config.name + "...") debug("starting in", project_path) # Create a dictionary of Sublime Text variables variables = window.extract_variables() # Expand language server command line environment variables expanded_args = list( sublime.expand_variables(os.path.expanduser(arg), variables) for arg in config.binary_args ) # Override OS environment variables env = os.environ.copy() for var, value in config.env.items(): # Expand both ST and OS environment variables env[var] = os.path.expandvars(sublime.expand_variables(value, variables)) client = start_server(expanded_args, project_path, env) if not client: window.status_message("Could not start " + config.name + ", disabling") debug("Could not start", config.binary_args, ", disabling") return None initializeParams = { "processId": client.process.pid, "rootUri": filename_to_uri(project_path), "rootPath": project_path, "capabilities": { "textDocument": { "completion": { "completionItem": { "snippetSupport": True } }, "synchronization": { "didSave": True } }, "workspace": { "applyEdit": True } } } if config.init_options: initializeParams['initializationOptions'] = config.init_options client.send_request( Request.initialize(initializeParams), lambda result: handle_initialize_result(result, client, window, config)) return client
def get_active_views(window: sublime.Window, current_buffer_only: bool) -> List[sublime.View]: """Returns all currently visible views""" if current_buffer_only: group_indexes = [window.active_group()] else: group_indexes = list(range(window.num_groups())) return [window.active_view_in_group(idx) for idx in group_indexes] # type: ignore
def lookup(self, window: sublime.Window) -> WindowManager: wm = self._windows.get(window.id()) if wm: return wm workspace = ProjectFolders(window) window_configs = self._configs.for_window(window) state = WindowManager(window=window, workspace=workspace, configs=window_configs) self._windows[window.id()] = state return state
def show_error_result(window: sublime.Window, message: str) -> None: """show error panel""" panel = window.create_output_panel(ERROR_RESPONSE_PANEL_NAME) panel.set_read_only(False) panel.run_command( "append", {"characters": message}, ) window.run_command("show_panel", {"panel": f"output.{ERROR_RESPONSE_PANEL_NAME}"})
def agenda_meta_info_get_or_create_view(window: sublime.Window, meta_info: Agenda.AgendaItemMetaInfo): if meta_info.file_name is not None: view = window.find_open_file(meta_info.file_name) if view is not None: return view return window.open_file(meta_info.file_name) for view in window.views(): if view.id() == meta_info.view_id: return view raise ZorgmodeError("Cannot find file for this item")
def for_window(window: sublime.Window, create: bool = False) -> 'Optional[DebuggerInterface]': instance = DebuggerInterface.instances.get(window.id()) if not instance and create: try: main = DebuggerInterface(window) DebuggerInterface.instances[window.id()] = main return main except dap.Error as e: core.log_exception() if create: instance.show() return instance
def disable_in_project(window: sublime.Window, config_name: str) -> None: project_data = window.project_data() if isinstance(project_data, dict): project_settings = project_data.setdefault('settings', dict()) project_lsp_settings = project_settings.setdefault('LSP', dict()) project_client_settings = project_lsp_settings.setdefault( config_name, dict()) project_client_settings['enabled'] = False window.set_project_data(project_data) else: sublime.message_dialog( "Can't disable {} in the current workspace. Ensure that the project is saved first." .format(config_name))
def update_file_diagnostics(window: sublime.Window, file_path: str, source: str, diagnostics: 'List[Diagnostic]'): if diagnostics: window_file_diagnostics.setdefault(window.id(), dict()).setdefault( file_path, dict())[source] = diagnostics else: if window.id() in window_file_diagnostics: file_diagnostics = window_file_diagnostics[window.id()] if file_path in file_diagnostics: if source in file_diagnostics[file_path]: del file_diagnostics[file_path][source] if not file_diagnostics[file_path]: del file_diagnostics[file_path]
def on_start(cls, window: sublime.Window) -> bool: if cls.manages_server(): server = cls.get_server() if server is None or server.get_status() != ServerStatus.READY: log_and_show_message('{}: Server not ready'.format(cls.get_displayed_name())) return False startup_view = window.active_view() workspace_folders = [WorkspaceFolder.from_path(folder) for folder in window.folders()] message = cls.is_allowed_to_start(window, startup_view, workspace_folders) if message: log_and_show_message('{}: {}'.format(cls.get_displayed_name(), message)) return False return True
def start_window_config(window: sublime.Window, project_path: str, config: ClientConfig, on_created: 'Callable'): args, env = get_window_env(window, config) config.binary_args = args session = create_session( config, project_path, env, settings, on_created=on_created, on_ended=lambda: on_session_ended(window, config.name)) clients_by_window.setdefault(window.id(), {})[config.name] = session debug("{} client registered for window {}".format(config.name, window.id()))
def start_terminus_repl(window: sublime.Window, focus: bool): settings = sublime.load_settings(SETTINGS_FILE) julia_executable = settings.get("julia_executable_path") or "julia" # start in current project environment if available cmd = [julia_executable, "--banner=no", "--project"] window.run_command( "terminus_open", { "cmd": cmd, "cwd": "${file_path:${folder}}", "panel_name": JULIA_REPL_NAME, "focus": focus, "tag": JULIA_REPL_TAG, "env": settings.get("repl_env_variables") })
def show_input_panel( window: sublime.Window, caption: str, *, initial_text: str = "", on_done: "Callback[[str], None]" = None ) -> None: window.show_input_panel( caption=caption, initial_text=initial_text, on_done=on_done, on_change=None, on_cancel=None, )
def run(window: sublime.Window, on_output_callback, args) -> core.awaitable[None]: global on_finished_futures global id id += 1 future = core.create_future() on_finished_futures[id] = future on_output_callbacks[id] = on_output_callback print(args) window.run_command("debugger_build_exec", { "id": id, "args": args, }) return future
def sublime_show_input_panel_async( window: sublime.Window, caption: str, initial_text: str, on_change: Callable[[str], None]) -> awaitable[Optional[str]]: result = main_loop.create_future() def on_done(value: str) -> None: result.set_result(value) def on_cancel() -> None: result.set_result(None) window.show_input_panel(caption, initial_text, on_done, on_change, on_cancel) r = yield from result return r
def log_server_message(window: sublime.Window, prefix: str, message: str) -> None: window_id = window.id() if not window.is_valid( ) or window_id not in WindowPanelListener.server_log_map: return WindowPanelListener.server_log_map[window_id].append((prefix, message)) list_len = len(WindowPanelListener.server_log_map[window_id]) if list_len >= SERVER_PANEL_MAX_LINES: # Trim leading items in the list, leaving only the max allowed count. del WindowPanelListener.server_log_map[ window_id][:list_len - SERVER_PANEL_MAX_LINES] panel = ensure_server_panel(window) if is_server_panel_open(window) and panel: update_server_panel(panel, window_id)
def on_session_ended(window: sublime.Window, config_name: str): configs = window_configs(window) del configs[config_name] if not configs: debug("all clients unloaded") if clients_unloaded_handler: clients_unloaded_handler(window.id())
def start_window_client(view: sublime.View, window: sublime.Window, config: ClientConfig): project_path = get_project_path(window) if project_path is None: debug('Cannot start without a project folder') return if can_start_config(window, config.name): if config.name in client_start_listeners: handler_startup_hook = client_start_listeners[config.name] if not handler_startup_hook(window): return if settings.show_status_messages: window.status_message("Starting " + config.name + "...") debug("starting in", project_path) start_window_config(window, project_path, config, lambda session: handle_session_started(session, window, project_path, config)) else: debug('Already starting on this window:', config.name)
def create_references_panel(window: sublime.Window): panel = create_output_panel(window, "references") panel.settings().set("result_file_regex", r"^\s+\S\s+(\S.+)\s+(\d+):?(\d+)$") panel.assign_syntax("Packages/" + PLUGIN_NAME + "/Syntaxes/References.sublime-syntax") # Call create_output_panel a second time after assigning the above # settings, so that it'll be picked up as a result buffer # see: Packages/Default/exec.py#L228-L230 panel = window.create_output_panel("references") return panel
def update_diagnostics_panel(window: sublime.Window): assert window, "missing window!" base_dir = get_project_path(window) panel = ensure_diagnostics_panel(window) if not panel and not window.is_valid(): return assert panel, "must have a panel now!" diagnostics_by_file = get_window_diagnostics(window) if diagnostics_by_file is not None: active_panel = window.active_panel() is_active_panel = (active_panel == "output.diagnostics") panel.settings().set("result_base_dir", base_dir) panel.set_read_only(False) if diagnostics_by_file: to_render = [] for file_path, source_diagnostics in diagnostics_by_file.items(): try: relative_file_path = os.path.relpath(file_path, base_dir) if base_dir else file_path except ValueError: relative_file_path = file_path if source_diagnostics: to_render.append(format_diagnostics(relative_file_path, source_diagnostics)) panel.run_command("code_intel_update_panel", {"characters": "\n".join(to_render)}) if settings.auto_show_diagnostics_panel and not active_panel: window.run_command("show_panel", {"panel": "output.diagnostics"}) else: panel.run_command("code_intel_clear_panel") if is_active_panel: window.run_command("hide_panel", {"panel": "output.diagnostics"}) panel.set_read_only(True)
def get_project_path(window: sublime.Window) -> 'Optional[str]': """ Returns the common root of all open folders in the window """ if len(window.folders()): folder_paths = window.folders() return folder_paths[0] else: view = window.active_view() if view: filename = view.file_name() if filename: project_path = os.path.dirname(filename) debug("Couldn't determine project directory since no folders are open!", "Using", project_path, "as a fallback.") return project_path else: debug("Couldn't determine project directory since no folders are open", "and the current file isn't saved on the disk.") return None else: debug("No view is active in current window") return None # https://github.com/tomv564/LSP/issues/219
def get_window_env(window: sublime.Window, config: ClientConfig): # Create a dictionary of Sublime Text variables variables = window.extract_variables() # Expand language server command line environment variables expanded_args = list( sublime.expand_variables(os.path.expanduser(arg), variables) for arg in config.binary_args ) # Override OS environment variables env = os.environ.copy() for var, value in config.env.items(): # Expand both ST and OS environment variables env[var] = os.path.expandvars(sublime.expand_variables(value, variables)) return expanded_args, env
def start_client(window: sublime.Window, project_path: str, config: ClientConfig): if config.name in client_start_listeners: handler_startup_hook = client_start_listeners[config.name] if not handler_startup_hook(window): return if settings.show_status_messages: window.status_message("Starting " + config.name + "...") debug("starting in", project_path) # Create a dictionary of Sublime Text variables variables = window.extract_variables() # Expand language server command line environment variables expanded_args = list( sublime.expand_variables(os.path.expanduser(arg), variables) for arg in config.binary_args ) # Override OS environment variables env = os.environ.copy() for var, value in config.env.items(): # Expand both ST and OS environment variables env[var] = os.path.expandvars(sublime.expand_variables(value, variables)) # TODO: don't start process if tcp already up or command empty? process = start_server(expanded_args, project_path, env) if not process: window.status_message("Could not start " + config.name + ", disabling") debug("Could not start", config.binary_args, ", disabling") return None if config.tcp_port is not None: client = attach_tcp_client(config.tcp_port, process, settings) else: client = attach_stdio_client(process, settings) if not client: window.status_message("Could not connect to " + config.name + ", disabling") return None return client
def get_document_state(window: sublime.Window, path: str) -> DocumentState: window_document_states = document_states.setdefault(window.id(), {}) if path not in window_document_states: window_document_states[path] = DocumentState(path) return window_document_states[path]
def get_project_config(window: sublime.Window) -> dict: project_data = window.project_data() or dict() project_settings = project_data.setdefault('settings', dict()) project_code_intel_settings = project_settings.setdefault('SublimeCodeIntel', dict()) return project_code_intel_settings
def remove_window_client(window: sublime.Window, config_name: str): del clients_by_window[window.id()][config_name]
def window_configs(window: sublime.Window) -> 'Dict[str, Session]': if window.id() in clients_by_window: return clients_by_window[window.id()] else: # debug("no configs found for window", window.id()) return {}
def restart_window_clients(window: sublime.Window): debug('Restarting clients for window {}'.format(window.id())) clear_document_states(window) restarting_window_ids.add(window.id()) unload_window_sessions(window.id())
def clear_document_states(window: sublime.Window): if window.id() in document_states: del document_states[window.id()]
def ensure_references_panel(window: sublime.Window): return window.find_output_panel("references") or create_references_panel(window)
def create_output_panel(window: sublime.Window, name: str) -> sublime.View: panel = window.create_output_panel(name) settings = panel.settings() for key, value in OUTPUT_PANEL_SETTINGS.items(): settings.set(key, value) return panel
def ensure_diagnostics_panel(window: sublime.Window): return window.find_output_panel("diagnostics") or create_diagnostics_panel(window)
def clear_document_state(window: sublime.Window, path: str): window_id = window.id() if window_id in document_states: del document_states[window_id][path]
def has_document_state(window: sublime.Window, path: str): window_id = window.id() if window_id not in document_states: return False return path in document_states[window_id]