ZedScripts 0.0.2__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.
- ZedScripts/__about__.py +1 -0
- ZedScripts/__init__.py +38 -0
- ZedScripts/enums/Diagnostic.py +165 -0
- ZedScripts/enums/SyntaxErrorType.py +7 -0
- ZedScripts/environment/__init__.py +33 -0
- ZedScripts/environment/config.py +82 -0
- ZedScripts/environment/document.py +374 -0
- ZedScripts/environment/mod.py +62 -0
- ZedScripts/environment/version.py +202 -0
- ZedScripts/environment/workspace.py +320 -0
- ZedScripts/locale/en.json +38 -0
- ZedScripts/main.py +7 -0
- ZedScripts/providers/actions.py +13 -0
- ZedScripts/providers/capabilities.py +107 -0
- ZedScripts/providers/configuration.py +26 -0
- ZedScripts/providers/debug.py +59 -0
- ZedScripts/providers/diagnostics.py +101 -0
- ZedScripts/providers/hover.py +37 -0
- ZedScripts/providers/http.py +49 -0
- ZedScripts/providers/locale.py +80 -0
- ZedScripts/providers/notifications.py +45 -0
- ZedScripts/providers/semantic_tokens.py +189 -0
- ZedScripts/scripts/__init__.py +160 -0
- ZedScripts/scripts/block.py +547 -0
- ZedScripts/scripts/dataset.py +355 -0
- ZedScripts/scripts/value.py +706 -0
- ZedScripts/scripts/version.py +75 -0
- ZedScripts/server.py +288 -0
- ZedScripts/structure/ast.py +143 -0
- ZedScripts/structure/lexer.py +482 -0
- ZedScripts/structure/parser.py +243 -0
- ZedScripts/utils.py +85 -0
- zedscripts-0.0.2.dist-info/METADATA +105 -0
- zedscripts-0.0.2.dist-info/RECORD +37 -0
- zedscripts-0.0.2.dist-info/WHEEL +5 -0
- zedscripts-0.0.2.dist-info/licenses/LICENSE +21 -0
- zedscripts-0.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from lsprotocol import types
|
|
4
|
+
|
|
5
|
+
from ..utils import textrange_to_lsp
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from ..structure.lexer import TextRange
|
|
9
|
+
|
|
10
|
+
def make_hover_information(description: str, range: 'TextRange | None' = None) -> types.Hover | None:
|
|
11
|
+
if description == "":
|
|
12
|
+
return None
|
|
13
|
+
return types.Hover(
|
|
14
|
+
contents=types.MarkupContent(
|
|
15
|
+
kind=types.MarkupKind.Markdown,
|
|
16
|
+
value=_format_description(description),
|
|
17
|
+
),
|
|
18
|
+
range=textrange_to_lsp(range) if range is not None else None
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
def format_tree(tree: str) -> str:
|
|
22
|
+
return f"```zedserver\n{tree}\n```"
|
|
23
|
+
|
|
24
|
+
# TODO: this could possibly not work in other IDEs
|
|
25
|
+
def _format_description(description: str) -> str:
|
|
26
|
+
"""
|
|
27
|
+
Format the description for hover information.
|
|
28
|
+
The C++ code blocks which are used in the datasets for script blocks are
|
|
29
|
+
replaced with proper zedscripts code blocks.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
description (str):
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
str:
|
|
36
|
+
"""
|
|
37
|
+
return description.replace("```cpp", "```zedscripts")
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import logging
|
|
3
|
+
import requests_cache
|
|
4
|
+
|
|
5
|
+
from .. import ZEDSCRIPT_CACHE_DIR
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_default_expire_after = datetime.timedelta(hours=12)
|
|
11
|
+
def get_session(expire_after: datetime.timedelta | float = _default_expire_after) -> requests_cache.CachedSession:
|
|
12
|
+
"""
|
|
13
|
+
Get a cached HTTP session with the specified expiration time.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
expire_after (datetime.timedelta | float, optional): The duration after which the cache expires. Defaults to datetime.timedelta(days=1).
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
requests_cache.CachedSession: A cached HTTP session with the specified expiration time.
|
|
20
|
+
"""
|
|
21
|
+
session = requests_cache.CachedSession(
|
|
22
|
+
cache_name=str(ZEDSCRIPT_CACHE_DIR / "http_cache"),
|
|
23
|
+
expire_after=expire_after,
|
|
24
|
+
)
|
|
25
|
+
return session
|
|
26
|
+
|
|
27
|
+
def load_json(url: str, expire_after: datetime.timedelta | float = _default_expire_after) -> dict:
|
|
28
|
+
"""
|
|
29
|
+
Load JSON data from the specified URL using a cached HTTP session.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
url (str): The URL to load JSON data from.
|
|
33
|
+
expire_after (datetime.timedelta | float, optional): The duration after which the cache expires. Defaults to _default_expire_after.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
dict: The JSON data loaded from the specified URL.
|
|
37
|
+
"""
|
|
38
|
+
session = get_session(expire_after=expire_after)
|
|
39
|
+
response = session.get(url)
|
|
40
|
+
is_cached = getattr(response, 'from_cache', False)
|
|
41
|
+
logging.debug(f"Loading {url} - from_cache: {is_cached}")
|
|
42
|
+
response.raise_for_status()
|
|
43
|
+
return response.json()
|
|
44
|
+
|
|
45
|
+
def clear_cache():
|
|
46
|
+
"""Resets the session cache."""
|
|
47
|
+
session = get_session()
|
|
48
|
+
session.cache.clear()
|
|
49
|
+
logging.debug("HTTP cache cleared.")
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
from importlib.resources import files
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..enums.Diagnostic import DiagnosticType
|
|
8
|
+
from ..providers.diagnostics import DiagnosticDefinition
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Locale:
|
|
12
|
+
def __init__(self, code: str, strings: dict[str, str] | None = None) -> None:
|
|
13
|
+
if strings is None:
|
|
14
|
+
strings = {}
|
|
15
|
+
self.code: str = code
|
|
16
|
+
self.strings: dict[str, str] = strings
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Localizer:
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
self.locales: dict[str, Locale] = {}
|
|
22
|
+
self.default_locale: str = "en"
|
|
23
|
+
self.current_locale: str = self.default_locale
|
|
24
|
+
|
|
25
|
+
def localize_string(self,
|
|
26
|
+
identifier: 'DiagnosticType',
|
|
27
|
+
definition: 'DiagnosticDefinition',
|
|
28
|
+
locale: str | None = None,
|
|
29
|
+
args: dict[str, Any] | None = None) -> str:
|
|
30
|
+
if locale is None:
|
|
31
|
+
locale = self.current_locale
|
|
32
|
+
assert locale in self.locales, "Locale not loaded or invalid: {}".format(locale)
|
|
33
|
+
|
|
34
|
+
string = self.locales[locale].strings.get(identifier.name)
|
|
35
|
+
if string is None:
|
|
36
|
+
logging.warning("Missing string: %s (%s).", identifier.name, locale)
|
|
37
|
+
return identifier.name
|
|
38
|
+
|
|
39
|
+
if args is not None:
|
|
40
|
+
args_formatted = {}
|
|
41
|
+
args_def = definition.args
|
|
42
|
+
|
|
43
|
+
# ensure that each keys in args_def are present in args
|
|
44
|
+
for k in args_def.keys():
|
|
45
|
+
if k not in args:
|
|
46
|
+
raise ValueError(f"Missing argument '{k}' for localization.")
|
|
47
|
+
|
|
48
|
+
# format args properly based on the definition's argument types
|
|
49
|
+
for k,v in args.items():
|
|
50
|
+
assert k in args_def.keys(), f"Unexpected argument '{k}' for localization."
|
|
51
|
+
assert isinstance(v, args_def[k]), f"Argument '{k}' must be of type {args_def[k].__name__}."
|
|
52
|
+
|
|
53
|
+
# format based on the type
|
|
54
|
+
if isinstance(args_def[k], list):
|
|
55
|
+
args_formatted[k] = ", ".join(f"'{item}'" for item in v)
|
|
56
|
+
else:
|
|
57
|
+
args_formatted[k] = str(v)
|
|
58
|
+
return string.format(**args_formatted)
|
|
59
|
+
|
|
60
|
+
return string
|
|
61
|
+
|
|
62
|
+
def load_locale_files(self) -> None:
|
|
63
|
+
for locale in files("ZedScripts.locale").iterdir():
|
|
64
|
+
# safeguards, probably not needed tbh
|
|
65
|
+
if not locale.is_file():
|
|
66
|
+
continue
|
|
67
|
+
if not locale.name.endswith(".json"):
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
# access the locale data
|
|
71
|
+
with locale.open('r') as file:
|
|
72
|
+
raw: dict[str, Any] = json.load(file)
|
|
73
|
+
|
|
74
|
+
assert "code" in raw, "Locale file missing 'code'"
|
|
75
|
+
assert "strings" in raw, "Locale file missing 'strings'"
|
|
76
|
+
|
|
77
|
+
self.locales[raw["code"]] = Locale(raw["code"], raw["strings"])
|
|
78
|
+
|
|
79
|
+
# initialize the localizer here to prevent circular imports
|
|
80
|
+
zedlocalizer = Localizer()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
from typing import TypedDict
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from ..utils import path_to_uri
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..environment import WorkspaceType
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ZedNotification(enum.StrEnum):
|
|
13
|
+
SET_ZEDSCRIPTS = "zedscripts/setZedScripts"
|
|
14
|
+
|
|
15
|
+
LOADING_DOCUMENTS = "zedscripts/loadingDocuments"
|
|
16
|
+
LOADING_DOCUMENTS_DONE = "zedscripts/loadingDocumentsDone"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NotificationParams(TypedDict): ...
|
|
21
|
+
|
|
22
|
+
class SetZedScriptsNotificationParams(NotificationParams):
|
|
23
|
+
uri: str
|
|
24
|
+
|
|
25
|
+
class LoadingDocumentsNotificationParams(NotificationParams):
|
|
26
|
+
uri: str
|
|
27
|
+
progress: float
|
|
28
|
+
workspace_type: 'WorkspaceType'
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def send_progress_notification(type: ZedNotification, folder: Path, progress: float, workspace_type: 'WorkspaceType'):
|
|
34
|
+
from ..server import ZedServer
|
|
35
|
+
server = ZedServer.instance
|
|
36
|
+
if server is None:
|
|
37
|
+
return
|
|
38
|
+
server.send_notification(
|
|
39
|
+
type,
|
|
40
|
+
LoadingDocumentsNotificationParams(
|
|
41
|
+
uri=path_to_uri(folder),
|
|
42
|
+
progress=progress,
|
|
43
|
+
workspace_type=workspace_type
|
|
44
|
+
)
|
|
45
|
+
)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import enum
|
|
4
|
+
import re
|
|
5
|
+
import logging
|
|
6
|
+
from typing import TYPE_CHECKING, cast
|
|
7
|
+
from operator import attrgetter
|
|
8
|
+
|
|
9
|
+
from lsprotocol.types import SemanticTokens
|
|
10
|
+
|
|
11
|
+
from .. import IS_DEBUG
|
|
12
|
+
from ..structure.lexer import TokenType, chars_in_range, Token, TokenCollection, TextPosition, TextRange
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from ..environment.document import Document
|
|
16
|
+
|
|
17
|
+
PATTERN_FLOAT = re.compile("-?\\d+(?:\\.\\d+)?")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SemanticTokenType(enum.IntEnum):
|
|
21
|
+
NAMESPACE = 0
|
|
22
|
+
TYPE = 1
|
|
23
|
+
CLASS = 2
|
|
24
|
+
ENUM = 3
|
|
25
|
+
INTERFACE = 4
|
|
26
|
+
STRUCT = 5
|
|
27
|
+
TYPE_PARAMETER = 6
|
|
28
|
+
PARAMETER = 7
|
|
29
|
+
VARIABLE = 8
|
|
30
|
+
PROPERTY = 9
|
|
31
|
+
ENUM_MEMBER = 10
|
|
32
|
+
EVENT = 11
|
|
33
|
+
FUNCTION = 12
|
|
34
|
+
METHOD = 13
|
|
35
|
+
MACRO = 14
|
|
36
|
+
KEYWORD = 15
|
|
37
|
+
MODIFIER = 16
|
|
38
|
+
COMMENT = 17
|
|
39
|
+
STRING = 18
|
|
40
|
+
NUMBER = 19
|
|
41
|
+
REGEXP = 20
|
|
42
|
+
OPERATOR = 21
|
|
43
|
+
DECORATOR = 22
|
|
44
|
+
LABEL = 23
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SemanticTokenModifier(enum.IntFlag):
|
|
48
|
+
DECLARATION = enum.auto()
|
|
49
|
+
"""declaration site of a symbol"""
|
|
50
|
+
DEFINITION = enum.auto()
|
|
51
|
+
"""definition (vs. just a declaration)"""
|
|
52
|
+
READONLY = enum.auto()
|
|
53
|
+
"""symbol can't be reassigned (e.g. a constant)"""
|
|
54
|
+
STATIC = enum.auto()
|
|
55
|
+
"""static member/variable"""
|
|
56
|
+
DEPRECATED = enum.auto()
|
|
57
|
+
"""symbol is deprecated, editors often show strikethrough"""
|
|
58
|
+
ABSTRACT = enum.auto()
|
|
59
|
+
"""abstract method/class"""
|
|
60
|
+
ASYNC = enum.auto()
|
|
61
|
+
"""async function/method"""
|
|
62
|
+
MODIFICATION = enum.auto()
|
|
63
|
+
"""token is a write/assignment to the symbol, not just a read"""
|
|
64
|
+
DOCUMENTATION = enum.auto()
|
|
65
|
+
"""token appears inside documentation"""
|
|
66
|
+
DEFAULT_LIBRARY = enum.auto()
|
|
67
|
+
"""symbol comes from a built-in/default library"""
|
|
68
|
+
|
|
69
|
+
def get_tokens() -> tuple[list[str], list[str]]:
|
|
70
|
+
"""
|
|
71
|
+
Returns the list of semantic token types and modifiers as strings.
|
|
72
|
+
"""
|
|
73
|
+
return [t.name.lower() for t in SemanticTokenType], [cast(str, m.name).lower() for m in SemanticTokenModifier]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class SemanticToken:
|
|
81
|
+
def __init__(
|
|
82
|
+
self, range: TextRange, type: SemanticTokenType, modifiers: SemanticTokenModifier | None = None
|
|
83
|
+
) -> None:
|
|
84
|
+
if modifiers is None:
|
|
85
|
+
modifiers = SemanticTokenModifier(0)
|
|
86
|
+
self.range: TextRange = range
|
|
87
|
+
self.type: SemanticTokenType = type
|
|
88
|
+
self.modifiers: SemanticTokenModifier = modifiers
|
|
89
|
+
|
|
90
|
+
def __repr__(self) -> str:
|
|
91
|
+
return f"SemanticToken(range={self.range}, type={self.type.name}[{self.type}], modifiers={self.modifiers})"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class SemanticTokenCollection(list[SemanticToken]):
|
|
95
|
+
def __init__(self, document: Document, *args: SemanticToken) -> None:
|
|
96
|
+
super().__init__(args)
|
|
97
|
+
self.document = document
|
|
98
|
+
|
|
99
|
+
def sort_tokens(self) -> "SemanticTokenCollection":
|
|
100
|
+
self.sort(key=attrgetter("range.start.line", "range.start.offset"))
|
|
101
|
+
return self
|
|
102
|
+
|
|
103
|
+
def to_lsp(self) -> list[int]:
|
|
104
|
+
lsp_tokens: list[int] = []
|
|
105
|
+
last_pos: TextPosition = TextPosition(0, 0)
|
|
106
|
+
|
|
107
|
+
# make sure to sort the semantic tokens by their position
|
|
108
|
+
semantic_tokens = self.sort_tokens()
|
|
109
|
+
for i in range(len(semantic_tokens)):
|
|
110
|
+
token = semantic_tokens[i]
|
|
111
|
+
|
|
112
|
+
line_delta = token.range.start.line - last_pos.line
|
|
113
|
+
if line_delta == 0:
|
|
114
|
+
offset_delta = token.range.start.offset - last_pos.offset
|
|
115
|
+
else:
|
|
116
|
+
offset_delta = token.range.start.offset
|
|
117
|
+
|
|
118
|
+
characters = chars_in_range(self.document.get_text(), token.range)
|
|
119
|
+
if characters == "":
|
|
120
|
+
continue
|
|
121
|
+
|
|
122
|
+
lines = characters.split("\n")
|
|
123
|
+
lsp_tokens.append(line_delta)
|
|
124
|
+
lsp_tokens.append(offset_delta)
|
|
125
|
+
lsp_tokens.append(len(lines[0]))
|
|
126
|
+
lsp_tokens.append(token.type.value)
|
|
127
|
+
# false positive U_U
|
|
128
|
+
lsp_tokens.append(int(token.modifiers))
|
|
129
|
+
for line in lines[1:]:
|
|
130
|
+
lsp_tokens.append(1)
|
|
131
|
+
lsp_tokens.append(0)
|
|
132
|
+
lsp_tokens.append(len(line))
|
|
133
|
+
lsp_tokens.append(token.type.value)
|
|
134
|
+
# false positive U_U
|
|
135
|
+
lsp_tokens.append(int(token.modifiers))
|
|
136
|
+
|
|
137
|
+
last_pos = token.range.start if len(lines) == 1 else TextPosition(token.range.start.line + len(lines) - 1, 0)
|
|
138
|
+
|
|
139
|
+
# just so we don't have a for loop when not in debug mode
|
|
140
|
+
# if IS_DEBUG:
|
|
141
|
+
# logging.debug("Semantic tokens for document")
|
|
142
|
+
# max_token_length = 70
|
|
143
|
+
# for line, token in enumerate(semantic_tokens):
|
|
144
|
+
# logging.debug(f"{str(token).ljust(max_token_length+2)} {lsp_tokens[line * 5: (line + 1) * 5]}")
|
|
145
|
+
|
|
146
|
+
return lsp_tokens
|
|
147
|
+
|
|
148
|
+
def add(self, type: SemanticTokenType, location: TextRange, modifiers: list[SemanticTokenModifier] = []):
|
|
149
|
+
"""
|
|
150
|
+
Add a new semantic token to the collection.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
type (SemanticTokenType): The type of the semantic token.
|
|
154
|
+
location (TextRange): The range in the document that the token covers.
|
|
155
|
+
"""
|
|
156
|
+
combined_modifiers = SemanticTokenModifier(0)
|
|
157
|
+
for modifier in modifiers:
|
|
158
|
+
combined_modifiers |= modifier
|
|
159
|
+
self.append(SemanticToken(location, type, combined_modifiers))
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def build_syntactic_tokens(document: Document):
|
|
163
|
+
"""
|
|
164
|
+
Builds tokens that can be inferred directly from the lexical tokens without further context.
|
|
165
|
+
:param document:
|
|
166
|
+
:return:
|
|
167
|
+
"""
|
|
168
|
+
semantic_tokens = document.syntactic_semantic_tokens
|
|
169
|
+
semantic_tokens.clear() # reset syntactic semantic tokens
|
|
170
|
+
|
|
171
|
+
# mark every typical lexical token with a corresponding semantic token
|
|
172
|
+
for token in document.lexical_tokens:
|
|
173
|
+
match token.type:
|
|
174
|
+
case TokenType.ELEMENT_DELIMITER:
|
|
175
|
+
semantic_tokens.add(
|
|
176
|
+
SemanticTokenType.KEYWORD,
|
|
177
|
+
TextRange(token.pos, token.end),
|
|
178
|
+
)
|
|
179
|
+
case TokenType.COMMENT:
|
|
180
|
+
semantic_tokens.add(
|
|
181
|
+
SemanticTokenType.COMMENT,
|
|
182
|
+
TextRange(token.pos, token.end),
|
|
183
|
+
)
|
|
184
|
+
case TokenType.PUNCTUATOR:
|
|
185
|
+
semantic_tokens.add(
|
|
186
|
+
SemanticTokenType.KEYWORD,
|
|
187
|
+
TextRange(token.pos, token.end),
|
|
188
|
+
)
|
|
189
|
+
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
from typing import (TYPE_CHECKING,
|
|
3
|
+
TypedDict, Literal, NotRequired,
|
|
4
|
+
TypeVar, Generic)
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ..structure.ast import ValueNode, BlockNode, Chunk
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ScriptBlockData(TypedDict):
|
|
12
|
+
"""
|
|
13
|
+
The data structure representing a script block.
|
|
14
|
+
"""
|
|
15
|
+
name: str
|
|
16
|
+
description: str
|
|
17
|
+
shortDescription: NotRequired[str]
|
|
18
|
+
needsChildren: NotRequired[list[str]]
|
|
19
|
+
parents: list[str]
|
|
20
|
+
ID: NotRequired['ScriptBlockID']
|
|
21
|
+
parameters: dict[str, 'ScriptBlockParameter']
|
|
22
|
+
# properties: #TODO
|
|
23
|
+
variantOf: NotRequired[str]
|
|
24
|
+
"""if this block is a variant of another block, the name of the base block"""
|
|
25
|
+
|
|
26
|
+
# those should basically be unused here now
|
|
27
|
+
isRoot: NotRequired[bool]
|
|
28
|
+
pattern: NotRequired[list[str]]
|
|
29
|
+
"""to be used as regex patterns for identification"""
|
|
30
|
+
noComma: NotRequired[bool]
|
|
31
|
+
"""default is false"""
|
|
32
|
+
|
|
33
|
+
class ScriptBlockID(TypedDict):
|
|
34
|
+
"""
|
|
35
|
+
Provides information about the ID of the block.
|
|
36
|
+
"""
|
|
37
|
+
parentsWithout: NotRequired[list[str]]
|
|
38
|
+
values: NotRequired[list[str]]
|
|
39
|
+
asType: NotRequired[bool]
|
|
40
|
+
canHaveSpace: NotRequired[bool]
|
|
41
|
+
translation: NotRequired['TranslationProperties']
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## key-value types
|
|
45
|
+
|
|
46
|
+
type ScriptBlockValue = str | int | float | bool
|
|
47
|
+
class ScriptBlockParameter(TypedDict):
|
|
48
|
+
"""
|
|
49
|
+
Represents a parameter of a script block.
|
|
50
|
+
"""
|
|
51
|
+
name: str
|
|
52
|
+
description: NotRequired[str]
|
|
53
|
+
allowedDuplicate: NotRequired[bool]
|
|
54
|
+
canBeEmpty: NotRequired[bool]
|
|
55
|
+
default: NotRequired[ScriptBlockValue | list]
|
|
56
|
+
type: NotRequired['ParameterType']
|
|
57
|
+
deprecated: NotRequired['DeprecatedInfo']
|
|
58
|
+
values: NotRequired[list[ScriptBlockValue]]
|
|
59
|
+
needs: NotRequired['ScriptBlockNeeds']
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ValueType(enum.StrEnum):
|
|
63
|
+
STRING = "string"
|
|
64
|
+
INTEGER = "integer"
|
|
65
|
+
FLOAT = "float"
|
|
66
|
+
BOOLEAN = "boolean"
|
|
67
|
+
ARRAY = "array"
|
|
68
|
+
OBJECT = "object"
|
|
69
|
+
BLOCK = "block"
|
|
70
|
+
CALLBACK = "callback"
|
|
71
|
+
TRANSLATION = "translation"
|
|
72
|
+
|
|
73
|
+
_early_return: set[ValueType] = {
|
|
74
|
+
ValueType.STRING,
|
|
75
|
+
ValueType.ARRAY,
|
|
76
|
+
ValueType.OBJECT,
|
|
77
|
+
ValueType.BLOCK,
|
|
78
|
+
ValueType.CALLBACK,
|
|
79
|
+
ValueType.TRANSLATION,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
def cant_self_validate(value_type: ValueType) -> bool:
|
|
83
|
+
"""Verifies whenever the provided value_type cannot be validated by the value itself."""
|
|
84
|
+
return value_type in _early_return
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ParameterType(TypedDict):
|
|
88
|
+
main: ValueType
|
|
89
|
+
array: NotRequired['ArrayType']
|
|
90
|
+
object: NotRequired['ObjectType']
|
|
91
|
+
block: NotRequired['BlockType']
|
|
92
|
+
translation: NotRequired['TranslationProperties']
|
|
93
|
+
callback: NotRequired['CallbackType']
|
|
94
|
+
|
|
95
|
+
class ArrayType(TypedDict):
|
|
96
|
+
separator: str
|
|
97
|
+
type: Literal[ValueType.STRING, ValueType.INTEGER, ValueType.FLOAT, ValueType.BOOLEAN]
|
|
98
|
+
|
|
99
|
+
class ObjectType(TypedDict):
|
|
100
|
+
keyValueSeparator: str
|
|
101
|
+
"""the separator used to split the key and value"""
|
|
102
|
+
keyType: Literal[ValueType.STRING, ValueType.INTEGER, ValueType.FLOAT, ValueType.BOOLEAN]
|
|
103
|
+
valueType: Literal[ValueType.STRING, ValueType.INTEGER, ValueType.FLOAT, ValueType.BOOLEAN]
|
|
104
|
+
pairsSeparator: str
|
|
105
|
+
"""the separator used to split different key-value pairs"""
|
|
106
|
+
|
|
107
|
+
class BlockType(TypedDict):
|
|
108
|
+
name: str
|
|
109
|
+
"""the block type (e.g. "sound", "item", "model"...)"""
|
|
110
|
+
fullType: NotRequired[bool]
|
|
111
|
+
"""if true, this should use the module to reference the block"""
|
|
112
|
+
noAutoImport: NotRequired[bool]
|
|
113
|
+
"""if true, the will not automatically check its own parent block module when fullType is set to true"""
|
|
114
|
+
|
|
115
|
+
class CallbackType(TypedDict):
|
|
116
|
+
parameters: list['CallbackParameter']
|
|
117
|
+
returns: NotRequired[str]
|
|
118
|
+
|
|
119
|
+
class CallbackParameter(TypedDict):
|
|
120
|
+
name: str
|
|
121
|
+
type: str
|
|
122
|
+
isJava: bool
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class ScriptBlockNeeds(TypedDict):
|
|
126
|
+
name: str
|
|
127
|
+
"""the dependent parameter"""
|
|
128
|
+
values: NotRequired[list[ScriptBlockValue]]
|
|
129
|
+
"""list of possible values for the dependent parameter"""
|
|
130
|
+
valueToType: NotRequired[dict[str, str]]
|
|
131
|
+
"""mapping from dependent parameter values to their types
|
|
132
|
+
(for dynamic typing based on other parameter's value)"""
|
|
133
|
+
|
|
134
|
+
## extra metadata
|
|
135
|
+
|
|
136
|
+
class DeprecatedInfo(TypedDict):
|
|
137
|
+
replacedBy: NotRequired[str]
|
|
138
|
+
description: NotRequired[str]
|
|
139
|
+
version: NotRequired[str]
|
|
140
|
+
|
|
141
|
+
class TranslationProperties(TypedDict):
|
|
142
|
+
"""
|
|
143
|
+
Used to indicate translation properties to an element.
|
|
144
|
+
For example a key=value pair where the value needs a specific translation key.
|
|
145
|
+
"""
|
|
146
|
+
keyPattern: str
|
|
147
|
+
sourceFile: str
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
## block and values base classes
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
NodeT = TypeVar("NodeT")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class Element(Generic[NodeT]):
|
|
157
|
+
def __init__(self) -> None:
|
|
158
|
+
super().__init__()
|
|
159
|
+
self.node: NodeT
|
|
160
|
+
"""AST node of the element."""
|