ZedScripts 1.0.0__tar.gz → 1.1.0__tar.gz
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-1.0.0/src/ZedScripts.egg-info → zedscripts-1.1.0}/PKG-INFO +7 -1
- {zedscripts-1.0.0 → zedscripts-1.1.0}/README.md +4 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/pyproject.toml +4 -1
- zedscripts-1.1.0/src/ZedScripts/__about__.py +7 -0
- zedscripts-1.1.0/src/ZedScripts/__init__.py +75 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/enums/Diagnostic.py +1 -1
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/environment/__init__.py +8 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/environment/config.py +91 -8
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/environment/document.py +2 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/environment/workspace.py +14 -1
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/scripts/block.py +1 -1
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/server.py +9 -1
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/structure/lexer.py +24 -19
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/structure/parser.py +0 -4
- zedscripts-1.1.0/src/ZedScripts/tests/parser/test_lexer.py +261 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0/src/ZedScripts.egg-info}/PKG-INFO +7 -1
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts.egg-info/SOURCES.txt +3 -1
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts.egg-info/requires.txt +3 -0
- zedscripts-1.1.0/tests/test_config_cli.py +31 -0
- zedscripts-1.0.0/src/ZedScripts/__about__.py +0 -4
- zedscripts-1.0.0/src/ZedScripts/__init__.py +0 -38
- {zedscripts-1.0.0 → zedscripts-1.1.0}/LICENSE +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/setup.cfg +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/enums/SyntaxErrorType.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/environment/mod.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/environment/version.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/locale/en.json +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/main.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/providers/actions.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/providers/capabilities.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/providers/debug.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/providers/diagnostics.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/providers/hover.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/providers/http.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/providers/locale.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/providers/notifications.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/providers/semantic_tokens.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/scripts/__init__.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/scripts/dataset.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/scripts/value.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/scripts/version.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/structure/ast.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts/utils.py +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts.egg-info/dependency_links.txt +0 -0
- {zedscripts-1.0.0 → zedscripts-1.1.0}/src/ZedScripts.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ZedScripts
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Author: SimKDT
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -36,6 +36,8 @@ Provides-Extra: build
|
|
|
36
36
|
Requires-Dist: pyinstaller; extra == "build"
|
|
37
37
|
Provides-Extra: debug
|
|
38
38
|
Requires-Dist: debugpy; extra == "debug"
|
|
39
|
+
Provides-Extra: test
|
|
40
|
+
Requires-Dist: pytest; extra == "test"
|
|
39
41
|
Dynamic: license-file
|
|
40
42
|
|
|
41
43
|
# ZedScripts LSP
|
|
@@ -211,6 +213,10 @@ Each diagnostic by default have a specific set of modifiers that can be overridd
|
|
|
211
213
|
}
|
|
212
214
|
```
|
|
213
215
|
|
|
216
|
+
### Server
|
|
217
|
+
A list of server-related configurations for the LSP. The following parameters are available:
|
|
218
|
+
- `logging_level` (optional, default: `INFO`): specifies the logging level for the server. This option is only available in the global configuration and accepts values such as `NOTSET`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, and `CRITICAL`.
|
|
219
|
+
|
|
214
220
|
## IDE configuration
|
|
215
221
|
Based on the IDE you use, you may need to install the ZedScripts LSP application for your OS in the [releases](https://github.com/PZ-Wiki-Modding/ZedScripts-LSP/releases) (Linux, Windows).
|
|
216
222
|
|
|
@@ -171,6 +171,10 @@ Each diagnostic by default have a specific set of modifiers that can be overridd
|
|
|
171
171
|
}
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
+
### Server
|
|
175
|
+
A list of server-related configurations for the LSP. The following parameters are available:
|
|
176
|
+
- `logging_level` (optional, default: `INFO`): specifies the logging level for the server. This option is only available in the global configuration and accepts values such as `NOTSET`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, and `CRITICAL`.
|
|
177
|
+
|
|
174
178
|
## IDE configuration
|
|
175
179
|
Based on the IDE you use, you may need to install the ZedScripts LSP application for your OS in the [releases](https://github.com/PZ-Wiki-Modding/ZedScripts-LSP/releases) (Linux, Windows).
|
|
176
180
|
|
|
@@ -3,7 +3,7 @@ name = "ZedScripts"
|
|
|
3
3
|
description = ""
|
|
4
4
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
5
5
|
requires-python = ">=3.12.3"
|
|
6
|
-
version = "1.
|
|
6
|
+
version = "1.1.0"
|
|
7
7
|
license = {file = "LICENSE"}
|
|
8
8
|
authors = [
|
|
9
9
|
{ name = "SimKDT" },
|
|
@@ -23,6 +23,9 @@ build = [
|
|
|
23
23
|
debug = [
|
|
24
24
|
"debugpy"
|
|
25
25
|
]
|
|
26
|
+
test = [
|
|
27
|
+
"pytest"
|
|
28
|
+
]
|
|
26
29
|
|
|
27
30
|
# used to load locale JSON files
|
|
28
31
|
[build-system]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
IDENTIFIER = "ZedScripts"
|
|
7
|
+
SOURCE = IDENTIFIER
|
|
8
|
+
|
|
9
|
+
IS_DEBUG = os.getenv("ZEDSCRIPTS_DEBUG_MODE") == "1"
|
|
10
|
+
|
|
11
|
+
# zedscript's cache directory
|
|
12
|
+
ZEDSCRIPT_CACHE_DIR = Path.home() / ".zedscripts"
|
|
13
|
+
"""
|
|
14
|
+
Cache directory to hold various information related to ZedScripts.
|
|
15
|
+
"""
|
|
16
|
+
ZEDSCRIPT_CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
17
|
+
|
|
18
|
+
# config files naming
|
|
19
|
+
CONFIGURATION_FILE_NAME = ".zedscripts.json"
|
|
20
|
+
GLOBAL_CONFIGURATION_FILE = (ZEDSCRIPT_CACHE_DIR / CONFIGURATION_FILE_NAME).resolve()
|
|
21
|
+
|
|
22
|
+
# setup the logger
|
|
23
|
+
|
|
24
|
+
log_dir = Path.home() / ".zedscripts" / "logs"
|
|
25
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
26
|
+
|
|
27
|
+
# define logger
|
|
28
|
+
logger = logging.getLogger()
|
|
29
|
+
|
|
30
|
+
# set early level, this is changed later by the user configuration
|
|
31
|
+
# in the server initialization
|
|
32
|
+
logger.setLevel(logging.DEBUG if IS_DEBUG else logging.INFO)
|
|
33
|
+
|
|
34
|
+
formater = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
|
|
35
|
+
|
|
36
|
+
# setup the time-based log handler
|
|
37
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d-%H-%M-%S-%f")
|
|
38
|
+
time_log_file = log_dir / f"server-{timestamp}.log"
|
|
39
|
+
time_handler = logging.FileHandler(time_log_file, encoding="utf-8")
|
|
40
|
+
time_handler.setFormatter(
|
|
41
|
+
formater
|
|
42
|
+
)
|
|
43
|
+
logger.addHandler(time_handler)
|
|
44
|
+
|
|
45
|
+
# setup the debug log handler which stays in the same file to make it easier to view logs
|
|
46
|
+
# when restarting the server for example
|
|
47
|
+
if IS_DEBUG:
|
|
48
|
+
global_log_file = log_dir / f"server-debug.log"
|
|
49
|
+
global_handler = logging.FileHandler(
|
|
50
|
+
global_log_file, encoding="utf-8", mode="w"
|
|
51
|
+
)
|
|
52
|
+
global_handler.setFormatter(
|
|
53
|
+
formater
|
|
54
|
+
)
|
|
55
|
+
logger.addHandler(global_handler)
|
|
56
|
+
|
|
57
|
+
# filenames sort chronologically because the timestamp comes first
|
|
58
|
+
keep_logs = 5
|
|
59
|
+
log_files = sorted(log_dir.glob("server-*.log"), reverse=True)
|
|
60
|
+
|
|
61
|
+
# delete the oldest log file
|
|
62
|
+
for old_file in log_files[keep_logs:]:
|
|
63
|
+
try:
|
|
64
|
+
old_file.unlink()
|
|
65
|
+
except OSError:
|
|
66
|
+
logger.exception("Failed to delete old log file: %s", old_file)
|
|
67
|
+
|
|
68
|
+
logging.info("ZedScripts logger initialized. Debug mode: %s", IS_DEBUG)
|
|
69
|
+
|
|
70
|
+
# datasets
|
|
71
|
+
SCRIPTS_DATA_MANIFEST = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/heads/main/manifest.json"
|
|
72
|
+
SCRIPTS_BLOCKS_DATA_LINK = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/tags/{best_tag}/out/scriptsBlocks.json"
|
|
73
|
+
ROOTS_DATA_LINK = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/tags/{best_tag}/out/roots.json"
|
|
74
|
+
|
|
75
|
+
SCRIPTSDOCS_LINK = "https://pz-wiki-modding.github.io/PZ-API-Docs/scripts/"
|
|
@@ -76,7 +76,7 @@ DiagnosticDefinition(DiagnosticType.BLOCK_UNEXPECTED_ID,
|
|
|
76
76
|
tags=[DiagnosticTag.Unnecessary])
|
|
77
77
|
DiagnosticDefinition(DiagnosticType.BLOCK_MISSING_ID,
|
|
78
78
|
DiagnosticSeverity.Error,
|
|
79
|
-
args={"type": str
|
|
79
|
+
args={"type": str})
|
|
80
80
|
DiagnosticDefinition(DiagnosticType.BLOCK_HAS_ID_IN_PARENT,
|
|
81
81
|
DiagnosticSeverity.Error,
|
|
82
82
|
args={"type": str, "parent_type": str, "invalid_blocks": list},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import enum
|
|
2
|
+
import logging
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class WorkspaceType(enum.StrEnum):
|
|
@@ -31,3 +32,10 @@ class VersionType(enum.StrEnum):
|
|
|
31
32
|
BASE_GAME = enum.auto()
|
|
32
33
|
"""Base game files (e.g., `ProjectZomboid/media`)."""
|
|
33
34
|
|
|
35
|
+
class LoggerLevel(enum.IntEnum):
|
|
36
|
+
NOTSET = logging.NOTSET
|
|
37
|
+
DEBUG = logging.DEBUG
|
|
38
|
+
INFO = logging.INFO
|
|
39
|
+
WARNING = logging.WARNING
|
|
40
|
+
ERROR = logging.ERROR
|
|
41
|
+
CRITICAL = logging.CRITICAL
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provides the configuration models and schema generation for the ZedScripts environment.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python -m ZedScripts.environment.config schema -o schema/zedscripts.json
|
|
6
|
+
python -m ZedScripts.environment.config default -o default_config.json
|
|
7
|
+
"""
|
|
1
8
|
import logging
|
|
2
9
|
import enum
|
|
3
10
|
from pathlib import Path
|
|
4
11
|
from typing import Annotated, Optional
|
|
5
|
-
from pydantic import BaseModel, BeforeValidator, Field, WithJsonSchema
|
|
12
|
+
from pydantic import BaseModel, BeforeValidator, Field, WithJsonSchema, ConfigDict
|
|
6
13
|
|
|
7
14
|
from lsprotocol.types import DiagnosticSeverity, DiagnosticTag
|
|
8
15
|
|
|
16
|
+
from ..environment import LoggerLevel
|
|
9
17
|
from ..scripts.version import DataVersion
|
|
10
18
|
from ..enums.Diagnostic import DiagnosticType
|
|
11
19
|
|
|
@@ -68,6 +76,24 @@ DiagnosticTagName = Annotated[
|
|
|
68
76
|
}),
|
|
69
77
|
]
|
|
70
78
|
|
|
79
|
+
def _parse_logger_level_name(value: object) -> object:
|
|
80
|
+
if isinstance(value, LoggerLevel):
|
|
81
|
+
return value
|
|
82
|
+
if isinstance(value, str):
|
|
83
|
+
try:
|
|
84
|
+
return LoggerLevel[value]
|
|
85
|
+
except KeyError:
|
|
86
|
+
raise ValueError("level must be a LoggerLevel member name") from None
|
|
87
|
+
return value
|
|
88
|
+
LoggerLevelName = Annotated[
|
|
89
|
+
LoggerLevel,
|
|
90
|
+
BeforeValidator(_parse_logger_level_name),
|
|
91
|
+
WithJsonSchema({
|
|
92
|
+
"type": "string",
|
|
93
|
+
"enum": [member.name for member in LoggerLevel],
|
|
94
|
+
}),
|
|
95
|
+
]
|
|
96
|
+
|
|
71
97
|
|
|
72
98
|
|
|
73
99
|
# define the models
|
|
@@ -77,8 +103,13 @@ class DatasetTag(enum.StrEnum):
|
|
|
77
103
|
STABLE = "stable"
|
|
78
104
|
|
|
79
105
|
|
|
106
|
+
class ZedBaseModel(BaseModel):
|
|
107
|
+
"""Base model for all ZedScripts configuration models.
|
|
108
|
+
Notably used to set additionalProperties to false for everything."""
|
|
109
|
+
model_config = ConfigDict(extra='forbid')
|
|
110
|
+
|
|
80
111
|
|
|
81
|
-
class ReleaseModel(
|
|
112
|
+
class ReleaseModel(ZedBaseModel):
|
|
82
113
|
major: int = Field(
|
|
83
114
|
description="Build game version number.",
|
|
84
115
|
ge=42,
|
|
@@ -97,7 +128,7 @@ class ReleaseModel(BaseModel):
|
|
|
97
128
|
ge=0,
|
|
98
129
|
)
|
|
99
130
|
|
|
100
|
-
class DatasetModel(
|
|
131
|
+
class DatasetModel(ZedBaseModel):
|
|
101
132
|
release: Optional[ReleaseModel] = Field(
|
|
102
133
|
description="Provides a configuration for the dataset version to use for validation. This overrides the `latest` and `stable` fields if set.",
|
|
103
134
|
default=None,
|
|
@@ -126,7 +157,7 @@ class DatasetModel(BaseModel):
|
|
|
126
157
|
logging.error(f"Failed to get data version from release model: {e}. Defaulting to 'stable'.")
|
|
127
158
|
return DatasetTag.STABLE
|
|
128
159
|
|
|
129
|
-
class DiagnosticsModel(
|
|
160
|
+
class DiagnosticsModel(ZedBaseModel):
|
|
130
161
|
"""Represents the diagnostics configuration for the workspace environment."""
|
|
131
162
|
type: DiagnosticTypeName = Field(
|
|
132
163
|
description="Specifies the diagnostic member name to enable.",
|
|
@@ -144,7 +175,15 @@ class DiagnosticsModel(BaseModel):
|
|
|
144
175
|
default=None,
|
|
145
176
|
)
|
|
146
177
|
|
|
147
|
-
class
|
|
178
|
+
class ServerModel(ZedBaseModel):
|
|
179
|
+
"""Represents the server configuration for the workspace environment."""
|
|
180
|
+
logging_level: Optional[LoggerLevelName] = Field(
|
|
181
|
+
description="Specifies the logging level for the server. This option only works in the global configuration.",
|
|
182
|
+
default=LoggerLevel.INFO,
|
|
183
|
+
exclude=True,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
class ConfigurationModel(ZedBaseModel):
|
|
148
187
|
"""Represents the content of the configuration files for workspace environments."""
|
|
149
188
|
dataset: DatasetModel = Field(
|
|
150
189
|
description="Provides configuration for the dataset version to use for diagnostics.",
|
|
@@ -166,6 +205,10 @@ class ConfigurationModel(BaseModel):
|
|
|
166
205
|
description="List of diagnostics configurations for the workspace environment.",
|
|
167
206
|
default_factory=list,
|
|
168
207
|
)
|
|
208
|
+
server: ServerModel = Field(
|
|
209
|
+
description="Provides configuration for the server in the workspace environment.",
|
|
210
|
+
default_factory=ServerModel,
|
|
211
|
+
)
|
|
169
212
|
|
|
170
213
|
def find_diagnostic(self, type: DiagnosticType) -> DiagnosticsModel | None:
|
|
171
214
|
for diagnostic in self.diagnostics:
|
|
@@ -174,6 +217,46 @@ class ConfigurationModel(BaseModel):
|
|
|
174
217
|
return None
|
|
175
218
|
|
|
176
219
|
if __name__ == "__main__":
|
|
177
|
-
import
|
|
178
|
-
|
|
179
|
-
|
|
220
|
+
import argparse
|
|
221
|
+
|
|
222
|
+
def print_schema(args: argparse.Namespace):
|
|
223
|
+
import json
|
|
224
|
+
config = ConfigurationModel()
|
|
225
|
+
out = json.dumps(config.model_json_schema(), indent=4)
|
|
226
|
+
print(out)
|
|
227
|
+
if args.output_path:
|
|
228
|
+
with open(args.output_path, "w") as f:
|
|
229
|
+
f.write(out)
|
|
230
|
+
|
|
231
|
+
def print_default_config(args: argparse.Namespace):
|
|
232
|
+
config = ConfigurationModel()
|
|
233
|
+
out = config.model_dump_json(indent=4)
|
|
234
|
+
print(out)
|
|
235
|
+
if args.output_path:
|
|
236
|
+
with open(args.output_path, "w") as f:
|
|
237
|
+
f.write(out)
|
|
238
|
+
|
|
239
|
+
main_parser = argparse.ArgumentParser(description="ZedScripts configuration tool")
|
|
240
|
+
|
|
241
|
+
# schema generation command
|
|
242
|
+
subparser = main_parser.add_subparsers(
|
|
243
|
+
dest="command",
|
|
244
|
+
required=True,
|
|
245
|
+
help="Available commands"
|
|
246
|
+
)
|
|
247
|
+
schema_parser = subparser.add_parser("schema",
|
|
248
|
+
help="Print the JSON schema of the configuration model.")
|
|
249
|
+
schema_parser.add_argument("-o", "--output-path",
|
|
250
|
+
help="Path to output the JSON schema.")
|
|
251
|
+
schema_parser.set_defaults(func=print_schema)
|
|
252
|
+
|
|
253
|
+
# default configuration command
|
|
254
|
+
default_config_subparser = subparser.add_parser("default",
|
|
255
|
+
help="Print the default configuration of the workspace environment.")
|
|
256
|
+
default_config_subparser.add_argument("-o", "--output-path",
|
|
257
|
+
help="Path to output the default configuration.")
|
|
258
|
+
default_config_subparser.set_defaults(func=print_default_config)
|
|
259
|
+
|
|
260
|
+
args = main_parser.parse_args()
|
|
261
|
+
|
|
262
|
+
args.func(args)
|
|
@@ -131,12 +131,14 @@ class Document:
|
|
|
131
131
|
def make(dataset: 'Dataset', path: Path, workspace: 'Workspace') -> 'Document | None':
|
|
132
132
|
# find the rootType of the document
|
|
133
133
|
rootType = dataset.test_for_root(path)
|
|
134
|
+
logging.debug(f"Tested {path} for root, got: {rootType}")
|
|
134
135
|
if rootType is None:
|
|
135
136
|
return None
|
|
136
137
|
|
|
137
138
|
# find the version and skip if pre 42
|
|
138
139
|
# since we don't validate that
|
|
139
140
|
version = Version.find_or_make_version(path)
|
|
141
|
+
logging.debug(f"Tested {path} for version, got: {version}")
|
|
140
142
|
if version.type == VersionType.PRE_42:
|
|
141
143
|
return None
|
|
142
144
|
|
|
@@ -260,7 +260,20 @@ class Workspace:
|
|
|
260
260
|
@staticmethod
|
|
261
261
|
def update_global_configuration(text: str | None = None) -> None:
|
|
262
262
|
logging.info("Updating global configuration from %s", GLOBAL_CONFIGURATION_FILE)
|
|
263
|
-
|
|
263
|
+
# init file if it doesn't exist to default values
|
|
264
|
+
if not GLOBAL_CONFIGURATION_FILE.exists():
|
|
265
|
+
with open(GLOBAL_CONFIGURATION_FILE, "w") as f:
|
|
266
|
+
config = ConfigurationModel()
|
|
267
|
+
f.write(config.model_dump_json(
|
|
268
|
+
indent=4
|
|
269
|
+
))
|
|
270
|
+
|
|
271
|
+
# load existing global
|
|
272
|
+
else:
|
|
273
|
+
config = Workspace.load_configuration(GLOBAL_CONFIGURATION_FILE, text=text)
|
|
274
|
+
|
|
275
|
+
# cache current snapshot of the global configuration
|
|
276
|
+
Workspace.global_configuration = config
|
|
264
277
|
logging.debug(Workspace.global_configuration)
|
|
265
278
|
|
|
266
279
|
@staticmethod
|
|
@@ -148,7 +148,7 @@ zedserver = ZedServer()
|
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
|
|
151
|
-
##
|
|
151
|
+
## INITIALIZERS
|
|
152
152
|
|
|
153
153
|
@zedserver.feature(types.INITIALIZE)
|
|
154
154
|
def initialize(server: ZedServer, params: types.InitializeParams):
|
|
@@ -165,6 +165,11 @@ def initialize(server: ZedServer, params: types.InitializeParams):
|
|
|
165
165
|
# this will store what the server can currently do
|
|
166
166
|
capabilities.register_client_capabilities(params)
|
|
167
167
|
|
|
168
|
+
global_config = Workspace.get_global_configuration()
|
|
169
|
+
if global_config.server.logging_level is not None:
|
|
170
|
+
logging.info("Setting logging level to %s", global_config.server.logging_level)
|
|
171
|
+
logging.getLogger().setLevel(global_config.server.logging_level)
|
|
172
|
+
|
|
168
173
|
# from the above, we determine what the server capabilities should be
|
|
169
174
|
return types.InitializeResult(
|
|
170
175
|
capabilities=capabilities.get_server_capabilities(),
|
|
@@ -217,6 +222,9 @@ def initialized(server: ZedServer, params: types.InitializedParams) -> None:
|
|
|
217
222
|
notifications.send_notification(notifications.ZedNotification.LOADING_DOCUMENTS_DONE)
|
|
218
223
|
|
|
219
224
|
|
|
225
|
+
|
|
226
|
+
## EVENT HANDLERS
|
|
227
|
+
|
|
220
228
|
@zedserver.feature(types.TEXT_DOCUMENT_DID_OPEN)
|
|
221
229
|
def did_open(server: ZedServer, params: types.DidOpenTextDocumentParams) -> None:
|
|
222
230
|
server.on_document_changed(uri_to_path(params.text_document.uri), params.text_document.text)
|
|
@@ -194,25 +194,21 @@ WHITESPACE_NO_NEWLINE = string.whitespace.replace("\n", "")
|
|
|
194
194
|
|
|
195
195
|
|
|
196
196
|
|
|
197
|
-
class ZedscriptSource:
|
|
198
|
-
def __init__(self, text: str) -> None:
|
|
199
|
-
self.string: str = text
|
|
200
|
-
self.tokens: TokenCollection = TokenCollection()
|
|
201
|
-
|
|
202
|
-
|
|
203
197
|
class Token:
|
|
204
198
|
def __init__(
|
|
205
199
|
self, type: TokenType, pos: TextPosition, end: TextPosition,
|
|
206
|
-
|
|
200
|
+
text: str
|
|
207
201
|
) -> None:
|
|
208
202
|
self.type: TokenType = type
|
|
209
203
|
self.pos: TextPosition = pos
|
|
210
204
|
"""Start position of the token."""
|
|
211
205
|
self.end: TextPosition = end
|
|
212
206
|
"""End position of the token."""
|
|
213
|
-
self.source: ZedscriptSource = source
|
|
214
207
|
self.text = text
|
|
215
208
|
|
|
209
|
+
def __repr__(self) -> str:
|
|
210
|
+
return f"Token(type={self.type}, text={self.text!r}, pos={self.pos}, end={self.end})"
|
|
211
|
+
|
|
216
212
|
def pos_of(self, i: int) -> TextPosition:
|
|
217
213
|
"""
|
|
218
214
|
Returns the TextPosition of the character at index i in the string.
|
|
@@ -233,6 +229,16 @@ class Token:
|
|
|
233
229
|
def __str__(self) -> str:
|
|
234
230
|
return self.text
|
|
235
231
|
|
|
232
|
+
def __eq__(self, value: object) -> bool:
|
|
233
|
+
if not isinstance(value, Token):
|
|
234
|
+
return NotImplemented
|
|
235
|
+
return (
|
|
236
|
+
self.type == value.type and
|
|
237
|
+
self.pos == value.pos and
|
|
238
|
+
self.end == value.end and
|
|
239
|
+
self.text == value.text
|
|
240
|
+
)
|
|
241
|
+
|
|
236
242
|
|
|
237
243
|
class TokenCollection(UserList[Token]):
|
|
238
244
|
def at(self, pos: TextPosition) -> Token | None:
|
|
@@ -314,23 +320,22 @@ class TokenCollection(UserList[Token]):
|
|
|
314
320
|
|
|
315
321
|
# TODO: this class is kinda redundant after all
|
|
316
322
|
class TokenBuilder:
|
|
317
|
-
def __init__(self, pos: TextPosition, type: TokenType
|
|
323
|
+
def __init__(self, pos: TextPosition, type: TokenType) -> None:
|
|
318
324
|
self.type: TokenType = type
|
|
319
325
|
self.pos: TextPosition = pos
|
|
320
326
|
self.text: str = ""
|
|
321
|
-
self.source: ZedscriptSource = source
|
|
322
327
|
|
|
323
328
|
def add(self, char: str) -> None:
|
|
324
329
|
self.text += char
|
|
325
330
|
|
|
326
331
|
def build(self, end: TextPosition) -> Token:
|
|
327
|
-
return Token(self.type, self.pos, end, self.
|
|
332
|
+
return Token(self.type, self.pos, end, self.text)
|
|
328
333
|
|
|
329
334
|
|
|
330
335
|
class Lexer:
|
|
331
336
|
def __init__(self, text: str) -> None:
|
|
332
337
|
self.text: str = text
|
|
333
|
-
self.
|
|
338
|
+
self.tokens = TokenCollection()
|
|
334
339
|
self.pos: int = -1
|
|
335
340
|
self.line: int = 0
|
|
336
341
|
self.offset: int = 0
|
|
@@ -364,7 +369,7 @@ class Lexer:
|
|
|
364
369
|
return self.text[self.pos + num_characters]
|
|
365
370
|
|
|
366
371
|
def start_token(self, type: TokenType) -> TokenBuilder:
|
|
367
|
-
return TokenBuilder(TextPosition(self.line, self.offset), type
|
|
372
|
+
return TokenBuilder(TextPosition(self.line, self.offset), type)
|
|
368
373
|
|
|
369
374
|
def tokenize_comment(self) -> None:
|
|
370
375
|
assert self.check_comment()
|
|
@@ -381,7 +386,7 @@ class Lexer:
|
|
|
381
386
|
token.add(self.next())
|
|
382
387
|
break
|
|
383
388
|
|
|
384
|
-
self.
|
|
389
|
+
self.tokens.append(
|
|
385
390
|
token.build(
|
|
386
391
|
TextPosition(self.line, self.offset)
|
|
387
392
|
)
|
|
@@ -396,7 +401,7 @@ class Lexer:
|
|
|
396
401
|
def tokenize_punctuator(self) -> None:
|
|
397
402
|
token = self.start_token(TokenType.PUNCTUATOR)
|
|
398
403
|
token.add(self.next())
|
|
399
|
-
self.
|
|
404
|
+
self.tokens.append(
|
|
400
405
|
token.build(
|
|
401
406
|
TextPosition(self.line, self.offset)
|
|
402
407
|
)
|
|
@@ -414,7 +419,7 @@ class Lexer:
|
|
|
414
419
|
break
|
|
415
420
|
token.add(self.next())
|
|
416
421
|
|
|
417
|
-
self.
|
|
422
|
+
self.tokens.append(
|
|
418
423
|
token.build(
|
|
419
424
|
TextPosition(self.line, self.offset)
|
|
420
425
|
)
|
|
@@ -429,7 +434,7 @@ class Lexer:
|
|
|
429
434
|
break
|
|
430
435
|
token.add(self.next())
|
|
431
436
|
|
|
432
|
-
self.
|
|
437
|
+
self.tokens.append(
|
|
433
438
|
token.build(
|
|
434
439
|
TextPosition(self.line, self.offset)
|
|
435
440
|
)
|
|
@@ -449,7 +454,7 @@ class Lexer:
|
|
|
449
454
|
elif char in ELEMENTS_DELIMITERS:
|
|
450
455
|
token = lexer.start_token(TokenType.ELEMENT_DELIMITER)
|
|
451
456
|
token.add(lexer.next())
|
|
452
|
-
lexer.
|
|
457
|
+
lexer.tokens.append(
|
|
453
458
|
token.build(
|
|
454
459
|
TextPosition(lexer.line, lexer.offset)
|
|
455
460
|
)
|
|
@@ -460,7 +465,7 @@ class Lexer:
|
|
|
460
465
|
continue
|
|
461
466
|
lexer.tokenize_text()
|
|
462
467
|
|
|
463
|
-
return TokenCollection(lexer.
|
|
468
|
+
return TokenCollection(lexer.tokens)
|
|
464
469
|
|
|
465
470
|
|
|
466
471
|
if __name__ == "__main__":
|
|
@@ -93,10 +93,6 @@ def parse_type_id(tokens: list[Token]) -> tuple[TokenCollection | None, TokenCol
|
|
|
93
93
|
return type_token, id_token
|
|
94
94
|
|
|
95
95
|
|
|
96
|
-
def parse(text: str) -> ParseResult:
|
|
97
|
-
return parse_tokens(Lexer.tokenize(text))
|
|
98
|
-
|
|
99
|
-
|
|
100
96
|
def parse_tokens(tokens: TokenCollection) -> ParseResult:
|
|
101
97
|
parser = Parser()
|
|
102
98
|
unparsed_tokens: list[Token] = []
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
|
|
2
|
+
from ZedScripts.structure.lexer import Lexer, TextPosition, TokenType, Token, TokenCollection
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def parse_example_content() -> TokenCollection:
|
|
6
|
+
EXAMPLE_SCRIPT_CONTENT = r"""
|
|
7
|
+
module Base {
|
|
8
|
+
item ItemTest {
|
|
9
|
+
param1 = value,
|
|
10
|
+
param2 = val1;val2;val3,
|
|
11
|
+
param3 = key1:val1;key2:val2,
|
|
12
|
+
|
|
13
|
+
param4= value,
|
|
14
|
+
param5 =value,
|
|
15
|
+
param6=value,
|
|
16
|
+
param7 = fuck you ?,
|
|
17
|
+
|
|
18
|
+
weird_param = key1=val1:key2=val2,
|
|
19
|
+
|
|
20
|
+
to_new_line =
|
|
21
|
+
value,
|
|
22
|
+
|
|
23
|
+
/* values */
|
|
24
|
+
value1,
|
|
25
|
+
value2,
|
|
26
|
+
value3,
|
|
27
|
+
value4:value5,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
sound SoundTest {
|
|
31
|
+
clip = this/path/to/clip, volume = 0.8,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
model id with spaces {
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* comment, different syntax block under
|
|
39
|
+
hello world *//* comment continued */
|
|
40
|
+
|
|
41
|
+
this_is_a_weird_block {
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
item ItemTest2
|
|
46
|
+
{
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
""".strip()
|
|
51
|
+
return Lexer.tokenize(EXAMPLE_SCRIPT_CONTENT)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_theorical_tokens():
|
|
55
|
+
EXPECTED_LEXICAL_TOKENS = TokenCollection(
|
|
56
|
+
[
|
|
57
|
+
Token(TokenType.TEXT, TextPosition(0, 0), TextPosition(0, 6), 'module'),
|
|
58
|
+
Token(TokenType.WHITESPACE, TextPosition(0, 6), TextPosition(0, 7), ' '),
|
|
59
|
+
Token(TokenType.TEXT, TextPosition(0, 7), TextPosition(0, 11), 'Base'),
|
|
60
|
+
Token(TokenType.WHITESPACE, TextPosition(0, 11), TextPosition(0, 12), ' '),
|
|
61
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(0, 12), TextPosition(0, 13), '{'),
|
|
62
|
+
Token(TokenType.WHITESPACE, TextPosition(0, 13), TextPosition(1, 4), '\n '),
|
|
63
|
+
Token(TokenType.TEXT, TextPosition(1, 4), TextPosition(1, 8), 'item'),
|
|
64
|
+
Token(TokenType.WHITESPACE, TextPosition(1, 8), TextPosition(1, 9), ' '),
|
|
65
|
+
Token(TokenType.TEXT, TextPosition(1, 9), TextPosition(1, 17), 'ItemTest'),
|
|
66
|
+
Token(TokenType.WHITESPACE, TextPosition(1, 17), TextPosition(1, 18), ' '),
|
|
67
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(1, 18), TextPosition(1, 19), '{'),
|
|
68
|
+
Token(TokenType.WHITESPACE, TextPosition(1, 19), TextPosition(2, 8), '\n '),
|
|
69
|
+
Token(TokenType.TEXT, TextPosition(2, 8), TextPosition(2, 14), 'param1'),
|
|
70
|
+
Token(TokenType.WHITESPACE, TextPosition(2, 14), TextPosition(2, 15), ' '),
|
|
71
|
+
Token(TokenType.PUNCTUATOR, TextPosition(2, 15), TextPosition(2, 16), '='),
|
|
72
|
+
Token(TokenType.WHITESPACE, TextPosition(2, 16), TextPosition(2, 17), ' '),
|
|
73
|
+
Token(TokenType.TEXT, TextPosition(2, 17), TextPosition(2, 22), 'value'),
|
|
74
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(2, 22), TextPosition(2, 23), ','),
|
|
75
|
+
Token(TokenType.WHITESPACE, TextPosition(2, 23), TextPosition(3, 8), '\n '),
|
|
76
|
+
Token(TokenType.TEXT, TextPosition(3, 8), TextPosition(3, 14), 'param2'),
|
|
77
|
+
Token(TokenType.WHITESPACE, TextPosition(3, 14), TextPosition(3, 15), ' '),
|
|
78
|
+
Token(TokenType.PUNCTUATOR, TextPosition(3, 15), TextPosition(3, 16), '='),
|
|
79
|
+
Token(TokenType.WHITESPACE, TextPosition(3, 16), TextPosition(3, 17), ' '),
|
|
80
|
+
Token(TokenType.TEXT, TextPosition(3, 17), TextPosition(3, 21), 'val1'),
|
|
81
|
+
Token(TokenType.PUNCTUATOR, TextPosition(3, 21), TextPosition(3, 22), ';'),
|
|
82
|
+
Token(TokenType.TEXT, TextPosition(3, 22), TextPosition(3, 26), 'val2'),
|
|
83
|
+
Token(TokenType.PUNCTUATOR, TextPosition(3, 26), TextPosition(3, 27), ';'),
|
|
84
|
+
Token(TokenType.TEXT, TextPosition(3, 27), TextPosition(3, 31), 'val3'),
|
|
85
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(3, 31), TextPosition(3, 32), ','),
|
|
86
|
+
Token(TokenType.WHITESPACE, TextPosition(3, 32), TextPosition(4, 8), '\n '),
|
|
87
|
+
Token(TokenType.TEXT, TextPosition(4, 8), TextPosition(4, 14), 'param3'),
|
|
88
|
+
Token(TokenType.WHITESPACE, TextPosition(4, 14), TextPosition(4, 15), ' '),
|
|
89
|
+
Token(TokenType.PUNCTUATOR, TextPosition(4, 15), TextPosition(4, 16), '='),
|
|
90
|
+
Token(TokenType.WHITESPACE, TextPosition(4, 16), TextPosition(4, 17), ' '),
|
|
91
|
+
Token(TokenType.TEXT, TextPosition(4, 17), TextPosition(4, 21), 'key1'),
|
|
92
|
+
Token(TokenType.PUNCTUATOR, TextPosition(4, 21), TextPosition(4, 22), ':'),
|
|
93
|
+
Token(TokenType.TEXT, TextPosition(4, 22), TextPosition(4, 26), 'val1'),
|
|
94
|
+
Token(TokenType.PUNCTUATOR, TextPosition(4, 26), TextPosition(4, 27), ';'),
|
|
95
|
+
Token(TokenType.TEXT, TextPosition(4, 27), TextPosition(4, 31), 'key2'),
|
|
96
|
+
Token(TokenType.PUNCTUATOR, TextPosition(4, 31), TextPosition(4, 32), ':'),
|
|
97
|
+
Token(TokenType.TEXT, TextPosition(4, 32), TextPosition(4, 36), 'val2'),
|
|
98
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(4, 36), TextPosition(4, 37), ','),
|
|
99
|
+
Token(TokenType.WHITESPACE, TextPosition(4, 37), TextPosition(6, 8), '\n\n '),
|
|
100
|
+
Token(TokenType.TEXT, TextPosition(6, 8), TextPosition(6, 14), 'param4'),
|
|
101
|
+
Token(TokenType.PUNCTUATOR, TextPosition(6, 14), TextPosition(6, 15), '='),
|
|
102
|
+
Token(TokenType.WHITESPACE, TextPosition(6, 15), TextPosition(6, 16), ' '),
|
|
103
|
+
Token(TokenType.TEXT, TextPosition(6, 16), TextPosition(6, 21), 'value'),
|
|
104
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(6, 21), TextPosition(6, 22), ','),
|
|
105
|
+
Token(TokenType.WHITESPACE, TextPosition(6, 22), TextPosition(7, 8), '\n '),
|
|
106
|
+
Token(TokenType.TEXT, TextPosition(7, 8), TextPosition(7, 14), 'param5'),
|
|
107
|
+
Token(TokenType.WHITESPACE, TextPosition(7, 14), TextPosition(7, 15), ' '),
|
|
108
|
+
Token(TokenType.PUNCTUATOR, TextPosition(7, 15), TextPosition(7, 16), '='),
|
|
109
|
+
Token(TokenType.TEXT, TextPosition(7, 16), TextPosition(7, 21), 'value'),
|
|
110
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(7, 21), TextPosition(7, 22), ','),
|
|
111
|
+
Token(TokenType.WHITESPACE, TextPosition(7, 22), TextPosition(8, 8), '\n '),
|
|
112
|
+
Token(TokenType.TEXT, TextPosition(8, 8), TextPosition(8, 14), 'param6'),
|
|
113
|
+
Token(TokenType.PUNCTUATOR, TextPosition(8, 14), TextPosition(8, 15), '='),
|
|
114
|
+
Token(TokenType.TEXT, TextPosition(8, 15), TextPosition(8, 20), 'value'),
|
|
115
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(8, 20), TextPosition(8, 21), ','),
|
|
116
|
+
Token(TokenType.WHITESPACE, TextPosition(8, 21), TextPosition(9, 8), '\n '),
|
|
117
|
+
Token(TokenType.TEXT, TextPosition(9, 8), TextPosition(9, 14), 'param7'),
|
|
118
|
+
Token(TokenType.WHITESPACE, TextPosition(9, 14), TextPosition(9, 21), ' '),
|
|
119
|
+
Token(TokenType.PUNCTUATOR, TextPosition(9, 21), TextPosition(9, 22), '='),
|
|
120
|
+
Token(TokenType.WHITESPACE, TextPosition(9, 22), TextPosition(9, 41), ' '),
|
|
121
|
+
Token(TokenType.TEXT, TextPosition(9, 41), TextPosition(9, 45), 'fuck'),
|
|
122
|
+
Token(TokenType.WHITESPACE, TextPosition(9, 45), TextPosition(9, 46), ' '),
|
|
123
|
+
Token(TokenType.TEXT, TextPosition(9, 46), TextPosition(9, 49), 'you'),
|
|
124
|
+
Token(TokenType.WHITESPACE, TextPosition(9, 49), TextPosition(9, 50), ' '),
|
|
125
|
+
Token(TokenType.TEXT, TextPosition(9, 50), TextPosition(9, 51), '?'),
|
|
126
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(9, 51), TextPosition(9, 52), ','),
|
|
127
|
+
Token(TokenType.WHITESPACE, TextPosition(9, 52), TextPosition(11, 8), '\n\n '),
|
|
128
|
+
Token(TokenType.TEXT, TextPosition(11, 8), TextPosition(11, 19), 'weird_param'),
|
|
129
|
+
Token(TokenType.WHITESPACE, TextPosition(11, 19), TextPosition(11, 20), ' '),
|
|
130
|
+
Token(TokenType.PUNCTUATOR, TextPosition(11, 20), TextPosition(11, 21), '='),
|
|
131
|
+
Token(TokenType.WHITESPACE, TextPosition(11, 21), TextPosition(11, 22), ' '),
|
|
132
|
+
Token(TokenType.TEXT, TextPosition(11, 22), TextPosition(11, 26), 'key1'),
|
|
133
|
+
Token(TokenType.PUNCTUATOR, TextPosition(11, 26), TextPosition(11, 27), '='),
|
|
134
|
+
Token(TokenType.TEXT, TextPosition(11, 27), TextPosition(11, 31), 'val1'),
|
|
135
|
+
Token(TokenType.PUNCTUATOR, TextPosition(11, 31), TextPosition(11, 32), ':'),
|
|
136
|
+
Token(TokenType.TEXT, TextPosition(11, 32), TextPosition(11, 36), 'key2'),
|
|
137
|
+
Token(TokenType.PUNCTUATOR, TextPosition(11, 36), TextPosition(11, 37), '='),
|
|
138
|
+
Token(TokenType.TEXT, TextPosition(11, 37), TextPosition(11, 41), 'val2'),
|
|
139
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(11, 41), TextPosition(11, 42), ','),
|
|
140
|
+
Token(TokenType.WHITESPACE, TextPosition(11, 42), TextPosition(13, 8), '\n\n '),
|
|
141
|
+
Token(TokenType.TEXT, TextPosition(13, 8), TextPosition(13, 19), 'to_new_line'),
|
|
142
|
+
Token(TokenType.WHITESPACE, TextPosition(13, 19), TextPosition(13, 20), ' '),
|
|
143
|
+
Token(TokenType.PUNCTUATOR, TextPosition(13, 20), TextPosition(13, 21), '='),
|
|
144
|
+
Token(TokenType.WHITESPACE, TextPosition(13, 21), TextPosition(14, 8), ' \n '),
|
|
145
|
+
Token(TokenType.TEXT, TextPosition(14, 8), TextPosition(14, 13), 'value'),
|
|
146
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(14, 13), TextPosition(14, 14), ','),
|
|
147
|
+
Token(TokenType.WHITESPACE, TextPosition(14, 14), TextPosition(16, 8), '\n\n '),
|
|
148
|
+
Token(TokenType.COMMENT, TextPosition(16, 8), TextPosition(16, 20), '/* values */'),
|
|
149
|
+
Token(TokenType.WHITESPACE, TextPosition(16, 20), TextPosition(17, 8), '\n '),
|
|
150
|
+
Token(TokenType.TEXT, TextPosition(17, 8), TextPosition(17, 14), 'value1'),
|
|
151
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(17, 14), TextPosition(17, 15), ','),
|
|
152
|
+
Token(TokenType.WHITESPACE, TextPosition(17, 15), TextPosition(18, 8), '\n '),
|
|
153
|
+
Token(TokenType.TEXT, TextPosition(18, 8), TextPosition(18, 14), 'value2'),
|
|
154
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(18, 14), TextPosition(18, 15), ','),
|
|
155
|
+
Token(TokenType.WHITESPACE, TextPosition(18, 15), TextPosition(19, 8), '\n '),
|
|
156
|
+
Token(TokenType.TEXT, TextPosition(19, 8), TextPosition(19, 14), 'value3'),
|
|
157
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(19, 14), TextPosition(19, 15), ','),
|
|
158
|
+
Token(TokenType.WHITESPACE, TextPosition(19, 15), TextPosition(20, 8), '\n '),
|
|
159
|
+
Token(TokenType.TEXT, TextPosition(20, 8), TextPosition(20, 14), 'value4'),
|
|
160
|
+
Token(TokenType.PUNCTUATOR, TextPosition(20, 14), TextPosition(20, 15), ':'),
|
|
161
|
+
Token(TokenType.TEXT, TextPosition(20, 15), TextPosition(20, 21), 'value5'),
|
|
162
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(20, 21), TextPosition(20, 22), ','),
|
|
163
|
+
Token(TokenType.WHITESPACE, TextPosition(20, 22), TextPosition(21, 4), '\n '),
|
|
164
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(21, 4), TextPosition(21, 5), '}'),
|
|
165
|
+
Token(TokenType.WHITESPACE, TextPosition(21, 5), TextPosition(23, 4), '\n\n '),
|
|
166
|
+
Token(TokenType.TEXT, TextPosition(23, 4), TextPosition(23, 9), 'sound'),
|
|
167
|
+
Token(TokenType.WHITESPACE, TextPosition(23, 9), TextPosition(23, 10), ' '),
|
|
168
|
+
Token(TokenType.TEXT, TextPosition(23, 10), TextPosition(23, 19), 'SoundTest'),
|
|
169
|
+
Token(TokenType.WHITESPACE, TextPosition(23, 19), TextPosition(23, 20), ' '),
|
|
170
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(23, 20), TextPosition(23, 21), '{'),
|
|
171
|
+
Token(TokenType.WHITESPACE, TextPosition(23, 21), TextPosition(24, 8), '\n '),
|
|
172
|
+
Token(TokenType.TEXT, TextPosition(24, 8), TextPosition(24, 12), 'clip'),
|
|
173
|
+
Token(TokenType.WHITESPACE, TextPosition(24, 12), TextPosition(24, 13), ' '),
|
|
174
|
+
Token(TokenType.PUNCTUATOR, TextPosition(24, 13), TextPosition(24, 14), '='),
|
|
175
|
+
Token(TokenType.WHITESPACE, TextPosition(24, 14), TextPosition(24, 15), ' '),
|
|
176
|
+
Token(TokenType.TEXT, TextPosition(24, 15), TextPosition(24, 32), 'this/path/to/clip'),
|
|
177
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(24, 32), TextPosition(24, 33), ','),
|
|
178
|
+
Token(TokenType.WHITESPACE, TextPosition(24, 33), TextPosition(24, 34), ' '),
|
|
179
|
+
Token(TokenType.TEXT, TextPosition(24, 34), TextPosition(24, 40), 'volume'),
|
|
180
|
+
Token(TokenType.WHITESPACE, TextPosition(24, 40), TextPosition(24, 41), ' '),
|
|
181
|
+
Token(TokenType.PUNCTUATOR, TextPosition(24, 41), TextPosition(24, 42), '='),
|
|
182
|
+
Token(TokenType.WHITESPACE, TextPosition(24, 42), TextPosition(24, 43), ' '),
|
|
183
|
+
Token(TokenType.TEXT, TextPosition(24, 43), TextPosition(24, 44), '0'),
|
|
184
|
+
Token(TokenType.PUNCTUATOR, TextPosition(24, 44), TextPosition(24, 45), '.'),
|
|
185
|
+
Token(TokenType.TEXT, TextPosition(24, 45), TextPosition(24, 46), '8'),
|
|
186
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(24, 46), TextPosition(24, 47), ','),
|
|
187
|
+
Token(TokenType.WHITESPACE, TextPosition(24, 47), TextPosition(25, 4), '\n '),
|
|
188
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(25, 4), TextPosition(25, 5), '}'),
|
|
189
|
+
Token(TokenType.WHITESPACE, TextPosition(25, 5), TextPosition(27, 4), '\n\n '),
|
|
190
|
+
Token(TokenType.TEXT, TextPosition(27, 4), TextPosition(27, 9), 'model'),
|
|
191
|
+
Token(TokenType.WHITESPACE, TextPosition(27, 9), TextPosition(27, 10), ' '),
|
|
192
|
+
Token(TokenType.TEXT, TextPosition(27, 10), TextPosition(27, 12), 'id'),
|
|
193
|
+
Token(TokenType.WHITESPACE, TextPosition(27, 12), TextPosition(27, 13), ' '),
|
|
194
|
+
Token(TokenType.TEXT, TextPosition(27, 13), TextPosition(27, 17), 'with'),
|
|
195
|
+
Token(TokenType.WHITESPACE, TextPosition(27, 17), TextPosition(27, 18), ' '),
|
|
196
|
+
Token(TokenType.TEXT, TextPosition(27, 18), TextPosition(27, 24), 'spaces'),
|
|
197
|
+
Token(TokenType.WHITESPACE, TextPosition(27, 24), TextPosition(27, 25), ' '),
|
|
198
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(27, 25), TextPosition(27, 26), '{'),
|
|
199
|
+
Token(TokenType.WHITESPACE, TextPosition(27, 26), TextPosition(29, 4), '\n \n '),
|
|
200
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(29, 4), TextPosition(29, 5), '}'),
|
|
201
|
+
Token(TokenType.WHITESPACE, TextPosition(29, 5), TextPosition(31, 4), '\n\n '),
|
|
202
|
+
Token(TokenType.COMMENT, TextPosition(31, 4), TextPosition(32, 18), '/* comment, different syntax block under\n hello world */'),
|
|
203
|
+
Token(TokenType.COMMENT, TextPosition(32, 18), TextPosition(32, 41), '/* comment continued */'),
|
|
204
|
+
Token(TokenType.WHITESPACE, TextPosition(32, 41), TextPosition(34, 4), '\n \n '),
|
|
205
|
+
Token(TokenType.TEXT, TextPosition(34, 4), TextPosition(34, 25), 'this_is_a_weird_block'),
|
|
206
|
+
Token(TokenType.WHITESPACE, TextPosition(34, 25), TextPosition(34, 26), ' '),
|
|
207
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(34, 26), TextPosition(34, 27), '{'),
|
|
208
|
+
Token(TokenType.WHITESPACE, TextPosition(34, 27), TextPosition(36, 4), '\n \n '),
|
|
209
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(36, 4), TextPosition(36, 5), '}'),
|
|
210
|
+
Token(TokenType.WHITESPACE, TextPosition(36, 5), TextPosition(38, 4), '\n\n '),
|
|
211
|
+
Token(TokenType.TEXT, TextPosition(38, 4), TextPosition(38, 8), 'item'),
|
|
212
|
+
Token(TokenType.WHITESPACE, TextPosition(38, 8), TextPosition(38, 9), ' '),
|
|
213
|
+
Token(TokenType.TEXT, TextPosition(38, 9), TextPosition(38, 18), 'ItemTest2'),
|
|
214
|
+
Token(TokenType.WHITESPACE, TextPosition(38, 18), TextPosition(39, 4), '\n '),
|
|
215
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(39, 4), TextPosition(39, 5), '{'),
|
|
216
|
+
Token(TokenType.WHITESPACE, TextPosition(39, 5), TextPosition(41, 4), '\n\n '),
|
|
217
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(41, 4), TextPosition(41, 5), '}'),
|
|
218
|
+
Token(TokenType.WHITESPACE, TextPosition(41, 5), TextPosition(42, 0), '\n'),
|
|
219
|
+
Token(TokenType.ELEMENT_DELIMITER, TextPosition(42, 0), TextPosition(42, 1), '}'),
|
|
220
|
+
]
|
|
221
|
+
)
|
|
222
|
+
return EXPECTED_LEXICAL_TOKENS
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def test_lexer():
|
|
228
|
+
"""Test the lexer by comparing its output against the theorical tokens."""
|
|
229
|
+
lexical_tokens = parse_example_content()
|
|
230
|
+
theorical_tokens = get_theorical_tokens()
|
|
231
|
+
|
|
232
|
+
for token, theorical_token in zip(lexical_tokens, theorical_tokens):
|
|
233
|
+
print(repr(theorical_token).ljust(80), repr(token).ljust(80))
|
|
234
|
+
assert token == theorical_token, f"Theorical token: {theorical_token!r}, Actual token: {token!r}"
|
|
235
|
+
|
|
236
|
+
def generate_theorical_tokens():
|
|
237
|
+
"""
|
|
238
|
+
Assuming the tokens currently being parsed are already the right ones, generate the comparison list of theorical tokens
|
|
239
|
+
we are supposed to compare against.
|
|
240
|
+
"""
|
|
241
|
+
lexical_tokens = parse_example_content()
|
|
242
|
+
for token in lexical_tokens:
|
|
243
|
+
print(f"Token({token.type}, TextPosition({token.pos.line}, {token.pos.offset}), TextPosition({token.end.line}, {token.end.offset}), {token.text!r}),")
|
|
244
|
+
|
|
245
|
+
def main():
|
|
246
|
+
import argparse
|
|
247
|
+
|
|
248
|
+
parser = argparse.ArgumentParser(description="Lexer test.")
|
|
249
|
+
parser.add_argument("--run", action="store_true", help="Run the lexer test.")
|
|
250
|
+
parser.add_argument("--generate", action="store_true", help="Generate the theorical tokens.")
|
|
251
|
+
args = parser.parse_args()
|
|
252
|
+
if args.run:
|
|
253
|
+
test_lexer()
|
|
254
|
+
elif args.generate:
|
|
255
|
+
generate_theorical_tokens()
|
|
256
|
+
else:
|
|
257
|
+
parser.print_help()
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
if __name__ == "__main__":
|
|
261
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ZedScripts
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Author: SimKDT
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -36,6 +36,8 @@ Provides-Extra: build
|
|
|
36
36
|
Requires-Dist: pyinstaller; extra == "build"
|
|
37
37
|
Provides-Extra: debug
|
|
38
38
|
Requires-Dist: debugpy; extra == "debug"
|
|
39
|
+
Provides-Extra: test
|
|
40
|
+
Requires-Dist: pytest; extra == "test"
|
|
39
41
|
Dynamic: license-file
|
|
40
42
|
|
|
41
43
|
# ZedScripts LSP
|
|
@@ -211,6 +213,10 @@ Each diagnostic by default have a specific set of modifiers that can be overridd
|
|
|
211
213
|
}
|
|
212
214
|
```
|
|
213
215
|
|
|
216
|
+
### Server
|
|
217
|
+
A list of server-related configurations for the LSP. The following parameters are available:
|
|
218
|
+
- `logging_level` (optional, default: `INFO`): specifies the logging level for the server. This option is only available in the global configuration and accepts values such as `NOTSET`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, and `CRITICAL`.
|
|
219
|
+
|
|
214
220
|
## IDE configuration
|
|
215
221
|
Based on the IDE you use, you may need to install the ZedScripts LSP application for your OS in the [releases](https://github.com/PZ-Wiki-Modding/ZedScripts-LSP/releases) (Linux, Windows).
|
|
216
222
|
|
|
@@ -36,4 +36,6 @@ src/ZedScripts/scripts/value.py
|
|
|
36
36
|
src/ZedScripts/scripts/version.py
|
|
37
37
|
src/ZedScripts/structure/ast.py
|
|
38
38
|
src/ZedScripts/structure/lexer.py
|
|
39
|
-
src/ZedScripts/structure/parser.py
|
|
39
|
+
src/ZedScripts/structure/parser.py
|
|
40
|
+
src/ZedScripts/tests/parser/test_lexer.py
|
|
41
|
+
tests/test_config_cli.py
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from ZedScripts.environment.config import ConfigurationModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_default_config_cli():
|
|
9
|
+
result = subprocess.run(
|
|
10
|
+
[sys.executable, "-m", "ZedScripts.environment.config", "default"],
|
|
11
|
+
capture_output=True,
|
|
12
|
+
text=True,
|
|
13
|
+
check=True,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
output = json.loads(result.stdout)
|
|
17
|
+
assert output["libraries"] == []
|
|
18
|
+
assert isinstance(output["ignored"], list)
|
|
19
|
+
assert ConfigurationModel.model_validate_json(result.stdout) == ConfigurationModel()
|
|
20
|
+
assert '\n "dataset": {' in result.stdout
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_config_schema_cli():
|
|
24
|
+
result = subprocess.run(
|
|
25
|
+
[sys.executable, "-m", "ZedScripts.environment.config", "schema"],
|
|
26
|
+
capture_output=True,
|
|
27
|
+
text=True,
|
|
28
|
+
check=True,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
assert json.loads(result.stdout) == ConfigurationModel.model_json_schema()
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import logging
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
IS_DEBUG = os.getenv("ZEDSCRIPTS_DEBUG_MODE") == "1"
|
|
6
|
-
|
|
7
|
-
ZEDSCRIPT_CACHE_DIR = Path.home() / ".zedscripts"
|
|
8
|
-
"""
|
|
9
|
-
Cache directory to hold various information related to ZedScripts.
|
|
10
|
-
"""
|
|
11
|
-
ZEDSCRIPT_CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
12
|
-
|
|
13
|
-
# config files
|
|
14
|
-
CONFIGURATION_FILE_NAME = ".zedscripts.json"
|
|
15
|
-
GLOBAL_CONFIGURATION_FILE = (ZEDSCRIPT_CACHE_DIR / CONFIGURATION_FILE_NAME).resolve()
|
|
16
|
-
|
|
17
|
-
# setup the logger
|
|
18
|
-
log_file = ZEDSCRIPT_CACHE_DIR / "server.log"
|
|
19
|
-
|
|
20
|
-
level = logging.DEBUG if IS_DEBUG else logging.INFO
|
|
21
|
-
logging.basicConfig(
|
|
22
|
-
filename=log_file,
|
|
23
|
-
level=level,
|
|
24
|
-
format="%(asctime)s %(levelname)s %(message)s",
|
|
25
|
-
filemode="w",
|
|
26
|
-
# force=True,
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
logging.info("ZedScripts logger initialized. Debug mode: %s", IS_DEBUG)
|
|
30
|
-
|
|
31
|
-
IDENTIFIER = "ZedScripts"
|
|
32
|
-
SOURCE = IDENTIFIER
|
|
33
|
-
|
|
34
|
-
SCRIPTS_DATA_MANIFEST = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/heads/main/manifest.json"
|
|
35
|
-
SCRIPTS_BLOCKS_DATA_LINK = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/tags/{best_tag}/out/scriptsBlocks.json"
|
|
36
|
-
ROOTS_DATA_LINK = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/tags/{best_tag}/out/roots.json"
|
|
37
|
-
|
|
38
|
-
SCRIPTSDOCS_LINK = "https://pz-wiki-modding.github.io/PZ-API-Docs/scripts/"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|