feat(tooling): JetBrains IDE support, LSP navigation, barrel imports, package scripts and hooks

- JetBrains plugin 1.4.0: semantic colours (builtin / vendor / own), template strings,
  brace handling, run configurations and a test console, package.ludic and
  package.lock.ludic editing (completion, docs, app preview, colour previews, asset
  navigation), External Libraries for the runtime and packages, doc pages for built-ins
- ludic-lsp: go to definition for imports, document links, hover with inferred types,
  type definition, signature help with parameters, docs from docs/language
- `import "dir"` resolves a barrel `dir/index.ludic`
- package.ludic `entry`, `script` and `hook before|after <command>`; `ludic <script>`,
  `ludic script`, `ludic scripts`
- `ludic test --verbose` and `--test NAME`; the test runner filters by name

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-16 16:00:28 +03:00
parent da57b20156
commit f92d7f89c6
75 changed files with 46802 additions and 40343 deletions

View file

@ -50,6 +50,37 @@ A consumer project's manifest is the same file, usually with only `require`
lines (the `package`/`version` fields describe a *publishable* package and are
optional for a leaf application).
### The entry program
`entry "src/game.ludic"` names the program `ludic run`, `build` and `bundle` compile
when no file is given. Without it the CLI tries `src/main.ludic`, `main.ludic`, then
the one file under `src/` that declares a `program`.
### Scripts and hooks
```
script "dev" "ludic run --headless" # ludic dev (or: ludic script dev)
script "shots" "tools/shots.sh" # ludic shots a b -> tools/shots.sh 'a' 'b'
hook before build "tools/gen_atlas.sh" # a failing before hook stops the build
hook after bundle "tools/notarize.sh" # after hooks run only when the command succeeded
hook before dev "ludic fmt --check" # a script is an event too
```
A **script** is a named shell command, run from the package root. `ludic <name>` runs
it when no built-in command has that name; `ludic script <name>` always does, and
`ludic scripts` lists them. Arguments after the name are passed on, quoted.
A **hook** runs a command `before` or `after` a built-in command (`build`, `run`,
`test`, `bundle`, `pack`, `clean`, `fmt`, `get`, `add`, `update`, `verify`, `vendor`,
`assets`, `build-lib`) or a script. Hooks of one event run in the order written. A
`before` hook that exits non-zero stops the command with that exit code; `after`
hooks run only when the command succeeded.
Both run through the shell with the toolchain's `bin/` first on `PATH` and
`LUDIC_PACKAGE_ROOT`, `LUDIC_EVENT` (the command or script) and `LUDIC_PHASE`
(`before`, `after` or `run`) set. Inside a quoted value, `\"` is a quote and `\\` a
backslash.
## The lockfile — `package.lock.ludic`
Generated by `ludic get`; do not edit by hand. One line per resolved module, pinning

View file

@ -11,6 +11,8 @@ order: 10
An <code>import</code> pulls the declarations of another Ludic file into this program, letting you split a game across many files instead of one giant block. The imported file is a <b>fragment</b> — bare declarations with no `program` wrapper — and its contents are spliced in as if written here. Paths resolve relative to the importing file, imports may nest, and each resolved path is include-guarded, so importing the same file twice (even through different chains) pulls it in exactly once. Diagnostics still point at the real source file, so errors in an imported fragment report that file's name and line.
A path without <code>.ludic</code> names a directory through its barrel, <code>index.ludic</code>: <code>import "camp"</code> reads <code>camp/index.ludic</code>, which lists the directory's modules with imports relative to itself. <code>import "dir/*.ludic"</code> instead takes every file in the directory, in name order.
```ludic
# doc-check: skip — illustrative; the imported paths are example files, not
# resolvable when this snippet is checked in isolation.