From 65b32375c212ad26f37c8269c57ffde3f873283d Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Sun, 17 May 2026 18:41:44 +0200 Subject: [PATCH 1/7] add additional exports to public API --- plugin/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugin/__init__.py b/plugin/__init__.py index 8c7643996..b462ff86f 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -46,6 +46,7 @@ from .core.url import parse_uri from .core.url import uri_to_filename # deprecated from .core.version import __version__ +from .core.views import position_to_offset from .core.views import uri_from_view from .core.workspace import WorkspaceFolder @@ -92,6 +93,7 @@ 'matches_pattern', 'notification_handler', 'parse_uri', + 'position_to_offset', 'register_file_watcher_implementation', 'register_plugin', 'request_handler', From 95db343aa7a21e91be0c614dfd43ec207aa7026c Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Sun, 17 May 2026 22:15:40 +0200 Subject: [PATCH 2/7] Point --- plugin/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugin/__init__.py b/plugin/__init__.py index b462ff86f..eb9bc16c4 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -28,6 +28,7 @@ from .core.protocol import ClientResponse from .core.protocol import Error from .core.protocol import Notification +from .core.protocol import Point from .core.protocol import Request from .core.protocol import Response from .core.protocol import ServerNotification @@ -74,6 +75,7 @@ 'OnPreStartContext', 'PackagedTask', 'PluginStartError', + 'Point', 'Promise', 'Request', 'Response', From 75a19ae7ab7b523ba1c28f7a2b0409e8d703a7d9 Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Fri, 22 May 2026 22:32:51 +0200 Subject: [PATCH 3/7] more exports --- plugin/__init__.py | 20 ++++++++++++++++++-- plugin/core/protocol.py | 8 +++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/plugin/__init__.py b/plugin/__init__.py index eb9bc16c4..d99c38fc8 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -28,12 +28,12 @@ from .core.protocol import ClientResponse from .core.protocol import Error from .core.protocol import Notification -from .core.protocol import Point from .core.protocol import Request from .core.protocol import Response from .core.protocol import ServerNotification from .core.protocol import ServerRequest from .core.protocol import ServerResponse +from .core.protocol import TextPosition from .core.registry import LspTextCommand from .core.registry import LspWindowCommand from .core.sessions import Session @@ -47,9 +47,17 @@ from .core.url import parse_uri from .core.url import uri_to_filename # deprecated from .core.version import __version__ +from .core.views import first_selection_region +from .core.views import point_to_offset +from .core.views import position from .core.views import position_to_offset +from .core.views import region_to_range +from .core.views import text_document_identifier +from .core.views import text_document_position_params from .core.views import uri_from_view from .core.workspace import WorkspaceFolder +from .execute_command import LspExecuteCommand +from .locationpicker import LocationPicker # This is the public API for LSP-* packages __all__ = [ @@ -67,6 +75,8 @@ 'FileWatcherEventType', 'FileWatcherProtocol', 'IsApplicableContext', + 'LocationPicker', + 'LspExecuteCommand', 'LspPlugin', 'LspTextCommand', 'LspWindowCommand', @@ -75,7 +85,6 @@ 'OnPreStartContext', 'PackagedTask', 'PluginStartError', - 'Point', 'Promise', 'Request', 'Response', @@ -85,6 +94,7 @@ 'Session', 'SessionBufferProtocol', 'SessionViewProtocol', + 'TextPosition', 'TransportWrapper', 'WorkspaceFolder', '__version__', @@ -92,13 +102,19 @@ 'command_handler', 'css', 'filename_to_uri', + 'first_selection_region', 'matches_pattern', 'notification_handler', 'parse_uri', + 'point_to_offset', + 'position', 'position_to_offset', + 'region_to_range', 'register_file_watcher_implementation', 'register_plugin', 'request_handler', + 'text_document_identifier', + 'text_document_position_params', 'unregister_plugin', 'uri_from_view', 'uri_handler', diff --git a/plugin/core/protocol.py b/plugin/core/protocol.py index a4f6b9829..e9bb7c2b0 100644 --- a/plugin/core/protocol.py +++ b/plugin/core/protocol.py @@ -12,6 +12,7 @@ from typing import TypedDict from typing import TypeVar from typing import Union +from typing_extensions import deprecated from typing_extensions import NotRequired from typing_extensions import TypeAlias @@ -401,7 +402,7 @@ def to_payload(self) -> NotificationMessage: @total_ordering -class Point: +class TextPosition: def __init__(self, row: int, col: int) -> None: self.row = int(row) self.col = int(col) # in UTF-16 @@ -433,6 +434,11 @@ def to_lsp(self) -> Position: } +@deprecated('Use TextPosition instead') +class Point(TextPosition): + pass + + class ResponseError(TypedDict): code: int message: str From a97d5c84d9db9f341c6e9000a7ba7b1c987a2951 Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Fri, 22 May 2026 22:45:51 +0200 Subject: [PATCH 4/7] add offset_to_text_position --- plugin/__init__.py | 4 ++-- plugin/core/views.py | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/plugin/__init__.py b/plugin/__init__.py index d99c38fc8..8e09c7d47 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -48,8 +48,8 @@ from .core.url import uri_to_filename # deprecated from .core.version import __version__ from .core.views import first_selection_region +from .core.views import offset_to_text_position from .core.views import point_to_offset -from .core.views import position from .core.views import position_to_offset from .core.views import region_to_range from .core.views import text_document_identifier @@ -105,9 +105,9 @@ 'first_selection_region', 'matches_pattern', 'notification_handler', + 'offset_to_text_position', 'parse_uri', 'point_to_offset', - 'position', 'position_to_offset', 'region_to_range', 'register_file_watcher_implementation', diff --git a/plugin/core/views.py b/plugin/core/views.py index 0dc0773fd..1ef42ac45 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -66,6 +66,7 @@ from typing import Iterable from typing import Sequence from typing import TYPE_CHECKING +from typing_extensions import deprecated import html import itertools import linecache @@ -180,10 +181,15 @@ def offset_to_point(view: sublime.View, offset: int) -> Point: return Point(*view.rowcol_utf16(offset)) -def position(view: sublime.View, offset: int) -> Position: +def offset_to_text_position(view: sublime.View, offset: int) -> Position: return offset_to_point(view, offset).to_lsp() +@deprecated('Use offset_to_text_position() instead') +def position(view: sublime.View, offset: int) -> Position: + return offset_to_text_position(view, offset) + + def position_to_offset(position: Position, view: sublime.View) -> int: return point_to_offset(Point.from_lsp(position), view) @@ -306,7 +312,7 @@ def versioned_text_document_identifier(view: sublime.View, version: int) -> Vers def text_document_position_params(view: sublime.View, location: int) -> TextDocumentPositionParams: - return {"textDocument": text_document_identifier(view), "position": position(view, location)} + return {"textDocument": text_document_identifier(view), "position": offset_to_text_position(view, location)} def did_open_text_document_params(view: sublime.View, language_id: str) -> DidOpenTextDocumentParams: @@ -439,7 +445,7 @@ def text_document_ranges_formatting( def selection_range_params(view: sublime.View) -> SelectionRangeParams: return { "textDocument": text_document_identifier(view), - "positions": [position(view, r.b) for r in view.sel()] + "positions": [offset_to_text_position(view, r.b) for r in view.sel()] } From 72bf5b4736074b606fdc7469b3d837fdf8e6b4aa Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Fri, 22 May 2026 23:57:39 +0200 Subject: [PATCH 5/7] Point -> TextPosition --- plugin/core/protocol.py | 8 ++++---- plugin/core/views.py | 10 +++++----- plugin/references.py | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plugin/core/protocol.py b/plugin/core/protocol.py index e9bb7c2b0..94322a26e 100644 --- a/plugin/core/protocol.py +++ b/plugin/core/protocol.py @@ -411,12 +411,12 @@ def __repr__(self) -> str: return f"{self.row}:{self.col}" def __eq__(self, other: object) -> bool: - if not isinstance(other, Point): + if not isinstance(other, self.__class__): return NotImplemented return self.row == other.row and self.col == other.col def __lt__(self, other: object) -> bool: - if not isinstance(other, Point): + if not isinstance(other, self.__class__): return NotImplemented return (self.row, self.col) < (other.row, other.col) @@ -424,8 +424,8 @@ def __hash__(self) -> int: return hash(self.__repr__()) @classmethod - def from_lsp(cls, point: Position) -> Point: - return Point(point['line'], point['character']) + def from_lsp(cls, position: Position) -> TextPosition: + return TextPosition(position['line'], position['character']) def to_lsp(self) -> Position: return { diff --git a/plugin/core/views.py b/plugin/core/views.py index 1ef42ac45..a6a7b38e2 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -49,8 +49,8 @@ from .constants import SublimeKind from .css import css as lsp_css from .protocol import Notification -from .protocol import Point from .protocol import Request +from .protocol import TextPosition from .settings import userprefs from .url import encode_code_action_uri from .url import parse_uri @@ -171,14 +171,14 @@ def extract_variables(window: sublime.Window) -> dict[str, str]: return variables -def point_to_offset(point: Point, view: sublime.View) -> int: +def point_to_offset(point: TextPosition, view: sublime.View) -> int: # @see https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#position # If the character value is greater than the line length it defaults back to the line length. return view.text_point_utf16(point.row, point.col, clamp_column=True) -def offset_to_point(view: sublime.View, offset: int) -> Point: - return Point(*view.rowcol_utf16(offset)) +def offset_to_point(view: sublime.View, offset: int) -> TextPosition: + return TextPosition(*view.rowcol_utf16(offset)) def offset_to_text_position(view: sublime.View, offset: int) -> Position: @@ -191,7 +191,7 @@ def position(view: sublime.View, offset: int) -> Position: def position_to_offset(position: Position, view: sublime.View) -> int: - return point_to_offset(Point.from_lsp(position), view) + return point_to_offset(TextPosition.from_lsp(position), view) def get_symbol_kind_from_scope(scope_name: str) -> SublimeKind: diff --git a/plugin/references.py b/plugin/references.py index 4ba0d18ff..50b4a4b5d 100644 --- a/plugin/references.py +++ b/plugin/references.py @@ -1,8 +1,8 @@ from __future__ import annotations from .core.constants import RegionKey -from .core.protocol import Point from .core.protocol import Request +from .core.protocol import TextPosition from .core.registry import get_position from .core.registry import LspTextCommand from .core.registry import windows @@ -181,7 +181,7 @@ def _show_references_in_quick_panel( placeholder = "References to " + word kind = get_symbol_kind_from_scope(self.view.scope_name(position)) index = 0 - locations.sort(key=lambda location: (location['uri'], Point.from_lsp(location['range']['start']))) + locations.sort(key=lambda location: (location['uri'], TextPosition.from_lsp(location['range']['start']))) if len(selection): pt = selection[0].b view_filename = self.view.file_name() @@ -244,13 +244,13 @@ def _group_locations_by_uri( window: sublime.Window, config: ClientConfig, locations: list[Location] -) -> dict[str, list[tuple[Point, str]]]: +) -> dict[str, list[tuple[TextPosition, str]]]: """Return a dictionary that groups locations by the URI it belongs.""" - grouped_locations: dict[str, list[tuple[Point, str]]] = {} + grouped_locations: dict[str, list[tuple[TextPosition, str]]] = {} for location in locations: uri, position = get_uri_and_position_from_location(location) file_path = config.map_server_uri_to_client_path(uri) - point = Point.from_lsp(position) + point = TextPosition.from_lsp(position) # get line of the reference, to showcase its use reference_line = get_line(window, file_path, point.row) if grouped_locations.get(file_path) is None: From 9db873f48ea8b1856a6746353f732b44c3244736 Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Sat, 23 May 2026 00:09:43 +0200 Subject: [PATCH 6/7] convert to dataclass --- plugin/core/protocol.py | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/plugin/core/protocol.py b/plugin/core/protocol.py index 94322a26e..f07893c27 100644 --- a/plugin/core/protocol.py +++ b/plugin/core/protocol.py @@ -2,7 +2,7 @@ from __future__ import annotations from ...protocol import * # For backward compatibility with LSP packages. # noqa: F403 -from functools import total_ordering +from dataclasses import dataclass from typing import Any from typing import Callable from typing import Generic @@ -401,31 +401,17 @@ def to_payload(self) -> NotificationMessage: return payload -@total_ordering +@dataclass(frozen=True, order=True) class TextPosition: - def __init__(self, row: int, col: int) -> None: - self.row = int(row) - self.col = int(col) # in UTF-16 + row: int + col: int # in UTF-16 def __repr__(self) -> str: return f"{self.row}:{self.col}" - def __eq__(self, other: object) -> bool: - if not isinstance(other, self.__class__): - return NotImplemented - return self.row == other.row and self.col == other.col - - def __lt__(self, other: object) -> bool: - if not isinstance(other, self.__class__): - return NotImplemented - return (self.row, self.col) < (other.row, other.col) - - def __hash__(self) -> int: - return hash(self.__repr__()) - @classmethod def from_lsp(cls, position: Position) -> TextPosition: - return TextPosition(position['line'], position['character']) + return cls(position['line'], position['character']) def to_lsp(self) -> Position: return { From ff93322c53a2ef2be386a528db84203ad0093630 Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Wed, 22 Jul 2026 21:56:13 +0200 Subject: [PATCH 7/7] offset_to_text_position -> offset_to_position --- plugin/__init__.py | 4 ++-- plugin/core/views.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugin/__init__.py b/plugin/__init__.py index 8e09c7d47..4ec30c111 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -48,7 +48,7 @@ from .core.url import uri_to_filename # deprecated from .core.version import __version__ from .core.views import first_selection_region -from .core.views import offset_to_text_position +from .core.views import offset_to_position from .core.views import point_to_offset from .core.views import position_to_offset from .core.views import region_to_range @@ -105,7 +105,7 @@ 'first_selection_region', 'matches_pattern', 'notification_handler', - 'offset_to_text_position', + 'offset_to_position', 'parse_uri', 'point_to_offset', 'position_to_offset', diff --git a/plugin/core/views.py b/plugin/core/views.py index 5b3edbdd9..6226f9709 100644 --- a/plugin/core/views.py +++ b/plugin/core/views.py @@ -193,13 +193,13 @@ def offset_to_point(view: sublime.View, offset: int) -> TextPosition: return TextPosition(*view.rowcol_utf16(offset)) -def offset_to_text_position(view: sublime.View, offset: int) -> Position: +def offset_to_position(view: sublime.View, offset: int) -> Position: return offset_to_point(view, offset).to_lsp() -@deprecated('Use offset_to_text_position() instead') +@deprecated('Use offset_to_position() instead') def position(view: sublime.View, offset: int) -> Position: - return offset_to_text_position(view, offset) + return offset_to_position(view, offset) def position_to_offset(position: Position, view: sublime.View) -> int: @@ -324,7 +324,7 @@ def versioned_text_document_identifier(view: sublime.View, version: int) -> Vers def text_document_position_params(view: sublime.View, location: int) -> TextDocumentPositionParams: - return {"textDocument": text_document_identifier(view), "position": offset_to_text_position(view, location)} + return {"textDocument": text_document_identifier(view), "position": offset_to_position(view, location)} def did_open_text_document_params(view: sublime.View, language_id: str) -> DidOpenTextDocumentParams: @@ -457,7 +457,7 @@ def text_document_ranges_formatting( def selection_range_params(view: sublime.View) -> SelectionRangeParams: return { "textDocument": text_document_identifier(view), - "positions": [offset_to_text_position(view, r.b) for r in view.sel()] + "positions": [offset_to_position(view, r.b) for r in view.sel()] }