Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,24 @@ repos:
# - id: gmat-script-format-check # or: check only, never write (CI)
```

## Editor tooling

[![VS Marketplace](https://img.shields.io/visual-studio-marketplace/v/astro-tools.gmat-script?label=VS%20Marketplace&color=311B92)](https://marketplace.visualstudio.com/items?itemName=astro-tools.gmat-script)
[![Open VSX](https://img.shields.io/open-vsx/v/astro-tools/gmat-script?label=Open%20VSX&color=311B92)](https://open-vsx.org/extension/astro-tools/gmat-script)

The same engine drives an editor experience — highlighting, hover docs, live diagnostics, completion,
go-to-definition, an outline, and format-on-save.

- **VS Code** — the **GMAT Script** extension, on the
[Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=astro-tools.gmat-script)
and [Open VSX](https://open-vsx.org/extension/astro-tools/gmat-script). Highlighting works on
install; the richer features come from the language server (`pip install "gmat-script[lsp]"`).
- **Neovim, Emacs, Helix, and the rest** — a Language Server Protocol server (`gmat-script-lsp`,
from the `lsp` extra) backs any LSP-capable editor.

See the [VS Code extension guide](https://astro-tools.github.io/gmat-script/vscode/) and the
[language server guide](https://astro-tools.github.io/gmat-script/lsp/).

## The grammar surface

The tree-sitter grammar parses GMAT scripts (`.script`) and GmatFunctions (`.gmf`) — the same
Expand All @@ -112,6 +130,11 @@ parsing is effectively version-independent — scripts from other releases parse
*do* vary by release (valid field names, enums, defaults) belong to the linter and are scoped
to R2026a.

Those semantics live in a **field catalogue** reflected from R2026a (102 resource types, 2614
fields) and shipped as data inside the wheel, so the linter and editor tooling need no GMAT install.
It is version-pinned and provenance-stamped, selectable per release, and adding another GMAT version
is a data file — not a code change. See [the field catalogue](https://astro-tools.github.io/gmat-script/catalogue/).

## What gmat-script is not

- **Not a propagator or astrodynamics engine.** It reads and transforms script *text*; it computes
Expand All @@ -123,7 +146,8 @@ to R2026a.
## Documentation

Full documentation — getting started, the grammar surface, the typed AST and editing guides, the
formatter, the linter, the command-line tool, the error-reporting model, and the API reference — is at
formatter, the linter and the field catalogue, the command-line tool, the language server and VS Code
extension, the error-reporting model, and the API reference — is at
**[astro-tools.github.io/gmat-script](https://astro-tools.github.io/gmat-script/)**.

## License
Expand Down
8 changes: 5 additions & 3 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
The public surface of `gmat_script`. It is deliberately minimal and additive: the `parse` entry
point and the `Tree` it returns, the `ErrorNode` / `Position` records that describe syntax errors,
the typed `Script` overlay with its mutation API (`ObjectRef`, `RawValue`, `MutationError`), the
canonical `format` pretty-printer, and the `lint` checker with its `Diagnostic` / `Severity` records.
Each layer is re-exported here as it lands.
canonical `format` pretty-printer, the `lint` checker with its `Diagnostic` / `Severity` records, and
the `Catalog` field catalogue (`load_catalog`, `FieldSpec`, `TypeSpec`). Each layer is re-exported
here as it lands.

For a worked introduction see [Getting started](getting-started.md); for the error model see
[Error reporting](errors.md); for the linter see [Linting](lint.md).
[Error reporting](errors.md); for the linter see [Linting](lint.md); for the catalogue see
[The field catalogue](catalogue.md).

::: gmat_script
120 changes: 120 additions & 0 deletions docs/catalogue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# The field catalogue

The field catalogue is the knowledge base behind everything that reasons about GMAT *semantics*: the
[linter](lint.md), and the [language server](lsp.md)'s hover docs and completion. It records, for
every GMAT resource type, the fields that type defines — each field's type, allowed enumeration,
reference target, default, and unit — so a tool can tell that `Spacecraft.SMA` wants a number, that
`ImpulsiveBurn.Axes` is one of four values, or that `Spacecraft.Tanks` points at a `FuelTank`,
without running GMAT.

It is shipped as **data**, not derived at runtime. The wheel carries a precompiled JSON catalogue;
loading it imports no GMAT and touches no `gmatpy`.

## Where it comes from — and the GMAT-free guarantee

The catalogue is reflected from a real GMAT install at build time, by a single generator, and then
frozen into the package. Two halves, deliberately kept apart:

- **Generation (build/CI time, GMAT required).** `gmat_script.tools.gen_catalog` is the *only*
GMAT-touching code in the project. It imports `gmatpy`, walks GMAT's object factory and each
object's parameter metadata, normalises the types, and writes
`gmat_script/data/fields-<version>.json`.
- **Loading (runtime, no GMAT).** `gmat_script.catalog` reads that JSON and answers queries. It
**never imports `gmatpy`** — so a plain `pip install gmat-script` carries the full catalogue with
no GMAT install anywhere.

This split is the GMAT-free guarantee in practice: GMAT's knowledge is captured once, offline, and
travels with the package as a data file. (See the [design decisions](design/decisions.md).)

The shipped catalogue is reflected from **GMAT R2026a** and records its 102 resource types and 2614
fields.

## Reading the catalogue

`load_catalog()` returns a cached, queryable `Catalog`. It and its records are part of the public
API:

```python
from gmat_script import load_catalog

