ZedScripts 0.0.2__tar.gz → 0.0.3__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.
Files changed (44) hide show
  1. {zedscripts-0.0.2/src/ZedScripts.egg-info → zedscripts-0.0.3}/PKG-INFO +33 -6
  2. {zedscripts-0.0.2 → zedscripts-0.0.3}/README.md +32 -5
  3. {zedscripts-0.0.2 → zedscripts-0.0.3}/pyproject.toml +8 -1
  4. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/enums/Diagnostic.py +1 -1
  5. zedscripts-0.0.3/src/ZedScripts/environment/config.py +179 -0
  6. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/environment/document.py +10 -14
  7. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/environment/workspace.py +46 -18
  8. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/providers/diagnostics.py +38 -11
  9. zedscripts-0.0.3/src/ZedScripts/providers/notifications.py +57 -0
  10. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/scripts/block.py +2 -1
  11. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/scripts/value.py +1 -1
  12. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/server.py +30 -10
  13. {zedscripts-0.0.2 → zedscripts-0.0.3/src/ZedScripts.egg-info}/PKG-INFO +33 -6
  14. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts.egg-info/SOURCES.txt +0 -1
  15. zedscripts-0.0.2/src/ZedScripts/environment/config.py +0 -82
  16. zedscripts-0.0.2/src/ZedScripts/providers/configuration.py +0 -26
  17. zedscripts-0.0.2/src/ZedScripts/providers/notifications.py +0 -45
  18. {zedscripts-0.0.2 → zedscripts-0.0.3}/LICENSE +0 -0
  19. {zedscripts-0.0.2 → zedscripts-0.0.3}/setup.cfg +0 -0
  20. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/__about__.py +0 -0
  21. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/__init__.py +0 -0
  22. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/enums/SyntaxErrorType.py +0 -0
  23. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/environment/__init__.py +0 -0
  24. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/environment/mod.py +0 -0
  25. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/environment/version.py +0 -0
  26. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/locale/en.json +0 -0
  27. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/main.py +0 -0
  28. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/providers/actions.py +0 -0
  29. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/providers/capabilities.py +0 -0
  30. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/providers/debug.py +0 -0
  31. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/providers/hover.py +0 -0
  32. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/providers/http.py +0 -0
  33. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/providers/locale.py +0 -0
  34. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/providers/semantic_tokens.py +0 -0
  35. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/scripts/__init__.py +0 -0
  36. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/scripts/dataset.py +0 -0
  37. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/scripts/version.py +0 -0
  38. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/structure/ast.py +0 -0
  39. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/structure/lexer.py +0 -0
  40. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/structure/parser.py +0 -0
  41. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts/utils.py +0 -0
  42. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts.egg-info/dependency_links.txt +0 -0
  43. {zedscripts-0.0.2 → zedscripts-0.0.3}/src/ZedScripts.egg-info/requires.txt +0 -0
  44. {zedscripts-0.0.2 → zedscripts-0.0.3}/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: 0.0.2
3
+ Version: 0.0.3
4
4
  Author: SimKDT
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.12.3
@@ -19,11 +19,11 @@ Dynamic: license-file
19
19
 
20
20
  # ZedScripts LSP
21
21
 
