diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index b762ff3..8ef8618 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: true contact_links: - name: Support - url: https://github.com/ViTeXFTW/ZeroSyntaxV2/blob/main/SUPPORT.md + url: https://github.com/ViTeXFTW/ZeroSyntaxV2/blob/dev/SUPPORT.md about: Read where to ask support questions before opening an issue. diff --git a/.gitignore b/.gitignore index b99d825..880081c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ editors/vscode/out editors/vscode/out-test editors/vscode/*.vsix editors/vscode/server +editors/vscode/icon/ # Reference game source (large, external) /GeneralsCode diff --git a/AGENTS.md b/AGENTS.md index dd8e2b9..f3f7033 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -195,14 +195,14 @@ take `absent = true` to pin that a diagnostic is *not* emitted. ### Editor client `editors/vscode/` is a thin reference LSP client (claims the `generals-ini` -language id). The server speaks stdio LSP, so Neovim/Helix/Zed configs are in -the README. `GeneralsCode/` is the engine source the schema is modeled on (a -reference for hand-authoring `schema.json`) — it is not part of any crate. +language id). The server speaks stdio LSP; client setup is documented in +`docs/language-server.md`. `GeneralsCode/` is the engine source the schema is +modeled on (a reference for hand-authoring `schema.json`) — it is not part of +any crate. ## Conventions -- License is **GPL-3.0-or-later** (matches the engine source the schema - derives from); keep crate headers consistent. +- License is **MIT**. - `schema.json` is hand-written — edit the JSON directly, modeling new entries on the engine's `FieldParse` tables in `GeneralsCode/`. Keep `value_type`s faithful and fall back to `Unknown { parse_fn }` when a type is unclear. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d2c084..4cc11cb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ cargo clippy --locked --all-targets --all-features -- -D warnings cd editors/vscode npm ci -npm run compile +npm test ``` The optional real-game corpus is not committed. Fetch it only when you need the @@ -43,4 +43,5 @@ describe the workflow the feature should improve. ## Releases -Maintainers release from tags. See [docs/release.md](docs/release.md). +Maintainers release from the `dev` and `prod` branches. See the +[release process](docs/release.md). diff --git a/Cargo.lock b/Cargo.lock index cd1151e..5f73db6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1528,7 +1528,7 @@ dependencies = [ [[package]] name = "zerosyntax-analysis" -version = "1.0.5" +version = "1.1.0" dependencies = [ "criterion", "rowan", @@ -1540,7 +1540,7 @@ dependencies = [ [[package]] name = "zerosyntax-schema" -version = "1.0.5" +version = "1.1.0" dependencies = [ "serde", "serde_json", @@ -1548,7 +1548,7 @@ dependencies = [ [[package]] name = "zerosyntax-server" -version = "1.0.5" +version = "1.1.0" dependencies = [ "anyhow", "dashmap 6.2.1", @@ -1567,7 +1567,7 @@ dependencies = [ [[package]] name = "zerosyntax-syntax" -version = "1.0.5" +version = "1.1.0" dependencies = [ "criterion", "logos", diff --git a/Cargo.toml b/Cargo.toml index 236dcf6..1884462 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["crates/schema", "crates/syntax", "crates/analysis", "crates/server"] [workspace.package] -version = "1.0.5" +version = "1.1.0" edition = "2021" license = "MIT" repository = "https://github.com/ViTeXFTW/ZeroSyntaxV2" diff --git a/README.md b/README.md index f44042e..8a8cfda 100644 --- a/README.md +++ b/README.md @@ -1,204 +1,88 @@ # ZeroSyntax v2 +

+ ZeroSyntax logo +

