Skip to content

Commit

Permalink
refactor: server Position class
Browse files Browse the repository at this point in the history
Still no actual support for client encodings apart from UTF-16
  • Loading branch information
tombh committed Sep 24, 2023
1 parent d719b17 commit e271ba5
Show file tree
Hide file tree
Showing 6 changed files with 376 additions and 331 deletions.
14 changes: 11 additions & 3 deletions pygls/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and #
# limitations under the License. #
############################################################################
from __future__ import annotations
import asyncio
import enum
import functools
Expand All @@ -27,7 +28,11 @@
from concurrent.futures import Future
from functools import lru_cache, partial
from itertools import zip_longest
from typing import Any, Callable, List, Optional, Type, TypeVar, Union
from typing import Any, Callable, List, Optional, Type, TypeVar, Union, TYPE_CHECKING

if TYPE_CHECKING:
from pygls.server import Server


import attrs
from cattrs.errors import ClassValidationError
Expand Down Expand Up @@ -244,7 +249,7 @@ class JsonRPCProtocol(asyncio.Protocol):

VERSION = "2.0"

def __init__(self, server, converter):
def __init__(self, server: Server, converter):
self._server = server
self._converter = converter

Expand Down Expand Up @@ -817,7 +822,10 @@ def lsp_initialize(self, params: InitializeParams) -> InitializeResult:
# Initialize the workspace
workspace_folders = params.workspace_folders or []
self._workspace = Workspace(
root_uri, text_document_sync_kind, workspace_folders
root_uri,
text_document_sync_kind,
workspace_folders,
self.server_capabilities.position_encoding,
)

self.trace = TraceValues.Off
Expand Down
64 changes: 33 additions & 31 deletions pygls/workspace/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,12 @@
import logging
import os
import re
from typing import List, Optional, Pattern
from typing import List, Optional, Pattern, Union

from lsprotocol.types import (
Position,
TextDocumentContentChangeEvent,
TextDocumentContentChangeEvent_Type1,
TextDocumentSyncKind,
)
from lsprotocol import types

from pygls.uris import to_fs_path
from pygls.workspace.position import (
position_from_utf16,
range_from_utf16,
utf16_num_units,
)
from pygls.workspace.position import Position

# TODO: this is not the best e.g. we capture numbers
RE_END_WORD = re.compile("^[A-Za-z_0-9]*")
Expand All @@ -51,7 +42,10 @@ def __init__(
version: Optional[int] = None,
language_id: Optional[str] = None,
local: bool = True,
sync_kind: TextDocumentSyncKind = TextDocumentSyncKind.Incremental,
sync_kind: types.TextDocumentSyncKind = types.TextDocumentSyncKind.Incremental,
position_encoding: Optional[
Union[types.PositionEncodingKind, str]
] = types.PositionEncodingKind.Utf16,
):
self.uri = uri
self.version = version
Expand All @@ -65,22 +59,26 @@ def __init__(
self._local = local
self._source = source

self._is_sync_kind_full = sync_kind == TextDocumentSyncKind.Full
self._is_sync_kind_incremental = sync_kind == TextDocumentSyncKind.Incremental
self._is_sync_kind_none = sync_kind == TextDocumentSyncKind.None_
self._is_sync_kind_full = sync_kind == types.TextDocumentSyncKind.Full
self._is_sync_kind_incremental = (
sync_kind == types.TextDocumentSyncKind.Incremental
)
self._is_sync_kind_none = sync_kind == types.TextDocumentSyncKind.None_

self.position = Position(encoding=position_encoding)

def __str__(self):
return str(self.uri)

def _apply_incremental_change(
self, change: TextDocumentContentChangeEvent_Type1
self, change: types.TextDocumentContentChangeEvent_Type1
) -> None:
"""Apply an ``Incremental`` text change to the document"""
lines = self.lines
text = change.text
change_range = change.range

range = range_from_utf16(lines, change_range) # type: ignore
range = self.position.range_from_client_units(lines, change_range)
start_line = range.start.line
start_col = range.start.character
end_line = range.end.line
Expand Down Expand Up @@ -114,18 +112,18 @@ def _apply_incremental_change(

self._source = new.getvalue()

def _apply_full_change(self, change: TextDocumentContentChangeEvent) -> None:
def _apply_full_change(self, change: types.TextDocumentContentChangeEvent) -> None:
"""Apply a ``Full`` text change to the document."""
self._source = change.text

def _apply_none_change(self, _: TextDocumentContentChangeEvent) -> None:
def _apply_none_change(self, _: types.TextDocumentContentChangeEvent) -> None:
"""Apply a ``None`` text change to the document
Currently does nothing, provided for consistency.
"""
pass

def apply_change(self, change: TextDocumentContentChangeEvent) -> None:
def apply_change(self, change: types.TextDocumentContentChangeEvent) -> None:
"""Apply a text change to a document, considering TextDocumentSyncKind
Performs either
Expand All @@ -142,7 +140,7 @@ def apply_change(self, change: TextDocumentContentChangeEvent) -> None:
content update client requests in the pygls Python library.
"""
if isinstance(change, TextDocumentContentChangeEvent_Type1):
if isinstance(change, types.TextDocumentContentChangeEvent_Type1):
if self._is_sync_kind_incremental:
self._apply_incremental_change(change)
return
Expand All @@ -164,12 +162,14 @@ def apply_change(self, change: TextDocumentContentChangeEvent) -> None:
def lines(self) -> List[str]:
return self.source.splitlines(True)

def offset_at_position(self, position: Position) -> int:
"""Return the character offset pointed at by the given position."""
def offset_at_position(self, client_position: types.Position) -> int:
"""Return the character offset pointed at by the given client_position."""
lines = self.lines
pos = position_from_utf16(lines, position)
row, col = pos.line, pos.character
return col + sum(utf16_num_units(line) for line in lines[:row])
server_position = self.position.position_from_client_units(
lines, client_position
)
row, col = server_position.line, server_position.character
return col + sum(self.position.utf16_num_units(line) for line in lines[:row])

@property
def source(self) -> str:
Expand All @@ -180,7 +180,7 @@ def source(self) -> str:

def word_at_position(
self,
position: Position,
client_position: types.Position,
re_start_word: Pattern[str] = RE_START_WORD,
re_end_word: Pattern[str] = RE_END_WORD,
) -> str:
Expand Down Expand Up @@ -214,11 +214,13 @@ def word_at_position(
The word (obtained by concatenating the two matches) at position.
"""
lines = self.lines
if position.line >= len(lines):
if client_position.line >= len(lines):
return ""

pos = position_from_utf16(lines, position)
row, col = pos.line, pos.character
server_position = self.position.position_from_client_units(
lines, client_position
)
row, col = server_position.line, server_position.character
line = lines[row]
# Split word in two
start = line[:col]
Expand Down
Loading

0 comments on commit e271ba5

Please sign in to comment.