modelable 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of modelable might be problematic. Click here for more details.
- modelable/__init__.py +1 -0
- modelable/__main__.py +3 -0
- modelable/_pydantic_py314_compat.py +31 -0
- modelable/cli.py +41 -0
- modelable/commands/__init__.py +1 -0
- modelable/commands/apicurio.py +84 -0
- modelable/commands/codegen.py +241 -0
- modelable/commands/common.py +43 -0
- modelable/commands/compile.py +237 -0
- modelable/commands/create.py +164 -0
- modelable/commands/diff.py +82 -0
- modelable/commands/graph.py +53 -0
- modelable/commands/llm.py +564 -0
- modelable/commands/lsp.py +15 -0
- modelable/commands/runtime.py +37 -0
- modelable/commands/scenario.py +104 -0
- modelable/commands/spec.py +197 -0
- modelable/commands/workspace.py +240 -0
- modelable/compat/__init__.py +11 -0
- modelable/compat/checker.py +179 -0
- modelable/compat/diff.py +169 -0
- modelable/compiler/__init__.py +3 -0
- modelable/compiler/compiler.py +19 -0
- modelable/compiler/workspace.py +346 -0
- modelable/diagnostics/__init__.py +3 -0
- modelable/diagnostics/model.py +27 -0
- modelable/emitters/__init__.py +0 -0
- modelable/emitters/base.py +22 -0
- modelable/emitters/csharp.py +245 -0
- modelable/emitters/dbt_yaml.py +290 -0
- modelable/emitters/diagnostics.py +25 -0
- modelable/emitters/fhir.py +694 -0
- modelable/emitters/fhir_validator.py +36 -0
- modelable/emitters/go.py +334 -0
- modelable/emitters/java.py +264 -0
- modelable/emitters/json_schema.py +458 -0
- modelable/emitters/markdown.py +252 -0
- modelable/emitters/odcs.py +355 -0
- modelable/emitters/openlineage.py +315 -0
- modelable/emitters/openmetadata.py +258 -0
- modelable/emitters/python.py +282 -0
- modelable/emitters/rust.py +643 -0
- modelable/emitters/shapes.py +261 -0
- modelable/emitters/sql.py +266 -0
- modelable/emitters/targets.py +141 -0
- modelable/emitters/typescript.py +352 -0
- modelable/expressions/__init__.py +0 -0
- modelable/expressions/cel.py +547 -0
- modelable/governance/__init__.py +3 -0
- modelable/governance/checker.py +271 -0
- modelable/governance/por.py +46 -0
- modelable/grammar/__init__.py +1 -0
- modelable/grammar/modelable.lark +257 -0
- modelable/graph/__init__.py +5 -0
- modelable/graph/export.py +442 -0
- modelable/llm/__init__.py +43 -0
- modelable/llm/chat.py +255 -0
- modelable/llm/config.py +87 -0
- modelable/llm/context.py +194 -0
- modelable/llm/engine.py +976 -0
- modelable/llm/importers.py +1077 -0
- modelable/llm/provenance.py +84 -0
- modelable/llm/providers.py +182 -0
- modelable/llm/qa.py +126 -0
- modelable/llm/recommendations.py +33 -0
- modelable/llm/redaction.py +19 -0
- modelable/llm/render.py +279 -0
- modelable/llm/update_plan.py +101 -0
- modelable/llm/validation_help.py +10 -0
- modelable/lsp/__init__.py +3 -0
- modelable/lsp/__main__.py +4 -0
- modelable/lsp/code_actions.py +210 -0
- modelable/lsp/completion.py +480 -0
- modelable/lsp/definition.py +343 -0
- modelable/lsp/diagnostics.py +31 -0
- modelable/lsp/document_symbols.py +197 -0
- modelable/lsp/federation.py +261 -0
- modelable/lsp/folding.py +33 -0
- modelable/lsp/formatting.py +64 -0
- modelable/lsp/highlight.py +30 -0
- modelable/lsp/hover.py +370 -0
- modelable/lsp/inlay_hints.py +158 -0
- modelable/lsp/references.py +511 -0
- modelable/lsp/rename.py +564 -0
- modelable/lsp/semantic_tokens.py +412 -0
- modelable/lsp/server.py +370 -0
- modelable/lsp/workspace.py +83 -0
- modelable/lsp/workspace_symbols.py +104 -0
- modelable/parser/__init__.py +94 -0
- modelable/parser/ir.py +451 -0
- modelable/parser/parse.py +47 -0
- modelable/parser/transformer.py +798 -0
- modelable/parser/wire.py +68 -0
- modelable/planner/__init__.py +0 -0
- modelable/planner/lineage.py +91 -0
- modelable/planner/planner.py +134 -0
- modelable/planner/plans.py +122 -0
- modelable/py.typed +0 -0
- modelable/registry/__init__.py +9 -0
- modelable/registry/apicurio.py +166 -0
- modelable/registry/base.py +18 -0
- modelable/registry/factory.py +18 -0
- modelable/registry/index.py +419 -0
- modelable/registry/local.py +26 -0
- modelable/registry/oci.py +22 -0
- modelable/registry/resolver.py +213 -0
- modelable/registry/schema.sql +119 -0
- modelable/registry/signature.py +26 -0
- modelable/release.py +125 -0
- modelable/runtime/__init__.py +5 -0
- modelable/runtime/adapter/__init__.py +17 -0
- modelable/runtime/adapter/base.py +18 -0
- modelable/runtime/adapter/postgres.py +82 -0
- modelable/specs/__init__.py +23 -0
- modelable/specs/tracking.py +220 -0
- modelable/validation/__init__.py +3 -0
- modelable/validation/semantic.py +659 -0
- modelable-1.0.0.dist-info/METADATA +61 -0
- modelable-1.0.0.dist-info/RECORD +122 -0
- modelable-1.0.0.dist-info/WHEEL +4 -0
- modelable-1.0.0.dist-info/entry_points.txt +2 -0
- modelable-1.0.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from lsprotocol import types
|
|
7
|
+
|
|
8
|
+
_TOKEN_TYPES = [
|
|
9
|
+
types.SemanticTokenTypes.Namespace.value,
|
|
10
|
+
types.SemanticTokenTypes.Class.value,
|
|
11
|
+
types.SemanticTokenTypes.Property.value,
|
|
12
|
+
types.SemanticTokenTypes.Parameter.value,
|
|
13
|
+
types.SemanticTokenTypes.Variable.value,
|
|
14
|
+
types.SemanticTokenTypes.Type.value,
|
|
15
|
+
types.SemanticTokenTypes.Keyword.value,
|
|
16
|
+
types.SemanticTokenTypes.Decorator.value,
|
|
17
|
+
types.SemanticTokenTypes.Comment.value,
|
|
18
|
+
types.SemanticTokenTypes.String.value,
|
|
19
|
+
types.SemanticTokenTypes.Number.value,
|
|
20
|
+
types.SemanticTokenTypes.Operator.value,
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
_KEYWORDS = {
|
|
24
|
+
"access",
|
|
25
|
+
"additive",
|
|
26
|
+
"aggregate",
|
|
27
|
+
"ai",
|
|
28
|
+
"as",
|
|
29
|
+
"asyncapi",
|
|
30
|
+
"binding",
|
|
31
|
+
"by",
|
|
32
|
+
"cardinality",
|
|
33
|
+
"clickhouse",
|
|
34
|
+
"contact",
|
|
35
|
+
"consumer",
|
|
36
|
+
"description",
|
|
37
|
+
"derive",
|
|
38
|
+
"docs",
|
|
39
|
+
"domain",
|
|
40
|
+
"entity",
|
|
41
|
+
"event",
|
|
42
|
+
"exclude",
|
|
43
|
+
"from",
|
|
44
|
+
"generate",
|
|
45
|
+
"group",
|
|
46
|
+
"internal",
|
|
47
|
+
"join",
|
|
48
|
+
"left",
|
|
49
|
+
"materialisation",
|
|
50
|
+
"mysql",
|
|
51
|
+
"on",
|
|
52
|
+
"openapi",
|
|
53
|
+
"owner",
|
|
54
|
+
"peers",
|
|
55
|
+
"pii",
|
|
56
|
+
"postgres",
|
|
57
|
+
"project",
|
|
58
|
+
"projection",
|
|
59
|
+
"property",
|
|
60
|
+
"protobuf",
|
|
61
|
+
"read",
|
|
62
|
+
"redact",
|
|
63
|
+
"registry",
|
|
64
|
+
"restricted",
|
|
65
|
+
"sql",
|
|
66
|
+
"sqlite",
|
|
67
|
+
"string",
|
|
68
|
+
"subscribe",
|
|
69
|
+
"subscription",
|
|
70
|
+
"transfer",
|
|
71
|
+
"typescript",
|
|
72
|
+
"value",
|
|
73
|
+
"where",
|
|
74
|
+
"workspace",
|
|
75
|
+
"write",
|
|
76
|
+
"jsonschema",
|
|
77
|
+
}
|
|
78
|
+
_TYPE_WORDS = {
|
|
79
|
+
"array",
|
|
80
|
+
"binary",
|
|
81
|
+
"bool",
|
|
82
|
+
"date",
|
|
83
|
+
"decimal",
|
|
84
|
+
"duration",
|
|
85
|
+
"enum",
|
|
86
|
+
"float",
|
|
87
|
+
"int",
|
|
88
|
+
"map",
|
|
89
|
+
"object",
|
|
90
|
+
"ref",
|
|
91
|
+
"time",
|
|
92
|
+
"timestamp",
|
|
93
|
+
"uuid",
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
_ANNOTATION_PATTERN = re.compile(r"@[A-Za-z_][A-Za-z0-9_-]*")
|
|
97
|
+
_WORD_PATTERN = re.compile(r"[A-Za-z_][A-Za-z0-9_-]*")
|
|
98
|
+
_NUMBER_PATTERN = re.compile(r"\d+")
|
|
99
|
+
_DOMAIN_DECL_PATTERN = re.compile(r"^\s*domain\s+(?P<name>[A-Za-z_][A-Za-z0-9_]*)")
|
|
100
|
+
_DECL_PATTERN = re.compile(
|
|
101
|
+
r"^\s*(?P<kind>entity|aggregate|event|value|projection)\s+"
|
|
102
|
+
r"(?P<name>[A-Za-z_][A-Za-z0-9_]*)\s*@\s*(?P<version>\d+)"
|
|
103
|
+
)
|
|
104
|
+
_FIELD_PATTERN = re.compile(
|
|
105
|
+
r"^\s*(?:@[A-Za-z_][A-Za-z0-9_-]*(?:\([^)]*\))?\s+)*(?P<name>[A-Za-z_][A-Za-z0-9_]*)\??\s*:"
|
|
106
|
+
)
|
|
107
|
+
_PROJECTION_FIELD_PATTERN = re.compile(
|
|
108
|
+
r"^\s*(?:@[A-Za-z_][A-Za-z0-9_-]*(?:\([^)]*\))?\s+)*(?P<name>[A-Za-z_][A-Za-z0-9_]*)\s*(?P<op><-|=)"
|
|
109
|
+
)
|
|
110
|
+
_SOURCE_PATTERN = re.compile(
|
|
111
|
+
r"^\s*(?P<join_prefix>left\s+)?(?P<kind>from|join)\s+"
|
|
112
|
+
r"(?P<domain>[A-Za-z_][A-Za-z0-9_.-]*)\.(?P<model>[A-Za-z_][A-Za-z0-9_.-]*)\s*@\s*"
|
|
113
|
+
r"(?P<version>\d+)\s+as\s+(?P<alias>[A-Za-z_][A-Za-z0-9_]*)"
|
|
114
|
+
)
|
|
115
|
+
_IMPORT_PINNED_PATTERN = re.compile(
|
|
116
|
+
r"^\s*import\s+domain\s+(?P<import_domain>[A-Za-z_][A-Za-z0-9_.-]*)\s+"
|
|
117
|
+
r"from\s+registry\s+\"[^\"]*\"\s+at\s+"
|
|
118
|
+
r"(?P<domain>[A-Za-z_][A-Za-z0-9_.-]*)\.(?P<model>[A-Za-z_][A-Za-z0-9_.-]*)\s*@\s*"
|
|
119
|
+
r"(?P<version>\d+)"
|
|
120
|
+
)
|
|
121
|
+
_IMPORT_DOMAIN_PATTERN = re.compile(r"^\s*import\s+domain\s+(?P<import_domain>[A-Za-z_][A-Za-z0-9_.-]*)")
|
|
122
|
+
_ALIASED_REFERENCE_PATTERN = re.compile(r"(?P<alias>[A-Za-z_][A-Za-z0-9_]*)\.(?P<field>[A-Za-z_][A-Za-z0-9_]*)")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass(frozen=True)
|
|
126
|
+
class _Token:
|
|
127
|
+
line: int
|
|
128
|
+
start: int
|
|
129
|
+
length: int
|
|
130
|
+
token_type: str
|
|
131
|
+
token_modifiers: int = 0
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def semantic_tokens_legend() -> types.SemanticTokensLegend:
|
|
135
|
+
return types.SemanticTokensLegend(token_types=_TOKEN_TYPES, token_modifiers=[])
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def build_semantic_tokens(text: str) -> types.SemanticTokens:
|
|
139
|
+
tokens: list[_Token] = []
|
|
140
|
+
for line_no, line in enumerate(text.splitlines()):
|
|
141
|
+
spans = _lex_line_spans(line)
|
|
142
|
+
_add_line_tokens(tokens, line_no, line, spans)
|
|
143
|
+
_add_span_tokens(tokens, line_no, spans)
|
|
144
|
+
return types.SemanticTokens(data=_encode_tokens(_dedupe_tokens(tokens)))
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _add_line_tokens(tokens: list[_Token], line_no: int, line: str, spans: list[tuple[int, int, str]]) -> None:
|
|
148
|
+
_add_match_token(tokens, line_no, line, _DOMAIN_DECL_PATTERN, "name", types.SemanticTokenTypes.Namespace.value)
|
|
149
|
+
_add_match_token(tokens, line_no, line, _DECL_PATTERN, "kind", types.SemanticTokenTypes.Keyword.value)
|
|
150
|
+
_add_match_token(tokens, line_no, line, _DECL_PATTERN, "name", types.SemanticTokenTypes.Class.value)
|
|
151
|
+
_add_match_token(tokens, line_no, line, _DECL_PATTERN, "version", types.SemanticTokenTypes.Number.value)
|
|
152
|
+
_add_match_token(tokens, line_no, line, _FIELD_PATTERN, "name", types.SemanticTokenTypes.Property.value)
|
|
153
|
+
_add_match_token(
|
|
154
|
+
tokens,
|
|
155
|
+
line_no,
|
|
156
|
+
line,
|
|
157
|
+
_PROJECTION_FIELD_PATTERN,
|
|
158
|
+
"name",
|
|
159
|
+
types.SemanticTokenTypes.Property.value,
|
|
160
|
+
)
|
|
161
|
+
_add_match_token(
|
|
162
|
+
tokens,
|
|
163
|
+
line_no,
|
|
164
|
+
line,
|
|
165
|
+
_PROJECTION_FIELD_PATTERN,
|
|
166
|
+
"op",
|
|
167
|
+
types.SemanticTokenTypes.Operator.value,
|
|
168
|
+
)
|
|
169
|
+
_add_match_token(tokens, line_no, line, _SOURCE_PATTERN, "kind", types.SemanticTokenTypes.Keyword.value)
|
|
170
|
+
_add_match_token(tokens, line_no, line, _SOURCE_PATTERN, "version", types.SemanticTokenTypes.Number.value)
|
|
171
|
+
_add_match_token(tokens, line_no, line, _SOURCE_PATTERN, "alias", types.SemanticTokenTypes.Parameter.value)
|
|
172
|
+
_add_dotted_reference_token(
|
|
173
|
+
tokens, line_no, line, _SOURCE_PATTERN, "domain", types.SemanticTokenTypes.Namespace.value
|
|
174
|
+
)
|
|
175
|
+
_add_dotted_reference_token(tokens, line_no, line, _SOURCE_PATTERN, "model", types.SemanticTokenTypes.Class.value)
|
|
176
|
+
_add_match_token(
|
|
177
|
+
tokens,
|
|
178
|
+
line_no,
|
|
179
|
+
line,
|
|
180
|
+
_IMPORT_DOMAIN_PATTERN,
|
|
181
|
+
"import_domain",
|
|
182
|
+
types.SemanticTokenTypes.Namespace.value,
|
|
183
|
+
)
|
|
184
|
+
_add_dotted_reference_token(
|
|
185
|
+
tokens,
|
|
186
|
+
line_no,
|
|
187
|
+
line,
|
|
188
|
+
_IMPORT_PINNED_PATTERN,
|
|
189
|
+
"domain",
|
|
190
|
+
types.SemanticTokenTypes.Namespace.value,
|
|
191
|
+
)
|
|
192
|
+
_add_dotted_reference_token(
|
|
193
|
+
tokens,
|
|
194
|
+
line_no,
|
|
195
|
+
line,
|
|
196
|
+
_IMPORT_PINNED_PATTERN,
|
|
197
|
+
"model",
|
|
198
|
+
types.SemanticTokenTypes.Class.value,
|
|
199
|
+
)
|
|
200
|
+
_add_match_token(tokens, line_no, line, _IMPORT_PINNED_PATTERN, "version", types.SemanticTokenTypes.Number.value)
|
|
201
|
+
|
|
202
|
+
if "<-" in line or "=" in line:
|
|
203
|
+
for match in _ALIASED_REFERENCE_PATTERN.finditer(line):
|
|
204
|
+
if _overlaps_any(match.start(), match.end(), spans):
|
|
205
|
+
continue
|
|
206
|
+
tokens.append(
|
|
207
|
+
_Token(
|
|
208
|
+
line=line_no,
|
|
209
|
+
start=match.start("alias"),
|
|
210
|
+
length=match.end("alias") - match.start("alias"),
|
|
211
|
+
token_type=types.SemanticTokenTypes.Variable.value,
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
tokens.append(
|
|
215
|
+
_Token(
|
|
216
|
+
line=line_no,
|
|
217
|
+
start=match.start("field"),
|
|
218
|
+
length=match.end("field") - match.start("field"),
|
|
219
|
+
token_type=types.SemanticTokenTypes.Property.value,
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
for annotation in _ANNOTATION_PATTERN.finditer(line):
|
|
224
|
+
if _overlaps_any(annotation.start(), annotation.end(), spans):
|
|
225
|
+
continue
|
|
226
|
+
tokens.append(
|
|
227
|
+
_Token(
|
|
228
|
+
line=line_no,
|
|
229
|
+
start=annotation.start(),
|
|
230
|
+
length=annotation.end() - annotation.start(),
|
|
231
|
+
token_type=types.SemanticTokenTypes.Decorator.value,
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
for match in _WORD_PATTERN.finditer(line):
|
|
236
|
+
if _overlaps_any(match.start(), match.end(), spans):
|
|
237
|
+
continue
|
|
238
|
+
if _covered_by_existing_token(tokens, line_no, match.start(), match.end()):
|
|
239
|
+
continue
|
|
240
|
+
token_type = _classify_word(match.group(0))
|
|
241
|
+
if token_type is None:
|
|
242
|
+
continue
|
|
243
|
+
tokens.append(
|
|
244
|
+
_Token(
|
|
245
|
+
line=line_no,
|
|
246
|
+
start=match.start(),
|
|
247
|
+
length=match.end() - match.start(),
|
|
248
|
+
token_type=token_type,
|
|
249
|
+
)
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
for match in _NUMBER_PATTERN.finditer(line):
|
|
253
|
+
if _overlaps_any(match.start(), match.end(), spans):
|
|
254
|
+
continue
|
|
255
|
+
if _covered_by_existing_token(tokens, line_no, match.start(), match.end()):
|
|
256
|
+
continue
|
|
257
|
+
tokens.append(
|
|
258
|
+
_Token(
|
|
259
|
+
line=line_no,
|
|
260
|
+
start=match.start(),
|
|
261
|
+
length=match.end() - match.start(),
|
|
262
|
+
token_type=types.SemanticTokenTypes.Number.value,
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
_add_operator_tokens(tokens, line_no, line, spans)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _add_span_tokens(tokens: list[_Token], line_no: int, spans: list[tuple[int, int, str]]) -> None:
|
|
270
|
+
for start, end, kind in spans:
|
|
271
|
+
token_type = (
|
|
272
|
+
types.SemanticTokenTypes.Comment.value if kind == "comment" else types.SemanticTokenTypes.String.value
|
|
273
|
+
)
|
|
274
|
+
tokens.append(
|
|
275
|
+
_Token(
|
|
276
|
+
line=line_no,
|
|
277
|
+
start=start,
|
|
278
|
+
length=end - start,
|
|
279
|
+
token_type=token_type,
|
|
280
|
+
)
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _classify_word(word: str) -> str | None:
|
|
285
|
+
lowered = word.lower()
|
|
286
|
+
if lowered in _TYPE_WORDS:
|
|
287
|
+
return types.SemanticTokenTypes.Type.value
|
|
288
|
+
if lowered in _KEYWORDS:
|
|
289
|
+
return types.SemanticTokenTypes.Keyword.value
|
|
290
|
+
return None
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _lex_line_spans(line: str) -> list[tuple[int, int, str]]:
|
|
294
|
+
spans: list[tuple[int, int, str]] = []
|
|
295
|
+
i = 0
|
|
296
|
+
while i < len(line):
|
|
297
|
+
if line.startswith("//", i):
|
|
298
|
+
spans.append((i, len(line), "comment"))
|
|
299
|
+
break
|
|
300
|
+
if line[i] == '"':
|
|
301
|
+
start = i
|
|
302
|
+
i += 1
|
|
303
|
+
while i < len(line):
|
|
304
|
+
if line[i] == "\\":
|
|
305
|
+
i += 2
|
|
306
|
+
continue
|
|
307
|
+
if line[i] == '"':
|
|
308
|
+
i += 1
|
|
309
|
+
break
|
|
310
|
+
i += 1
|
|
311
|
+
spans.append((start, i, "string"))
|
|
312
|
+
continue
|
|
313
|
+
i += 1
|
|
314
|
+
return spans
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _add_operator_tokens(tokens: list[_Token], line_no: int, line: str, spans: list[tuple[int, int, str]]) -> None:
|
|
318
|
+
i = 0
|
|
319
|
+
while i < len(line):
|
|
320
|
+
if _overlaps_any(i, i + 1, spans):
|
|
321
|
+
i += 1
|
|
322
|
+
continue
|
|
323
|
+
if line.startswith("<-", i):
|
|
324
|
+
tokens.append(_Token(line=line_no, start=i, length=2, token_type=types.SemanticTokenTypes.Operator.value))
|
|
325
|
+
i += 2
|
|
326
|
+
continue
|
|
327
|
+
if line[i] in "{}()[]:,.<>#=":
|
|
328
|
+
tokens.append(_Token(line=line_no, start=i, length=1, token_type=types.SemanticTokenTypes.Operator.value))
|
|
329
|
+
i += 1
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _add_match_token(
|
|
333
|
+
tokens: list[_Token],
|
|
334
|
+
line_no: int,
|
|
335
|
+
line: str,
|
|
336
|
+
pattern: re.Pattern[str],
|
|
337
|
+
group_name: str,
|
|
338
|
+
token_type: str,
|
|
339
|
+
) -> None:
|
|
340
|
+
match = pattern.match(line)
|
|
341
|
+
if match is None:
|
|
342
|
+
return
|
|
343
|
+
start = match.start(group_name)
|
|
344
|
+
end = match.end(group_name)
|
|
345
|
+
if start < 0 or end <= start:
|
|
346
|
+
return
|
|
347
|
+
tokens.append(_Token(line=line_no, start=start, length=end - start, token_type=token_type))
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _add_dotted_reference_token(
|
|
351
|
+
tokens: list[_Token],
|
|
352
|
+
line_no: int,
|
|
353
|
+
line: str,
|
|
354
|
+
pattern: re.Pattern[str],
|
|
355
|
+
group_name: str,
|
|
356
|
+
token_type: str,
|
|
357
|
+
) -> None:
|
|
358
|
+
match = pattern.match(line)
|
|
359
|
+
if match is None:
|
|
360
|
+
return
|
|
361
|
+
value = match.group(group_name)
|
|
362
|
+
start = match.start(group_name)
|
|
363
|
+
parts = value.split(".")
|
|
364
|
+
offset = 0
|
|
365
|
+
for index, part in enumerate(parts):
|
|
366
|
+
part_type = token_type if index == len(parts) - 1 else types.SemanticTokenTypes.Namespace.value
|
|
367
|
+
tokens.append(
|
|
368
|
+
_Token(
|
|
369
|
+
line=line_no,
|
|
370
|
+
start=start + offset,
|
|
371
|
+
length=len(part),
|
|
372
|
+
token_type=part_type,
|
|
373
|
+
)
|
|
374
|
+
)
|
|
375
|
+
offset += len(part) + 1
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _overlaps_any(start: int, end: int, spans: list[tuple[int, int, str]]) -> bool:
|
|
379
|
+
return any(start < span_end and end > span_start for span_start, span_end, _kind in spans)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _covered_by_existing_token(tokens: list[_Token], line_no: int, start: int, end: int) -> bool:
|
|
383
|
+
return any(token.line == line_no and start < token.start + token.length and end > token.start for token in tokens)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _dedupe_tokens(tokens: list[_Token]) -> list[_Token]:
|
|
387
|
+
seen: set[tuple[int, int, int, str, int]] = set()
|
|
388
|
+
deduped: list[_Token] = []
|
|
389
|
+
for token in tokens:
|
|
390
|
+
key = (token.line, token.start, token.length, token.token_type, token.token_modifiers)
|
|
391
|
+
if key in seen:
|
|
392
|
+
continue
|
|
393
|
+
seen.add(key)
|
|
394
|
+
deduped.append(token)
|
|
395
|
+
return deduped
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def _encode_tokens(tokens: list[_Token]) -> list[int]:
|
|
399
|
+
ordered = sorted(tokens, key=lambda token: (token.line, token.start, token.length))
|
|
400
|
+
data: list[int] = []
|
|
401
|
+
previous_line = 0
|
|
402
|
+
previous_start = 0
|
|
403
|
+
type_index = {token_type: index for index, token_type in enumerate(_TOKEN_TYPES)}
|
|
404
|
+
|
|
405
|
+
for token in ordered:
|
|
406
|
+
delta_line = token.line - previous_line
|
|
407
|
+
delta_start = token.start - previous_start if delta_line == 0 else token.start
|
|
408
|
+
data.extend([delta_line, delta_start, token.length, type_index[token.token_type], token.token_modifiers])
|
|
409
|
+
previous_line = token.line
|
|
410
|
+
previous_start = token.start
|
|
411
|
+
|
|
412
|
+
return data
|