Coverage for src/debputy/lsp/text_util.py: 67%
79 statements
« prev ^ index » next coverage.py v7.6.0, created at 2025-01-27 13:59 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2025-01-27 13:59 +0000
1from typing import List, Optional, Sequence, Union, Iterable, TYPE_CHECKING
3from debputy.commands.debputy_cmd.output import MAN_URL_REWRITE
4from debputy.lsprotocol.types import (
5 TextEdit,
6 Position,
7 Range,
8 WillSaveTextDocumentParams,
9 DocumentFormattingParams,
10)
12from debputy.linting.lint_util import LinterPositionCodec
14try:
15 from debputy.lsp.vendoring._deb822_repro.locatable import (
16 Position as TEPosition,
17 Range as TERange,
18 )
19 from debputy.lsp.debputy_ls import DebputyLanguageServer
20except ImportError:
21 pass
23try:
24 from pygls.server import LanguageServer
25 from pygls.workspace import TextDocument, PositionCodec
26except ImportError:
27 pass
29if TYPE_CHECKING:
30 LintCapablePositionCodec = Union[LinterPositionCodec, PositionCodec]
31else:
32 LintCapablePositionCodec = LinterPositionCodec
35def markdown_urlify(uri: str) -> str:
36 if uri.startswith("man:"): 36 ↛ 44line 36 didn't jump to line 44 because the condition on line 36 was always true
37 m = MAN_URL_REWRITE.match(uri)
38 if m: 38 ↛ 42line 38 didn't jump to line 42 because the condition on line 38 was always true
39 page, section = m.groups()
40 link_url = f"https://manpages.debian.org/{page}.{section}"
41 return f"[{uri}]({link_url})"
42 return uri
44 return f"<{uri}>"
47def normalize_dctrl_field_name(f: str) -> str:
48 if not f or not f.startswith(("x", "X")):
49 return f
50 i = 0
51 for i in range(1, len(f)): 51 ↛ 57line 51 didn't jump to line 57 because the loop on line 51 didn't complete
52 if f[i] == "-":
53 i += 1
54 break
55 if f[i] not in ("b", "B", "s", "S", "c", "C"): 55 ↛ 56line 55 didn't jump to line 56 because the condition on line 55 was never true
56 return f
57 assert i > 0
58 return f[i:]
61def on_save_trim_end_of_line_whitespace(
62 ls: "LanguageServer",
63 params: Union[WillSaveTextDocumentParams, DocumentFormattingParams],
64) -> Optional[Sequence[TextEdit]]:
65 doc = ls.workspace.get_text_document(params.text_document.uri)
66 return trim_end_of_line_whitespace(doc.position_codec, doc.lines)
69def trim_end_of_line_whitespace(
70 position_codec: "LintCapablePositionCodec",
71 lines: List[str],
72 *,
73 line_range: Optional[Iterable[int]] = None,
74 line_relative_line_no: int = 0,
75) -> Optional[Sequence[TextEdit]]:
76 edits = []
77 if line_range is None:
78 line_range = range(0, len(lines))
79 for line_no in line_range:
80 orig_line = lines[line_no]
81 orig_len = len(orig_line)
82 if orig_line.endswith("\n"):
83 orig_len -= 1
84 stripped_len = len(orig_line.rstrip())
85 if stripped_len == orig_len:
86 continue
88 stripped_len_client_off = position_codec.client_num_units(
89 orig_line[:stripped_len]
90 )
91 orig_len_client_off = position_codec.client_num_units(orig_line[:orig_len])
92 edit_range = position_codec.range_to_client_units(
93 lines,
94 Range(
95 Position(
96 line_no + line_relative_line_no,
97 stripped_len_client_off,
98 ),
99 Position(
100 line_no + line_relative_line_no,
101 orig_len_client_off,
102 ),
103 ),
104 )
105 edits.append(
106 TextEdit(
107 edit_range,
108 "",
109 )
110 )
112 return edits
115class SemanticTokensState:
116 __slots__ = ("ls", "doc", "lines", "tokens", "_previous_line", "_previous_col")
118 def __init__(
119 self,
120 ls: "DebputyLanguageServer",
121 doc: "TextDocument",
122 lines: List[str],
123 tokens: List[int],
124 ) -> None:
125 self.ls = ls
126 self.doc = doc
127 self.lines = lines
128 self.tokens = tokens
129 self._previous_line = 0
130 self._previous_col = 0
132 def emit_token(
133 self,
134 start_pos: Position,
135 len_client_units: int,
136 token_code: int,
137 *,
138 token_modifiers: int = 0,
139 ) -> None:
140 line_delta = start_pos.line - self._previous_line
141 self._previous_line = start_pos.line
142 previous_col = self._previous_col
144 if line_delta:
145 previous_col = 0
147 column_delta = start_pos.character - previous_col
148 self._previous_col = start_pos.character
150 tokens = self.tokens
151 tokens.append(line_delta) # Line delta
152 tokens.append(column_delta) # Token column delta
153 tokens.append(len_client_units) # Token length
154 tokens.append(token_code)
155 tokens.append(token_modifiers)