diff --git a/README.md b/README.md index d51f133..e7a92d8 100644 --- a/README.md +++ b/README.md @@ -87,61 +87,38 @@ vim.api.nvim_create_autocmd("FileType", { **VS Code** (any generic LSP client extension): command `glint`, args `["lsp"]`, language id / file glob `*.glint`. -**Zed** (dev extension in this repo — see also [`editors/zed/README.md`](editors/zed/README.md)): +**Zed** (extension in [`editors/zed/`](editors/zed/) — see that README for full detail): -#### Why is there Rust under `editors/zed/`? - -Not because we want the extension authored in Rust. **Zed forces it today.** - -On **Install Dev Extension**, when `extension.toml` has `[lib] kind = "Rust"` -(the only allowed kind), Zed always runs roughly: - -```text -cargo build --target wasm32-wasip2 -``` - -and writes `extension.wasm` from that crate. There is **no** install path that -only loads a prebuilt component from `gleam export zed-extension`. Without -`Cargo.toml` + `src/*.rs` you get `failed to compile Rust extension`. - -So `editors/zed/src/glint.rs` is a **thin temporary guest** (same behaviour as -`zed_glint.gleam`: resolve `glint`, start LSP). Direction of travel is pure -Gleam via our [wasm fork](https://tangled.org/nandi.uk/gleam); the `.rs` crate -can go once Zed accepts prebuilt guests (or we use another install path). - -| Path | Role | -|------|------| -| `editors/zed/src/glint.rs` | What Zed can compile and install **now** | -| `editors/zed/src/zed_glint.gleam` | Target authoring surface (pure Gleam) | -| [nandi.uk/gleam](https://tangled.org/nandi.uk/gleam) `wasm` | Compiler that builds a real `zed:extension` component | - -### 1. Build the glint binary +The extension is **Gleam** (`editors/zed/src/zed_glint.gleam`). Zed Install Dev +Extension only knows how to run `cargo build --target wasm32-wasip2`, so that +directory includes a small cargo **build driver** (`build.rs` + linker script) +that runs `gleam export zed-extension` and installs the Gleam component as the +crate’s `.wasm`. There is no separate Rust implementation of the guest. ```sh -cd /home/nandi/code/glint -nix build # → result/bin/glint -``` +# glint LSP +cd /home/nandi/code/glint && nix build -### 2. Compile the Zed guest (Rust) +# wasm-capable gleam: https://tangled.org/nandi.uk/gleam (branch wasm) +export GLEAM=/home/nandi/code/gleam/target/debug/gleam -Zed Install Dev Extension always runs `cargo build --target wasm32-wasip2`. -Build yourself first if you want to check the toolchain: - -```sh +# build driver (same command Zed runs) cd /home/nandi/code/glint/editors/zed rustup target add wasm32-wasip2 # once cargo build --release --target wasm32-wasip2 -cp target/wasm32-wasip2/release/zed_glint.wasm extension.wasm -file extension.wasm # WebAssembly component ``` -### 3. Install in Zed +Preferred (flake, prebuilt Gleam guest): -1. **Extensions → Install Dev Extension…** -2. Select: `/home/nandi/code/glint/editors/zed` -3. Open a `*.glint` file — the guest starts `glint lsp` +```sh +nix build .#zed-extension +nix run .#install-zed-extension -- ~/glint-zed-dev +# Zed → Install Dev Extension → ~/glint-zed-dev +``` + +Or point Install Dev at `editors/zed` after `nix develop` (sets `GLEAM`). -Optional settings override: +Optional LSP path: ```json { @@ -156,28 +133,6 @@ Optional settings override: } ``` -### Pure Gleam guest (experimental) - -Compiler work lives on our Gleam **wasm** fork: - -- **https://tangled.org/nandi.uk/gleam** (`wasm` branch) - -```sh -# build the forked compiler -git clone https://tangled.org/nandi.uk/gleam -cd gleam && git checkout wasm -cargo build -p gleam - -# from this extension (optional; Install Dev still uses Rust today) -cd /home/nandi/code/glint/editors/zed -/path/to/gleam/target/debug/gleam export zed-extension -``` - -Notes: grammar id is `gleam` (exports `tree_sitter_gleam`). Zed does not yet -install a prebuilt Gleam `extension.wasm` without cargo — see -[docs on the fork](https://tangled.org/nandi.uk/gleam) / -`docs/compiler/wasm-zed-extensions.md`. - ## Library diff --git a/editors/zed/.cargo/config.toml b/editors/zed/.cargo/config.toml index f0c56e1..05bff7b 100644 --- a/editors/zed/.cargo/config.toml +++ b/editors/zed/.cargo/config.toml @@ -1,4 +1,7 @@ -# Zed's extension builder compiles with `cargo build --target wasm32-wasip2` -# (WASI preview2 + component model). That is the only guest shape the host loads. +# Zed's extension builder runs `cargo build --target wasm32-wasip2`. +# Use a custom linker that emits the Gleam-built component (see build.rs). [build] target = "wasm32-wasip2" + +[target.wasm32-wasip2] +linker = "scripts/gleam-wasm-linker" diff --git a/editors/zed/Cargo.toml b/editors/zed/Cargo.toml index 13d0cdd..eead746 100644 --- a/editors/zed/Cargo.toml +++ b/editors/zed/Cargo.toml @@ -1,13 +1,18 @@ +# Standalone package (not part of a parent Cargo workspace). +[workspace] + [package] name = "zed_glint" version = "0.1.0" edition = "2021" publish = false license = "Apache-2.0" +build = "build.rs" + +# Build driver only: Zed runs `cargo build --target wasm32-wasip2`. +# build.rs → gleam export zed-extension; linker installs that component. +# Guest source: src/zed_glint.gleam [lib] -path = "src/glint.rs" +path = "src/lib.rs" crate-type = ["cdylib"] - -[dependencies] -zed_extension_api = "0.7.0" diff --git a/editors/zed/README.md b/editors/zed/README.md index 50f87b5..f9ca7ba 100644 --- a/editors/zed/README.md +++ b/editors/zed/README.md @@ -1,100 +1,99 @@ # Glint for Zed -Zed extension for Glint — language config, tree-sitter highlighting (via -tree-sitter-gleam), and the glint language server. +Gleam-authored Zed extension for Glint: tree-sitter highlighting (via +tree-sitter-gleam) and the glint language server. -## Why is there Rust code here? +**Source of truth:** `src/zed_glint.gleam` +**Artifact:** `extension.wasm` (Zed `zed:extension` component, API 0.7.0) -Not because we want the extension authored in Rust. **Zed forces it today.** +## Nix (recommended install) -On **Install Dev Extension**, when `extension.toml` has: +From the glint repo (uses the [gleam wasm fork](https://tangled.org/nandi.uk/gleam) as a flake input): -```toml -[lib] -kind = "Rust" -``` +```sh +cd /home/nandi/code/glint +# optional: use a local gleam checkout +# nix build .#zed-extension --override-input gleam path:../gleam -(`"Rust"` is the only allowed `kind`.) Zed always runs roughly: +nix build .#zed-extension +# → ./result/{extension.toml,extension.wasm,languages/…} -```text -cargo build --target wasm32-wasip2 +nix run .#install-zed-extension -- ~/glint-zed-dev +# then Zed → Extensions → Install Dev Extension → ~/glint-zed-dev ``` -and writes `extension.wasm` from that crate. There is **no** install path that -only loads a prebuilt Wasm from `gleam export zed-extension`. +`nix develop` sets `GLEAM` to the wasm-capable compiler for the cargo build +driver in this directory. -Without `Cargo.toml` + `src/*.rs` you get: -- `failed to compile Rust extension` (no Cargo.toml), or -- invalid `kind` if you try anything other than `"Rust"`. +## Build -So `src/glint.rs` is a **thin temporary guest** that implements the same idea -as `src/zed_glint.gleam` (resolve `glint` on PATH / fallback, start the LSP). -It is a **host/toolchain constraint**, not the product direction. +Zed’s Install Dev Extension only knows how to run: -| Path | Role | -|------|------| -| `src/glint.rs` | What Zed can compile and install **now** | -| `src/zed_glint.gleam` | Target authoring surface (pure Gleam) | -| [nandi.uk/gleam](https://tangled.org/nandi.uk/gleam) (`wasm` branch) | Compiler that builds a real `zed:extension` component | +```text +cargo build --target wasm32-wasip2 +``` + +So this package includes a tiny cargo **build driver** that does not implement +the extension. It only: -Once Zed accepts a prebuilt component (or we stop using Install Dev’s cargo -path), the `.rs` crate can go away and the Gleam export path can own the guest. +1. Runs `gleam export zed-extension` (`build.rs`) +2. Installs that component as the crate’s `.wasm` output (`scripts/gleam-wasm-linker`) -## Compile +`src/lib.rs` is an empty compile stub required by cargo; its code is discarded. -### Glint binary (LSP) +### Prerequisites ```sh -cd /home/nandi/code/glint -nix build # → result/bin/glint +# Glint LSP binary +cd /home/nandi/code/glint && nix build # → result/bin/glint + +# Wasm-capable Gleam (our fork) +# https://tangled.org/nandi.uk/gleam — branch wasm +git clone https://tangled.org/nandi.uk/gleam +cd gleam && git checkout wasm +cargo build -p gleam +export GLEAM="$PWD/target/debug/gleam" + +# Cargo target so `cargo build --target wasm32-wasip2` can run +rustup target add wasm32-wasip2 ``` -### Zed guest (Rust — required for Install Dev Extension today) +`build.rs` also auto-finds `../../gleam/target/debug/gleam` when `glint` and +`gleam` are siblings (e.g. under `~/code/`). + +### Build the extension ```sh cd /home/nandi/code/glint/editors/zed -rustup target add wasm32-wasip2 # once +export GLEAM=/path/to/gleam/target/debug/gleam # if not auto-found cargo build --release --target wasm32-wasip2 -cp target/wasm32-wasip2/release/zed_glint.wasm extension.wasm -file extension.wasm # WebAssembly (wasm) binary version 0x1000d (component) +# → target/wasm32-wasip2/release/zed_glint.wasm (from Gleam) ``` -### Pure Gleam guest (experimental) - -Compiler work is on our Gleam **wasm** fork on Tangled: - -**https://tangled.org/nandi.uk/gleam** (`wasm` branch) +Or call Gleam directly: ```sh -git clone https://tangled.org/nandi.uk/gleam -cd gleam && git checkout wasm -cargo build -p gleam - -cd /home/nandi/code/glint/editors/zed -/path/to/gleam/target/debug/gleam export zed-extension +$GLEAM export zed-extension # → extension.wasm ``` -See `docs/compiler/wasm-zed-extensions.md` in that repo. Until Zed can load a -prebuilt guest without cargo, **Install Dev Extension still overwrites with the -Rust build**. - ## Install in Zed -1. Build `glint` (`nix build` above). -2. **Extensions → Install Dev Extension…** → this directory: +1. Ensure `GLEAM` is set in the environment Zed inherits, **or** keep a sibling + `~/code/gleam` build at `target/debug/gleam`. +2. **Extensions → Install Dev Extension…** → ```text /home/nandi/code/glint/editors/zed ``` -3. Open a `*.glint` file. The guest runs `glint lsp`. +3. Open a `*.glint` file — the guest starts `glint lsp`. ### LSP binary resolution 1. `lsp.glint.binary` in Zed settings -2. `glint` on `$PATH` (`worktree.which`) +2. `glint` on `$PATH` 3. Fallback: `/home/nandi/code/glint/result/bin/glint` ```json @@ -114,11 +113,15 @@ Rust build**. | Path | Role | |------|------| -| `extension.toml` | Zed manifest | -| `src/glint.rs` | Temporary installable guest (Zed cargo path) | -| `src/zed_glint.gleam` | Pure-Gleam authoring sketch | +| `src/zed_glint.gleam` | Extension (Gleam) | +| `build.rs` | Driver: `gleam export zed-extension` | +| `scripts/gleam-wasm-linker` | Driver: cargo output = Gleam component | +| `src/lib.rs` | Empty stub for cargo only | +| `extension.toml` | Zed manifest (`kind = "Rust"` is required by Zed’s schema) | | `languages/glint/` | Tree-sitter queries + language config | -| `extension.wasm` | Built component (Zed / cargo) | +| `extension.wasm` | Built component | + +Grammar id is **`gleam`** (exports `tree_sitter_gleam`). Language name / path +suffix stay **Glint** / `*.glint`. -Grammar id is **`gleam`** so the linker finds `tree_sitter_gleam`. Language -name / path suffix stay **Glint** / `*.glint`. +Compiler docs: https://tangled.org/nandi.uk/gleam (`docs/compiler/wasm-zed-extensions.md`). diff --git a/editors/zed/build.rs b/editors/zed/build.rs new file mode 100644 index 0000000..8a7ce53 --- /dev/null +++ b/editors/zed/build.rs @@ -0,0 +1,100 @@ +//! Build the real Zed guest with Gleam, then leave the component where the +//! custom wasm linker (`scripts/gleam-wasm-linker`) can install it as the +//! crate's `cdylib` output (what Zed copies to `extension.wasm`). + +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=src/zed_glint.gleam"); + println!("cargo:rerun-if-changed=gleam.toml"); + println!("cargo:rerun-if-changed=manifest.toml"); + println!("cargo:rerun-if-changed=scripts/gleam-wasm-linker"); + println!("cargo:rerun-if-env-changed=GLEAM"); + + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let gleam = find_gleam(&manifest_dir); + eprintln!("zed_glint build.rs: using gleam at {}", gleam.display()); + + // Fixed path the linker wrapper reads (relative to package root). + let out_wasm = manifest_dir.join("target/gleam-extension.wasm"); + if let Some(parent) = out_wasm.parent() { + fs::create_dir_all(parent).expect("create target/"); + } + + let status = Command::new(&gleam) + .current_dir(&manifest_dir) + .args(["export", "zed-extension"]) + .status() + .unwrap_or_else(|err| { + panic!( + "failed to spawn gleam at {}: {err}\n\ + Set GLEAM to a wasm-capable gleam binary \ + (https://tangled.org/nandi.uk/gleam , branch wasm).", + gleam.display() + ) + }); + + if !status.success() { + panic!( + "`gleam export zed-extension` failed (status {status}).\n\ + You need a Gleam build with Zed Wasm support:\n\ + https://tangled.org/nandi.uk/gleam (branch wasm)\n\ + cargo build -p gleam\n\ + export GLEAM=/path/to/gleam/target/debug/gleam\n\ + Current GLEAM candidate: {}", + gleam.display() + ); + } + + // CLI writes package-root extension.wasm; copy to the linker input path. + let exported = manifest_dir.join("extension.wasm"); + if !exported.is_file() { + panic!( + "gleam export succeeded but {} was not created", + exported.display() + ); + } + fs::copy(&exported, &out_wasm).unwrap_or_else(|err| { + panic!( + "copy {} → {}: {err}", + exported.display(), + out_wasm.display() + ); + }); + + eprintln!( + "zed_glint build.rs: Gleam component ready at {}", + out_wasm.display() + ); +} + +fn find_gleam(manifest_dir: &Path) -> PathBuf { + if let Ok(path) = env::var("GLEAM") { + let p = PathBuf::from(path); + if p.is_file() { + return p; + } + panic!("GLEAM={p:?} is not a file"); + } + + // Common local checkouts (glint and gleam as siblings under code/). + let candidates = [ + manifest_dir.join("../../gleam/target/debug/gleam"), + manifest_dir.join("../../gleam/target/release/gleam"), + manifest_dir.join("../../../gleam/target/debug/gleam"), + manifest_dir.join("../../../gleam/target/release/gleam"), + ]; + for candidate in candidates { + if candidate.is_file() { + return candidate + .canonicalize() + .unwrap_or(candidate); + } + } + + // PATH + PathBuf::from("gleam") +} diff --git a/editors/zed/extension.toml b/editors/zed/extension.toml index 0bc56e8..8e04c1e 100644 --- a/editors/zed/extension.toml +++ b/editors/zed/extension.toml @@ -3,12 +3,12 @@ name = "Glint" version = "0.1.0" schema_version = 1 authors = ["nandi"] -description = "Glint language support — syntax highlighting and the glint language server" +description = "Glint language support — highlighting and glint LSP (Gleam Wasm guest)" repository = "https://github.com/nandi/glint" [lib] +# Schema only allows "Rust". Cargo is a build driver for the Gleam guest (build.rs). kind = "Rust" -# Matches zed_extension_api; Zed also reads this from the wasm `zed:api-version` section. version = "0.7.0" [language_servers.glint] diff --git a/editors/zed/gleam.toml b/editors/zed/gleam.toml index 35de812..a5aff41 100644 --- a/editors/zed/gleam.toml +++ b/editors/zed/gleam.toml @@ -2,6 +2,5 @@ name = "zed_glint" version = "0.1.0" target = "wasm" -# Future: pure Gleam guest via the Gleam wasm branch -# gleam export zed-extension -# Today Zed Install Dev Extension still requires Cargo (src/glint.rs). +# Real guest: `gleam export zed-extension` (invoked by build.rs when Zed runs cargo). +# Needs a wasm-capable gleam: https://tangled.org/nandi.uk/gleam (branch wasm) diff --git a/editors/zed/scripts/gleam-wasm-linker b/editors/zed/scripts/gleam-wasm-linker new file mode 100755 index 0000000..8f0aab5 --- /dev/null +++ b/editors/zed/scripts/gleam-wasm-linker @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# Custom linker for wasm32-wasip2: ignore rustc's object files and emit the +# Gleam-built component (written by build.rs to target/gleam-extension.wasm). +# +# Zed Install Dev Extension runs `cargo build --target wasm32-wasip2` and then +# copies this crate's .wasm to extension.wasm — so the "link" product must be +# the real guest. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +SRC="${GLEAM_EXTENSION_WASM:-$ROOT/target/gleam-extension.wasm}" + +if [[ ! -f "$SRC" ]]; then + echo "gleam-wasm-linker: missing $SRC" >&2 + echo " build.rs should have run \`gleam export zed-extension\` first." >&2 + echo " Set GLEAM to a wasm-capable gleam binary if needed." >&2 + exit 1 +fi + +# Expand rustc response files (@path) into a flat arg list, then find -o OUT. +OUT="" +args=() +for arg in "$@"; do + if [[ "$arg" == @* ]]; then + resp="${arg#@}" + if [[ -f "$resp" ]]; then + # shellcheck disable=SC2207 + mapfile -t lines <"$resp" + args+=("${lines[@]}") + fi + else + args+=("$arg") + fi +done + +i=0 +while [[ $i -lt ${#args[@]} ]]; do + case "${args[$i]}" in + -o) + i=$((i + 1)) + OUT="${args[$i]:-}" + ;; + -o*) + OUT="${args[$i]#-o}" + ;; + --output=*) + OUT="${args[$i]#--output=}" + ;; + esac + i=$((i + 1)) +done + +if [[ -z "$OUT" ]]; then + echo "gleam-wasm-linker: could not find -o output path in linker args" >&2 + printf ' %q\n' "$@" >&2 + exit 1 +fi + +mkdir -p "$(dirname "$OUT")" +cp -f "$SRC" "$OUT" +echo "gleam-wasm-linker: installed Gleam component → $OUT" >&2 diff --git a/editors/zed/src/glint.rs b/editors/zed/src/glint.rs deleted file mode 100644 index bad2a4b..0000000 --- a/editors/zed/src/glint.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::fs; - -use zed_extension_api::{self as zed, LanguageServerId, Result, settings::LspSettings}; - -/// Default local checkout used when `glint` is not on `$PATH`. -const DEFAULT_GLINT_BINARY: &str = "/home/nandi/code/glint/result/bin/glint"; - -struct GlintExtension; - -impl GlintExtension { - fn language_server_binary( - &self, - language_server_id: &LanguageServerId, - worktree: &zed::Worktree, - ) -> Result<(String, Vec)> { - let settings = LspSettings::for_worktree(language_server_id.as_ref(), worktree)?; - - // Prefer explicit config: - // "lsp": { "glint": { "binary": { "path": "...", "arguments": ["lsp"] } } } - if let Some(binary) = settings.binary { - if let Some(path) = binary.path { - let args = binary.arguments.unwrap_or_else(|| vec!["lsp".into()]); - return Ok((path, args)); - } - if let Some(args) = binary.arguments { - // Path omitted: resolve binary, use custom args. - let path = self.resolve_binary(worktree)?; - return Ok((path, args)); - } - } - - Ok((self.resolve_binary(worktree)?, vec!["lsp".into()])) - } - - fn resolve_binary(&self, worktree: &zed::Worktree) -> Result { - if let Some(path) = worktree.which("glint") { - return Ok(path); - } - - if fs::metadata(DEFAULT_GLINT_BINARY) - .map(|m| m.is_file()) - .unwrap_or(false) - { - return Ok(DEFAULT_GLINT_BINARY.into()); - } - - Err( - "glint binary not found. Install it (nix build in /home/nandi/code/glint) \ - or put `glint` on PATH, or set lsp.glint.binary.path in settings.json." - .into(), - ) - } -} - -impl zed::Extension for GlintExtension { - fn new() -> Self { - Self - } - - fn language_server_command( - &mut self, - language_server_id: &LanguageServerId, - worktree: &zed::Worktree, - ) -> Result { - let (command, args) = self.language_server_binary(language_server_id, worktree)?; - Ok(zed::Command { - command, - args, - env: Default::default(), - }) - } - - fn language_server_initialization_options( - &mut self, - language_server_id: &LanguageServerId, - worktree: &zed::Worktree, - ) -> Result> { - LspSettings::for_worktree(language_server_id.as_ref(), worktree) - .map(|s| s.initialization_options) - } - - fn language_server_workspace_configuration( - &mut self, - language_server_id: &LanguageServerId, - worktree: &zed::Worktree, - ) -> Result> { - LspSettings::for_worktree(language_server_id.as_ref(), worktree) - .map(|s| s.settings) - } -} - -zed::register_extension!(GlintExtension); diff --git a/editors/zed/src/lib.rs b/editors/zed/src/lib.rs new file mode 100644 index 0000000..ff67883 --- /dev/null +++ b/editors/zed/src/lib.rs @@ -0,0 +1,9 @@ +//! Empty compile unit for cargo. Not extension logic. +//! +//! `build.rs` runs `gleam export zed-extension`; `scripts/gleam-wasm-linker` +//! installs that component as this crate's `.wasm` output. + +#![allow(dead_code)] + +#[no_mangle] +pub extern "C" fn _zed_glint_build_stub() {} diff --git a/editors/zed/src/zed_glint.gleam b/editors/zed/src/zed_glint.gleam index 2881305..f33e9ab 100644 --- a/editors/zed/src/zed_glint.gleam +++ b/editors/zed/src/zed_glint.gleam @@ -1,13 +1,11 @@ -//// Zed extension written in Gleam. +//// Glint Zed extension (Gleam). //// -//// `gleam export zed-extension` packages a full `zed:extension` world -//// component. The guest shell implements every world export; this module -//// documents the intended `language_server_command` callback that the -//// compiler wires into the CABI layer. +//// Built with `gleam export zed-extension` (also invoked by the cargo build +//// driver when Zed runs Install Dev Extension). //// //// Resolution order for the glint binary: -//// 1. `worktree.which("glint")` (PATH) -//// 2. Fallback: `/home/nandi/code/glint/result/bin/glint` +//// 1. worktree.which("glint") on PATH +//// 2. Fallback: /home/nandi/code/glint/result/bin/glint /// Opaque worktree handle from the Zed host (resource `i32`). pub type Worktree diff --git a/flake.lock b/flake.lock index 087ad28..bb579dc 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,50 @@ { "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "gleam", + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1785054764, + "narHash": "sha256-qZ5lzSBKLXReYMf0+t+Ra4tmh+3reLgf8+IJaNdDTCA=", + "owner": "nix-community", + "repo": "fenix", + "rev": "896e0b5796797f8f6ba3e61036d61b5597281b1f", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "gleam": { + "inputs": { + "fenix": "fenix", + "nixpkgs": [ + "nixpkgs" + ], + "systems": "systems" + }, + "locked": { + "lastModified": 1785056822, + "narHash": "sha256-9fz16KwNDe5bvYSxO6Ap9mz3dNj6Jizi+GeitKvyjGE=", + "ref": "wasm", + "rev": "b81d39c1d98f15c4dc7846d277568ab1d62d94a5", + "revCount": 9825, + "type": "git", + "url": "https://tangled.org/nandi.uk/gleam" + }, + "original": { + "ref": "wasm", + "type": "git", + "url": "https://tangled.org/nandi.uk/gleam" + } + }, "nixpkgs": { "locked": { "lastModified": 1784796856, @@ -18,8 +63,26 @@ }, "root": { "inputs": { + "gleam": "gleam", "nixpkgs": "nixpkgs", - "systems": "systems" + "systems": "systems_2" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1785010335, + "narHash": "sha256-fHb7HPztVRBp1pzQEnoMZ4k4cM4nJsZ5qXZ1TEspVSY=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "b5aa6668ae12f439169ea84b67a15f6b579f1041", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" } }, "systems": { @@ -36,6 +99,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 4c14da3..19bd0ee 100644 --- a/flake.nix +++ b/flake.nix @@ -4,6 +4,13 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; systems.url = "github:nix-systems/default"; + + # Wasm-capable Gleam (Zed extension export). Override locally with: + # nix build --override-input gleam path:../gleam + gleam = { + url = "git+https://tangled.org/nandi.uk/gleam?ref=wasm"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = @@ -11,6 +18,7 @@ self, nixpkgs, systems, + gleam, ... }: let @@ -32,6 +40,9 @@ [ "[packages]" ] ++ map (p: "${p.name} = \"${p.version}\"") hexPackages ); + # Wasm-capable gleam from the flake input. + gleamBin = gleam.packages.${system}.gleam; + # Self-contained store package: Erlang shipment + fixed-path wrapper. glint = pkgs.stdenv.mkDerivation { pname = "glint"; @@ -47,7 +58,9 @@ lib.cleanSourceFilter path type && base != "build" && base != "result" + && base != "result-zed" && base != "erl_crash.dump" + && base != "editors" && !(lib.hasSuffix ".escript" base); }; @@ -117,38 +130,152 @@ license = lib.licenses.asl20; }; }; + + # Prebuilt Glint Zed extension (Gleam guest). Point Install Dev + # Extension at a *writable copy* of this dir, or use `nix run .#install-zed-extension`. + zed-extension = pkgs.stdenv.mkDerivation { + pname = "glint-zed-extension"; + version = gleamToml.version; + + src = lib.cleanSourceWith { + src = ./editors/zed; + filter = + path: type: + let + base = baseNameOf path; + in + lib.cleanSourceFilter path type + && base != "target" + && base != "build" + && base != "grammars" + && !(lib.hasSuffix ".wasm" base) + && base != "Cargo.lock"; + }; + + nativeBuildInputs = [ + gleamBin + ]; + + buildPhase = '' + runHook preBuild + export HOME=$TMPDIR + # `gleam export zed-extension` writes extension.wasm in the package root. + gleam export zed-extension + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + mkdir -p $out + cp extension.wasm $out/ + cp -r languages $out/ + + # No [lib] section: Install Dev Extension must not run cargo. + # Prebuilt extension.wasm is the Gleam guest. + cat > $out/extension.toml <<'EOF' + id = "glint" + name = "Glint" + version = "0.1.0" + schema_version = 1 + authors = ["nandi"] + description = "Glint language support — highlighting and glint LSP (Gleam Wasm guest)" + repository = "https://tangled.org/nandi.uk/glint" + + [language_servers.glint] + name = "Glint LSP" + languages = ["Glint"] + + [language_servers.glint.language_ids] + Glint = "glint" + + [grammars.gleam] + repository = "https://github.com/gleam-lang/tree-sitter-gleam" + rev = "6ea757f7eb8d391dbf24dbb9461990757946dd5e" + EOF + + cat > $out/README.txt <