cat = load_catalog()

cat.gmat_version # 'R2026a' — the release this was reflected from
cat.generated # '2026-06-08' — the ISO date it was generated

cat.has_type("Spacecraft") # True
cat.field_type("Spacecraft", "SMA") # 'real'
cat.enum_values("ImpulsiveBurn", "Axes")
# ('VNB', 'LVLH', 'MJ2000Eq', 'SpacecraftBody')
cat.ref_target("Spacecraft", "Tanks") # 'FuelTank'
```

A type name that GMAT spells differently in scripts resolves through an alias (`Propagator` ->
`PropSetup`, `ODEModel` -> `ForceModel`), so `cat.has_type("Propagator")` is `True`. Any query for an
unknown type or field returns `None` (or `[]`) rather than raising — the linter and editor leans on
this to degrade to "no finding" wherever the catalogue is silent.

### What a field carries

`cat.field(type, name)` returns a `FieldSpec`:

| Attribute | Meaning |
|-----------|---------|
| `type` | The normalised catalogue type: `real`, `integer`, `string`, `bool`, `enum`, `object`, `object_array`, `string_array`, `real_array`, `matrix`, `filename`, `on_off`, `color`, `gmat_time`, … |
| `gmat_type` | GMAT's own raw type label, kept verbatim (e.g. `Real`, `Rmatrix`). |
| `read_only` | Whether GMAT exposes the field as output-only (not settable from a script). |
| `allowed` | The enum's allowed values, where GMAT exposes them — else `None`. |
| `ref_target` | The target GMAT type for an object-reference field — else `None`. |
| `default` | The default for a settable scalar field — else `None`. |
| `unit` | The field's unit, where GMAT reports one — else `None`. |

`cat.type_spec(type)` returns a `TypeSpec` carrying the type's GMAT object-type `category` and its
`fields` mapping. The lower-level `Catalog.load(target_version=...)` builds an uncached catalogue;
`load_catalog` is the convenience entry point most consumers want.

## Versioning

The semantics a script must satisfy — valid field names, enums, defaults — vary by GMAT release, so
the catalogue is **version-pinned and provenance-stamped**: every catalogue carries the GMAT version
it was reflected from and the date it was generated. The grammar, by contrast, never enumerates types
or keywords and is effectively version-independent; the catalogue is the one version-coupled artifact.

`load_catalog()` defaults to the newest shipped catalogue. Pass `target_version` to pin a specific
release:

```python
load_catalog("R2026a") # an explicit release
load_catalog() # the newest available (currently R2026a)
```

The linter exposes the same selector — `lint(source, target_version="R2026a")` — and the language
server uses the default. Requesting a version that is not shipped raises `ValueError` listing what is
available.

Supporting another GMAT release is **additive**: a new `fields-<version>.json` is dropped into
`gmat_script/data/`, and `load_catalog()`'s "newest" default and the `target_version` selector pick
it up with no code change. There is no per-version code fork.

## Regenerating the catalogue (the version-bump process)

Regenerating is only needed when targeting a new GMAT release or picking up a point-release metadata
change. It needs a real GMAT install — `gmatpy` is imported from it directly and is never
pip-installed.

```console
# Regenerate and overwrite the shipped file, against a GMAT install:
$ python -m gmat_script.tools.gen_catalog
# Regenerate in memory and fail on any drift from the committed catalogue (the CI check):
$ python -m gmat_script.tools.gen_catalog --check
```

The generator locates GMAT from `--gmat-root`, else the `GMAT_ROOT` environment variable, else a set
of platform-standard install paths. A dedicated CI job runs the `--check` form on a schedule (and on
demand) against a freshly provisioned GMAT install, so a catalogue that drifts from the current GMAT
metadata is surfaced rather than silently rotting. The generation date is ignored in the drift
comparison, so re-running on a different day is not spurious drift.

To bump to a new release: regenerate against that GMAT version (so the file is written as
`fields-<new-version>.json`), commit the new data file alongside the existing one, and the loader's
"newest" default begins serving it. Keeping the prior file lets callers pin the older release through
`target_version`.
15 changes: 9 additions & 6 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,17 @@ tree.has_errors # False
tree.to_source() # round-trips byte-for-byte to the input
```

