ZedScripts 1.0.1__tar.gz → 1.2.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.1/src/ZedScripts.egg-info → zedscripts-1.2.0}/PKG-INFO +28 -1
- {zedscripts-1.0.1 → zedscripts-1.2.0}/README.md +23 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/pyproject.toml +10 -1
- zedscripts-1.2.0/src/ZedScripts/__about__.py +7 -0
- zedscripts-1.2.0/src/ZedScripts/__init__.py +75 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/enums/Diagnostic.py +7 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/environment/__init__.py +8 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/environment/config.py +91 -8
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/environment/document.py +2 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/environment/workspace.py +14 -1
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/locale/en.json +3 -1
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/scripts/value.py +9 -3
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/server.py +10 -2
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/structure/lexer.py +24 -19
- zedscripts-1.2.0/src/ZedScripts/structure/pygments_lexer.py +79 -0
- zedscripts-1.2.0/src/ZedScripts/tests/parser/test_lexer.py +318 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0/src/ZedScripts.egg-info}/PKG-INFO +28 -1
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts.egg-info/SOURCES.txt +5 -1
- zedscripts-1.2.0/src/ZedScripts.egg-info/entry_points.txt +2 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts.egg-info/requires.txt +6 -0
- zedscripts-1.2.0/tests/test_config_cli.py +31 -0
- zedscripts-1.0.1/src/ZedScripts/__about__.py +0 -4
- zedscripts-1.0.1/src/ZedScripts/__init__.py +0 -38
- {zedscripts-1.0.1 → zedscripts-1.2.0}/LICENSE +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/setup.cfg +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/enums/SyntaxErrorType.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/environment/mod.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/environment/version.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/main.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/providers/actions.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/providers/capabilities.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/providers/debug.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/providers/diagnostics.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/providers/hover.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/providers/http.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/providers/locale.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/providers/notifications.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/providers/semantic_tokens.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/scripts/__init__.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/scripts/block.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/scripts/dataset.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/scripts/version.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/structure/ast.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/structure/parser.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts/utils.py +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.0}/src/ZedScripts.egg-info/dependency_links.txt +0 -0
- {zedscripts-1.0.1 → zedscripts-1.2.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.0
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Author: SimKDT
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -36,6 +36,10 @@ 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"
|
|
41
|
+
Provides-Extra: pygments
|
|
42
|
+
Requires-Dist: pygments; extra == "pygments"
|
|
39
43
|
Dynamic: license-file
|
|
40
44
|
|
|
41
45
|
# ZedScripts LSP
|
|
@@ -211,6 +215,10 @@ Each diagnostic by default have a specific set of modifiers that can be overridd
|
|
|
211
215
|
}
|
|
212
216
|
```
|
|
213
217
|
|
|
218
|
+
### Server
|
|
219
|
+
A list of server-related configurations for the LSP. The following parameters are available:
|
|
220
|
+
- `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`.
|
|
221
|
+
|
|
214
222
|
## IDE configuration
|
|
215
223
|
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
224
|
|
|
@@ -238,5 +246,24 @@ autocmd User LspSetup call LspAddServer(lspServers)
|
|
|
238
246
|
> [!NOTE]
|
|
239
247
|
> You can see [this video](https://www.youtube.com/watch?v=-5lb_jLQmKc) which explains how to set up VIM, and notably setup a LSP server.
|
|
240
248
|
|
|
249
|
+
## Pygments
|
|
250
|
+
|
|
251
|
+
The ZedScripts LSP provides a Pygments lexer for syntax highlighting of ZedScripts code. It is available in the `ZedScripts.structure.pygments_lexer` module. You can notably use the command-line wrapper to highlight any file's content as ZedScripts:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
uv pip install ZedScripts[pygments]
|
|
255
|
+
highlight-zedscripts path/to/file.txt
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+

|
|
259
|
+
|
|
260
|
+
To use the lexer in Sphinx docs, you can add the following to your `conf.py`:
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
from ZedScripts.structure.pygments_lexer import ZedScriptsLexer
|
|
264
|
+
from sphinx.highlighting import lexers
|
|
265
|
+
lexers['zedscripts'] = ZedScriptsLexer()
|
|
266
|
+
```
|
|
267
|
+
|
|
241
268
|
## License and contribution
|
|
242
269
|
This project is licensed under a [MIT license](LICENSE). Before contributing to the project, please see [CONTRIBUTING](CONTRIBUTING.md).
|
|
@@ -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
|
|
|
@@ -198,5 +202,24 @@ autocmd User LspSetup call LspAddServer(lspServers)
|
|
|
198
202
|
> [!NOTE]
|
|
199
203
|
> You can see [this video](https://www.youtube.com/watch?v=-5lb_jLQmKc) which explains how to set up VIM, and notably setup a LSP server.
|
|
200
204
|
|
|
205
|
+
## Pygments
|
|
206
|
+
|
|
207
|
+
The ZedScripts LSP provides a Pygments lexer for syntax highlighting of ZedScripts code. It is available in the `ZedScripts.structure.pygments_lexer` module. You can notably use the command-line wrapper to highlight any file's content as ZedScripts:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
uv pip install ZedScripts[pygments]
|
|
211
|
+
highlight-zedscripts path/to/file.txt
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+

|
|
215
|
+
|
|
216
|
+
To use the lexer in Sphinx docs, you can add the following to your `conf.py`:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from ZedScripts.structure.pygments_lexer import ZedScriptsLexer
|
|
220
|
+
from sphinx.highlighting import lexers
|
|
221
|
+
lexers['zedscripts'] = ZedScriptsLexer()
|
|
222
|
+
```
|
|
223
|
+
|
|
201
224
|
## License and contribution
|
|
202
225
|
This project is licensed under a [MIT license](LICENSE). Before contributing to the project, please see [CONTRIBUTING](CONTRIBUTING.md).
|
|
@@ -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.0
|
|
6
|
+
version = "1.2.0"
|
|
7
7
|
license = {file = "LICENSE"}
|
|
8
8
|
authors = [
|
|
9
9
|
{ name = "SimKDT" },
|
|
@@ -23,12 +23,21 @@ build = [
|
|
|
23
23
|
debug = [
|
|
24
24
|
"debugpy"
|
|
25
25
|
]
|
|
26
|
+
test = [
|
|
27
|
+
"pytest"
|
|
28
|
+
]
|
|
29
|
+
pygments = [
|
|
30
|
+
"pygments"
|
|
31
|
+
]
|
|
26
32
|
|
|
27
33
|
# used to load locale JSON files
|
|
28
34
|
[build-system]
|
|
29
35
|
requires = ["setuptools>=61"]
|
|
30
36
|
build-backend = "setuptools.build_meta"
|
|
31
37
|
|
|
38
|
+
[project.scripts]
|
|
39
|
+
highlight-zedscripts = "ZedScripts.structure.pygments_lexer:main"
|
|
40
|
+
|
|
32
41
|
[tool.setuptools.package-data]
|
|
33
42
|
ZedScripts = [
|
|
34
43
|
"locale/*.json"
|
|
@@ -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/"
|
|
@@ -52,6 +52,9 @@ class DiagnosticType(enum.StrEnum):
|
|
|
52
52
|
VALUE_UNMATCHED_BLOCK_REF = enum.auto()
|
|
53
53
|
VALUE_MULTIPLE_BLOCK_REFS = enum.auto()
|
|
54
54
|
|
|
55
|
+
# debug
|
|
56
|
+
DEBUG_MISSING_PARAMETER_DESCRIPTION = enum.auto()
|
|
57
|
+
|
|
55
58
|
|
|
56
59
|
# syntax diagnostics
|
|
57
60
|
DiagnosticDefinition(DiagnosticType.PARSER_TOO_MANY_CLOSING_BRACKETS,
|
|
@@ -155,6 +158,10 @@ DiagnosticDefinition(DiagnosticType.VALUE_MULTIPLE_BLOCK_REFS,
|
|
|
155
158
|
DiagnosticSeverity.Error,
|
|
156
159
|
args={"value": str, "parameter": str, "expected_block": str})
|
|
157
160
|
|
|
161
|
+
DiagnosticDefinition(DiagnosticType.DEBUG_MISSING_PARAMETER_DESCRIPTION,
|
|
162
|
+
DiagnosticSeverity.Information,
|
|
163
|
+
args={"type": str, "key": str})
|
|
164
|
+
|
|
158
165
|
|
|
159
166
|
# ensure that all diagnostic types have a corresponding definition
|
|
160
167
|
for diagnostic_type in DiagnosticType:
|
|
@@ -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
|
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
"VALUE_CANNOT_PROVIDE_MODULE": "Value '{value}' cannot contain a module '{module}' reference, only direct block references are allowed.",
|
|
34
34
|
"VALUE_BLOCK_REF_CANNOT_BE_EMPTY": "Value '{value}' has an empty block reference, which is not allowed.",
|
|
35
35
|
"VALUE_UNMATCHED_BLOCK_REF": "Value '{value}' for parameter '{parameter}' does not match any existing block (expects '{expected_block}' block). Make sure the referenced block exists.",
|
|
36
|
-
"VALUE_MULTIPLE_BLOCK_REFS": "Multiple block references found for '{value}' for parameter '{parameter}' (expects '{expected_block}' block). Make sure duplicate block references are not present."
|
|
36
|
+
"VALUE_MULTIPLE_BLOCK_REFS": "Multiple block references found for '{value}' for parameter '{parameter}' (expects '{expected_block}' block). Make sure duplicate block references are not present.",
|
|
37
|
+
|
|
38
|
+
"DEBUG_MISSING_PARAMETER_DESCRIPTION": "DEBUG: Parameter '{key}' in block '{type}' is missing a description."
|
|
37
39
|
}
|
|
38
40
|
}
|
|
@@ -240,7 +240,7 @@ class Value(Element["ValueNode"]):
|
|
|
240
240
|
if not dataset.can_block_have_parameter(parent_type, key):
|
|
241
241
|
return ''
|
|
242
242
|
param_data = dataset.get_parameter_data(parent_type, key)
|
|
243
|
-
return param_data.get('description', '')
|
|
243
|
+
return param_data.get('description', '')
|
|
244
244
|
|
|
245
245
|
def get_scriptsdocs_url(self, tree: list[str]) -> str | None:
|
|
246
246
|
return self.parent.get_scriptsdocs_url(tree) + f"#scripts-{self.parent.type}-{self.key()}".replace(' ', '-').lower()
|
|
@@ -413,6 +413,14 @@ class Value(Element["ValueNode"]):
|
|
|
413
413
|
|
|
414
414
|
param_data = dataset.get_parameter_data(parent_type, key)
|
|
415
415
|
|
|
416
|
+
# DEBUG: add information diagnostic if there is not description for the parameter
|
|
417
|
+
if not param_data.get('description', ''):
|
|
418
|
+
self.parent.add_diagnostic(
|
|
419
|
+
type=DiagnosticType.DEBUG_MISSING_PARAMETER_DESCRIPTION,
|
|
420
|
+
location=key_node.to_range(),
|
|
421
|
+
args={"type": parent_type, "key": key}
|
|
422
|
+
)
|
|
423
|
+
|
|
416
424
|
# do the type highlight before anything else
|
|
417
425
|
# so we highlight by type even if the value is otherwise invalid
|
|
418
426
|
type_data = param_data.get('type')
|
|
@@ -652,8 +660,6 @@ class Value(Element["ValueNode"]):
|
|
|
652
660
|
)
|
|
653
661
|
return False
|
|
654
662
|
|
|
655
|
-
# TODO: look for the source block
|
|
656
|
-
|
|
657
663
|
root = self.parent.get_root()
|
|
658
664
|
|
|
659
665
|
# retrieve imports
|
|
@@ -47,7 +47,7 @@ class ZedServer(LanguageServer):
|
|
|
47
47
|
try:
|
|
48
48
|
import debugpy
|
|
49
49
|
except ImportError:
|
|
50
|
-
logging.error("debugpy is not installed. Debugging will not be available.")
|
|
50
|
+
logging.error("debugpy is not installed. Debugging will not be available. Install the `debug` group dependencies (`pip install ZedScripts[debug]`).")
|
|
51
51
|
return
|
|
52
52
|
|
|
53
53
|
debug_port = 5678
|
|
@@ -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__":
|