22
- ![ZedScripts-LSP](https://img.shields.io/badge/Latest_version-0.0.2-blue)
23
- [![License](https://img.shields.io/github/license/PZ-Wiki-Modding/ZedScripts-LSP?label=License)](LICENSE)
24
- ![Code Size](https://img.shields.io/github/languages/code-size/PZ-Wiki-Modding/ZedScripts-LSP?label=Code%20Size)
25
- [![PyPi Version](https://img.shields.io/pypi/v/ZedScripts-LSP)](https://pypi.org/project/ZedScripts-LSP/)
26
- [![PyPi Downloads](https://img.shields.io/pypi/dm/ZedScripts-LSP)](https://pypi.org/project/ZedScripts-LSP/)
22
+ [![License](https://img.shields.io/github/license/PZ-Wiki-Modding/ZedScripts?label=License)](LICENSE)
23
+ ![Code Size](https://img.shields.io/github/languages/code-size/PZ-Wiki-Modding/ZedScripts?label=Code%20Size)
24
+ [![PyPi Version](https://img.shields.io/pypi/v/ZedScripts)](https://pypi.org/project/ZedScripts/)
25
+ [![PyPi Downloads](https://img.shields.io/pypi/dm/ZedScripts)](https://pypi.org/project/ZedScripts/)
26
+ ![PyPI Python Version](https://img.shields.io/pypi/pyversions/ZedScripts)
27
27
 
28
28
  > [!IMPORTANT]
29
29
  > Work In Progress, this is a BETA release
@@ -101,5 +101,32 @@ Example configuration:
101
101
 
102
102
  WIP: proper documentation will come soon
103
103
 
104
+ ## IDE configuration
105
+ 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).
106
+
107
+ ### Visual Studio Code
108
+
109
+ Install the [ZedScripts extension](https://marketplace.visualstudio.com/items?itemName=SimKDT.ZedScripts) from the Visual Studio Code marketplace. The extension will handle the downloading and setup of the ZedScripts LSP server for you.
110
+
111
+ ### VIM
112
+
113
+ Using [yegappan/lsp](https://github.com/yegappan/lsp), setup the ZedScripts LSP server as follows:
114
+
115
+ ```vim
116
+ let lspServers = [
117
+ \ #{
118
+ \ name: 'zedscripts',
119
+ \ filetype: ['text', 'zedscripts'],
120
+ \ path: 'path/to/ZedScripts',
121
+ \ args: []
122
+ \ }
123
+ \ ]
124
+
125
+ autocmd User LspSetup call LspAddServer(lspServers)
126
+ ```
127
+
128
+ > [!NOTE]
129
+ > 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.
130
+
104
131
  ## License
105
132
  See [LICENSE](LICENSE) for details.
@@ -1,10 +1,10 @@
1
1
  # ZedScripts LSP
2
2
 
3
- ![ZedScripts-LSP](https://img.shields.io/badge/Latest_version-0.0.2-blue)
4
- [![License](https://img.shields.io/github/license/PZ-Wiki-Modding/ZedScripts-LSP?label=License)](LICENSE)
5
- ![Code Size](https://img.shields.io/github/languages/code-size/PZ-Wiki-Modding/ZedScripts-LSP?label=Code%20Size)
6
- [![PyPi Version](https://img.shields.io/pypi/v/ZedScripts-LSP)](https://pypi.org/project/ZedScripts-LSP/)
7
- [![PyPi Downloads](https://img.shields.io/pypi/dm/ZedScripts-LSP)](https://pypi.org/project/ZedScripts-LSP/)
3
+ [![License](https://img.shields.io/github/license/PZ-Wiki-Modding/ZedScripts?label=License)](LICENSE)
4
+ ![Code Size](https://img.shields.io/github/languages/code-size/PZ-Wiki-Modding/ZedScripts?label=Code%20Size)
5
+ [![PyPi Version](https://img.shields.io/pypi/v/ZedScripts)](https://pypi.org/project/ZedScripts/)
6
+ [![PyPi Downloads](https://img.shields.io/pypi/dm/ZedScripts)](https://pypi.org/project/ZedScripts/)
7
+ ![PyPI Python Version](https://img.shields.io/pypi/pyversions/ZedScripts)
8
8
 
9
9
  > [!IMPORTANT]
10
10
  > Work In Progress, this is a BETA release
@@ -82,5 +82,32 @@ Example configuration:
82
82
 
83
83
  WIP: proper documentation will come soon
84
84
 
85
+ ## IDE configuration
86
+ 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).
87
+
88
+ ### Visual Studio Code
89
+
90
+ Install the [ZedScripts extension](https://marketplace.visualstudio.com/items?itemName=SimKDT.ZedScripts) from the Visual Studio Code marketplace. The extension will handle the downloading and setup of the ZedScripts LSP server for you.
91
+
92
+ ### VIM
93
+
94
+ Using [yegappan/lsp](https://github.com/yegappan/lsp), setup the ZedScripts LSP server as follows:
95
+
96
+ ```vim
97
+ let lspServers = [
98
+ \ #{
99
+ \ name: 'zedscripts',
100
+ \ filetype: ['text', 'zedscripts'],
101
+ \ path: 'path/to/ZedScripts',
102
+ \ args: []
103
+ \ }
104
+ \ ]
105
+
106
+ autocmd User LspSetup call LspAddServer(lspServers)
107
+ ```
108
+
109
+ > [!NOTE]
110
+ > 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.
111
+
85
112
  ## License
86
113
  See [LICENSE](LICENSE) for details.
@@ -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 = "0.0.2"
6
+ version = "0.0.3"
7
7
  license = "MIT"
8
8
  license-files = ["LICENSE"]
9
9
  authors = [
@@ -34,3 +34,10 @@ build-backend = "setuptools.build_meta"
34
34
  ZedScripts = [
35
35
  "locale/*.json"
36
36
  ]
37
+
38
+ # uv configs
39
+ [[tool.uv.index]]
40
+ name = "testpypi"
41
+ url = "https://test.pypi.org/simple/"
42
+ publish-url = "https://test.pypi.org/legacy/"
43
+ explicit = true
@@ -7,7 +7,7 @@ from ..enums.SyntaxErrorType import SyntaxErrorType
7
7
  from ..providers.diagnostics import DiagnosticDefinition
8
8
 
9
9
 
10
- class DiagnosticType(enum.Enum):
10
+ class DiagnosticType(enum.StrEnum):
11
11
  @classmethod
12
12
  def _missing_(cls, value: Any) -> 'DiagnosticType | None':
13
13
  if isinstance(value, SyntaxErrorType):
@@ -0,0 +1,179 @@
1
+ import logging
2
+ import enum
3
+ from pathlib import Path
4
+ from typing import Annotated, Optional
5
+ from pydantic import BaseModel, BeforeValidator, Field, WithJsonSchema
6
+
7
+ from lsprotocol.types import DiagnosticSeverity, DiagnosticTag
8
+
9
+ from ..scripts.version import DataVersion
10
+ from ..enums.Diagnostic import DiagnosticType
11
+
12
+
13
+
14
+ # the auto enum of DiagnosticType makes them lowercase, but we'll want to use
15
+ # uppercase, that is the member's name
16
+
17
+ def _parse_diagnostic_type_name(value: object) -> object:
18
+ if isinstance(value, DiagnosticType):
19
+ return value
20
+ if isinstance(value, str):
21
+ try:
22
+ return DiagnosticType[value]
23
+ except KeyError:
24
+ raise ValueError("type must be a DiagnosticType member name") from None
25
+ return value
26
+ DiagnosticTypeName = Annotated[
27
+ DiagnosticType,
28
+ BeforeValidator(_parse_diagnostic_type_name),
29
+ WithJsonSchema({
30
+ "type": "string",
31
+ "enum": [member.name for member in DiagnosticType],
32
+ }),
33
+ ]
34
+
35
+ def _parse_diagnostic_severity_name(value: object) -> object:
36
+ if isinstance(value, DiagnosticSeverity):
37
+ return value
38
+ if isinstance(value, str):
39
+ try:
40
+ return DiagnosticSeverity[value]
41
+ except KeyError:
42
+ raise ValueError("severity must be a DiagnosticSeverity member name") from None
43
+ return value
44
+ DiagnosticSeverityName = Annotated[
45
+ DiagnosticSeverity,
46
+ BeforeValidator(_parse_diagnostic_severity_name),
47
+ WithJsonSchema({
48
+ "type": "string",
49
+ "enum": [member.name for member in DiagnosticSeverity],
50
+ }),
51
+ ]
52
+
53
+ def _parse_diagnostic_tag_name(value: object) -> object:
54
+ if isinstance(value, DiagnosticTag):
55
+ return value
56
+ if isinstance(value, str):
57
+ try:
58
+ return DiagnosticTag[value]
59
+ except KeyError:
60
+ raise ValueError("tag must be a DiagnosticTag member name") from None
61
+ return value
62
+ DiagnosticTagName = Annotated[
63
+ DiagnosticTag,
64
+ BeforeValidator(_parse_diagnostic_tag_name),
65
+ WithJsonSchema({
66
+ "type": "string",
67
+ "enum": [member.name for member in DiagnosticTag],
68
+ }),
69
+ ]
70
+
71
+
72
+
73
+ # define the models
74
+
75
+ class DatasetTag(enum.StrEnum):
76
+ LATEST = "latest"
77
+ STABLE = "stable"
78
+
79
+
80
+
81
+ class ReleaseModel(BaseModel):
82
+ major: int = Field(
83
+ description="Build game version number.",
84
+ ge=42,
85
+ )
86
+ minor: int = Field(
87
+ description="Minor game version number.",
88
+ ge=0,
89
+ )
90
+ patch: int = Field(
91
+ description="Patch game version number.",
92
+ ge=0,
93
+ )
94
+ version: Optional[int] = Field(
95
+ description="Dataset version number for the provided Build release (major.minor.patch). This is incremented whenever the dataset for a specific version is updated.",
96
+ default=None,
97
+ ge=0,
98
+ )
99
+
100
+ class DatasetModel(BaseModel):
101
+ release: Optional[ReleaseModel] = Field(
102
+ description="Provides a configuration for the dataset version to use for validation. This overrides the `latest` and `stable` fields if set.",
103
+ default=None,
104
+ )
105
+ latest: Optional[bool] = Field(
106
+ description="Indicates whether to use the latest dataset version for validation. This is ignored if `release` is set. Overrides the `stable` field if set.",
107
+ default=False,
108
+ )
109
+ stable: Optional[bool] = Field(
110
+ description="Provides a configuration for the stable dataset version to use for validation. Ignored if `release` or `latest` is set.",
111
+ default=True,
112
+ )
113
+
114
+ def to_data_version(self) -> DataVersion | DatasetTag:
115
+ if self.release is None:
116
+ if self.latest:
117
+ return DatasetTag.LATEST
118
+ if self.stable:
119
+ return DatasetTag.STABLE
120
+ logging.warning("No dataset version specified, defaulting to 'stable'.")
121
+ return DatasetTag.STABLE
122
+ from ..scripts.dataset import Manifest
123
+ try:
124
+ return Manifest.from_release_model(self.release)
125
+ except Exception as e:
126
+ logging.error(f"Failed to get data version from release model: {e}. Defaulting to 'stable'.")
127
+ return DatasetTag.STABLE
128
+
129
+ class DiagnosticsModel(BaseModel):
130
+ """Represents the diagnostics configuration for the workspace environment."""
131
+ type: DiagnosticTypeName = Field(
132
+ description="Specifies the diagnostic member name to enable.",
133
+ )
134
+ enable: Optional[bool] = Field(
135
+ description="Indicates whether diagnostics are enabled.",
136
+ default=True,
137
+ )
138
+ severity: Optional[DiagnosticSeverityName] = Field(
139
+ description="Specifies the severity level of the diagnostics.",
140
+ default=None,
141
+ )
142
+ tags: Optional[list[DiagnosticTagName]] = Field(
143
+ description="Specifies the tags associated with the diagnostics.",
144
+ default=None,
145
+ )
146
+
147
+ class ConfigurationModel(BaseModel):
148
+ """Represents the content of the configuration files for workspace environments."""
149
+ dataset: DatasetModel = Field(
150
+ description="Provides configuration for the dataset version to use for diagnostics.",
151
+ default_factory=DatasetModel,
152
+ )
153
+ libraries: set[Path] = Field(
154
+ description="Set of library folders to reference. Usually this should contain the base game folder.",
155
+ default_factory=set,
156
+ )
157
+ ignored: set[str] = Field(
158
+ description="Regex patterns for files and folders to ignore in the workspace.",
159
+ default=set([
160
+ r"tileGeometry\.txt",
161
+ r"tileDepthTextureAssignments\.txt",
162
+ r".+\.tiles\.txt"
163
+ ]),
164
+ )
165
+ diagnostics: list[DiagnosticsModel] = Field(
166
+ description="List of diagnostics configurations for the workspace environment.",
167
+ default_factory=list,
168
+ )
169
+
170
+ def find_diagnostic(self, type: DiagnosticType) -> DiagnosticsModel | None:
171
+ for diagnostic in self.diagnostics:
172
+ if diagnostic.type == type:
173
+ return diagnostic
174
+ return None
175
+
176
+ if __name__ == "__main__":
177
+ import json
178
+ config = ConfigurationModel()
179
+ print(json.dumps(config.model_json_schema(), indent=4))
@@ -10,8 +10,8 @@ from ..utils import path_to_uri, position_to_texposition
10
10
  from ..enums.Diagnostic import DiagnosticType
11
11
  from ..structure.lexer import Lexer, TokenCollection
12
12
  from ..structure.parser import parse_tokens, chunk_to_root
13
+ from ..providers import notifications
13
14
  from ..providers.diagnostics import DiagnosticReport, WorkspaceDiagnosticReport, DiagnosticCollection
14
- from ..providers.notifications import ZedNotification, SetZedScriptsNotificationParams
15
15
  from ..providers.semantic_tokens import SemanticTokenCollection, build_syntactic_tokens
16
16
  from ..providers.actions import make_linked_editing_ranges
17
17
 
@@ -21,8 +21,7 @@ if TYPE_CHECKING:
21
21
 
22
22
  from .workspace import Workspace
23
23
  from .mod import Mod
24
- from ..server import ZedServer
25
- from ..scripts.block import Block, Root
24
+ from ..scripts.block import Root
26
25
  from ..scripts.dataset import Dataset
27
26
 
28
27
  class Document:
@@ -85,16 +84,10 @@ class Document:
85
84
  self.mod = mod
86
85
 
87
86
  def make_zedscripts(self) -> None:
88
- # import here to avoid circular dependency
89
- from ..server import ZedServer
90
- server = ZedServer.instance
91
- if server is not None:
92
- server.send_notification(
93
- ZedNotification.SET_ZEDSCRIPTS,
94
- SetZedScriptsNotificationParams(
95
- uri=path_to_uri(self.path)
96
- )
97
- )
87
+ notifications.send_notification(
88
+ notifications.ZedNotification.SET_ZEDSCRIPTS,
89
+ uri=self.get_uri()
90
+ )
98
91
 
99
92
  def get_text(self) -> str:
100
93
  if self.text is None:
@@ -111,7 +104,8 @@ class Document:
111
104
  return path_to_uri(self.path)
112
105
 
113
106
  def get_lsp_diagnostics(self) -> list[types.Diagnostic]:
114
- lsp_diagnostics = self.diagnostics.to_lsp() + self.syntactic_diagnostics.to_lsp()
107
+ config = self.workspace.get_configuration()
108
+ lsp_diagnostics = self.diagnostics.to_lsp(config) + self.syntactic_diagnostics.to_lsp(config)
115
109
  logging.debug(f"LSP Diagnostics for {self.get_uri()}: {len(lsp_diagnostics)}")
116
110
  return lsp_diagnostics
117
111
 
@@ -267,8 +261,10 @@ class Document:
267
261
  # set new syntactic diagnostics
268
262
  syntactic_diagnostics = self.syntactic_diagnostics
269
263
  syntactic_diagnostics.clear() # reset previous syntactic diagnostics
264
+ config = self.workspace.configuration
270
265
  for error in result.errors:
271
266
  syntactic_diagnostics.add(
267
+ config,
272
268
  type=DiagnosticType(error.type),
273
269
  location=error.location
274
270
  )
@@ -12,8 +12,8 @@ from .document import Document
12
12
  from .mod import Mod, ModCollection
13
13
  from .version import Version
14
14
  from .config import ConfigurationModel
15
- from ..providers.notifications import ZedNotification, send_progress_notification
16
- from ..utils import merge_pydantic_models
15
+ from ..providers import notifications
16
+ from ..utils import merge_pydantic_models, path_to_uri
17
17
  from ..scripts.dataset import Dataset
18
18
 
19
19
  if TYPE_CHECKING:
@@ -43,19 +43,20 @@ class Workspace:
43
43
  def __repr__(self) -> str:
44
44
  return f"Workspace(type={self.workspace_type}, folder={self.folder})"
45
45
 
46
- def load(self) -> None:
46
+ def load(self) -> tuple[int, int]:
47
47
  """
48
48
  Retrieve every script files and cache them as Document instances.
49
+
50
+ Returns:
51
+ tuple[int, int]: A tuple containing the number of loaded mods and the number of loaded documents.
49
52
  """
50
53
  logging.info(f"Loading workspace: {self.folder}")
51
- self.load_mods()
54
+ loaded_mod_count = self.load_mods()
52
55
  # self.load_documents()
53
- self.load_documents()
54
-
55
- def load_documents(self) -> None:
56
- send_progress_notification(ZedNotification.LOADING_DOCUMENTS,
57
- self.folder, 0.0, self.workspace_type)
56
+ loaded_document_count = self.load_documents()
57
+ return loaded_mod_count, loaded_document_count
58
58
 
59
+ def load_documents(self) -> int:
59
60
  # inside the workspace folder, find every txt files, and try to load them
60
61
  # as documents
61
62
  files = list(self.folder.rglob("*.txt"))
@@ -63,39 +64,51 @@ class Workspace:
63
64
  last_progress = 0
64
65
  step = 10
65
66
  step_count = step * total_files / 100
67
+ loaded_file_count = 0
66
68
  for progress, path in enumerate(files):
67
- self.load_document(path)
69
+ document = self.load_document(path)
70
+ if document is not None:
71
+ loaded_file_count += 1
68
72
 
69
73
  # send update notification to client
70
74
  if progress - last_progress >= step_count:
71
75
  logging.info(f"Loading progress: {progress}/{total_files}")
72
- send_progress_notification(ZedNotification.LOADING_DOCUMENTS,
73
- self.folder, progress / total_files * 100, self.workspace_type)
76
+ notifications.send_notification(
77
+ notifications.ZedNotification.SET_PROGRESS,
78
+ progress=progress / total_files * 100,
79
+ )
74
80
  last_progress = progress
75
81
 
76
82
  # send final progress notification
77
- send_progress_notification(ZedNotification.LOADING_DOCUMENTS,
78
- self.folder, 100, self.workspace_type)
83
+ notifications.send_notification(
84
+ notifications.ZedNotification.SET_PROGRESS,
85
+ progress=100
86
+ )
87
+ return loaded_file_count
79
88
 
80
- def load_mods(self) -> None:
89
+ def load_mods(self) -> int:
81
90
  logging.info(f"Loading mods for workspace: {self.folder}")
82
91
  # look for mod.info files
83
92
  # one or more of these files are associated to a specific mod
93
+ mod_count = 0
84
94
  for file in self.folder.rglob("mod.info"):
85
95
  # only consider versioning and common folders
86
96
  version = Version.find_or_make_version(file)
87
97
  if version.type in {VersionType.VERSIONING, VersionType.COMMON}:
88
- mod_folder = file.parent.parent
98
+ mod_folder = file.parent.parent # the folder containing the 42 / common folders
89
99
  mod = Mod.find_or_make_mod(mod_folder, self)
90
100
  mod.add_mod_info_file(file, version)
91
101
  self.mods[mod_folder] = mod
102
+ mod_count += 1
92
103
  continue
93
104
 
94
105
  # if it's OTHER, then it's probably not a mod file
95
106
  # TODO: should we handle those ?
96
107
 
108
+ return mod_count
109
+
97
110
  @staticmethod
98
- def load_libraries() -> None:
111
+ def load_libraries() -> int:
99
112
  logging.info("Loading libraries for all workspaces.")
100
113
 
101
114
  # gather libraries from configs
@@ -111,10 +124,25 @@ class Workspace:
111
124
  libraries = Workspace.filter_out_projects(libraries)
112
125
 
113
126
  Workspace.workspace_cache[WorkspaceType.LIBRARY] = {}
127
+ libraries_count = 0
114
128
  for library in libraries:
115
129
  Workspace.workspace_cache[WorkspaceType.LIBRARY][library] = Workspace(library, WorkspaceType.LIBRARY)
116
- for library in Workspace.workspace_cache[WorkspaceType.LIBRARY].values():
130
+ libraries_count += 1
131
+
132
+ notifications.send_notification(
133
+ notifications.ZedNotification.SET_LIBRARIES_COUNT,
134
+ count=libraries_count
135
+ )
136
+
137
+ for i, library in enumerate(Workspace.workspace_cache[WorkspaceType.LIBRARY].values()):
138
+ notifications.send_notification(
139
+ notifications.ZedNotification.LOADING_DOCUMENTS,
140
+ uri=str(library.folder),
141
+ workspace_type=library.workspace_type,
142
+ index=i
143
+ )
117
144
  library.load()
145
+ return libraries_count
118
146
 
119
147
  def should_ignore(self, path: Path) -> bool:
120
148
  for pattern in self.ignored_patterns:
@@ -1,20 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
- from lsprotocol.types import DiagnosticSeverity, DiagnosticTag
3
+ from lsprotocol import types
4
4
 
5
5
  import enum
6
6
  from typing import TYPE_CHECKING, ClassVar, Any
7
7
  from dataclasses import dataclass
8
8
 
9
- from lsprotocol import types
10
9
 
11
10
  import ZedScripts
12
11
  from ..utils import textrange_to_lsp
13
- from ..enums.SyntaxErrorType import SyntaxErrorType
14
12
  from ..structure.lexer import TextRange
15
13
  from ..providers.locale import zedlocalizer
16
14
 
17
15
  if TYPE_CHECKING:
16
+ from ..environment.config import ConfigurationModel
18
17
  from ..enums.Diagnostic import DiagnosticType
19
18
 
20
19
  type DiagnosticReport = (
@@ -37,19 +36,30 @@ class DiagnosticDefinition:
37
36
  type: DiagnosticType,
38
37
  severity: types.DiagnosticSeverity,
39
38
  args: dict[str, Any] = {},
40
- tags: list[DiagnosticTag] = [],
39
+ tags: list[types.DiagnosticTag] = [],
41
40
  ) -> None:
42
41
  self.type: DiagnosticType = type
43
42
  self.severity: types.DiagnosticSeverity = severity
44
43
  self.args: dict[str, Any] = args
45
- self.tags: list[DiagnosticTag] = tags
44
+ self.tags: list[types.DiagnosticTag] = tags
46
45
 
47
46
  DiagnosticDefinition.by_type[type] = self
48
47
 
48
+ @staticmethod
49
+ def get(type: DiagnosticType) -> DiagnosticDefinition:
50
+ assert type in DiagnosticDefinition.by_type, f"WEIRD: Diagnostic type '{type}' is not registered, but this is verified earlier"
51
+ return DiagnosticDefinition.by_type[type]
52
+
49
53
  def get_name(self) -> str:
50
54
  """Provides an identifier for the diagnostic type."""
51
55
  return self.type.name
52
56
 
57
+ def get_severity(self) -> types.DiagnosticSeverity:
58
+ return self.severity
59
+
60
+ def get_tags(self) -> list[types.DiagnosticTag]:
61
+ return self.tags
62
+
53
63
 
54
64
  @dataclass
55
65
  class DiagnosticInfo:
@@ -59,7 +69,7 @@ class DiagnosticInfo:
59
69
 
60
70
 
61
71
  class DiagnosticCollection(list[DiagnosticInfo]):
62
- def to_lsp(self) -> list[types.Diagnostic]:
72
+ def to_lsp(self, config: ConfigurationModel) -> list[types.Diagnostic]:
63
73
  """
64
74
  Converts the different diagnostic information into LSP-compatible diagnostics.
65
75
 
@@ -67,8 +77,19 @@ class DiagnosticCollection(list[DiagnosticInfo]):
67
77
  list[types.Diagnostic]: A list of LSP-compatible diagnostic objects.
68
78
  """
69
79
  lsp_diagnostics: list[types.Diagnostic] = []
70
- for diagnostic in self:
71
- definition = DiagnosticDefinition.by_type[diagnostic.type]
80
+ for i, diagnostic in enumerate(reversed(self)):
81
+ definition = DiagnosticDefinition.get(diagnostic.type)
82
+ severity = definition.get_severity()
83
+ tags = definition.get_tags()
84
+
85
+ config_def = config.find_diagnostic(diagnostic.type)
86
+ if config_def is not None:
87
+ config_severity = config_def.severity
88
+ if config_severity is not None:
89
+ severity = config_severity
90
+ config_tags = config_def.tags
91
+ if config_tags is not None:
92
+ tags = config_tags
72
93
 
73
94
  # ensure that the correct arguments are always passed
74
95
  for name, arg_type in definition.args.items():
@@ -80,22 +101,28 @@ class DiagnosticCollection(list[DiagnosticInfo]):
80
101
  range=textrange_to_lsp(diagnostic.location),
81
102
  message=zedlocalizer.localize_string(definition.type, definition,
82
103
  args=diagnostic.args),
83
- severity=definition.severity,
104
+ severity=severity,
84
105
  source=ZedScripts.SOURCE,
85
106
  code=definition.get_name(),
86
- tags=definition.tags
107
+ tags=tags
87
108
  )
88
109
  )
89
110
  return lsp_diagnostics
90
111
 
91
- def add(self, type: DiagnosticType, location: TextRange, args: dict[str, Any] = {}) -> None:
112
+ def add(self, config: ConfigurationModel, type: DiagnosticType, location: TextRange, args: dict[str, Any] = {}) -> None:
92
113
  """
93
114
  Adds a new diagnostic to the collection.
94
115
 
95
116
  Args:
117
+ config (ConfigurationModel): The configuration model to use for the diagnostic.
96
118
  type (DiagnosticType): The type of the diagnostic.
97
119
  location (TextRange): The location in the text where the diagnostic applies.
98
120
  args (dict[str, Any], optional): Additional arguments for the diagnostic. Defaults to {}.
99
121
  """
122
+ config_def = config.find_diagnostic(type)
123
+ if config_def is not None:
124
+ enabled = config_def.enable
125
+ if not enabled:
126
+ return
100
127
  self.append(DiagnosticInfo(type=type, location=location, args=args))
101
128
 
@@ -0,0 +1,57 @@
1
+ import enum
2
+
3
+ from typing import TYPE_CHECKING, Literal, overload
4
+ if TYPE_CHECKING:
5
+ from ..environment import WorkspaceType
6
+
7
+
8
+ class ZedNotification(enum.StrEnum):
9
+ SET_ZEDSCRIPTS = "zedscripts/setZedScripts"
10
+
11
+ SET_PROGRESS = "zedscripts/setProgress"
12
+ SET_WORKSPACE_COUNT = "zedscripts/setWorkspaceCount"
13
+ SET_LIBRARIES_COUNT = "zedscripts/setLibrariesCount"
14
+
15
+ LOADING_DOCUMENTS = "zedscripts/loadingDocuments"
16
+ LOADING_DOCUMENTS_DONE = "zedscripts/loadingDocumentsDone"
17
+
18
+
19
+ @overload
20
+ def send_notification(
21
+ notification: Literal[ZedNotification.SET_ZEDSCRIPTS],
22
+ *, uri: str) -> None: ...
23
+
24
+
25
+ @overload
26
+ def send_notification(
27
+ notification: Literal[ZedNotification.SET_PROGRESS],
28
+ *, progress: float) -> None: ...
29
+ @overload
30
+ def send_notification(
31
+ notification: Literal[ZedNotification.SET_WORKSPACE_COUNT],
32
+ *, count: int) -> None: ...
33
+ @overload
34
+ def send_notification(
35
+ notification: Literal[ZedNotification.SET_LIBRARIES_COUNT],
36
+ *, count: int) -> None: ...
37
+
38
+
39
+ @overload
40
+ def send_notification(
41
+ notification: Literal[ZedNotification.LOADING_DOCUMENTS],
42
+ *, uri: str, workspace_type: 'WorkspaceType', index: int) -> None: ...
43
+
44
+
45
+ def send_notification(notification: ZedNotification, **kargs) -> None:
46
+ """
47
+ Sends a notification to the Zed server.
48
+
49
+ Args:
50
+ notification (ZedNotification): The type of notification to send.
51
+ **kargs: Additional keyword arguments specific to the notification type.
52
+ """
53
+ from ..server import ZedServer
54
+ server = ZedServer.instance
55
+ if server is None:
56
+ return
57
+ server.send_notification(notification, kargs)
@@ -116,7 +116,8 @@ class Block:
116
116
  return duplicate_count > 1 # > 1 bcs there's itself in the list
117
117
 
118
118
  def add_diagnostic(self, type: DiagnosticType, location: TextRange, args: dict[str, Any] = {}) -> None:
119
- self.document.diagnostics.add(type=type, location=location, args=args)
119
+ config = self.document.workspace.configuration
120
+ self.document.diagnostics.add(config, type=type, location=location, args=args)
120
121
 
121
122
  def add_semantic_token(self, type: SemanticTokenType, location: TextRange, modifiers: list[SemanticTokenModifier] = []) -> None:
122
123
  self.document.semantic_tokens.add(type=type, location=location, modifiers=modifiers)
@@ -280,7 +280,7 @@ class Value(Element["ValueNode"]):
280
280
 
281
281
  if IS_DEBUG:
282
282
  txt += "(debug)\n\n"
283
- txt += f"- Refs: {[str(ref) for ref in self.refs]}"
283
+ txt += f"- Refs: {[f"{ref.document.path}:{ref.get_ref_id_range()}" for ref in self.refs]}"
284
284
 
285
285
  return make_hover_information(txt, value_node.to_range())
286
286
 
@@ -7,15 +7,14 @@ from pygls.lsp.server import LanguageServer
7
7
  from pygls.protocol.language_server import LanguageServerProtocol
8
8
 
9
9
  from .__about__ import __version__
10
- from .utils import uri_to_path
10
+ from .utils import uri_to_path, path_to_uri
11
11
  from .environment import WorkspaceType
12
12
  from .environment.workspace import Workspace
13
13
  from .environment.document import Document
14
14
  from .providers.diagnostics import DiagnosticReport
15
15
  from .providers.semantic_tokens import get_tokens
16
16
  from .providers.locale import zedlocalizer
17
- from .providers import capabilities
18
- from .providers.notifications import ZedNotification, NotificationParams
17
+ from .providers import capabilities, notifications
19
18
 
20
19
 
21
20
 
@@ -32,7 +31,7 @@ class ZedServer(LanguageServer):
32
31
  zedlocalizer.load_locale_files()
33
32
  ZedServer.instance = self
34
33
 
35
- def send_notification(self, method: ZedNotification, params: NotificationParams | None = None) -> None:
34
+ def send_notification(self, method: notifications.ZedNotification, params: dict | None = None) -> None:
36
35
  logging.debug("Sending notification: %s with params: %s", method, params)
37
36
  self.protocol.notify(
38
37
  method,
@@ -186,16 +185,37 @@ def initialized(server: ZedServer, params: types.InitializedParams) -> None:
186
185
  # load the global dataset
187
186
  Workspace.global_dataset.load()
188
187
 
188
+ # load workspaces
189
189
  logging.debug("Server workspace folders")
190
190
  logging.debug(server.workspace.folders)
191
- for workspace_folder in server.workspace.folders.values():
192
- ws = Workspace(uri_to_path(workspace_folder.uri), WorkspaceType.PROJECT)
193
- ws.load()
194
- Workspace.load_libraries()
195
- server.send_notification(
196
- ZedNotification.LOADING_DOCUMENTS_DONE
191
+ should_load_libraries = False
192
+ folders = server.workspace.folders.values()
193
+
194
+ notifications.send_notification(
195
+ notifications.ZedNotification.SET_WORKSPACE_COUNT,
196
+ count=len(folders)
197
197
  )
198
198
 
199
+ for i, workspace_folder in enumerate(folders):
200
+ ws = Workspace(uri_to_path(workspace_folder.uri), WorkspaceType.PROJECT)
201
+
202
+ notifications.send_notification(
203
+ notifications.ZedNotification.LOADING_DOCUMENTS,
204
+ uri=path_to_uri(ws.folder),
205
+ workspace_type=ws.workspace_type,
206
+ index=i+1
207
+ )
208
+
209
+ _, file_count = ws.load()
210
+ if file_count != 0:
211
+ should_load_libraries = True
212
+
213
+ # load libraries if any ZedScripts files were found
214
+ if should_load_libraries:
215
+ Workspace.load_libraries()
216
+
217
+ server.send_notification(notifications.ZedNotification.LOADING_DOCUMENTS_DONE)
218
+
199
219
 
200
220
  @zedserver.feature(types.TEXT_DOCUMENT_DID_OPEN)
201
221
  def did_open(server: ZedServer, params: types.DidOpenTextDocumentParams) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ZedScripts
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Author: SimKDT
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.12.3
@@ -19,11 +19,11 @@ Dynamic: license-file
19
19
 
20
20
  # ZedScripts LSP
21
21
 
22
- ![ZedScripts-LSP](https://img.shields.io/badge/Latest_version-0.0.2-blue)
23
- [![License](https://img.shields.io/github/license/PZ-Wiki-Modding/ZedScripts-LSP?label=License)](LICENSE)
24
- ![Code Size](https://img.shields.io/github/languages/code-size/PZ-Wiki-Modding/ZedScripts-LSP?label=Code%20Size)
25
- [![PyPi Version](https://img.shields.io/pypi/v/ZedScripts-LSP)](https://pypi.org/project/ZedScripts-LSP/)
26
- [![PyPi Downloads](https://img.shields.io/pypi/dm/ZedScripts-LSP)](https://pypi.org/project/ZedScripts-LSP/)
22
+ [![License](https://img.shields.io/github/license/PZ-Wiki-Modding/ZedScripts?label=License)](LICENSE)
23
+ ![Code Size](https://img.shields.io/github/languages/code-size/PZ-Wiki-Modding/ZedScripts?label=Code%20Size)
24
+ [![PyPi Version](https://img.shields.io/pypi/v/ZedScripts)](https://pypi.org/project/ZedScripts/)
25
+ [![PyPi Downloads](https://img.shields.io/pypi/dm/ZedScripts)](https://pypi.org/project/ZedScripts/)
26
+ ![PyPI Python Version](https://img.shields.io/pypi/pyversions/ZedScripts)
27
27
 
28
28
  > [!IMPORTANT]
29
29
  > Work In Progress, this is a BETA release
@@ -101,5 +101,32 @@ Example configuration:
101
101
 
102
102
  WIP: proper documentation will come soon
103
103
 
104
+ ## IDE configuration
105
+ 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).
106
+
107
+ ### Visual Studio Code
108
+
109
+ Install the [ZedScripts extension](https://marketplace.visualstudio.com/items?itemName=SimKDT.ZedScripts) from the Visual Studio Code marketplace. The extension will handle the downloading and setup of the ZedScripts LSP server for you.
110
+
111
+ ### VIM
112
+
113
+ Using [yegappan/lsp](https://github.com/yegappan/lsp), setup the ZedScripts LSP server as follows:
114
+
115
+ ```vim
116
+ let lspServers = [
117
+ \ #{
118
+ \ name: 'zedscripts',
119
+ \ filetype: ['text', 'zedscripts'],
120
+ \ path: 'path/to/ZedScripts',
121
+ \ args: []
122
+ \ }
123
+ \ ]
124
+
125
+ autocmd User LspSetup call LspAddServer(lspServers)
126
+ ```
127
+
128
+ > [!NOTE]
129
+ > 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.
130
+
104
131
  ## License
105
132
  See [LICENSE](LICENSE) for details.
@@ -22,7 +22,6 @@ src/ZedScripts/environment/workspace.py
22
22
  src/ZedScripts/locale/en.json
23
23
  src/ZedScripts/providers/actions.py
24
24
  src/ZedScripts/providers/capabilities.py
25
- src/ZedScripts/providers/configuration.py
26
25
  src/ZedScripts/providers/debug.py
27
26
  src/ZedScripts/providers/diagnostics.py
28
27
  src/ZedScripts/providers/hover.py
@@ -1,82 +0,0 @@
1
- import logging
2
- import enum
3
- from pathlib import Path
4
- from typing import Optional
5
- from pydantic import BaseModel, Field
6
-
7
- from ..scripts.version import DataVersion
8
-
9
-
10
- class DatasetTag(enum.Enum):
11
- LATEST = "latest"
12
- STABLE = "stable"
13
-
14
-
15
-
16
- class ReleaseModel(BaseModel):
17
- major: int = Field(
18
- description="Build game version number.",
19
- ge=42,
20
- )
21
- minor: int = Field(
22
- description="Minor game version number.",
23
- ge=0,
24
- )
25
- patch: int = Field(
26
- description="Patch game version number.",
27
- ge=0,
28
- )
29
- version: Optional[int] = Field(
30
- description="Dataset version number for the provided Build release (major.minor.patch). This is incremented whenever the dataset for a specific version is updated.",
31
- default=None,
32
- ge=0,
33
- )
34
-
35
- class DatasetModel(BaseModel):
36
- release: Optional[ReleaseModel] = Field(
37
- description="Provides a configuration for the dataset version to use for validation. This overrides the `latest` and `stable` fields if set.",
38
- default=None,
39
- )
40
- latest: Optional[bool] = Field(
41
- description="Indicates whether to use the latest dataset version for validation. This is ignored if `release` is set. Overrides the `stable` field if set.",
42
- default=False,
43
- )
44
- stable: Optional[bool] = Field(
45
- description="Provides a configuration for the stable dataset version to use for validation. Ignored if `release` or `latest` is set.",
46
- default=True,
47
- )
48
-
49
- def to_data_version(self) -> DataVersion | DatasetTag:
50
- if self.release is None:
51
- if self.latest:
52
- return DatasetTag.LATEST
53
- if self.stable:
54
- return DatasetTag.STABLE
55
- logging.warning("No dataset version specified, defaulting to 'stable'.")
56
- return DatasetTag.STABLE
57
- from ..scripts.dataset import Manifest
58
- try:
59
- return Manifest.from_release_model(self.release)
60
- except Exception as e:
61
- logging.error(f"Failed to get data version from release model: {e}. Defaulting to 'stable'.")
62
- return DatasetTag.STABLE
63
-
64
-
65
- class ConfigurationModel(BaseModel):
66
- """Represents the content of the configuration files for workspace environments."""
67
- dataset: DatasetModel = Field(
68
- description="Provides configuration for the dataset version to use for diagnostics.",
69
- default_factory=DatasetModel,
70
- )
71
- libraries: set[Path] = Field(
72
- description="Set of library folders to reference. Usually this should contain the base game folder.",
73
- default_factory=set,
74
- )
75
- ignored: set[str] = Field(
76
- description="Regex patterns for files and folders to ignore in the workspace.",
77
- default=set([
78
- r"tileGeometry\.txt",
79
- r"tileDepthTextureAssignments\.txt",
80
- r".+\.tiles\.txt"
81
- ]),
82
- )
@@ -1,26 +0,0 @@
1
- import enum
2
- from typing import Any, TypedDict
3
-
4
-
5
- class ConfigurationType(enum.IntEnum):
6
- DATA_SOURCE = enum.auto()
7
-
8
- # TODO: implement fixed configuration values
9
-
10
-
11
- default_configuration: dict[ConfigurationType, Any] = {
12
- ConfigurationType.DATA_SOURCE: "latest"
13
- }
14
-
15
-
16
-
17
- class Configuration:
18
- def __init__(self):
19
- self.settings: dict[ConfigurationType, Any] = {}
20
-
21
- def get(self, key: ConfigurationType, default=None):
22
- return self.settings.get(key, default)
23
-
24
- def set(self, key: ConfigurationType, value):
25
- self.settings[key] = value
26
-
@@ -1,45 +0,0 @@
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
- )
File without changes
File without changes