+ [![CI](https://github.com/ViTeXFTW/ZeroSyntaxV2/actions/workflows/ci.yml/badge.svg)](https://github.com/ViTeXFTW/ZeroSyntaxV2/actions/workflows/ci.yml) [![Release](https://github.com/ViTeXFTW/ZeroSyntaxV2/actions/workflows/release.yml/badge.svg)](https://github.com/ViTeXFTW/ZeroSyntaxV2/actions/workflows/release.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) -ZeroSyntax v2 is a language server and VS Code extension for the INI files used by -*Command & Conquer: Generals - Zero Hour*. It gives modders and map authors -editor support for the game's object, weapon, upgrade, FX, audio, module, and -map override definitions. +ZeroSyntax v2 brings modern editor support to the INI scripting files used by +*Command & Conquer: Generals – Zero Hour*. It helps modders and map authors find +mistakes early and navigate large game or mod workspaces. + +The project includes the IDE-independent `zerosyntax-lsp` language server and a +VS Code extension with the server bundled. -The project is built around an IDE-agnostic Language Server Protocol (LSP) -binary, `zerosyntax-lsp`, plus a reference VS Code extension. The analyzer uses a -hand-maintained schema modeled on the game's INI parsing tables, so diagnostics -and completions follow the structure the engine actually expects instead of -treating the files as generic INI. +## What you get -## Features +- Schema-aware diagnostics and quick fixes for blocks, fields, modules, values, + references, module tags, and missing `End` statements. +- Context-aware completion for INI keywords, enum values, flags, and definitions + in your workspace. +- Hover information, go to definition, find references, rename, workspace and + document symbols, and folding. +- Semantic highlighting for Generals INI files. +- Optional indentation formatting, disabled by default so existing files are + never reformatted without your consent. +- Base-game and mod indexing for `map.ini` and `solo.ini`, including W3D model + and bone checks. -- Diagnostics for unknown blocks, fields, modules, invalid values, - unterminated blocks, unresolved references, duplicate module tags, and - unreachable upgrade-conditioned sets. -- Context-aware completions for block names, field names, module slots, module - types, enum values, bitflags, and workspace definitions. -- Hover, go to definition, find references, rename, workspace symbol search, - document symbols, and folding ranges. -- Semantic highlighting for Generals INI syntax and schema-aware tokens. -- Quick fixes for common issues such as missing `End` statements, misspelled - enum values, unresolved references, and suppressing diagnostics. -- Optional formatter for indentation normalization. -- Incremental document updates and cached diagnostics for large game files. -- Reference VS Code extension with bundled syntax highlighting and LSP client - integration. +## Install the VS Code extension -## Supported platforms +ZeroSyntax supports Windows x64 and Linux x64 release builds. -Release assets are built for Windows x64 and Linux x64. Other platforms can -build from source with Rust and Node.js. +1. Download the `.vsix` for your platform from the + [latest GitHub release](https://github.com/ViTeXFTW/ZeroSyntaxV2/releases/latest). +2. In VS Code, open **Extensions**, choose **Views and More Actions …**, + select **Install from VSIX…**, and open the downloaded file. +3. Open your mod or map folder, then open an `.ini` file. -## Getting Started +The extension treats `.ini` files as **Generals INI**. If a workspace also +contains unrelated INI files, use VS Code's `files.associations` setting to +limit that language association to the appropriate folders. -Install the latest platform-specific VS Code extension package from -[GitHub Releases](https://github.com/ViTeXFTW/ZeroSyntaxV2/releases), then open a -Zero Hour `.ini` file. The extension activates for the `generals-ini` language -and starts the bundled language server automatically. +See the [VS Code extension guide](editors/vscode/README.md) for settings and +troubleshooting. -Formatting is off by default. Enable it with `zerosyntax.format.enable` when you -want the server to advertise document formatting to VS Code. +## Use the standalone language server -For map/solo.ini diagnostics and W3D model/bone completions, set -`zerosyntax.baseIniRoots` to game or mod directories and/or `.big` archives. -INI definitions are treated as already loaded before the map file; W3D assets -are indexed for model and bone checks. +Download the `zerosyntax-lsp` archive for your platform from the +[latest release](https://github.com/ViTeXFTW/ZeroSyntaxV2/releases/latest), +extract it, and configure your editor to run the binary over stdio. -### Standalone language server +See the [language server guide](docs/language-server.md) for initialization +options and editor integration details. -Release assets also include the standalone `zerosyntax-lsp` binary. Any editor -with generic LSP support can run that binary over stdio for `.ini` files. Pass -these initialization options if you want formatting or base INI roots: +## Configure map and model checks + +For complete `map.ini` and `solo.ini` diagnostics, set +`zerosyntax.baseIniRoots` in VS Code to the base game or mod directories and/or +`.big` archives that load before the map. The same setting also enables W3D +model and bone completion and validation. ```json { - "format": { "enable": true }, - "baseIniRoots": ["C:/Games/Zero Hour", "C:/Mods/MyMod/Data/INI", "C:/Mods/MyMod.big"] + "zerosyntax.baseIniRoots": [ + "C:/Games/Zero Hour", + "C:/Mods/MyMod/Data/INI", + "C:/Mods/MyMod.big" + ] } ``` -## Development - -```sh -cargo test --locked -cargo fmt --all --check -cargo clippy --locked --all-targets --all-features -- -D warnings - -cd editors/vscode -npm ci -npm run compile -``` - -The optional `GeneralsCode/`, `corpus/`, and `examples/` directories are local -inputs only and are intentionally not committed. Generated binaries, -`node_modules`, compiled extension output, and `.vsix` packages are also ignored. - -See [CONTRIBUTING.md](CONTRIBUTING.md) for pull request guidance and -[docs/release.md](docs/release.md) for the release process. - -## Common Editor Setup - -### Neovim - -```lua -local configs = require("lspconfig.configs") -local lspconfig = require("lspconfig") - -if not configs.zerosyntax then - configs.zerosyntax = { - default_config = { - cmd = { "zerosyntax-lsp" }, - filetypes = { "generals_ini" }, - root_dir = lspconfig.util.root_pattern(".git", "*.ini"), - single_file_support = true, - init_options = { - format = { enable = false }, - baseIniRoots = { "C:/Games/Zero Hour" }, - }, - }, - } -end - -lspconfig.zerosyntax.setup({}) -``` - -## Diagnostic Suppression - -Suppress a diagnostic for one file with a file-scope comment: - -```ini -; zerosyntax-disable: unresolved-reference, unreachable-set -``` - -Use the diagnostic code shown by your editor. Multiple codes can be separated by -spaces or commas, and multiple pragma lines accumulate. Unknown suppression codes -are reported so typos do not silently hide problems. The old -`; zerosyntax-disable:` spelling remains supported for existing files. - -## Feature Showcase - -### Diagnostic codes - -ZeroSyntax v2 reports stable diagnostic codes so warnings can be searched, -suppressed, or tracked consistently: - -| Code | Meaning | -| --- | --- | -| `syntax` | The file cannot be parsed cleanly, such as a missing `End`. | -| `stray-field` | A field appears outside a valid block or module. | -| `unknown-block` | A top-level block name is not known to the Generals INI schema. | -| `overrides` | A map override redefines an existing object-style definition. | -| `duplicate-definition` | The same definition is declared more than once. | -| `unreachable-set` | A `WeaponSet` or `ArmorSet` uses upgrade conditions without the trigger module needed to activate it. | -| `unknown-field` | A field is not valid in the current block or module. | -| `missing-module-tag` | A module is missing its required `ModuleTag_*` name. | -| `unknown-module` | A module type is not known for the current module slot. | -| `missing-condition` | A conditional state block is missing its condition token. | -| `missing-value` | A field requires a value but none was provided. | -| `bad-bool` | A boolean field is not `Yes` or `No`. | -| `non-positive` | A value must be greater than zero. | -| `bad-percent` | A percentage value is malformed or out of range. | -| `bad-color` | A color value is malformed. | -| `bad-coord` | A coordinate value is malformed. | -| `bad-number` | A numeric field does not contain a valid number. | -| `bad-enum` | A value is not a member of the expected enum set. | -| `bad-flag` | A bitflag value is not a member of the expected flag set. | -| `unresolved-reference` | A field references a definition that is not found in the workspace. | -| `unknown-suppression` | A `zerosyntax-disable` comment names a code that does not exist. | -| `module-wrong-slot` | A module type is used under the wrong slot. | -| `duplicate-module-tag` | Two modules in the same object use the same module tag. | -| `editor-default-module` | A placeholder/default module value should be replaced before shipping. | - -### Quick fixes - -Supported quick fixes appear in the editor's lightbulb/code action menu: - -| Quick fix | When it appears | -| --- | --- | -| Insert missing `End` | A block or module is unterminated. | -| Replace with `` | An enum or bitflag value is close to a known valid value. | -| Create stub ` ` | A reference points to a missing definition that can be scaffolded safely. | -| Remove unreachable `WeaponSet` / `ArmorSet` | An upgrade-conditioned set can never activate. | -| Insert `WeaponSetUpgrade` / `ArmorUpgrade` trigger module | An object has an unreachable upgrade-conditioned set and needs a trigger module. | -| Suppress `` in this file | A warning or hint is intentional for the current file. | - -### Example - -```ini -; zerosyntax-disable: unresolved-reference - -Weapon DemoCannon - PrimaryDamage = lots ; bad-number - DeathType = EXPLODDED ; quick fix: Replace with `EXPLODED` - FireFX = DemoMissingFX ; suppressed unresolved-reference -End - -Object DemoTank - Behavior = PhysicsBehavior ModuleTag_01 - End - - WeaponSet - Conditions = PLAYER_UPGRADE - Weapon = PRIMARY DemoCannon - End ; unreachable-set -End -``` +## Documentation -In this example, ZeroSyntax v2 can flag the invalid number, suggest the corrected -death flag, suppress the intentionally missing FX reference, and offer either to -remove the unreachable `WeaponSet` or insert the matching trigger module. +- [Diagnostics, suppression, and quick fixes](docs/diagnostics.md) +- [Standalone language server setup](docs/language-server.md) +- [Contributing](CONTRIBUTING.md) +- [Support](SUPPORT.md) +- [Security policy](SECURITY.md) -## License +## License and trademarks -ZeroSyntax v2 is licensed under the MIT License. See [LICENSE](LICENSE). +ZeroSyntax v2 is available under the [MIT License](LICENSE). ZeroSyntax v2 is an unofficial community project and is not affiliated with, endorsed by, or sponsored by Electronic Arts. Command & Conquer and related diff --git a/SECURITY.md b/SECURITY.md index 62ac90d..09b592c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,9 +6,9 @@ Security fixes target the latest released version of ZeroSyntax v2. ## Reporting a vulnerability -Please do not open a public issue for vulnerabilities. Report security issues by -emailing the maintainer listed on the GitHub repository profile, or by using -GitHub private vulnerability reporting if it is enabled for the repository. +Please do not open a public issue for vulnerabilities. Use +[GitHub private vulnerability reporting](https://github.com/ViTeXFTW/ZeroSyntaxV2/security/advisories/new) +or email the maintainer listed on the GitHub repository profile. Include: diff --git a/SUPPORT.md b/SUPPORT.md index dd2ff2b..165e183 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,6 +1,8 @@ # Support -Use GitHub issues for reproducible bugs and focused feature requests. Or write a message in the discord server [ZeroSyntax](https://discord.gg/FShKq5s2hH) +Use [GitHub Issues](https://github.com/ViTeXFTW/ZeroSyntaxV2/issues) for +reproducible bugs and focused feature requests. For questions, join the +[ZeroSyntax Discord server](https://discord.gg/FShKq5s2hH). Before opening an issue: diff --git a/crates/analysis/src/diagnostics.rs b/crates/analysis/src/diagnostics.rs index 4804115..0c46ee5 100644 --- a/crates/analysis/src/diagnostics.rs +++ b/crates/analysis/src/diagnostics.rs @@ -629,12 +629,10 @@ impl<'a> Ctx<'a> { false } - /// Block-local dead-code check: a `WeaponSet`/`ArmorSet` carrying the - /// `PLAYER_UPGRADE` condition is only ever selected after a module on the - /// *same* object sets that flag (WeaponSetUpgrade / the TransportContain - /// family with armed passengers; ArmorUpgrade for armor). Both sides live - /// in the same top-level block, so the check is sound under the per-block - /// diagnostics cache. Skipped for map.ini-style override blocks + /// Block-local dead-code check for conditional weapon, armor, and locomotor + /// sets. Both the set and its selecting module live in the same top-level + /// block, so the check is sound under the per-block diagnostics cache. + /// Skipped for map.ini-style override blocks /// (`AddModule`/`RemoveModule`/`ReplaceModule` present): those are /// partial definitions. fn check_set_reachability(&mut self, node: &SyntaxNode) { @@ -688,6 +686,20 @@ impl<'a> Ctx<'a> { if is_override_patch { return; } + if !module_names.iter().any(|m| m == "LocomotorSetUpgrade") { + for tok in Block(node.clone()) + .fields() + .filter(|f| f.key().is_some_and(|k| k.text() == "Locomotor")) + .filter_map(|f| f.value_tokens().first().cloned()) + .filter(|t| t.text().eq_ignore_ascii_case("SET_NORMAL_UPGRADED")) + { + self.warning( + &tok, + "unreachable-set", + "this locomotor set requires `LocomotorSetUpgrade` — the set can never be selected".to_string(), + ); + } + } let has_player_upgrade_weapon_set = player_upgrade_sets.iter().any(|(kw, _)| *kw == "WeaponSet"); if !has_player_upgrade_weapon_set { diff --git a/crates/analysis/tests/spec/DeadCodeTest.ini b/crates/analysis/tests/spec/DeadCodeTest.ini index f665baa..7118391 100644 --- a/crates/analysis/tests/spec/DeadCodeTest.ini +++ b/crates/analysis/tests/spec/DeadCodeTest.ini @@ -54,6 +54,31 @@ Object DeadCodeOverride End End +; SET_NORMAL_UPGRADED is selected only after LocomotorSetUpgrade calls +; setLocomotorUpgrade(true). Without that module, this field is dead. +Object DeadCodeBadLocomotor + Locomotor = SET_NORMAL DeadCodeNormalLocomotor + Locomotor = SET_NORMAL_UPGRADED DeadCodeUpgradedLocomotor + Behavior = AIUpdateInterface ModuleTag_AI + End +End + +Object DeadCodeGoodLocomotor + Locomotor = SET_NORMAL DeadCodeNormalLocomotor + Locomotor = SET_NORMAL_UPGRADED DeadCodeUpgradedLocomotor + Behavior = AIUpdateInterface ModuleTag_AI + End + Behavior = LocomotorSetUpgrade ModuleTag_LocomotorUpgrade + TriggeredBy = Upgrade_DeadCodeTest + End +End + +; map.ini-style override patches may inherit LocomotorSetUpgrade: never flagged. +Object DeadCodeLocomotorOverride + RemoveModule ModuleTag_99 + Locomotor = SET_NORMAL_UPGRADED DeadCodeUpgradedLocomotor +End + Weapon DeadCodeTestGun PrimaryDamage = 10.0 End @@ -65,3 +90,9 @@ End Upgrade Upgrade_DeadCodeTest BuildTime = 1.0 End + +Locomotor DeadCodeNormalLocomotor +End + +Locomotor DeadCodeUpgradedLocomotor +End diff --git a/crates/analysis/tests/spec/DeadCodeTest.spec.toml b/crates/analysis/tests/spec/DeadCodeTest.spec.toml index b1ccb79..3b81a9e 100644 --- a/crates/analysis/tests/spec/DeadCodeTest.spec.toml +++ b/crates/analysis/tests/spec/DeadCodeTest.spec.toml @@ -20,3 +20,26 @@ severity = "warning" code = "unreachable-set" on = "PLAYER_UPGRADE" nth = 3 + +# DeadCodeBadLocomotor's upgraded set has no LocomotorSetUpgrade module. +[[diag]] +severity = "warning" +code = "unreachable-set" +on = "SET_NORMAL_UPGRADED" +nth = 1 + +# A matching upgrade module makes the upgraded locomotor reachable. +[[diag]] +severity = "warning" +code = "unreachable-set" +on = "SET_NORMAL_UPGRADED" +nth = 2 +absent = true + +# Partial override definitions may inherit the module from their base object. +[[diag]] +severity = "warning" +code = "unreachable-set" +on = "SET_NORMAL_UPGRADED" +nth = 3 +absent = true diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..774863b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,11 @@ +# Documentation + +- [Diagnostics, suppression, and quick fixes](diagnostics.md) +- [Standalone language server setup](language-server.md) +- [VS Code extension development](vscode-development.md) +- [Release process](release.md) + +Community and repository policies remain at the repository root so GitHub can +discover them automatically: [contributing](../CONTRIBUTING.md), +[support](../SUPPORT.md), [security](../SECURITY.md), and the +[code of conduct](../CODE_OF_CONDUCT.md). diff --git a/docs/diagnostics.md b/docs/diagnostics.md new file mode 100644 index 0000000..9ebd112 --- /dev/null +++ b/docs/diagnostics.md @@ -0,0 +1,67 @@ +# Diagnostics and quick fixes + +ZeroSyntax reports stable diagnostic codes. Use the code shown by your editor +when searching for an issue or suppressing an intentional warning. + +## Suppress a diagnostic in one file + +Add a file-scope comment before the first INI block: + +```ini +; zerosyntax-disable: unresolved-reference, unreachable-set +``` + +Separate multiple codes with spaces or commas. Multiple file-scope pragma lines +accumulate. A misspelled code produces `unknown-suppression` instead of silently +hiding nothing. + +Suppressions are intended for warnings and hints that are valid for a specific +file. Fix error-level syntax and schema problems rather than suppressing them. + +## Diagnostic codes + +| Code | Meaning | +| --- | --- | +| `syntax` | The file cannot be parsed cleanly, such as when an `End` is missing. | +| `stray-field` | A field appears outside a valid block or module. | +| `unknown-block` | A top-level block is not in the Generals INI schema. | +| `overrides` | A map layer redefines an existing object-style definition. | +| `duplicate-definition` | The same definition is declared more than once. | +| `map-forward-reference` | A map or solo INI references a definition declared later in that file, after the point where the game needs it. | +| `map-projectile-object` | A weapon uses a map-defined projectile object that the game cannot resolve reliably. | +| `unreachable-set` | A `WeaponSet` or `ArmorSet` cannot activate, or an upgrade module has no matching set. | +| `unknown-field` | A field is not valid in the current block or module. | +| `missing-module-tag` | A module is missing its required `ModuleTag_*` name. | +| `unknown-module` | A module type is not known for the current module slot. | +| `missing-condition` | A conditional state block is missing its condition token. | +| `missing-value` | A field requires a value but none was provided. | +| `bad-bool` | A boolean is not `Yes` or `No`. | +| `non-positive` | A value that must be greater than zero is not. | +| `bad-percent` | A percentage is malformed or outside its allowed range. | +| `bad-color` | A color value is malformed. | +| `bad-coord` | A coordinate value is malformed. | +| `bad-number` | A numeric field does not contain a valid number. | +| `bad-enum` | A value is not a member of the expected enum. | +| `bad-flag` | A bitflag is not a member of the expected flag set. | +| `bad-prefixed` | A tagged value does not use its required `Prefix:value` form. | +| `unresolved-reference` | A referenced definition is not found in the workspace or configured base INI roots. | +| `unknown-model` | A model name is not found in the indexed W3D assets. | +| `unknown-model-member` | A bone or subobject is not found in the models active in that scope. | +| `unknown-suppression` | A `zerosyntax-disable` comment names an unknown code. | +| `module-wrong-slot` | A module type is used under the wrong slot. | +| `duplicate-module-tag` | Two modules in one object use the same module tag. | +| `editor-default-module` | A placeholder module value should be replaced before shipping. | + +## Quick fixes + +Open your editor's lightbulb or code-action menu on a diagnostic to see the +available fix. + +| Quick fix | When it appears | +| --- | --- | +| Insert missing `End` | A block or module is unterminated. | +| Replace with a known value | An enum or bitflag is close to a valid value. | +| Create a stub definition | A reference points to a missing definition that can be scaffolded safely. | +| Remove an unreachable `WeaponSet` or `ArmorSet` | An upgrade-conditioned set can never activate. | +| Insert a matching upgrade module or set | An object has only one side of an upgrade-conditioned weapon or armor setup. | +| Suppress a code in this file | A warning or hint is intentional for the current file. | diff --git a/docs/language-server.md b/docs/language-server.md new file mode 100644 index 0000000..8248f40 --- /dev/null +++ b/docs/language-server.md @@ -0,0 +1,68 @@ +# Standalone language server + +Use `zerosyntax-lsp` with any editor that supports the Language Server Protocol +over stdio. + +## Install + +1. Download the Windows x64 or Linux x64 server archive from the + [latest release](https://github.com/ViTeXFTW/ZeroSyntaxV2/releases/latest). +2. Verify the archive against `SHA256SUMS.txt` from the same release. +3. Extract the archive and put `zerosyntax-lsp` (or `zerosyntax-lsp.exe`) on + your `PATH`, or configure its absolute path in your editor. +4. Register the server for the Generals/Zero Hour `.ini` files in your project. + +The server writes protocol messages to stdout, so clients must launch it using +stdio rather than a TCP port. + +## Client configuration + +Configure your LSP client with: + +| Option | Value | +| --- | --- | +| Command | `zerosyntax-lsp` | +| Transport | stdio | +| Files | Zero Hour `.ini` files | +| Workspace root | Your map or mod directory | + +The server indexes INI files under each workspace folder. Open the whole project +to enable cross-file completion, definitions, references, rename, and workspace +symbols. + +## Initialization options + +```json +{ + "format": { "enable": false }, + "baseIniRoots": [ + "C:/Games/Zero Hour", + "C:/Mods/MyMod/Data/INI", + "C:/Mods/MyMod.big" + ] +} +``` + +- `format.enable` controls whether the server advertises document formatting. + It defaults to `false`. +- `baseIniRoots` accepts directories and `.big` archives containing base game + or mod INI files and W3D assets. Those INI definitions are treated as loaded + before `map.ini` and `solo.ini`. + +Restart the language server after changing initialization options. + +## Supported LSP features + +ZeroSyntax supports incremental document sync, diagnostics, completion, hover, +go to definition, references, rename, semantic tokens, document and workspace +symbols, folding ranges, quick fixes, and optional document formatting. + +## Build from source + +Install Rust 1.75 or newer, clone the repository, and run: + +```sh +cargo build --locked --release -p zerosyntax-server +``` + +The binary is written to `target/release/zerosyntax-lsp` (`.exe` on Windows). diff --git a/docs/release.md b/docs/release.md index cbf9a36..2f3161f 100644 --- a/docs/release.md +++ b/docs/release.md @@ -36,10 +36,14 @@ cargo clippy --locked --all-targets --all-features -- -D warnings cd editors/vscode npm ci -npm run compile +npm test cd ../.. ``` +Review the root README, extension README, and files under `docs/` whenever a +release changes installation, settings, supported platforms, or user-visible +behavior. + The release workflow repeats CI and the real-corpus gate before building any release assets. @@ -68,6 +72,14 @@ configured. If `prod` contains changes that are not in `dev`, the workflow stops before building and asks for `prod` to be merged back into `dev` first. +## After release + +Verify that the GitHub Release contains both server archives, both +platform-specific `.vsix` files, and `SHA256SUMS.txt`. Install the matching +`.vsix` in a clean VS Code profile and open a Generals INI file. For a live +release, confirm the same version appears in the Marketplace when publishing is +enabled. + ## Repository settings - Keep `dev` as the default branch so the manual workflow is available. diff --git a/docs/vscode-development.md b/docs/vscode-development.md new file mode 100644 index 0000000..bdcba98 --- /dev/null +++ b/docs/vscode-development.md @@ -0,0 +1,44 @@ +# VS Code extension development + +These instructions are for contributors. Users should install a platform +package from [GitHub Releases](https://github.com/ViTeXFTW/ZeroSyntaxV2/releases/latest). + +## Build and test + +From the repository root: + +```sh +cargo build --locked -p zerosyntax-server + +cd editors/vscode +npm ci +npm test +``` + +To debug the extension, open `editors/vscode` in VS Code and press F5. +The repository's launch configuration points the Extension Development Host at +the locally built debug server. + +## Choose a server binary + +The extension resolves the server in this order: + +1. `zerosyntax.server.path`. +2. The `ZEROSYNTAX_LSP_PATH` environment variable used by development tooling. +3. `editors/vscode/server/zerosyntax-lsp[.exe]`. +4. `zerosyntax-lsp` on `PATH`. + +## Package locally + +Build the release server, copy it into `editors/vscode/server`, then package the +extension: + +```sh +cargo build --locked --release -p zerosyntax-server +cd editors/vscode +npm ci +npm run package +``` + +The release workflow performs the platform-specific binary staging and VSIX +target selection automatically. See the [release process](release.md). diff --git a/editors/vscode/README.md b/editors/vscode/README.md index 7e5ab1d..9919f8f 100644 --- a/editors/vscode/README.md +++ b/editors/vscode/README.md @@ -1,52 +1,79 @@ -# ZeroSyntax v2 — VS Code extension +# ZeroSyntax v2 for VS Code -Reference editor client for the ZeroSyntax v2 language server (see the project -root README for the full architecture). -Provides diagnostics, completions, hover, go-to-definition, and schema-aware -semantic highlighting for C&C Generals: Zero Hour `.ini` files. +ZeroSyntax adds diagnostics, completion, navigation, refactoring, semantic +highlighting, and quick fixes for *Command & Conquer: Generals – Zero Hour* INI +files. The platform-specific extension package includes the language server, so +there is nothing else to install. -The server itself is IDE-agnostic; this extension is just a thin client that -launches it and speaks LSP. +## Install -## Build +1. Download the `.vsix` for Windows x64 or Linux x64 from the + [latest release](https://github.com/ViTeXFTW/ZeroSyntaxV2/releases/latest). +2. In VS Code, open **Extensions**, choose **Views and More Actions …**, then + select **Install from VSIX…**. +3. Open the downloaded file and reload VS Code if prompted. +4. Open your map or mod folder and start editing an `.ini` file. + +You can also install from a terminal: ```sh -cd editors/vscode -npm install -npm run compile +code --install-extension zerosyntax-vscode--.vsix ``` -## Provide the server binary +## Recommended setup -Build the server from the workspace root and make it discoverable in one of -these ways (checked in order): +Open the folder containing your project rather than a single file. ZeroSyntax +indexes the workspace so definitions, references, rename, and completion work +across its INI files. -1. Set `zerosyntax.server.path` to the absolute path of `zerosyntax-lsp`. -2. Copy the binary to `editors/vscode/server/zerosyntax-lsp[.exe]` (bundled into - the `.vsix`). -3. Put `zerosyntax-lsp` on your `PATH`. +When editing `map.ini` or `solo.ini`, configure **ZeroSyntax v2: Base Ini Roots** +with any game or mod folders and `.big` archives that load before the map. This +prevents false unresolved-reference warnings and enables W3D model and bone +checks. -```sh -cargo build --release -p zerosyntax-server -# option 2: -mkdir -p editors/vscode/server -cp target/release/zerosyntax-lsp* editors/vscode/server/ -``` +## Settings -## Run / debug +| Setting | Default | Purpose | +| --- | --- | --- | +| `zerosyntax.baseIniRoots` | `[]` | Base game/mod directories and `.big` archives used for map and model checks. | +| `zerosyntax.format.enable` | `false` | Enables indentation formatting. Changing it restarts the server. | +| `zerosyntax.server.path` | empty | Uses a custom `zerosyntax-lsp` binary instead of the bundled one. | +| `zerosyntax.trace.server` | `off` | Logs LSP traffic for troubleshooting. | -Open this folder in VS Code and press F5 to launch an Extension -Development Host, then open any `.ini` file recognized as **Generals INI**. +Formatting is intentionally off by default. Enable it only when you want +**Format Document** or format-on-save to normalize indentation. -## Package +## INI file association -```sh -npm run package # produces zerosyntax-vscode-.vsix (needs @vscode/vsce) +The extension associates `.ini` files with **Generals INI**. If your workspace +also contains unrelated INI files, keep those as plain text and override the +association only for the folders you want ZeroSyntax to handle: + +```json +{ + "files.associations": { + "**/*.ini": "plaintext", + "Data/INI/**/*.ini": "generals-ini", + "Maps/**/*.ini": "generals-ini" + } +} ``` -## Note on `.ini` association +Use the language selector in VS Code's status bar to change an individual file. + +## Troubleshooting + +- If the server cannot be found, reinstall the `.vsix` for your platform. A + source checkout does not contain a bundled server until it is built. +- If map references are reported as missing, configure + `zerosyntax.baseIniRoots` and check that the paths point to the required INI + folders or `.big` archives. +- For detailed logs, set `zerosyntax.trace.server` to `messages` or `verbose`, + reproduce the problem, then open **Output → ZeroSyntax v2 Language Server**. + +Report reproducible problems through +[GitHub Issues](https://github.com/ViTeXFTW/ZeroSyntaxV2/issues) with a minimal +INI sample, your OS, VS Code version, and ZeroSyntax version. -This extension claims the `.ini` extension under the `generals-ini` language id. -If you also edit unrelated `.ini` files, scope it per workspace with -`files.associations`, e.g. only treat files under your mod folder as -`generals-ini`. +Contributor build and packaging instructions are in the +[VS Code development guide](https://github.com/ViTeXFTW/ZeroSyntaxV2/blob/dev/docs/vscode-development.md). diff --git a/editors/vscode/package.json b/editors/vscode/package.json index 5acd556..0931a33 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -9,6 +9,7 @@ "url": "https://github.com/ViTeXFTW/ZeroSyntaxV2" }, "publisher": "ViTeXFTW", + "icon": "icon/ZeroSyntaxLogo256.png", "engines": { "vscode": "^1.84.0" }, @@ -80,7 +81,7 @@ "compile": "tsc -p ./", "compile:test": "tsc -p ./tsconfig.test.json", "watch": "tsc -watch -p ./", - "vscode:prepublish": "npm run compile", + "vscode:prepublish": "node -e \"const fs=require('fs');fs.mkdirSync('icon',{recursive:true});fs.copyFileSync('../../icon/ZeroSyntaxLogo256.png','icon/ZeroSyntaxLogo256.png')\" && npm run compile", "package": "vsce package --allow-missing-repository", "test": "npm run compile && npm run compile:test && node ./out-test/src/test/runTest.js" }, diff --git a/icon/ZeroSyntaxLogo256.png b/icon/ZeroSyntaxLogo256.png new file mode 100644 index 0000000..137e0d9 Binary files /dev/null and b/icon/ZeroSyntaxLogo256.png differ