diff --git a/Main.sublime-menu b/Main.sublime-menu index c59db3bd7..8aea68e8f 100644 --- a/Main.sublime-menu +++ b/Main.sublime-menu @@ -25,50 +25,50 @@ }, { "command": "lsp_source_action", - "args": {"id": -1} + "args": {"index": -1} }, { "caption": "LSP: Source Action", "children": [ { "command": "lsp_source_action", - "args": {"id": 0} + "args": {"index": 0} }, { "command": "lsp_source_action", - "args": {"id": 1} + "args": {"index": 1} }, { "command": "lsp_source_action", - "args": {"id": 2} + "args": {"index": 2} }, { "command": "lsp_source_action", - "args": {"id": 3} + "args": {"index": 3} }, { "command": "lsp_source_action", - "args": {"id": 4} + "args": {"index": 4} }, { "command": "lsp_source_action", - "args": {"id": 5} + "args": {"index": 5} }, { "command": "lsp_source_action", - "args": {"id": 6} + "args": {"index": 6} }, { "command": "lsp_source_action", - "args": {"id": 7} + "args": {"index": 7} }, { "command": "lsp_source_action", - "args": {"id": 8} + "args": {"index": 8} }, { "command": "lsp_source_action", - "args": {"id": 9} + "args": {"index": 9} } ] }, @@ -81,43 +81,43 @@ }, { "command": "lsp_refactor", - "args": {"id": 0} + "args": {"index": 0} }, { "command": "lsp_refactor", - "args": {"id": 1} + "args": {"index": 1} }, { "command": "lsp_refactor", - "args": {"id": 2} + "args": {"index": 2} }, { "command": "lsp_refactor", - "args": {"id": 3} + "args": {"index": 3} }, { "command": "lsp_refactor", - "args": {"id": 4} + "args": {"index": 4} }, { "command": "lsp_refactor", - "args": {"id": 5} + "args": {"index": 5} }, { "command": "lsp_refactor", - "args": {"id": 6} + "args": {"index": 6} }, { "command": "lsp_refactor", - "args": {"id": 7} + "args": {"index": 7} }, { "command": "lsp_refactor", - "args": {"id": 8} + "args": {"index": 8} }, { "command": "lsp_refactor", - "args": {"id": 9} + "args": {"index": 9} } ] }, diff --git a/boot.py b/boot.py index a54c62206..dc4b8632e 100644 --- a/boot.py +++ b/boot.py @@ -21,8 +21,6 @@ from .plugin.core.open import opening_files from .plugin.core.panels import PanelName from .plugin.core.protocol import Error -from .plugin.core.registry import LspCollapseTreeItemCommand -from .plugin.core.registry import LspExpandTreeItemCommand from .plugin.core.registry import LspNextDiagnosticCommand from .plugin.core.registry import LspOpenLocationCommand from .plugin.core.registry import LspPrevDiagnosticCommand @@ -36,6 +34,8 @@ from .plugin.core.signature_help import LspSignatureHelpNavigateCommand from .plugin.core.signature_help import LspSignatureHelpShowCommand from .plugin.core.transports import kill_all_subprocesses +from .plugin.core.tree_view import LspCollapseTreeItemCommand +from .plugin.core.tree_view import LspExpandTreeItemCommand from .plugin.core.typing import Any, Optional, List, Type, Dict from .plugin.core.views import LspRunTextCommandHelperCommand from .plugin.document_link import LspOpenLinkCommand diff --git a/plugin/code_actions.py b/plugin/code_actions.py index c55877346..2bd13d7db 100644 --- a/plugin/code_actions.py +++ b/plugin/code_actions.py @@ -365,17 +365,17 @@ def actions_cache(self) -> List[Tuple[str, CodeAction]]: def view(self) -> Optional[sublime.View]: return self.window.active_view() - def is_enabled(self, id: int, event: Optional[dict] = None) -> bool: - if not -1 < id < len(self.actions_cache): + def is_enabled(self, index: int, event: Optional[dict] = None) -> bool: + if not -1 < index < len(self.actions_cache): return False return self._has_session(event) - def is_visible(self, id: int, event: Optional[dict] = None) -> bool: - if id == -1: + def is_visible(self, index: int, event: Optional[dict] = None) -> bool: + if index == -1: if self._has_session(event): sublime.set_timeout_async(partial(self._request_menu_actions_async, event)) return False - return id < len(self.actions_cache) and self._is_cache_valid(event) + return index < len(self.actions_cache) and self._is_cache_valid(event) def _has_session(self, event: Optional[dict] = None) -> bool: view = self.view @@ -389,19 +389,19 @@ def _has_session(self, event: Optional[dict] = None) -> bool: return False return bool(listener.session_async(self.capability, region.b)) - def description(self, id: int, event: Optional[dict] = None) -> Optional[str]: - if -1 < id < len(self.actions_cache): - return self.actions_cache[id][1]['title'] + def description(self, index: int, event: Optional[dict] = None) -> Optional[str]: + if -1 < index < len(self.actions_cache): + return self.actions_cache[index][1]['title'] def want_event(self) -> bool: return True - def run(self, id: int, event: Optional[dict] = None) -> None: - sublime.set_timeout_async(partial(self.run_async, id, event)) + def run(self, index: int, event: Optional[dict] = None) -> None: + sublime.set_timeout_async(partial(self.run_async, index, event)) - def run_async(self, id: int, event: Optional[dict]) -> None: + def run_async(self, index: int, event: Optional[dict]) -> None: if self._is_cache_valid(event): - config_name, action = self.actions_cache[id] + config_name, action = self.actions_cache[index] session = self.session_by_name(config_name) if session: session.run_code_action_async(action, progress=True, view=self.view) \ diff --git a/plugin/completion.py b/plugin/completion.py index 6efc612cf..2169eb097 100644 --- a/plugin/completion.py +++ b/plugin/completion.py @@ -1,3 +1,4 @@ +from .core.constants import COMPLETION_KINDS from .core.edit import parse_text_edit from .core.logging import debug from .core.promise import Promise @@ -19,7 +20,6 @@ from .core.sessions import Session from .core.settings import userprefs from .core.typing import Callable, List, Dict, Optional, Generator, Tuple, Union, cast, Any, TypeGuard -from .core.views import COMPLETION_KINDS from .core.views import FORMAT_STRING, FORMAT_MARKUP_CONTENT from .core.views import MarkdownLangMap from .core.views import minihtml diff --git a/plugin/core/constants.py b/plugin/core/constants.py index daa5b908d..efe2753b8 100644 --- a/plugin/core/constants.py +++ b/plugin/core/constants.py @@ -1,5 +1,14 @@ -# TODO: Move all constants which are shared by multiple modules into this file, so that they can be imported without -# causing import loops +from .protocol import CodeActionKind +from .protocol import CompletionItemKind +from .protocol import DiagnosticSeverity +from .protocol import DocumentHighlightKind +from .protocol import SymbolKind +from .typing import Dict, Tuple +import sublime + + +SublimeKind = Tuple[int, str, str] + # Keys for View.add_regions HOVER_HIGHLIGHT_KEY = 'lsp_hover_highlight' @@ -9,3 +18,196 @@ HOVER_ENABLED_KEY = 'lsp_show_hover_popups' HOVER_PROVIDER_COUNT_KEY = 'lsp_hover_provider_count' SHOW_DEFINITIONS_KEY = 'show_definitions' + +# sublime.Kind tuples for sublime.CompletionItem, sublime.QuickPanelItem, sublime.ListInputItem +# https://www.sublimetext.com/docs/api_reference.html#sublime.Kind +KIND_ARRAY = (sublime.KIND_ID_TYPE, "a", "Array") +KIND_BOOLEAN = (sublime.KIND_ID_VARIABLE, "b", "Boolean") +KIND_CLASS = (sublime.KIND_ID_TYPE, "c", "Class") +KIND_COLOR = (sublime.KIND_ID_MARKUP, "c", "Color") +KIND_CONSTANT = (sublime.KIND_ID_VARIABLE, "c", "Constant") +KIND_CONSTRUCTOR = (sublime.KIND_ID_FUNCTION, "c", "Constructor") +KIND_ENUM = (sublime.KIND_ID_TYPE, "e", "Enum") +KIND_ENUMMEMBER = (sublime.KIND_ID_VARIABLE, "e", "Enum Member") +KIND_EVENT = (sublime.KIND_ID_FUNCTION, "e", "Event") +KIND_FIELD = (sublime.KIND_ID_VARIABLE, "f", "Field") +KIND_FILE = (sublime.KIND_ID_NAVIGATION, "f", "File") +KIND_FOLDER = (sublime.KIND_ID_NAVIGATION, "f", "Folder") +KIND_FUNCTION = (sublime.KIND_ID_FUNCTION, "f", "Function") +KIND_INTERFACE = (sublime.KIND_ID_TYPE, "i", "Interface") +KIND_KEY = (sublime.KIND_ID_NAVIGATION, "k", "Key") +KIND_KEYWORD = (sublime.KIND_ID_KEYWORD, "k", "Keyword") +KIND_METHOD = (sublime.KIND_ID_FUNCTION, "m", "Method") +KIND_MODULE = (sublime.KIND_ID_NAMESPACE, "m", "Module") +KIND_NAMESPACE = (sublime.KIND_ID_NAMESPACE, "n", "Namespace") +KIND_NULL = (sublime.KIND_ID_VARIABLE, "n", "Null") +KIND_NUMBER = (sublime.KIND_ID_VARIABLE, "n", "Number") +KIND_OBJECT = (sublime.KIND_ID_TYPE, "o", "Object") +KIND_OPERATOR = (sublime.KIND_ID_KEYWORD, "o", "Operator") +KIND_PACKAGE = (sublime.KIND_ID_NAMESPACE, "p", "Package") +KIND_PROPERTY = (sublime.KIND_ID_VARIABLE, "p", "Property") +KIND_REFERENCE = (sublime.KIND_ID_NAVIGATION, "r", "Reference") +KIND_SNIPPET = (sublime.KIND_ID_SNIPPET, "s", "Snippet") +KIND_STRING = (sublime.KIND_ID_VARIABLE, "s", "String") +KIND_STRUCT = (sublime.KIND_ID_TYPE, "s", "Struct") +KIND_TEXT = (sublime.KIND_ID_MARKUP, "t", "Text") +KIND_TYPEPARAMETER = (sublime.KIND_ID_TYPE, "t", "Type Parameter") +KIND_UNIT = (sublime.KIND_ID_VARIABLE, "u", "Unit") +KIND_VALUE = (sublime.KIND_ID_VARIABLE, "v", "Value") +KIND_VARIABLE = (sublime.KIND_ID_VARIABLE, "v", "Variable") + +KIND_ERROR = (sublime.KIND_ID_COLOR_REDISH, "e", "Error") +KIND_WARNING = (sublime.KIND_ID_COLOR_YELLOWISH, "w", "Warning") +KIND_INFORMATION = (sublime.KIND_ID_COLOR_BLUISH, "i", "Information") +KIND_HINT = (sublime.KIND_ID_COLOR_BLUISH, "h", "Hint") + +KIND_QUICKFIX = (sublime.KIND_ID_COLOR_YELLOWISH, "f", "QuickFix") +KIND_REFACTOR = (sublime.KIND_ID_COLOR_CYANISH, "r", "Refactor") +KIND_SOURCE = (sublime.KIND_ID_COLOR_PURPLISH, "s", "Source") + +COMPLETION_KINDS = { + CompletionItemKind.Text: KIND_TEXT, + CompletionItemKind.Method: KIND_METHOD, + CompletionItemKind.Function: KIND_FUNCTION, + CompletionItemKind.Constructor: KIND_CONSTRUCTOR, + CompletionItemKind.Field: KIND_FIELD, + CompletionItemKind.Variable: KIND_VARIABLE, + CompletionItemKind.Class: KIND_CLASS, + CompletionItemKind.Interface: KIND_INTERFACE, + CompletionItemKind.Module: KIND_MODULE, + CompletionItemKind.Property: KIND_PROPERTY, + CompletionItemKind.Unit: KIND_UNIT, + CompletionItemKind.Value: KIND_VALUE, + CompletionItemKind.Enum: KIND_ENUM, + CompletionItemKind.Keyword: KIND_KEYWORD, + CompletionItemKind.Snippet: KIND_SNIPPET, + CompletionItemKind.Color: KIND_COLOR, + CompletionItemKind.File: KIND_FILE, + CompletionItemKind.Reference: KIND_REFERENCE, + CompletionItemKind.Folder: KIND_FOLDER, + CompletionItemKind.EnumMember: KIND_ENUMMEMBER, + CompletionItemKind.Constant: KIND_CONSTANT, + CompletionItemKind.Struct: KIND_STRUCT, + CompletionItemKind.Event: KIND_EVENT, + CompletionItemKind.Operator: KIND_OPERATOR, + CompletionItemKind.TypeParameter: KIND_TYPEPARAMETER +} # type: Dict[CompletionItemKind, SublimeKind] + +SYMBOL_KINDS = { + SymbolKind.File: KIND_FILE, + SymbolKind.Module: KIND_MODULE, + SymbolKind.Namespace: KIND_NAMESPACE, + SymbolKind.Package: KIND_PACKAGE, + SymbolKind.Class: KIND_CLASS, + SymbolKind.Method: KIND_METHOD, + SymbolKind.Property: KIND_PROPERTY, + SymbolKind.Field: KIND_FIELD, + SymbolKind.Constructor: KIND_CONSTRUCTOR, + SymbolKind.Enum: KIND_ENUM, + SymbolKind.Interface: KIND_INTERFACE, + SymbolKind.Function: KIND_FUNCTION, + SymbolKind.Variable: KIND_VARIABLE, + SymbolKind.Constant: KIND_CONSTANT, + SymbolKind.String: KIND_STRING, + SymbolKind.Number: KIND_NUMBER, + SymbolKind.Boolean: KIND_BOOLEAN, + SymbolKind.Array: KIND_ARRAY, + SymbolKind.Object: KIND_OBJECT, + SymbolKind.Key: KIND_KEY, + SymbolKind.Null: KIND_NULL, + SymbolKind.EnumMember: KIND_ENUMMEMBER, + SymbolKind.Struct: KIND_STRUCT, + SymbolKind.Event: KIND_EVENT, + SymbolKind.Operator: KIND_OPERATOR, + SymbolKind.TypeParameter: KIND_TYPEPARAMETER +} # type: Dict[SymbolKind, SublimeKind] + +DIAGNOSTIC_KINDS = { + DiagnosticSeverity.Error: KIND_ERROR, + DiagnosticSeverity.Warning: KIND_WARNING, + DiagnosticSeverity.Information: KIND_INFORMATION, + DiagnosticSeverity.Hint: KIND_HINT +} # type: Dict[DiagnosticSeverity, SublimeKind] + +CODE_ACTION_KINDS = { + CodeActionKind.QuickFix: KIND_QUICKFIX, + CodeActionKind.Refactor: KIND_REFACTOR, + CodeActionKind.Source: KIND_SOURCE +} # type: Dict[CodeActionKind, SublimeKind] + + +DOCUMENT_HIGHLIGHT_KIND_NAMES = { + DocumentHighlightKind.Text: "text", + DocumentHighlightKind.Read: "read", + DocumentHighlightKind.Write: "write" +} # type: Dict[DocumentHighlightKind, str] + + +# Symbol scope to kind mapping, based on https://github.com/sublimetext-io/docs.sublimetext.io/issues/30 +SUBLIME_KIND_SCOPES = { + sublime.KIND_KEYWORD: "keyword | storage.modifier | storage.type | keyword.declaration | variable.language | constant.language", # noqa: E501 + sublime.KIND_TYPE: "entity.name.type | entity.name.class | entity.name.enum | entity.name.trait | entity.name.struct | entity.name.impl | entity.name.interface | entity.name.union | support.type | support.class", # noqa: E501 + sublime.KIND_FUNCTION: "entity.name.function | entity.name.method | entity.name.macro | meta.method entity.name.function | support.function | meta.function-call variable.function | meta.function-call support.function | support.method | meta.method-call variable.function", # noqa: E501 + sublime.KIND_NAMESPACE: "entity.name.module | entity.name.namespace | support.module | support.namespace", + sublime.KIND_NAVIGATION: "entity.name.definition | entity.name.label | entity.name.section", + sublime.KIND_MARKUP: "entity.other.attribute-name | entity.name.tag | meta.toc-list.id.html", + sublime.KIND_VARIABLE: "entity.name.constant | constant.other | support.constant | variable.other | variable.parameter | variable.other.member | variable.other.readwrite.member" # noqa: E501 +} # type: Dict[SublimeKind, str] + +DOCUMENT_HIGHLIGHT_KIND_SCOPES = { + DocumentHighlightKind.Text: "region.bluish markup.highlight.text.lsp", + DocumentHighlightKind.Read: "region.greenish markup.highlight.read.lsp", + DocumentHighlightKind.Write: "region.yellowish markup.highlight.write.lsp" +} # type: Dict[DocumentHighlightKind, str] + +SEMANTIC_TOKENS_MAP = { + "namespace": "variable.other.namespace.lsp", + "namespace.declaration": "entity.name.namespace.lsp", + "namespace.definition": "entity.name.namespace.lsp", + "type": "storage.type.lsp", + "type.declaration": "entity.name.type.lsp", + "type.defaultLibrary": "support.type.lsp", + "type.definition": "entity.name.type.lsp", + "class": "storage.type.class.lsp", + "class.declaration": "entity.name.class.lsp", + "class.defaultLibrary": "support.class.lsp", + "class.definition": "entity.name.class.lsp", + "enum": "variable.other.enum.lsp", + "enum.declaration": "entity.name.enum.lsp", + "enum.definition": "entity.name.enum.lsp", + "interface": "entity.other.inherited-class.lsp", + "interface.declaration": "entity.name.interface.lsp", + "interface.definition": "entity.name.interface.lsp", + "struct": "storage.type.struct.lsp", + "struct.declaration": "entity.name.struct.lsp", + "struct.defaultLibrary": "support.struct.lsp", + "struct.definition": "entity.name.struct.lsp", + "typeParameter": "variable.parameter.generic.lsp", + "parameter": "variable.parameter.lsp", + "variable": "variable.other.lsp", + "variable.readonly": "variable.other.constant.lsp", + "property": "variable.other.property.lsp", + "enumMember": "constant.other.enum.lsp", + "event": "entity.name.function.lsp", + "function": "variable.function.lsp", + "function.declaration": "entity.name.function.lsp", + "function.defaultLibrary": "support.function.builtin.lsp", + "function.definition": "entity.name.function.lsp", + "method": "variable.function.lsp", + "method.declaration": "entity.name.function.lsp", + "method.defaultLibrary": "support.function.builtin.lsp", + "method.definition": "entity.name.function.lsp", + "macro": "variable.macro.lsp", + "macro.declaration": "entity.name.macro.lsp", + "macro.defaultLibrary": "support.macro.lsp", + "macro.definition": "entity.name.macro.lsp", + "keyword": "keyword.lsp", + "modifier": "storage.modifier.lsp", + "comment": "comment.lsp", + "comment.documentation": "comment.block.documentation.lsp", + "string": "string.lsp", + "number": "constant.numeric.lsp", + "regexp": "string.regexp.lsp", + "operator": "keyword.operator.lsp", + "decorator": "variable.annotation.lsp", +} diff --git a/plugin/core/registry.py b/plugin/core/registry.py index f07491bc5..c1708f3d9 100644 --- a/plugin/core/registry.py +++ b/plugin/core/registry.py @@ -3,8 +3,6 @@ from .protocol import LocationLink from .sessions import AbstractViewListener from .sessions import Session -from .tree_view import TreeDataProvider -from .tree_view import TreeViewSheet from .typing import Optional, Any, Generator, Iterable, List, Union from .views import first_selection_region from .views import get_uri_and_position_from_location @@ -16,56 +14,12 @@ from functools import partial import operator import sublime -import sublime_api # pyright: ignore[reportMissingImports] import sublime_plugin windows = WindowRegistry() -def new_tree_view_sheet( - window: sublime.Window, - name: str, - data_provider: TreeDataProvider, - header: str = "", - flags: int = 0, - group: int = -1 -) -> Optional[TreeViewSheet]: - """ - Use this function to create a new TreeView in form of a special HtmlSheet (TreeViewSheet). Only one TreeViewSheet - with the given name is allowed per window. If there already exists a TreeViewSheet with the same name, its content - will be replaced with the new data. The header argument is allowed to contain minihtml markup. - """ - wm = windows.lookup(window) - if not wm: - return None - if name in wm.tree_view_sheets: - tree_view_sheet = wm.tree_view_sheets[name] - sheet_id = tree_view_sheet.id() - if tree_view_sheet.window(): - tree_view_sheet.set_provider(data_provider, header) - if flags & sublime.ADD_TO_SELECTION: - # add to selected sheets if not already selected - selected_sheets = window.selected_sheets() - for sheet in window.sheets(): - if isinstance(sheet, sublime.HtmlSheet) and sheet.id() == sheet_id: - if sheet not in selected_sheets: - selected_sheets.append(sheet) - window.select_sheets(selected_sheets) - break - else: - window.focus_sheet(tree_view_sheet) - return tree_view_sheet - tree_view_sheet = TreeViewSheet( - sublime_api.window_new_html_sheet(window.window_id, name, "", flags, group), - name, - data_provider, - header - ) - wm.tree_view_sheets[name] = tree_view_sheet - return tree_view_sheet - - def best_session(view: sublime.View, sessions: Iterable[Session], point: Optional[int] = None) -> Optional[Session]: if point is None: try: @@ -320,28 +274,3 @@ class LspPrevDiagnosticCommand(LspTextCommand): def run(self, edit: sublime.Edit, point: Optional[int] = None) -> None: navigate_diagnostics(self.view, point, forward=False) - - -def toggle_tree_item(window: sublime.Window, name: str, id: str, expand: bool) -> None: - wm = windows.lookup(window) - if not wm: - return - sheet = wm.tree_view_sheets.get(name) - if not sheet: - return - if expand: - sheet.expand_item(id) - else: - sheet.collapse_item(id) - - -class LspExpandTreeItemCommand(LspWindowCommand): - - def run(self, name: str, id: str) -> None: - toggle_tree_item(self.window, name, id, True) - - -class LspCollapseTreeItemCommand(LspWindowCommand): - - def run(self, name: str, id: str) -> None: - toggle_tree_item(self.window, name, id, False) diff --git a/plugin/core/sessions.py b/plugin/core/sessions.py index 04db7656d..b7487ea29 100644 --- a/plugin/core/sessions.py +++ b/plugin/core/sessions.py @@ -1,4 +1,5 @@ from .collections import DottedDict +from .constants import SEMANTIC_TOKENS_MAP from .diagnostics_storage import DiagnosticsStorage from .edit import apply_edits from .edit import parse_workspace_edit @@ -103,7 +104,6 @@ from .views import get_storage_path from .views import get_uri_and_range_from_location from .views import MarkdownLangMap -from .views import SEMANTIC_TOKENS_MAP from .workspace import is_subpath_of from .workspace import WorkspaceFolder from abc import ABCMeta diff --git a/plugin/core/tree_view.py b/plugin/core/tree_view.py index dfb253887..1a2030767 100644 --- a/plugin/core/tree_view.py +++ b/plugin/core/tree_view.py @@ -1,12 +1,15 @@ +from .constants import SublimeKind from .css import css from .promise import Promise +from .registry import LspWindowCommand +from .registry import windows from .typing import Dict, IntEnum, List, Optional, TypeVar -from .views import SublimeKind from abc import ABCMeta from abc import abstractmethod from functools import partial import html import sublime +import sublime_api # pyright: ignore[reportMissingImports] import uuid # pyright: reportInvalidTypeVarUse=false @@ -270,3 +273,71 @@ def _subtree_html(self, id: str) -> str: if node.tree_item.collapsible_state == TreeItemCollapsibleState.EXPANDED: html += "".join([self._subtree_html(child_id) for child_id in node.child_ids]) return html + + +def new_tree_view_sheet( + window: sublime.Window, + name: str, + data_provider: TreeDataProvider, + header: str = "", + flags: int = 0, + group: int = -1 +) -> Optional[TreeViewSheet]: + """ + Use this function to create a new TreeView in form of a special HtmlSheet (TreeViewSheet). Only one TreeViewSheet + with the given name is allowed per window. If there already exists a TreeViewSheet with the same name, its content + will be replaced with the new data. The header argument is allowed to contain minihtml markup. + """ + wm = windows.lookup(window) + if not wm: + return None + if name in wm.tree_view_sheets: + tree_view_sheet = wm.tree_view_sheets[name] + sheet_id = tree_view_sheet.id() + if tree_view_sheet.window(): + tree_view_sheet.set_provider(data_provider, header) + if flags & sublime.ADD_TO_SELECTION: + # add to selected sheets if not already selected + selected_sheets = window.selected_sheets() + for sheet in window.sheets(): + if isinstance(sheet, sublime.HtmlSheet) and sheet.id() == sheet_id: + if sheet not in selected_sheets: + selected_sheets.append(sheet) + window.select_sheets(selected_sheets) + break + else: + window.focus_sheet(tree_view_sheet) + return tree_view_sheet + tree_view_sheet = TreeViewSheet( + sublime_api.window_new_html_sheet(window.window_id, name, "", flags, group), + name, + data_provider, + header + ) + wm.tree_view_sheets[name] = tree_view_sheet + return tree_view_sheet + + +def toggle_tree_item(window: sublime.Window, name: str, id: str, expand: bool) -> None: + wm = windows.lookup(window) + if not wm: + return + sheet = wm.tree_view_sheets.get(name) + if not sheet: + return + if expand: + sheet.expand_item(id) + else: + sheet.collapse_item(id) + + +class LspExpandTreeItemCommand(LspWindowCommand): + + def run(self, name: str, id: str) -> None: + toggle_tree_item(self.window, name, id, True) + + +class LspCollapseTreeItemCommand(LspWindowCommand): + + def run(self, name: str, id: str) -> None: + toggle_tree_item(self.window, name, id, False) diff --git a/plugin/core/typing.py b/plugin/core/typing.py index bcb251a85..cda3e92af 100644 --- a/plugin/core/typing.py +++ b/plugin/core/typing.py @@ -24,6 +24,7 @@ from typing import Set from typing import Tuple from typing import Type + from typing import TYPE_CHECKING from typing import TypedDict from typing import TypeGuard from typing import TypeVar @@ -31,6 +32,8 @@ else: + TYPE_CHECKING = False + def cast(typ, val): # type: ignore return val diff --git a/plugin/core/views.py b/plugin/core/views.py index 39e5ad07d..5ad99163e 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -1,3 +1,6 @@ +from .constants import CODE_ACTION_KINDS +from .constants import SUBLIME_KIND_SCOPES +from .constants import SublimeKind from .css import css as lsp_css from .protocol import CodeAction from .protocol import CodeActionKind @@ -7,7 +10,6 @@ from .protocol import Color from .protocol import ColorInformation from .protocol import Command -from .protocol import CompletionItemKind from .protocol import Diagnostic from .protocol import DiagnosticRelatedInformation from .protocol import DiagnosticSeverity @@ -16,7 +18,6 @@ from .protocol import DidOpenTextDocumentParams from .protocol import DidSaveTextDocumentParams from .protocol import DocumentColorParams -from .protocol import DocumentHighlightKind from .protocol import DocumentUri from .protocol import Location from .protocol import LocationLink @@ -28,7 +29,6 @@ from .protocol import Range from .protocol import Request from .protocol import SelectionRangeParams -from .protocol import SymbolKind from .protocol import TextDocumentContentChangeEvent from .protocol import TextDocumentIdentifier from .protocol import TextDocumentItem @@ -52,7 +52,6 @@ import tempfile MarkdownLangMap = Dict[str, Tuple[Tuple[str, ...], Tuple[str, ...]]] -SublimeKind = Tuple[int, str, str] DOCUMENT_LINK_FLAGS = sublime.HIDE_ON_MINIMAP | sublime.DRAW_NO_FILL | sublime.DRAW_NO_OUTLINE | sublime.DRAW_SOLID_UNDERLINE # noqa: E501 @@ -66,197 +65,6 @@ ("hint", "hints", "region.bluish markup.info.hint.lsp", "", _baseflags | sublime.DRAW_STIPPLED_UNDERLINE, sublime.DRAW_NO_FILL), # noqa: E501 ] # type: List[Tuple[str, str, str, str, int, int]] -# sublime.Kind tuples for sublime.CompletionItem, sublime.QuickPanelItem, sublime.ListInputItem -# https://www.sublimetext.com/docs/api_reference.html#sublime.Kind -KIND_ARRAY = (sublime.KIND_ID_TYPE, "a", "Array") -KIND_BOOLEAN = (sublime.KIND_ID_VARIABLE, "b", "Boolean") -KIND_CLASS = (sublime.KIND_ID_TYPE, "c", "Class") -KIND_COLOR = (sublime.KIND_ID_MARKUP, "c", "Color") -KIND_CONSTANT = (sublime.KIND_ID_VARIABLE, "c", "Constant") -KIND_CONSTRUCTOR = (sublime.KIND_ID_FUNCTION, "c", "Constructor") -KIND_ENUM = (sublime.KIND_ID_TYPE, "e", "Enum") -KIND_ENUMMEMBER = (sublime.KIND_ID_VARIABLE, "e", "Enum Member") -KIND_EVENT = (sublime.KIND_ID_FUNCTION, "e", "Event") -KIND_FIELD = (sublime.KIND_ID_VARIABLE, "f", "Field") -KIND_FILE = (sublime.KIND_ID_NAVIGATION, "f", "File") -KIND_FOLDER = (sublime.KIND_ID_NAVIGATION, "f", "Folder") -KIND_FUNCTION = (sublime.KIND_ID_FUNCTION, "f", "Function") -KIND_INTERFACE = (sublime.KIND_ID_TYPE, "i", "Interface") -KIND_KEY = (sublime.KIND_ID_NAVIGATION, "k", "Key") -KIND_KEYWORD = (sublime.KIND_ID_KEYWORD, "k", "Keyword") -KIND_METHOD = (sublime.KIND_ID_FUNCTION, "m", "Method") -KIND_MODULE = (sublime.KIND_ID_NAMESPACE, "m", "Module") -KIND_NAMESPACE = (sublime.KIND_ID_NAMESPACE, "n", "Namespace") -KIND_NULL = (sublime.KIND_ID_VARIABLE, "n", "Null") -KIND_NUMBER = (sublime.KIND_ID_VARIABLE, "n", "Number") -KIND_OBJECT = (sublime.KIND_ID_TYPE, "o", "Object") -KIND_OPERATOR = (sublime.KIND_ID_KEYWORD, "o", "Operator") -KIND_PACKAGE = (sublime.KIND_ID_NAMESPACE, "p", "Package") -KIND_PROPERTY = (sublime.KIND_ID_VARIABLE, "p", "Property") -KIND_REFERENCE = (sublime.KIND_ID_NAVIGATION, "r", "Reference") -KIND_SNIPPET = (sublime.KIND_ID_SNIPPET, "s", "Snippet") -KIND_STRING = (sublime.KIND_ID_VARIABLE, "s", "String") -KIND_STRUCT = (sublime.KIND_ID_TYPE, "s", "Struct") -KIND_TEXT = (sublime.KIND_ID_MARKUP, "t", "Text") -KIND_TYPEPARAMETER = (sublime.KIND_ID_TYPE, "t", "Type Parameter") -KIND_UNIT = (sublime.KIND_ID_VARIABLE, "u", "Unit") -KIND_VALUE = (sublime.KIND_ID_VARIABLE, "v", "Value") -KIND_VARIABLE = (sublime.KIND_ID_VARIABLE, "v", "Variable") - -KIND_ERROR = (sublime.KIND_ID_COLOR_REDISH, "e", "Error") -KIND_WARNING = (sublime.KIND_ID_COLOR_YELLOWISH, "w", "Warning") -KIND_INFORMATION = (sublime.KIND_ID_COLOR_BLUISH, "i", "Information") -KIND_HINT = (sublime.KIND_ID_COLOR_BLUISH, "h", "Hint") - -KIND_QUICKFIX = (sublime.KIND_ID_COLOR_YELLOWISH, "f", "QuickFix") -KIND_REFACTOR = (sublime.KIND_ID_COLOR_CYANISH, "r", "Refactor") -KIND_SOURCE = (sublime.KIND_ID_COLOR_PURPLISH, "s", "Source") - -COMPLETION_KINDS = { - CompletionItemKind.Text: KIND_TEXT, - CompletionItemKind.Method: KIND_METHOD, - CompletionItemKind.Function: KIND_FUNCTION, - CompletionItemKind.Constructor: KIND_CONSTRUCTOR, - CompletionItemKind.Field: KIND_FIELD, - CompletionItemKind.Variable: KIND_VARIABLE, - CompletionItemKind.Class: KIND_CLASS, - CompletionItemKind.Interface: KIND_INTERFACE, - CompletionItemKind.Module: KIND_MODULE, - CompletionItemKind.Property: KIND_PROPERTY, - CompletionItemKind.Unit: KIND_UNIT, - CompletionItemKind.Value: KIND_VALUE, - CompletionItemKind.Enum: KIND_ENUM, - CompletionItemKind.Keyword: KIND_KEYWORD, - CompletionItemKind.Snippet: KIND_SNIPPET, - CompletionItemKind.Color: KIND_COLOR, - CompletionItemKind.File: KIND_FILE, - CompletionItemKind.Reference: KIND_REFERENCE, - CompletionItemKind.Folder: KIND_FOLDER, - CompletionItemKind.EnumMember: KIND_ENUMMEMBER, - CompletionItemKind.Constant: KIND_CONSTANT, - CompletionItemKind.Struct: KIND_STRUCT, - CompletionItemKind.Event: KIND_EVENT, - CompletionItemKind.Operator: KIND_OPERATOR, - CompletionItemKind.TypeParameter: KIND_TYPEPARAMETER -} # type: Dict[CompletionItemKind, SublimeKind] - -SYMBOL_KINDS = { - SymbolKind.File: KIND_FILE, - SymbolKind.Module: KIND_MODULE, - SymbolKind.Namespace: KIND_NAMESPACE, - SymbolKind.Package: KIND_PACKAGE, - SymbolKind.Class: KIND_CLASS, - SymbolKind.Method: KIND_METHOD, - SymbolKind.Property: KIND_PROPERTY, - SymbolKind.Field: KIND_FIELD, - SymbolKind.Constructor: KIND_CONSTRUCTOR, - SymbolKind.Enum: KIND_ENUM, - SymbolKind.Interface: KIND_INTERFACE, - SymbolKind.Function: KIND_FUNCTION, - SymbolKind.Variable: KIND_VARIABLE, - SymbolKind.Constant: KIND_CONSTANT, - SymbolKind.String: KIND_STRING, - SymbolKind.Number: KIND_NUMBER, - SymbolKind.Boolean: KIND_BOOLEAN, - SymbolKind.Array: KIND_ARRAY, - SymbolKind.Object: KIND_OBJECT, - SymbolKind.Key: KIND_KEY, - SymbolKind.Null: KIND_NULL, - SymbolKind.EnumMember: KIND_ENUMMEMBER, - SymbolKind.Struct: KIND_STRUCT, - SymbolKind.Event: KIND_EVENT, - SymbolKind.Operator: KIND_OPERATOR, - SymbolKind.TypeParameter: KIND_TYPEPARAMETER -} # type: Dict[SymbolKind, SublimeKind] - -DIAGNOSTIC_KINDS = { - DiagnosticSeverity.Error: KIND_ERROR, - DiagnosticSeverity.Warning: KIND_WARNING, - DiagnosticSeverity.Information: KIND_INFORMATION, - DiagnosticSeverity.Hint: KIND_HINT -} # type: Dict[DiagnosticSeverity, SublimeKind] - -CODE_ACTION_KINDS = { - CodeActionKind.QuickFix: KIND_QUICKFIX, - CodeActionKind.Refactor: KIND_REFACTOR, - CodeActionKind.Source: KIND_SOURCE -} # type: Dict[CodeActionKind, SublimeKind] - -# Symbol scope to kind mapping, based on https://github.com/sublimetext-io/docs.sublimetext.io/issues/30 -SUBLIME_KIND_SCOPES = { - sublime.KIND_KEYWORD: "keyword | storage.modifier | storage.type | keyword.declaration | variable.language | constant.language", # noqa: E501 - sublime.KIND_TYPE: "entity.name.type | entity.name.class | entity.name.enum | entity.name.trait | entity.name.struct | entity.name.impl | entity.name.interface | entity.name.union | support.type | support.class", # noqa: E501 - sublime.KIND_FUNCTION: "entity.name.function | entity.name.method | entity.name.macro | meta.method entity.name.function | support.function | meta.function-call variable.function | meta.function-call support.function | support.method | meta.method-call variable.function", # noqa: E501 - sublime.KIND_NAMESPACE: "entity.name.module | entity.name.namespace | support.module | support.namespace", - sublime.KIND_NAVIGATION: "entity.name.definition | entity.name.label | entity.name.section", - sublime.KIND_MARKUP: "entity.other.attribute-name | entity.name.tag | meta.toc-list.id.html", - sublime.KIND_VARIABLE: "entity.name.constant | constant.other | support.constant | variable.other | variable.parameter | variable.other.member | variable.other.readwrite.member" # noqa: E501 -} # type: Dict[SublimeKind, str] - -DOCUMENT_HIGHLIGHT_KINDS = { - DocumentHighlightKind.Text: "text", - DocumentHighlightKind.Read: "read", - DocumentHighlightKind.Write: "write" -} # type: Dict[DocumentHighlightKind, str] - -DOCUMENT_HIGHLIGHT_KIND_SCOPES = { - DocumentHighlightKind.Text: "region.bluish markup.highlight.text.lsp", - DocumentHighlightKind.Read: "region.greenish markup.highlight.read.lsp", - DocumentHighlightKind.Write: "region.yellowish markup.highlight.write.lsp" -} # type: Dict[DocumentHighlightKind, str] - -SEMANTIC_TOKENS_MAP = { - "namespace": "variable.other.namespace.lsp", - "namespace.declaration": "entity.name.namespace.lsp", - "namespace.definition": "entity.name.namespace.lsp", - "type": "storage.type.lsp", - "type.declaration": "entity.name.type.lsp", - "type.defaultLibrary": "support.type.lsp", - "type.definition": "entity.name.type.lsp", - "class": "storage.type.class.lsp", - "class.declaration": "entity.name.class.lsp", - "class.defaultLibrary": "support.class.lsp", - "class.definition": "entity.name.class.lsp", - "enum": "variable.other.enum.lsp", - "enum.declaration": "entity.name.enum.lsp", - "enum.definition": "entity.name.enum.lsp", - "interface": "entity.other.inherited-class.lsp", - "interface.declaration": "entity.name.interface.lsp", - "interface.definition": "entity.name.interface.lsp", - "struct": "storage.type.struct.lsp", - "struct.declaration": "entity.name.struct.lsp", - "struct.defaultLibrary": "support.struct.lsp", - "struct.definition": "entity.name.struct.lsp", - "typeParameter": "variable.parameter.generic.lsp", - "parameter": "variable.parameter.lsp", - "variable": "variable.other.lsp", - "variable.readonly": "variable.other.constant.lsp", - "property": "variable.other.property.lsp", - "enumMember": "constant.other.enum.lsp", - "event": "entity.name.function.lsp", - "function": "variable.function.lsp", - "function.declaration": "entity.name.function.lsp", - "function.defaultLibrary": "support.function.builtin.lsp", - "function.definition": "entity.name.function.lsp", - "method": "variable.function.lsp", - "method.declaration": "entity.name.function.lsp", - "method.defaultLibrary": "support.function.builtin.lsp", - "method.definition": "entity.name.function.lsp", - "macro": "variable.macro.lsp", - "macro.declaration": "entity.name.macro.lsp", - "macro.defaultLibrary": "support.macro.lsp", - "macro.definition": "entity.name.macro.lsp", - "keyword": "keyword.lsp", - "modifier": "storage.modifier.lsp", - "comment": "comment.lsp", - "comment.documentation": "comment.block.documentation.lsp", - "string": "string.lsp", - "number": "constant.numeric.lsp", - "regexp": "string.regexp.lsp", - "operator": "keyword.operator.lsp", - "decorator": "variable.annotation.lsp", -} - class DiagnosticSeverityData: diff --git a/plugin/core/windows.py b/plugin/core/windows.py index 589f2f12d..cd1e8b578 100644 --- a/plugin/core/windows.py +++ b/plugin/core/windows.py @@ -22,11 +22,10 @@ from .settings import client_configs from .settings import userprefs from .transports import create_transport -from .tree_view import TreeViewSheet from .types import ClientConfig from .types import matches_pattern from .types import sublime_pattern_to_glob -from .typing import Optional, Any, Dict, Deque, List, Generator, Tuple +from .typing import Optional, Any, Dict, Deque, List, Generator, Tuple, TYPE_CHECKING from .url import parse_uri from .views import extract_variables from .views import format_diagnostic_for_panel @@ -46,6 +45,10 @@ import threading +if TYPE_CHECKING: + from tree_view import TreeViewSheet + + _NO_DIAGNOSTICS_PLACEHOLDER = " No diagnostics. Well done!" diff --git a/plugin/diagnostics.py b/plugin/diagnostics.py index 6c917ff40..0e0ea5831 100644 --- a/plugin/diagnostics.py +++ b/plugin/diagnostics.py @@ -1,8 +1,8 @@ +from .core.constants import DIAGNOSTIC_KINDS from .core.protocol import Diagnostic from .core.protocol import DiagnosticSeverity from .core.settings import userprefs from .core.typing import List, Tuple -from .core.views import DIAGNOSTIC_KINDS from .core.views import diagnostic_severity from .core.views import format_diagnostics_for_annotation import sublime diff --git a/plugin/documents.py b/plugin/documents.py index 150a65ada..13de9b713 100644 --- a/plugin/documents.py +++ b/plugin/documents.py @@ -2,6 +2,8 @@ from .code_actions import CodeActionOrCommand from .code_actions import CodeActionsByConfigName from .completion import QueryCompletionsTask +from .core.constants import DOCUMENT_HIGHLIGHT_KIND_NAMES +from .core.constants import DOCUMENT_HIGHLIGHT_KIND_SCOPES from .core.constants import HOVER_ENABLED_KEY from .core.logging import debug from .core.open import open_in_browser @@ -38,8 +40,6 @@ from .core.url import parse_uri from .core.url import view_to_uri from .core.views import diagnostic_severity -from .core.views import DOCUMENT_HIGHLIGHT_KIND_SCOPES -from .core.views import DOCUMENT_HIGHLIGHT_KINDS from .core.views import first_selection_region from .core.views import format_code_actions_for_quick_panel from .core.views import format_diagnostic_for_html @@ -764,7 +764,7 @@ def _resolve_visible_code_lenses_async(self) -> None: # --- textDocument/documentHighlight ------------------------------------------------------------------------------- def _highlights_key(self, kind: DocumentHighlightKind, multiline: bool) -> str: - return "lsp_highlight_{}{}".format(DOCUMENT_HIGHLIGHT_KINDS[kind], "m" if multiline else "s") + return "lsp_highlight_{}{}".format(DOCUMENT_HIGHLIGHT_KIND_NAMES[kind], "m" if multiline else "s") def _clear_highlight_regions(self) -> None: for kind in [DocumentHighlightKind.Text, DocumentHighlightKind.Read, DocumentHighlightKind.Write]: diff --git a/plugin/goto_diagnostic.py b/plugin/goto_diagnostic.py index 49b4aa918..d83341bcf 100644 --- a/plugin/goto_diagnostic.py +++ b/plugin/goto_diagnostic.py @@ -1,3 +1,4 @@ +from .core.constants import DIAGNOSTIC_KINDS from .core.diagnostics_storage import is_severity_included from .core.diagnostics_storage import ParsedUri from .core.paths import project_base_dir @@ -13,7 +14,6 @@ from .core.types import ClientConfig from .core.typing import Any, Dict, Iterator, List, Optional, Tuple, Union from .core.url import parse_uri, unparse_uri -from .core.views import DIAGNOSTIC_KINDS from .core.views import diagnostic_severity from .core.views import format_diagnostic_for_html from .core.views import format_diagnostic_source_and_code diff --git a/plugin/hierarchy.py b/plugin/hierarchy.py index 9ff07ae93..9f7f2e260 100644 --- a/plugin/hierarchy.py +++ b/plugin/hierarchy.py @@ -1,3 +1,4 @@ +from .core.constants import SYMBOL_KINDS from .core.paths import simple_path from .core.promise import Promise from .core.protocol import CallHierarchyIncomingCall @@ -13,14 +14,13 @@ from .core.registry import get_position from .core.registry import LspTextCommand from .core.registry import LspWindowCommand -from .core.registry import new_tree_view_sheet from .core.sessions import Session +from .core.tree_view import new_tree_view_sheet from .core.tree_view import TreeDataProvider from .core.tree_view import TreeItem from .core.typing import Callable, List, Optional, TypedDict, Union from .core.typing import cast from .core.views import make_command_link -from .core.views import SYMBOL_KINDS from .core.views import text_document_position_params from abc import ABCMeta from abc import abstractmethod diff --git a/plugin/hover.py b/plugin/hover.py index 4397c8fd6..51b72bdda 100644 --- a/plugin/hover.py +++ b/plugin/hover.py @@ -369,10 +369,10 @@ def on_select(targets: List[str], idx: int) -> None: position = {"line": row, "character": col_utf16} # type: Position r = {"start": position, "end": position} # type: Range sublime.set_timeout_async(partial(session.open_uri_async, uri, r)) - elif parse_uri(href)[0].lower() not in ("", "http", "https"): - sublime.set_timeout_async(partial(self.try_open_custom_uri_async, href)) - else: + elif parse_uri(href)[0].lower() in ("", "http", "https"): open_in_browser(href) + else: + sublime.set_timeout_async(partial(self.try_open_custom_uri_async, href)) def handle_code_action_select(self, config_name: str, actions: List[CodeActionOrCommand], index: int) -> None: if index == -1: diff --git a/plugin/locationpicker.py b/plugin/locationpicker.py index 16bac6662..2802d5118 100644 --- a/plugin/locationpicker.py +++ b/plugin/locationpicker.py @@ -1,3 +1,4 @@ +from .core.constants import SublimeKind from .core.logging import debug from .core.protocol import DocumentUri from .core.protocol import Location @@ -7,7 +8,6 @@ from .core.typing import Union, List, Optional, Tuple from .core.views import get_uri_and_position_from_location from .core.views import location_to_human_readable -from .core.views import SublimeKind from .core.views import to_encoded_filename from urllib.request import url2pathname import functools diff --git a/plugin/session_view.py b/plugin/session_view.py index 0e775acb9..f9df622c3 100644 --- a/plugin/session_view.py +++ b/plugin/session_view.py @@ -1,6 +1,7 @@ from .code_lens import CodeLensView from .code_lens import LspToggleCodeLensesCommand from .core.active_request import ActiveRequest +from .core.constants import DOCUMENT_HIGHLIGHT_KIND_NAMES from .core.constants import HOVER_ENABLED_KEY from .core.constants import HOVER_HIGHLIGHT_KEY from .core.constants import HOVER_PROVIDER_COUNT_KEY @@ -135,13 +136,12 @@ def _initialize_region_keys(self) -> None: r = [sublime.Region(0, 0)] document_highlight_style = userprefs().document_highlight_style hover_highlight_style = userprefs().hover_highlight_style - document_highlight_kinds = ["text", "read", "write"] line_modes = ["m", "s"] self.view.add_regions(self.CODE_ACTIONS_KEY, r) # code actions lightbulb icon should always be on top for key in range(1, 100): self.view.add_regions("lsp_semantic_{}".format(key), r) if document_highlight_style in ("background", "fill"): - for kind in document_highlight_kinds: + for kind in DOCUMENT_HIGHLIGHT_KIND_NAMES.values(): for mode in line_modes: self.view.add_regions("lsp_highlight_{}{}".format(kind, mode), r) if hover_highlight_style in ("background", "fill"): @@ -158,7 +158,7 @@ def _initialize_region_keys(self) -> None: for mode in line_modes: self.view.add_regions("lsp{}d{}{}_underline".format(self.session.config.name, mode, severity), r) if document_highlight_style in ("underline", "stippled"): - for kind in document_highlight_kinds: + for kind in DOCUMENT_HIGHLIGHT_KIND_NAMES.values(): for mode in line_modes: self.view.add_regions("lsp_highlight_{}{}".format(kind, mode), r) if hover_highlight_style in ("underline", "stippled"): diff --git a/plugin/symbols.py b/plugin/symbols.py index 1799f44ca..ad6186d8c 100644 --- a/plugin/symbols.py +++ b/plugin/symbols.py @@ -1,4 +1,6 @@ import weakref +from .core.constants import SublimeKind +from .core.constants import SYMBOL_KINDS from .core.protocol import DocumentSymbol from .core.protocol import DocumentSymbolParams from .core.protocol import Point @@ -11,8 +13,6 @@ from .core.typing import Any, List, Optional, Tuple, Dict, Union, cast from .core.views import offset_to_point from .core.views import range_to_region -from .core.views import SublimeKind -from .core.views import SYMBOL_KINDS from .core.views import text_document_identifier from .goto_diagnostic import PreselectedListInputHandler import os