jaclang 0.7.14__py3-none-any.whl → 0.7.17__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 jaclang might be problematic. Click here for more details.
- jaclang/cli/cli.py +147 -77
- jaclang/cli/cmdreg.py +9 -12
- jaclang/compiler/__init__.py +19 -53
- jaclang/compiler/absyntree.py +94 -16
- jaclang/compiler/constant.py +8 -8
- jaclang/compiler/jac.lark +4 -3
- jaclang/compiler/parser.py +41 -25
- jaclang/compiler/passes/ir_pass.py +4 -13
- jaclang/compiler/passes/main/__init__.py +1 -1
- jaclang/compiler/passes/main/access_modifier_pass.py +96 -147
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +155 -54
- jaclang/compiler/passes/main/import_pass.py +99 -75
- jaclang/compiler/passes/main/py_collect_dep_pass.py +70 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +328 -565
- jaclang/compiler/passes/main/pyast_load_pass.py +33 -6
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +7 -0
- jaclang/compiler/passes/main/registry_pass.py +37 -3
- jaclang/compiler/passes/main/schedules.py +9 -2
- jaclang/compiler/passes/main/sym_tab_build_pass.py +10 -6
- jaclang/compiler/passes/main/tests/__init__.py +1 -1
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.empty.impl.jac +0 -0
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.jac +1 -1
- jaclang/compiler/passes/main/tests/fixtures/py_imp_test.jac +29 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/color.py +3 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/constants.py +5 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/display.py +2 -0
- jaclang/compiler/passes/main/tests/test_import_pass.py +72 -13
- jaclang/compiler/passes/main/type_check_pass.py +22 -5
- jaclang/compiler/passes/tool/jac_formatter_pass.py +135 -89
- jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +37 -41
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +37 -42
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/access_mod_check.jac +27 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac +13 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comment_alignment.jac +11 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comments.jac +13 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/decorator_stack.jac +37 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/esc_keywords.jac +5 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/long_names.jac +19 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +6 -0
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +11 -0
- jaclang/compiler/passes/tool/tests/test_unparse_validate.py +33 -39
- jaclang/compiler/passes/transform.py +4 -0
- jaclang/compiler/passes/utils/mypy_ast_build.py +45 -0
- jaclang/compiler/semtable.py +31 -7
- jaclang/compiler/symtable.py +16 -11
- jaclang/compiler/tests/test_importer.py +25 -10
- jaclang/langserve/engine.py +104 -118
- jaclang/langserve/sem_manager.py +379 -0
- jaclang/langserve/server.py +24 -11
- jaclang/langserve/tests/fixtures/base_module_structure.jac +27 -6
- jaclang/langserve/tests/fixtures/circle.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
- jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
- jaclang/langserve/tests/fixtures/rename.jac +30 -0
- jaclang/langserve/tests/test_sem_tokens.py +277 -0
- jaclang/langserve/tests/test_server.py +287 -17
- jaclang/langserve/utils.py +184 -98
- jaclang/plugin/builtin.py +1 -1
- jaclang/plugin/default.py +288 -92
- jaclang/plugin/feature.py +65 -27
- jaclang/plugin/spec.py +62 -23
- jaclang/plugin/tests/fixtures/other_root_access.jac +82 -0
- jaclang/plugin/tests/test_jaseci.py +414 -42
- jaclang/runtimelib/architype.py +650 -0
- jaclang/{core → runtimelib}/constructs.py +5 -8
- jaclang/{core → runtimelib}/context.py +86 -59
- jaclang/runtimelib/importer.py +361 -0
- jaclang/runtimelib/machine.py +158 -0
- jaclang/runtimelib/memory.py +158 -0
- jaclang/{core → runtimelib}/utils.py +30 -15
- jaclang/settings.py +5 -4
- jaclang/tests/fixtures/abc.jac +3 -3
- jaclang/tests/fixtures/access_checker.jac +12 -17
- jaclang/tests/fixtures/access_modifier.jac +88 -33
- jaclang/tests/fixtures/baddy.jac +3 -0
- jaclang/tests/fixtures/baddy.test.jac +3 -0
- jaclang/tests/fixtures/bar.jac +34 -0
- jaclang/tests/fixtures/byllmissue.jac +1 -5
- jaclang/tests/fixtures/chandra_bugs2.jac +11 -10
- jaclang/tests/fixtures/cls_method.jac +41 -0
- jaclang/tests/fixtures/dblhello.jac +6 -0
- jaclang/tests/fixtures/deep/one_lev.jac +3 -3
- jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
- jaclang/tests/fixtures/deep_import_mods.jac +13 -0
- jaclang/tests/fixtures/edge_node_walk.jac +1 -1
- jaclang/tests/fixtures/edge_ops.jac +1 -1
- jaclang/tests/fixtures/edges_walk.jac +1 -1
- jaclang/tests/fixtures/err.impl.jac +3 -0
- jaclang/tests/fixtures/err.jac +4 -2
- jaclang/tests/fixtures/err_runtime.jac +15 -0
- jaclang/tests/fixtures/foo.jac +43 -0
- jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
- jaclang/tests/fixtures/hello.jac +4 -0
- jaclang/tests/fixtures/impl_grab.impl.jac +2 -1
- jaclang/tests/fixtures/impl_grab.jac +4 -1
- jaclang/tests/fixtures/import.jac +9 -0
- jaclang/tests/fixtures/index_slice.jac +30 -0
- jaclang/tests/fixtures/jp_importer_auto.jac +14 -0
- jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
- jaclang/tests/fixtures/needs_import.jac +2 -2
- jaclang/tests/fixtures/pyfunc_1.py +1 -1
- jaclang/tests/fixtures/pyfunc_2.py +5 -2
- jaclang/tests/fixtures/pygame_mock/__init__.py +3 -0
- jaclang/tests/fixtures/pygame_mock/color.py +3 -0
- jaclang/tests/fixtures/pygame_mock/constants.py +5 -0
- jaclang/tests/fixtures/pygame_mock/display.py +2 -0
- jaclang/tests/fixtures/pygame_mock/inner/__init__.py +0 -0
- jaclang/tests/fixtures/pygame_mock/inner/iner_mod.py +2 -0
- jaclang/tests/fixtures/registry.jac +9 -0
- jaclang/tests/fixtures/run_test.jac +4 -4
- jaclang/tests/fixtures/semstr.jac +1 -4
- jaclang/tests/fixtures/simple_archs.jac +1 -1
- jaclang/tests/test_cli.py +109 -3
- jaclang/tests/test_language.py +170 -68
- jaclang/tests/test_reference.py +2 -3
- jaclang/utils/helpers.py +45 -21
- jaclang/utils/test.py +9 -0
- jaclang/utils/treeprinter.py +30 -7
- {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/METADATA +3 -2
- {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/RECORD +126 -90
- jaclang/core/architype.py +0 -502
- jaclang/core/importer.py +0 -344
- jaclang/core/memory.py +0 -99
- jaclang/tests/fixtures/aott_raise.jac +0 -25
- jaclang/tests/fixtures/package_import.jac +0 -6
- /jaclang/{core → runtimelib}/__init__.py +0 -0
- /jaclang/{core → runtimelib}/test.py +0 -0
- {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/WHEEL +0 -0
- {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/entry_points.txt +0 -0
jaclang/langserve/utils.py
CHANGED
|
@@ -14,7 +14,6 @@ from jaclang.compiler.codeloc import CodeLocInfo
|
|
|
14
14
|
from jaclang.compiler.constant import SymbolType
|
|
15
15
|
from jaclang.compiler.passes.transform import Alert
|
|
16
16
|
from jaclang.compiler.symtable import Symbol, SymbolTable
|
|
17
|
-
from jaclang.utils.helpers import import_target_to_relative_path
|
|
18
17
|
from jaclang.vendor.pygls import uris
|
|
19
18
|
|
|
20
19
|
import lsprotocol.types as lspt
|
|
@@ -130,12 +129,25 @@ def position_within_node(node: ast.AstNode, line: int, character: int) -> bool:
|
|
|
130
129
|
return False
|
|
131
130
|
|
|
132
131
|
|
|
133
|
-
def
|
|
132
|
+
def find_index(
|
|
133
|
+
sem_tokens: list[int],
|
|
134
|
+
line: int,
|
|
135
|
+
char: int,
|
|
136
|
+
) -> Optional[int]:
|
|
137
|
+
"""Find index."""
|
|
138
|
+
index = None
|
|
139
|
+
for i, j in enumerate(
|
|
140
|
+
[get_token_start(i, sem_tokens) for i in range(0, len(sem_tokens), 5)]
|
|
141
|
+
):
|
|
142
|
+
if j[0] == line and j[1] <= char <= j[2]:
|
|
143
|
+
return i
|
|
144
|
+
|
|
145
|
+
return index
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_symbols_for_outline(node: SymbolTable) -> list[lspt.DocumentSymbol]:
|
|
134
149
|
"""Recursively collect symbols from the AST."""
|
|
135
150
|
symbols = []
|
|
136
|
-
if node is None:
|
|
137
|
-
return symbols
|
|
138
|
-
|
|
139
151
|
for key, item in node.tab.items():
|
|
140
152
|
if (
|
|
141
153
|
key in dir(builtins)
|
|
@@ -143,23 +155,20 @@ def collect_symbols(node: SymbolTable) -> list[lspt.DocumentSymbol]:
|
|
|
143
155
|
or item.decl.loc.mod_path != node.owner.loc.mod_path
|
|
144
156
|
):
|
|
145
157
|
continue
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)
|
|
156
|
-
symbols.append(symbol)
|
|
158
|
+
pos = create_range(item.decl.loc)
|
|
159
|
+
symbol = lspt.DocumentSymbol(
|
|
160
|
+
name=key,
|
|
161
|
+
kind=kind_map(item.decl),
|
|
162
|
+
range=pos,
|
|
163
|
+
selection_range=pos,
|
|
164
|
+
children=[],
|
|
165
|
+
)
|
|
166
|
+
symbols.append(symbol)
|
|
157
167
|
|
|
158
168
|
for sub_tab in [
|
|
159
169
|
i for i in node.kid if i.owner.loc.mod_path == node.owner.loc.mod_path
|
|
160
170
|
]:
|
|
161
|
-
sub_symbols =
|
|
162
|
-
|
|
171
|
+
sub_symbols = get_symbols_for_outline(sub_tab)
|
|
163
172
|
if isinstance(
|
|
164
173
|
sub_tab.owner,
|
|
165
174
|
(ast.IfStmt, ast.ElseStmt, ast.WhileStmt, ast.IterForStmt, ast.InForStmt),
|
|
@@ -264,19 +273,20 @@ def label_map(sub_tab: SymbolType) -> lspt.CompletionItemKind:
|
|
|
264
273
|
)
|
|
265
274
|
|
|
266
275
|
|
|
267
|
-
def get_mod_path(
|
|
276
|
+
def get_mod_path(
|
|
277
|
+
mod_path: ast.ModulePath, name_node: ast.Name
|
|
278
|
+
) -> str | None: # TODO: This should go away
|
|
268
279
|
"""Get path for a module import name."""
|
|
269
280
|
ret_target = None
|
|
270
|
-
module_location_path = mod_path.loc.mod_path
|
|
271
281
|
if mod_path.parent and (
|
|
272
282
|
(
|
|
273
283
|
isinstance(mod_path.parent.parent, ast.Import)
|
|
274
|
-
and mod_path.parent.parent.
|
|
284
|
+
and mod_path.parent.parent.is_py
|
|
275
285
|
)
|
|
276
286
|
or (
|
|
277
287
|
isinstance(mod_path.parent, ast.Import)
|
|
278
288
|
and mod_path.parent.from_loc
|
|
279
|
-
and mod_path.parent.
|
|
289
|
+
and mod_path.parent.is_py
|
|
280
290
|
)
|
|
281
291
|
):
|
|
282
292
|
if mod_path.path and name_node in mod_path.path:
|
|
@@ -286,39 +296,35 @@ def get_mod_path(mod_path: ast.ModulePath, name_node: ast.Name) -> str | None:
|
|
|
286
296
|
else ""
|
|
287
297
|
)
|
|
288
298
|
else:
|
|
289
|
-
temporary_path_str = mod_path.
|
|
290
|
-
sys.path.append(os.path.dirname(
|
|
299
|
+
temporary_path_str = mod_path.dot_path_str
|
|
300
|
+
sys.path.append(os.path.dirname(mod_path.loc.mod_path))
|
|
291
301
|
spec = importlib.util.find_spec(temporary_path_str)
|
|
292
|
-
sys.path.remove(os.path.dirname(
|
|
302
|
+
sys.path.remove(os.path.dirname(mod_path.loc.mod_path))
|
|
293
303
|
if spec and spec.origin and spec.origin.endswith(".py"):
|
|
294
304
|
ret_target = spec.origin
|
|
295
305
|
elif mod_path.parent and (
|
|
296
306
|
(
|
|
297
307
|
isinstance(mod_path.parent.parent, ast.Import)
|
|
298
|
-
and mod_path.parent.parent.
|
|
308
|
+
and mod_path.parent.parent.is_jac
|
|
299
309
|
)
|
|
300
310
|
or (
|
|
301
311
|
isinstance(mod_path.parent, ast.Import)
|
|
302
312
|
and mod_path.parent.from_loc
|
|
303
|
-
and mod_path.parent.
|
|
313
|
+
and mod_path.parent.is_jac
|
|
304
314
|
)
|
|
305
315
|
):
|
|
306
|
-
ret_target =
|
|
307
|
-
level=mod_path.level,
|
|
308
|
-
target=mod_path.path_str,
|
|
309
|
-
base_path=os.path.dirname(module_location_path),
|
|
310
|
-
)
|
|
316
|
+
ret_target = mod_path.resolve_relative_path()
|
|
311
317
|
return ret_target
|
|
312
318
|
|
|
313
319
|
|
|
314
320
|
def get_item_path(mod_item: ast.ModuleItem) -> tuple[str, tuple[int, int]] | None:
|
|
315
321
|
"""Get path."""
|
|
316
322
|
item_name = mod_item.name.value
|
|
317
|
-
if mod_item.from_parent.
|
|
323
|
+
if mod_item.from_parent.is_py and mod_item.from_parent.from_loc:
|
|
318
324
|
path = get_mod_path(mod_item.from_parent.from_loc, mod_item.name)
|
|
319
325
|
if path:
|
|
320
326
|
return get_definition_range(path, item_name)
|
|
321
|
-
elif mod_item.from_parent.
|
|
327
|
+
elif mod_item.from_parent.is_jac:
|
|
322
328
|
mod_node = mod_item.from_mod_path
|
|
323
329
|
if mod_node.sub_module and mod_node.sub_module._sym_tab:
|
|
324
330
|
for symbol_name, symbol in mod_node.sub_module._sym_tab.tab.items():
|
|
@@ -364,50 +370,6 @@ def get_definition_range(
|
|
|
364
370
|
return None
|
|
365
371
|
|
|
366
372
|
|
|
367
|
-
def locate_affected_token(
|
|
368
|
-
tokens: list[int],
|
|
369
|
-
change_start_line: int,
|
|
370
|
-
change_start_char: int,
|
|
371
|
-
change_end_line: int,
|
|
372
|
-
change_end_char: int,
|
|
373
|
-
) -> Optional[int]:
|
|
374
|
-
"""Find in which token change is occurring."""
|
|
375
|
-
token_index = 0
|
|
376
|
-
current_line = 0
|
|
377
|
-
line_char_offset = 0
|
|
378
|
-
|
|
379
|
-
while token_index < len(tokens):
|
|
380
|
-
token_line_delta = tokens[token_index]
|
|
381
|
-
token_start_char = tokens[token_index + 1]
|
|
382
|
-
token_length = tokens[token_index + 2]
|
|
383
|
-
|
|
384
|
-
if token_line_delta > 0:
|
|
385
|
-
current_line += token_line_delta
|
|
386
|
-
line_char_offset = 0
|
|
387
|
-
token_abs_start_char = line_char_offset + token_start_char
|
|
388
|
-
token_abs_end_char = token_abs_start_char + token_length
|
|
389
|
-
if (
|
|
390
|
-
current_line == change_start_line == change_end_line
|
|
391
|
-
and token_abs_start_char <= change_start_char
|
|
392
|
-
and change_end_char <= token_abs_end_char
|
|
393
|
-
):
|
|
394
|
-
return token_index
|
|
395
|
-
if (
|
|
396
|
-
current_line == change_start_line
|
|
397
|
-
and token_abs_start_char <= change_start_char < token_abs_end_char
|
|
398
|
-
):
|
|
399
|
-
return token_index
|
|
400
|
-
if (
|
|
401
|
-
current_line == change_end_line
|
|
402
|
-
and token_abs_start_char < change_end_char <= token_abs_end_char
|
|
403
|
-
):
|
|
404
|
-
return token_index
|
|
405
|
-
|
|
406
|
-
line_char_offset += token_start_char
|
|
407
|
-
token_index += 5
|
|
408
|
-
return None
|
|
409
|
-
|
|
410
|
-
|
|
411
373
|
def collect_all_symbols_in_scope(
|
|
412
374
|
sym_tab: SymbolTable, up_tree: bool = True
|
|
413
375
|
) -> list[lspt.CompletionItem]:
|
|
@@ -419,7 +381,7 @@ def collect_all_symbols_in_scope(
|
|
|
419
381
|
while current_tab is not None and current_tab not in visited:
|
|
420
382
|
visited.add(current_tab)
|
|
421
383
|
for name, symbol in current_tab.tab.items():
|
|
422
|
-
if name not in dir(builtins):
|
|
384
|
+
if name not in dir(builtins) and symbol.sym_type != SymbolType.IMPL:
|
|
423
385
|
symbols.append(
|
|
424
386
|
lspt.CompletionItem(label=name, kind=label_map(symbol.sym_type))
|
|
425
387
|
)
|
|
@@ -431,26 +393,32 @@ def collect_all_symbols_in_scope(
|
|
|
431
393
|
|
|
432
394
|
def parse_symbol_path(text: str, dot_position: int) -> list[str]:
|
|
433
395
|
"""Parse text and return a list of symbols."""
|
|
434
|
-
text = text[:dot_position].strip()
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
elif matches[i] == "":
|
|
448
|
-
pass
|
|
396
|
+
text = text[:dot_position][:-1].strip()
|
|
397
|
+
valid_character_pattern = re.compile(r"[a-zA-Z0-9_]")
|
|
398
|
+
|
|
399
|
+
reversed_text = text[::-1]
|
|
400
|
+
all_words = []
|
|
401
|
+
current_word = []
|
|
402
|
+
for char in reversed_text:
|
|
403
|
+
if valid_character_pattern.fullmatch(char):
|
|
404
|
+
current_word.append(char)
|
|
405
|
+
elif char == ".":
|
|
406
|
+
if current_word:
|
|
407
|
+
all_words.append("".join(current_word[::-1]))
|
|
408
|
+
current_word = []
|
|
449
409
|
else:
|
|
450
|
-
|
|
451
|
-
|
|
410
|
+
if current_word:
|
|
411
|
+
all_words.append("".join(current_word[::-1]))
|
|
412
|
+
current_word = []
|
|
413
|
+
break
|
|
414
|
+
|
|
415
|
+
all_words = (
|
|
416
|
+
all_words[::-1]
|
|
417
|
+
if not current_word
|
|
418
|
+
else ["".join(current_word[::-1])] + all_words[::-1]
|
|
419
|
+
)
|
|
452
420
|
|
|
453
|
-
return
|
|
421
|
+
return all_words
|
|
454
422
|
|
|
455
423
|
|
|
456
424
|
def resolve_symbol_path(sym_name: str, node_tab: SymbolTable) -> str:
|
|
@@ -463,6 +431,11 @@ def resolve_symbol_path(sym_name: str, node_tab: SymbolTable) -> str:
|
|
|
463
431
|
for name, symbol in current_tab.tab.items():
|
|
464
432
|
if name not in dir(builtins) and name == sym_name:
|
|
465
433
|
path = symbol.defn[0]._sym_type
|
|
434
|
+
if symbol.sym_type == SymbolType.ENUM_ARCH:
|
|
435
|
+
if isinstance(current_tab.owner, ast.Module):
|
|
436
|
+
return current_tab.owner.name + "." + sym_name
|
|
437
|
+
elif isinstance(current_tab.owner, ast.AstSymbolNode):
|
|
438
|
+
return current_tab.owner.name_spec._sym_type + "." + sym_name
|
|
466
439
|
return path
|
|
467
440
|
current_tab = current_tab.parent if current_tab.parent != current_tab else None
|
|
468
441
|
return ""
|
|
@@ -553,8 +526,121 @@ def resolve_completion_symbol_table(
|
|
|
553
526
|
)
|
|
554
527
|
else:
|
|
555
528
|
completion_items = []
|
|
556
|
-
|
|
529
|
+
if isinstance(current_symbol_table.owner, (ast.Ability, ast.AbilityDef)):
|
|
530
|
+
return completion_items
|
|
557
531
|
completion_items.extend(
|
|
558
532
|
collect_all_symbols_in_scope(current_symbol_table, up_tree=False)
|
|
559
533
|
)
|
|
560
534
|
return completion_items
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def get_token_start(
|
|
538
|
+
token_index: int | None, sem_tokens: list[int]
|
|
539
|
+
) -> tuple[int, int, int]:
|
|
540
|
+
"""Return the starting position of a token."""
|
|
541
|
+
if token_index is None or token_index >= len(sem_tokens):
|
|
542
|
+
return 0, 0, 0
|
|
543
|
+
|
|
544
|
+
current_line = 0
|
|
545
|
+
current_char = 0
|
|
546
|
+
current_tok_index = 0
|
|
547
|
+
|
|
548
|
+
while current_tok_index < len(sem_tokens):
|
|
549
|
+
token_line_delta = sem_tokens[current_tok_index]
|
|
550
|
+
token_start_char = sem_tokens[current_tok_index + 1]
|
|
551
|
+
|
|
552
|
+
if token_line_delta > 0:
|
|
553
|
+
current_line += token_line_delta
|
|
554
|
+
current_char = 0
|
|
555
|
+
if current_tok_index == token_index:
|
|
556
|
+
if token_line_delta > 0:
|
|
557
|
+
return (
|
|
558
|
+
current_line,
|
|
559
|
+
token_start_char,
|
|
560
|
+
token_start_char + sem_tokens[current_tok_index + 2],
|
|
561
|
+
)
|
|
562
|
+
return (
|
|
563
|
+
current_line,
|
|
564
|
+
current_char + token_start_char,
|
|
565
|
+
current_char + token_start_char + sem_tokens[current_tok_index + 2],
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
current_char += token_start_char
|
|
569
|
+
current_tok_index += 5
|
|
570
|
+
|
|
571
|
+
return (
|
|
572
|
+
current_line,
|
|
573
|
+
current_char,
|
|
574
|
+
current_char + sem_tokens[current_tok_index + 2],
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def find_surrounding_tokens(
|
|
579
|
+
change_start_line: int,
|
|
580
|
+
change_start_char: int,
|
|
581
|
+
change_end_line: int,
|
|
582
|
+
change_end_char: int,
|
|
583
|
+
sem_tokens: list[int],
|
|
584
|
+
) -> tuple[int | None, int | None, bool]:
|
|
585
|
+
"""Find the indices of the previous and next tokens surrounding the change."""
|
|
586
|
+
prev_token_index = None
|
|
587
|
+
next_token_index = None
|
|
588
|
+
inside_tok = False
|
|
589
|
+
for i, tok in enumerate(
|
|
590
|
+
[get_token_start(i, sem_tokens) for i in range(0, len(sem_tokens), 5)][0:]
|
|
591
|
+
):
|
|
592
|
+
if (not (prev_token_index is None or next_token_index is None)) and (
|
|
593
|
+
tok[0] > change_end_line
|
|
594
|
+
or (tok[0] == change_end_line and tok[1] > change_end_char)
|
|
595
|
+
):
|
|
596
|
+
prev_token_index = i * 5
|
|
597
|
+
break
|
|
598
|
+
elif (
|
|
599
|
+
change_start_line == tok[0] == change_end_line
|
|
600
|
+
and tok[1] <= change_start_char
|
|
601
|
+
and tok[2] >= change_end_char
|
|
602
|
+
):
|
|
603
|
+
prev_token_index = i * 5
|
|
604
|
+
inside_tok = True
|
|
605
|
+
break
|
|
606
|
+
elif (tok[0] < change_start_line) or (
|
|
607
|
+
tok[0] == change_start_line and tok[1] < change_start_char
|
|
608
|
+
):
|
|
609
|
+
prev_token_index = i * 5
|
|
610
|
+
elif (tok[0] > change_end_line) or (
|
|
611
|
+
tok[0] == change_end_line and tok[1] > change_end_char
|
|
612
|
+
):
|
|
613
|
+
next_token_index = i * 5
|
|
614
|
+
break
|
|
615
|
+
|
|
616
|
+
return prev_token_index, next_token_index, inside_tok
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
def get_line_of_code(line_number: int, lines: list[str]) -> Optional[tuple[str, int]]:
|
|
620
|
+
"""Get the line of code, and the first non-space character index."""
|
|
621
|
+
if 0 <= line_number < len(lines):
|
|
622
|
+
line = lines[line_number].rstrip("\n")
|
|
623
|
+
first_non_space = len(line) - len(line.lstrip())
|
|
624
|
+
return line, (
|
|
625
|
+
first_non_space + 4
|
|
626
|
+
if line.strip().endswith(("(", "{", "["))
|
|
627
|
+
else first_non_space
|
|
628
|
+
)
|
|
629
|
+
return None
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def add_unique_text_edit(
|
|
633
|
+
changes: dict[str, list[lspt.TextEdit]], key: str, new_edit: lspt.TextEdit
|
|
634
|
+
) -> None:
|
|
635
|
+
"""Add a new text edit to the changes dictionary if it is unique."""
|
|
636
|
+
if key not in changes:
|
|
637
|
+
changes[key] = [new_edit]
|
|
638
|
+
else:
|
|
639
|
+
for existing_edit in changes[key]:
|
|
640
|
+
if (
|
|
641
|
+
existing_edit.range.start == new_edit.range.start
|
|
642
|
+
and existing_edit.range.end == new_edit.range.end
|
|
643
|
+
and existing_edit.new_text == new_edit.new_text
|
|
644
|
+
):
|
|
645
|
+
return
|
|
646
|
+
changes[key].append(new_edit)
|