gmat-script ships the parser, a typed AST with a mutation API, and a canonical formatter, with a
`gmat-script` command-line tool over the same engine. The linter and editor tooling build on top of
the same tree as they land.
gmat-script ships the parser, a typed AST with a mutation API, a canonical formatter, and a static
linter, with a `gmat-script` command-line tool over the same engine — plus a language server and a
VS Code extension that bring it all to your editor.

## What it is

- A **tree-sitter grammar** for GMAT scripts (`.script`) and GmatFunctions (`.gmf`) that parses the
full R2026a sample corpus and re-emits it byte-for-byte.
- A Python library that loads that grammar from a **vendored, precompiled** binding — so
`pip install gmat-script` needs no C or Node toolchain, and never GMAT.
- A `gmat-script` command-line tool that parses and formats scripts from the shell or CI (with
linting to follow as it lands).
- A `gmat-script` command-line tool that parses, formats, and lints scripts from the shell or CI.

## What it is not

Expand All @@ -41,7 +40,11 @@ the same tree as they land.
- **[Typed AST](typed-ast.md)** — typed resources and dict-like field access over the tree.
- **[Editing](editing.md)** — set fields, rename resources, and splice commands.
- **[Formatter](formatting.md)** — canonical, idempotent re-emission.
- **[CLI](cli.md)** — the `parse` syntax gate and the `format` command.
- **[Linter](lint.md)** — structural checks against the bundled field catalogue.
- **[Field catalogue](catalogue.md)** — the version-pinned knowledge base behind the linter and editor.
- **[CLI](cli.md)** — the `parse` syntax gate and the `format` and `lint` commands.
- **[Language server](lsp.md)** — diagnostics, hover, and completion in any LSP editor.
- **[VS Code extension](vscode.md)** — highlighting, diagnostics, and format-on-save in VS Code.
- **[Error reporting](errors.md)** — how malformed input is surfaced.
- **[API reference](api.md)** — the public Python surface.
- **[Design decisions](design/decisions.md)** — the grammar scope, CST node taxonomy, and the
Expand Down
9 changes: 3 additions & 6 deletions docs/lsp.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,9 @@ vim.api.nvim_create_autocmd("FileType", {

### VS Code

Install the [**GMAT Script**](https://marketplace.visualstudio.com/items?itemName=astro-tools.gmat-script)
extension (also on [Open VSX](https://open-vsx.org/extension/astro-tools/gmat-script)). It bundles a
client that launches this server for you, and adds standalone syntax highlighting that works even
before the server is installed. Add the server with `pip install "gmat-script[lsp]"` — the extension
picks up `gmat-script-lsp` from your `PATH`, or point `gmatScript.server.pythonPath` at the Python
environment that has it. Format-on-save is enabled for GMAT files by default.
The [**GMAT Script**](vscode.md) extension bundles a client that launches this server for you, plus
standalone syntax highlighting that works before the server is installed. See the [VS Code extension
guide](vscode.md) for install (Marketplace / Open VSX), features, format-on-save, and settings.

## How it fits together

Expand Down
71 changes: 71 additions & 0 deletions docs/vscode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# The VS Code extension

**GMAT Script** brings the grammar, linter, and [language server](lsp.md) to Visual Studio Code:
syntax highlighting, hover docs, live diagnostics, completion, go-to-definition, an outline, and
format-on-save for `.script` and `.gmf` files.

## Installing

Install **GMAT Script** from either marketplace:

- [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=astro-tools.gmat-script)
— search *GMAT Script* in the Extensions view, or run
`code --install-extension astro-tools.gmat-script`.
- [Open VSX](https://open-vsx.org/extension/astro-tools/gmat-script) — for VSCodium, Cursor, Gitpod,
and other Open VSX–based editors.

The extension bundles **syntax highlighting** as a TextMate grammar, so colouring works the moment
it is installed — no Python, no further setup.

Everything richer — hover, diagnostics, completion, definition, references, the outline, and
formatting — is served by the `gmat-script` language server, which runs on Python. Install it into a
Python environment the extension can reach:

```console
$ pip install "gmat-script[lsp]"
```

This puts a `gmat-script-lsp` command on your `PATH`, which the extension launches automatically. If
it is not found, highlighting still works and the extension points you here. To use a specific
interpreter or virtual environment, set `gmatScript.server.pythonPath` (see
[Settings](#settings)).

## Features

| Feature | Needs the server? |
|---------|:-----------------:|
| Syntax highlighting — resources, commands, fields, control flow, solver blocks, GmatFunction headers | no |
| Hover — a field's type, default, allowed values, units, and reference target | yes |
| Live diagnostics as you type — syntax errors plus linter findings | yes |
| Completion — resource names, the fields valid for the resource under the cursor, and enum values | yes |
| Go to definition / find all references for resources | yes |
| Document outline — every `Create`'d resource and GmatFunction header | yes |
| Format on save — canonical, idempotent re-emission | yes |

The server-backed features are exactly those of the [language server](lsp.md); the extension is the
VS Code client that launches and talks to it. A half-typed buffer never breaks them — the
error-recovering parse keeps hover and completion working while you edit.

## Format on save

Format-on-save is **enabled for GMAT files by default**: the extension sets `editor.formatOnSave`
for the `gmat` language, so saving a `.script` or `.gmf` rewrites it into canonical form via the same
formatter the [CLI](cli.md) and library use. Turn it off per-language if you prefer:

```jsonc
"[gmat]": { "editor.formatOnSave": false }
```

## Settings

| Setting | Default | Description |
|---|---|---|
| `gmatScript.server.path` | `gmat-script-lsp` | Command used to launch the language server. Set an absolute path if it is not on your `PATH`. |
| `gmatScript.server.pythonPath` | _(empty)_ | A Python interpreter with `gmat-script[lsp]` installed; when set, the server runs as `<python> -m gmat_script.lsp` and takes precedence over `server.path`. Point it at a virtual environment. |
| `gmatScript.server.args` | `[]` | Extra arguments passed to the server on launch. |
| `gmatScript.trace.server` | `off` | Trace JSON-RPC traffic to the output channel (`off` / `messages` / `verbose`) when debugging. |

## Other editors

The same server powers any LSP-capable editor — Neovim, Emacs, Helix, and the rest. See the
[language server guide](lsp.md) for editor-agnostic setup.
48 changes: 44 additions & 4 deletions editors/vscode/images/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,48 @@
# Marketing assets

`demo.gif` — a short animated capture of the extension in action (highlighting + live diagnostics +
hover on a stock GMAT sample), shown at the top of the extension's marketplace listing.
hover + format-on-save), shown at the top of the extension's marketplace listing and referenced from
`../README.md` (the reference is commented out until the file exists).

To add it: record a `.script` editing session in VS Code (type into a sample, trigger a diagnostic,
hover a field), export an optimized GIF (≈800 px wide, a few seconds, looping), save it here as
`demo.gif`, and uncomment the image reference in `../README.md`.
## Recording `demo.gif`

A repeatable ~8-second capture. Keep it small and looping.

**Setup**

1. Install the extension (run the *Extension Development Host* from this folder, or install the
packaged `.vsix`) and `pip install "gmat-script[lsp]"` into the interpreter VS Code uses, so the
language server is live.
2. Use a clean window: dark theme, no minimap, font size ~16, a single editor column ~80 columns
wide. Hide the sidebar and status-bar clutter.
3. Create a new file `demo.script` and leave it empty.

**The take** (type at a natural pace; let each step settle for ~1 s)

1. Type the snippet below. Highlighting colours resources, fields, the burn axis, and commands as you
go:

```text
Create Spacecraft Sat
Sat.SMA = 7000

Create ImpulsiveBurn TOI
TOI.Axes = VNB

BeginMissionSequence
Maneuver TOI(Sat)
```

2. Change `Sat.SMA = 7000` to `Sat.SMA = 'high'`. A red squiggle appears under `'high'`; hover it to
show the `type-mismatch` diagnostic ("field 'SMA' expects a number, got a quoted string").
3. Hover `Axes` to show the field doc card (type + allowed values `VNB, LVLH, MJ2000Eq,
SpacecraftBody`).
4. Fix `Sat.SMA` back to `7000`; the squiggle clears.
5. Mangle the spacing (e.g. `GMAT Sat.SMA=7000;`) and press **Save** — format-on-save snaps it back to
canonical form.

**Export**

Capture the editor region only, export an optimized looping GIF (≈800 px wide, a few seconds), save
it here as `demo.gif`, and uncomment the `![GMAT Script in VS Code](images/demo.gif)` line in
`../README.md`.
6 changes: 4 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ python examples/edit_field.py
| [`edit_field.py`](edit_field.py) | Read a resource field, then set it — `script.spacecraft["Sat"]["SMA"] = 8000`. |
| [`rename_resource.py`](rename_resource.py) | Rename a resource with and without rewriting its references. |
| [`format_in_place.py`](format_in_place.py) | Format a messy script into canonical form, written back to the file. |
| [`lint_script.py`](lint_script.py) | Lint a flawed script — type, field, reference-target, and enum findings — then show it clean. |

For the concepts behind these, see the documentation:
[the typed AST](https://astro-tools.github.io/gmat-script/typed-ast/),
[editing](https://astro-tools.github.io/gmat-script/editing/), and
[the formatter](https://astro-tools.github.io/gmat-script/formatting/).
[editing](https://astro-tools.github.io/gmat-script/editing/),
[the formatter](https://astro-tools.github.io/gmat-script/formatting/), and
[the linter](https://astro-tools.github.io/gmat-script/lint/).
Loading
Loading