avrae-ls 0.3.0__py3-none-any.whl → 0.3.1__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.
avrae_ls/completions.py CHANGED
@@ -6,7 +6,7 @@ import re
6
6
  import typing
7
7
  from dataclasses import dataclass
8
8
  from functools import lru_cache
9
- from typing import Any, Dict, Iterable, List, Optional
9
+ from typing import Any, ClassVar, Dict, Iterable, List, Optional
10
10
 
11
11
  from lsprotocol import types
12
12
 
@@ -42,6 +42,76 @@ from .api import (
42
42
  )
43
43
  from .signature_help import FunctionSig
44
44
 
45
+
46
+ class _BuiltinList:
47
+ ATTRS: ClassVar[list[str]] = []
48
+ METHODS: ClassVar[list[str]] = ["append", "extend", "insert", "remove", "pop", "clear", "index", "count", "sort", "reverse", "copy"]
49
+
50
+ def __iter__(self) -> Iterable[Any]:
51
+ return iter([])
52
+
53
+ def append(self, value: Any) -> None: ...
54
+ def extend(self, iterable: Iterable[Any]) -> None: ...
55
+ def insert(self, index: int, value: Any) -> None: ...
56
+ def remove(self, value: Any) -> None: ...
57
+ def pop(self, index: int = -1) -> Any: ...
58
+ def clear(self) -> None: ...
59
+ def index(self, value: Any, start: int = 0, stop: int | None = None) -> int: ...
60
+ def count(self, value: Any) -> int: ...
61
+ def sort(self, *, key=None, reverse: bool = False) -> None: ...
62
+ def reverse(self) -> None: ...
63
+ def copy(self) -> list[Any]: ...
64
+
65
+
66
+ class _BuiltinDict:
67
+ ATTRS: ClassVar[list[str]] = []
68
+ METHODS: ClassVar[list[str]] = ["get", "keys", "values", "items", "pop", "popitem", "update", "setdefault", "clear", "copy"]
69
+
70
+ def __iter__(self) -> Iterable[Any]:
71
+ return iter({})
72
+
73
+ def get(self, key: Any, default: Any = None) -> Any: ...
74
+ def keys(self) -> Any: ...
75
+ def values(self) -> Any: ...
76
+ def items(self) -> Any: ...
77
+ def pop(self, key: Any, default: Any = None) -> Any: ...
78
+ def popitem(self) -> tuple[Any, Any]: ...
79
+ def update(self, *args, **kwargs) -> None: ...
80
+ def setdefault(self, key: Any, default: Any = None) -> Any: ...
81
+ def clear(self) -> None: ...
82
+ def copy(self) -> dict[Any, Any]: ...
83
+
84
+
85
+ class _BuiltinStr:
86
+ ATTRS: ClassVar[list[str]] = []
87
+ METHODS: ClassVar[list[str]] = [
88
+ "lower",
89
+ "upper",
90
+ "title",
91
+ "split",
92
+ "join",
93
+ "replace",
94
+ "strip",
95
+ "startswith",
96
+ "endswith",
97
+ "format",
98
+ ]
99
+
100
+ def __iter__(self) -> Iterable[str]:
101
+ return iter("")
102
+
103
+ def lower(self) -> str: ...
104
+ def upper(self) -> str: ...
105
+ def title(self) -> str: ...
106
+ def split(self, sep: str | None = None, maxsplit: int = -1) -> list[str]: ...
107
+ def join(self, iterable: Iterable[str]) -> str: ...
108
+ def replace(self, old: str, new: str, count: int = -1) -> str: ...
109
+ def strip(self, chars: str | None = None) -> str: ...
110
+ def startswith(self, prefix, start: int = 0, end: int | None = None) -> bool: ...
111
+ def endswith(self, suffix, start: int = 0, end: int | None = None) -> bool: ...
112
+ def format(self, *args, **kwargs) -> str: ...
113
+
114
+
45
115
  TYPE_MAP: Dict[str, object] = {
46
116
  "character": CharacterAPI,
47
117
  "combat": SimpleCombat,
@@ -74,6 +144,9 @@ TYPE_MAP: Dict[str, object] = {
74
144
  "SimpleGroup": SimpleGroup,
75
145
  "effect": SimpleEffect,
76
146
  "SimpleEffect": SimpleEffect,
147
+ "list": _BuiltinList,
148
+ "dict": _BuiltinDict,
149
+ "str": _BuiltinStr,
77
150
  }
78
151
 
79
152
 
@@ -108,6 +181,7 @@ class MethodMeta:
108
181
  class TypeMeta:
109
182
  attrs: Dict[str, AttrMeta]
110
183
  methods: Dict[str, MethodMeta]
184
+ element_type: str = ""
111
185
 
112
186
 
113
187
  def gather_suggestions(
@@ -304,9 +378,16 @@ def _attribute_receiver_and_prefix(code: str, line: int, character: int, capture
304
378
  dot = line_text.rfind(".")
305
379
  if dot == -1:
306
380
  return None
307
- prefix = line_text[dot + 1 :].strip()
381
+ tail = line_text[dot + 1 :]
382
+ prefix_match = re.match(r"\s*([A-Za-z_]\w*)?", tail)
383
+ prefix = prefix_match.group(1) or "" if prefix_match else ""
384
+ suffix = tail[prefix_match.end() if prefix_match else 0 :]
308
385
  placeholder = "__COMPLETE__"
309
- new_line = f"{line_text[:dot]}.{placeholder}"
386
+ new_line = f"{line_text[:dot]}.{placeholder}{suffix}"
387
+ # Close unmatched parentheses so the temporary code parses.
388
+ paren_balance = new_line.count("(") - new_line.count(")")
389
+ if paren_balance > 0:
390
+ new_line = new_line + (")" * paren_balance)
310
391
  mod_lines = list(lines)
311
392
  mod_lines[line] = new_line
312
393
  mod_code = "\n".join(mod_lines)
@@ -330,17 +411,29 @@ def _attribute_receiver_and_prefix(code: str, line: int, character: int, capture
330
411
  Finder().visit(tree)
331
412
  if receiver_src is None:
332
413
  return None
333
- cleaned = re.sub(r"\[[^\]]*\]", "", receiver_src)
334
- return cleaned, prefix
414
+ return receiver_src, prefix
335
415
 
336
416
 
337
417
  def _sanitize_incomplete_line(code: str, line: int, character: int) -> str:
338
418
  lines = code.splitlines()
339
419
  if 0 <= line < len(lines):
340
- prefix = lines[line][:character].rstrip()
341
- if prefix.endswith("."):
342
- prefix = prefix[:-1]
420
+ prefix = lines[line][:character]
421
+ trimmed = prefix.rstrip()
422
+ if trimmed.endswith("."):
423
+ prefix = trimmed[:-1]
424
+ else:
425
+ dot = prefix.rfind(".")
426
+ if dot != -1:
427
+ after = prefix[dot + 1 :]
428
+ if not re.match(r"\s*[A-Za-z_]", after):
429
+ prefix = prefix[:dot] + after
343
430
  lines[line] = prefix
431
+ candidate = "\n".join(lines)
432
+ try:
433
+ ast.parse(candidate)
434
+ except SyntaxError:
435
+ indent = re.match(r"[ \t]*", lines[line]).group(0)
436
+ lines[line] = indent + "pass"
344
437
  return "\n".join(lines)
345
438
 
346
439
 
@@ -370,54 +463,89 @@ def _infer_type_map(code: str) -> Dict[str, str]:
370
463
 
371
464
  class Visitor(ast.NodeVisitor):
372
465
  def visit_Assign(self, node: ast.Assign):
373
- val_type = self._value_type(node.value)
466
+ val_type, elem_type = self._value_type(node.value)
374
467
  for target in node.targets:
375
468
  if not isinstance(target, ast.Name):
376
469
  continue
377
470
  if val_type:
378
471
  type_map[target.id] = val_type
472
+ if elem_type:
473
+ type_map[f"{target.id}.__element__"] = elem_type
379
474
  self._record_dict_key_types(target.id, node.value)
380
475
  self.generic_visit(node)
381
476
 
477
+ def visit_For(self, node: ast.For):
478
+ iter_type, elem_type = self._value_type(node.iter)
479
+ if not elem_type and isinstance(node.iter, ast.Name):
480
+ elem_type = type_map.get(f"{node.iter.id}.__element__")
481
+ if elem_type and isinstance(node.target, ast.Name):
482
+ type_map[node.target.id] = elem_type
483
+ self.generic_visit(node)
484
+
382
485
  def visit_AnnAssign(self, node: ast.AnnAssign):
383
- val_type = self._value_type(node.value) if node.value else None
486
+ val_type, elem_type = self._value_type(node.value) if node.value else (None, None)
384
487
  if isinstance(node.target, ast.Name):
385
488
  if val_type:
386
489
  type_map[node.target.id] = val_type
490
+ if elem_type:
491
+ type_map[f"{node.target.id}.__element__"] = elem_type
387
492
  self._record_dict_key_types(node.target.id, node.value)
388
493
  self.generic_visit(node)
389
494
 
390
- def _value_type(self, value: ast.AST | None) -> Optional[str]:
495
+ def _value_type(self, value: ast.AST | None) -> tuple[Optional[str], Optional[str]]:
391
496
  if isinstance(value, ast.Call) and isinstance(value.func, ast.Name):
392
497
  if value.func.id in {"character", "combat"}:
393
- return value.func.id
498
+ return value.func.id, None
394
499
  if value.func.id == "vroll":
395
- return "SimpleRollResult"
500
+ return "SimpleRollResult", None
501
+ if value.func.id in {"list", "dict", "str"}:
502
+ return value.func.id, None
503
+ if isinstance(value, ast.List):
504
+ return "list", None
505
+ if isinstance(value, ast.Dict):
506
+ return "dict", None
507
+ if isinstance(value, ast.Constant):
508
+ if isinstance(value.value, str):
509
+ return "str", None
396
510
  if isinstance(value, ast.Name):
397
511
  if value.id in type_map:
398
- return type_map[value.id]
512
+ return type_map[value.id], type_map.get(f"{value.id}.__element__")
399
513
  if value.id in {"character", "combat", "ctx"}:
400
- return value.id
514
+ return value.id, None
401
515
  if isinstance(value, ast.Attribute):
402
516
  attr_name = value.attr
403
517
  base_type = None
518
+ base_elem = None
404
519
  if isinstance(value.value, ast.Name):
405
520
  base_type = type_map.get(value.value.id)
521
+ base_elem = type_map.get(f"{value.value.id}.__element__")
406
522
  if base_type is None:
407
- base_type = self._value_type(value.value)
408
- if base_type and attr_name in TYPE_MAP:
409
- return attr_name
410
- return None
411
- return None
523
+ base_type, base_elem = self._value_type(value.value)
524
+ if base_type:
525
+ meta = _type_meta(base_type)
526
+ attr_meta = meta.attrs.get(attr_name)
527
+ if attr_meta:
528
+ if attr_meta.type_name:
529
+ return attr_meta.type_name, attr_meta.element_type or None
530
+ if attr_meta.element_type:
531
+ return base_type, attr_meta.element_type
532
+ if base_elem:
533
+ return base_elem, None
534
+ if attr_name in TYPE_MAP:
535
+ return attr_name, None
536
+ return None, None
537
+ return None, None
412
538
 
413
539
  def _record_dict_key_types(self, var_name: str, value: ast.AST | None) -> None:
414
540
  if not isinstance(value, ast.Dict):
415
541
  return
416
542
  for key_node, val_node in zip(value.keys or [], value.values or []):
417
543
  if isinstance(key_node, ast.Constant) and isinstance(key_node.value, str):
418
- val_type = self._value_type(val_node)
544
+ val_type, elem_type = self._value_type(val_node)
419
545
  if val_type:
420
546
  type_map[f"{var_name}.{key_node.value}"] = val_type
547
+ if elem_type:
548
+ type_map[f"{var_name}.{key_node.value}.__element__"] = elem_type
421
549
 
422
550
  Visitor().visit(tree)
423
551
  return type_map
@@ -431,13 +559,26 @@ def _resolve_type_name(receiver: str, code: str, type_map: Dict[str, str] | None
431
559
  dict_key = f"{base}.{key}"
432
560
  if dict_key in mapping:
433
561
  return mapping[dict_key]
562
+ bracket = receiver.rfind("[")
563
+ if bracket != -1 and receiver.endswith("]"):
564
+ base_expr = receiver[:bracket]
565
+ elem_hint = mapping.get(f"{base_expr}.__element__")
566
+ if elem_hint:
567
+ return elem_hint
568
+ base_type = _resolve_type_name(base_expr, code, mapping)
569
+ if base_type:
570
+ base_meta = _type_meta(base_type)
571
+ if base_meta.element_type:
572
+ return base_meta.element_type
573
+ return base_type
434
574
  receiver = receiver.rstrip("()")
435
575
  if "." in receiver:
436
576
  base_expr, attr_name = receiver.rsplit(".", 1)
437
577
  base_type = _resolve_type_name(base_expr, code, mapping)
438
578
  if base_type:
439
579
  meta = _type_meta(base_type)
440
- attr_meta = meta.attrs.get(attr_name)
580
+ attr_key = attr_name.split("[", 1)[0]
581
+ attr_meta = meta.attrs.get(attr_key)
441
582
  if attr_meta:
442
583
  if attr_meta.element_type:
443
584
  return attr_meta.element_type
@@ -446,16 +587,19 @@ def _resolve_type_name(receiver: str, code: str, type_map: Dict[str, str] | None
446
587
 
447
588
  if receiver in mapping:
448
589
  return mapping[receiver]
590
+ elem_key = f"{receiver}.__element__"
591
+ if elem_key in mapping:
592
+ return mapping[elem_key]
449
593
  if receiver in TYPE_MAP:
450
594
  return receiver
451
- tail = receiver.split(".")[-1]
595
+ tail = receiver.split(".")[-1].split("[", 1)[0]
452
596
  if tail in TYPE_MAP:
453
597
  return tail
454
598
  return receiver
455
599
 
456
600
 
457
601
  def _type_meta(type_name: str) -> TypeMeta:
458
- return _type_meta_map().get(type_name, TypeMeta(attrs={}, methods={}))
602
+ return _type_meta_map().get(type_name, TypeMeta(attrs={}, methods={}, element_type=""))
459
603
 
460
604
 
461
605
  @lru_cache()
@@ -474,6 +618,7 @@ def _type_meta_map() -> Dict[str, TypeMeta]:
474
618
  for type_name, cls in TYPE_MAP.items():
475
619
  attrs: dict[str, AttrMeta] = {}
476
620
  methods: dict[str, MethodMeta] = {}
621
+ iterable_element = _iter_element_for_type_name(type_name)
477
622
 
478
623
  for attr in getattr(cls, "ATTRS", []):
479
624
  doc = ""
@@ -508,7 +653,7 @@ def _type_meta_map() -> Dict[str, TypeMeta]:
508
653
  doc = (meth_obj.__doc__ or "").strip()
509
654
  methods[meth] = MethodMeta(signature=sig_label, doc=doc)
510
655
 
511
- meta[type_name] = TypeMeta(attrs=attrs, methods=methods)
656
+ meta[type_name] = TypeMeta(attrs=attrs, methods=methods, element_type=iterable_element)
512
657
  return meta
513
658
 
514
659
 
@@ -571,8 +716,9 @@ def _type_names_from_annotation(ann: Any, reverse_type_map: Dict[type, str]) ->
571
716
  if args:
572
717
  elem = args[0]
573
718
  elem_name, _ = _type_names_from_annotation(elem, reverse_type_map)
574
- return "", elem_name
575
- return "", ""
719
+ container_name = reverse_type_map.get(origin) or "list"
720
+ return container_name, elem_name
721
+ return reverse_type_map.get(origin) or "list", ""
576
722
 
577
723
  if isinstance(ann, type) and ann in reverse_type_map:
578
724
  return reverse_type_map[ann], ""
avrae_ls/runtime.py CHANGED
@@ -129,12 +129,9 @@ def _default_builtins() -> Dict[str, Any]:
129
129
  "abs": abs,
130
130
  "range": range,
131
131
  "enumerate": enumerate,
132
- "sorted": sorted,
133
- "reversed": reversed,
134
132
  "int": int,
135
133
  "float": float,
136
134
  "str": str,
137
- "bool": bool,
138
135
  "round": round,
139
136
  "ceil": math.ceil,
140
137
  "floor": math.floor,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: avrae-ls
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Language server for Avrae draconic aliases
5
5
  Author: 1drturtle
6
6
  Requires-Python: >=3.11
@@ -4,18 +4,18 @@ avrae_ls/alias_preview.py,sha256=Wy_ZNRq73ojPiLFf6P6fJ7rG7DeFFsFLkLlhFrAjkKI,611
4
4
  avrae_ls/api.py,sha256=sGbwWs5o6M49lyk3qZi6gNdSH_u_BMdghsTlAjpviqQ,64852
5
5
  avrae_ls/argparser.py,sha256=-6RKrXavbSjEyyEeaoz8hRamnB-MEmJt3Cw2smRbmdI,13483
6
6
  avrae_ls/argument_parsing.py,sha256=ezKl65VwuNEDxt6KlYwVQcpy1110UDvf4BqZqgZTcqk,2122
7
- avrae_ls/completions.py,sha256=lMLaEB-8QWGIxdXYo6gUr-ZfXtws2faahAH7hhsbfe8,28183
7
+ avrae_ls/completions.py,sha256=WSyP967_t_oBhRMjLk-QQKyXWiNh4sJTH560VdMBQeg,34336
8
8
  avrae_ls/config.py,sha256=C51wfG6-82uX4dsffqcDLMiXbZZL9JQgfpulAr8eiqs,15267
9
9
  avrae_ls/context.py,sha256=w0uVSR6Pis3q1fF3kSPsqbTirKOE6n3k2XMO8UZI7sk,5719
10
10
  avrae_ls/cvars.py,sha256=0tcVbUHx_CKJ6aou3kEsKX37LRWAjkUWlqqIuSRFlXk,3197
11
11
  avrae_ls/diagnostics.py,sha256=EAdbckvvWu0pswopaZ6JyVGCHXUl5ZvGBhP2PDVCrVw,16180
12
12
  avrae_ls/dice.py,sha256=DY7V7L-EwAXaCgddgVe9xU1s9lVtiw5Zc2reipNgdTk,874
13
13
  avrae_ls/parser.py,sha256=AuNxNkkfquN9dcyTpmzAZxWcAQ7CV3PQLHUDYLMz_7U,1148
14
- avrae_ls/runtime.py,sha256=mxke-uwXQol82Syi6kKi0-GqZ0klRXAeAkZP0qUNW7s,20870
14
+ avrae_ls/runtime.py,sha256=sSN_C48TrF7F5NtsUpgHr_cZyYX6HCQHBfCpAYxsDJo,20792
15
15
  avrae_ls/server.py,sha256=L3qGTpCDOa64hWFmfWfoKbrDZ3262gmu5wYhHkdLBPY,13070
16
16
  avrae_ls/signature_help.py,sha256=JheaEzINV4FO72t5U0AJfL2ZX15y3-gcA6xk3M1jHcY,6980
17
17
  avrae_ls/symbols.py,sha256=8aMalHBDnRsRvhwdbmf0nOazio7G185qw9le45Xb5Mk,4449
18
- avrae_ls-0.3.0.dist-info/licenses/LICENSE,sha256=O-0zMbcEi6wXz1DiSdVgzMlQjJcNqNe5KDv08uYzqR0,1055
18
+ avrae_ls-0.3.1.dist-info/licenses/LICENSE,sha256=O-0zMbcEi6wXz1DiSdVgzMlQjJcNqNe5KDv08uYzqR0,1055
19
19
  draconic/LICENSE,sha256=Fzvu32_DafLKKn2mzxhEdlmrKZzAsigDZ87O7uoVqZI,1067
20
20
  draconic/__init__.py,sha256=YPH420Pcn_nTkfB62hJy_YqC5kpJdzSa78jP8n4z_xY,109
21
21
  draconic/exceptions.py,sha256=siahnHIsumbaUhKBDSrw_DmLZ-0oZks8L5oytPH8hD4,3753
@@ -25,8 +25,8 @@ draconic/string.py,sha256=kGrRc6wNHRq1y5xw8Os-fBhfINDtIY2nBWQWkyLSfQI,2858
25
25
  draconic/types.py,sha256=1Lsr6z8bW5agglGI4hLt_nPtYuZOIf_ueSpPDB4WDrs,13686
26
26
  draconic/utils.py,sha256=D4vJ-txqS2-rlqsEpXAC46_j1sZX4UjY-9zIgElo96k,3122
27
27
  draconic/versions.py,sha256=CUEsgUWjAmjez0432WwiBwZlIzWPIObwZUf8Yld18EE,84
28
- avrae_ls-0.3.0.dist-info/METADATA,sha256=wljFfTfBdkF2SBdWjThl9lpz64fTj5XL4ufqgFvtcS4,2107
29
- avrae_ls-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
- avrae_ls-0.3.0.dist-info/entry_points.txt,sha256=OtYXipMQzqmxpMoApgo0MeJYFmMbkbFN51Ibhpb8hF4,52
31
- avrae_ls-0.3.0.dist-info/top_level.txt,sha256=TL68uzGHmB2R2ID32_s2zocmcNnpMJVQ6_4NBvyo8a4,18
32
- avrae_ls-0.3.0.dist-info/RECORD,,
28
+ avrae_ls-0.3.1.dist-info/METADATA,sha256=TRJQs5mkNC4TPeYBkO2rXDnvkJhjf2jws1ginLXaXq4,2107
29
+ avrae_ls-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
+ avrae_ls-0.3.1.dist-info/entry_points.txt,sha256=OtYXipMQzqmxpMoApgo0MeJYFmMbkbFN51Ibhpb8hF4,52
31
+ avrae_ls-0.3.1.dist-info/top_level.txt,sha256=TL68uzGHmB2R2ID32_s2zocmcNnpMJVQ6_4NBvyo8a4,18
32
+ avrae_ls-0.3.1.dist-info/RECORD,,