# Editor Extension Roadmap

This document tracks the production-readiness plan for the Ox Content editor
extensions. The primary targets are **VS Code** and **Neovim**; Zed is kept in
lock-step where possible but is not a release gate. Every feature must be
runnable from a CLI so it can be wired into CI without an editor.

The roadmap is intentionally split into small, conventional pull requests so
each one can land independently with its own tests and changelog entry. A PR
must not depend on a later one in the list.

## Architecture Principles

1. **One server, many clients.** All language intelligence lives in
   `ox_content_lsp` (Rust). Editor plugins are thin clients that only
   translate LSP capabilities into editor-native UI.
2. **Every feature ships a CLI counterpart.** If a check or generator only
   exists inside the LSP, it cannot run in CI. The minimum bar is one binary
   per feature (`vpx oxct link-check`, a configured textlint command, …) returning
   non-zero on failure with a stable text/JSON output.
3. **Native dependencies stay native.** Type-aware features that need
   TypeScript talk to `typescript-go` via the `corsa_client` Rust crate, not
   through Node. The LSP spawns a single `tsgo` subprocess per workspace and
   reuses it.
4. **Editor plugins stay testable headlessly.** VS Code uses
   `@vscode/test-cli` + `@vscode/test-electron`. Neovim uses
   `nvim --headless` + `busted`. Both run in CI on every PR.
5. **No editor-specific feature.** A feature ships in the LSP first, with a
   CLI, and only then surfaces in any editor. This keeps Neovim, Zed, and
   future editors at parity.

## Feature Matrix

| #   | Feature                                         | LSP                      | CLI                          | VS Code                  | Neovim                      | Status           |
| --- | ----------------------------------------------- | ------------------------ | ---------------------------- | ------------------------ | --------------------------- | ---------------- |
| 1   | Markdown preview (HMR)                          | push channel             | none                         | subscribed webview       | external browser, on-demand | needs CLI + nvim |
| 2   | i18n preview / completion                       | present                  | `vpx oxct i18n`              | present                  | present                     | shipped          |
| 3   | MDC completion + type check                     | completion + diagnostics | `vpx oxct mdc-check`         | completion + diagnostics | completion + diagnostics    | shipped          |
| 4   | Vue / React props completion + jump + typecheck | crate scaffold           | planned                      | planned                  | planned                     | scaffold landed  |
| 5   | Asset path completion + diagnostics             | completion provider      | via link checker             | completion + diagnostics | completion + diagnostics    | shipped          |
| 6   | Dead link checker                               | diagnostics              | `vpx oxct link-check`        | diagnostics              | diagnostics                 | local: shipped   |
| 7   | textlint integration                            | diagnostics + quickfix   | via configured command       | enabled per setting      | enabled per setting         | shipped (opt-in) |
| 8   | Frontmatter schema completion + diagnostics     | present                  | none (validated through LSP) | present                  | present                     | built-in meta    |
| 9   | Document structure (outline + folding)          | symbols + folding ranges | none (unit-tested headless)  | outline + folding        | outline + folding           | shipped          |
| 10  | Document links (Markdown links + images)        | document link provider   | none (unit-tested headless)  | clickable links          | clickable links             | shipped          |
| 11  | Selection ranges (expand selection)             | selection range provider | none (unit-tested headless)  | expand/shrink selection  | expand/shrink selection     | shipped          |
| 12  | Document highlights (matching link targets)     | document highlight prov. | none (unit-tested headless)  | highlight same-target    | highlight same-target       | shipped          |
| 13  | Half/full-width spacing                         | diagnostics + formatting | none (validated through LSP) | quickfix + save fix      | quickfix + formatting       | shipped          |

## PR Sequence

Each item below is one PR. They are listed in execution order; later PRs may
depend on earlier ones, but never the reverse. Every PR ships:

- Rust unit tests for the new crate(s) and any new LSP request handlers.
- TypeScript Mocha tests for any VS Code wiring.
- Lua `busted` tests for any new Neovim surface.
- A CI job entry in `.github/workflows/ci.yml`.
- Documentation updates under `docs/content/` and the affected package README.

### 1. `test(vscode-ox-content): integration suite and CI job`

Foundation PR. Tightens the existing extension before adding features.

- Expand `npm/vscode-ox-content/src/test/extension.test.ts` into a suite that
  exercises: activation, command registration (LSP-advertised vs.
  webview-only), middleware guards, configuration round-trip, preview panel
  lifecycle, snippet contribution shape, and `oxContent.openPreview` happy and
  error paths.
- Add Node-only unit tests for `config.ts`, `client.ts` middleware predicates,
  `preview.ts` debouncing, and `constants.ts` (pure data) using `vitest`.
- New CI job `vscode-test` in `.github/workflows/ci.yml`, running on Linux
  with `xvfb-run` and pulling the LSP binary out of `target/release` to avoid
  `cargo run` at activation time.
- Document the test workflow in
  `npm/vscode-ox-content/README.md` and `CONTRIBUTING.md`.

### 2. `feat(lsp): preview HMR channel`

Replace the polling refresh path with an explicit push channel.

- ✅ LSP notification `oxContent/previewDidChange` with `{ uri, html, title }` payload
  (shipped, see `crates/ox_content_lsp/src/backend/commands.rs`).
- ✅ `oxContent.previewSubscribe` / `oxContent.previewUnsubscribe`
  execute-command handlers.
- ✅ VS Code webview subscribes on open, unsubscribes on dispose, and
  listens for `oxContent/previewDidChange` instead of debouncing on
  `onDidChangeTextDocument`.
- Pending follow-up: CLI `vpx oxct preview` that hosts an SSE endpoint
  backed by the same renderer (useful for `--watch` workflows and for the
  Neovim browser preview). Tracked as a separate PR so this one stays
  focused on the LSP push channel.
- Pending follow-up: Neovim preview opens the SSE URL instead of writing
  a temp file when `auto_refresh = true`.

### 3. `feat(link-checker): new crate, LSP integration, CLI`

- ✅ New crate `ox_content_link_checker` with deterministic local link
  resolution (relative paths, self-anchors, image targets). Offline-only
  by design; ships with 11 unit tests covering every link form documented
  in the crate README.
- ✅ CLI `vpx oxct link-check [paths…] [--src-dir DIR] [--ignore PATTERN]
[--format text|json]` with exit-code-1-on-error semantics for CI.
- ✅ LSP diagnostics under `source: "ox-content-link"`, wired into the
  per-document diagnostic publish path so they appear alongside parse,
  frontmatter, and MDC errors without an extra round trip.
- Pending follow-ups:
  - HTTP head-check pool behind a `http-check` feature flag.
  - Reference-link expansion (currently blocked on the parser).
  - Cross-file anchor resolution (currently emits a warning).
  - GitHub Actions snippet on the docs site.

### 4. `feat(lsp): asset path completion and diagnostics`

- ✅ LSP completion provider triggered on image and link openers
  (`![…](`, `[…](`, `<img src="`, `<a href="`, also `<video|audio|source|iframe|picture src="`).
  Image-context triggers narrow the file list to known media
  extensions; link-context triggers list every file plus directories.
- ✅ Hidden-file filtering, prefix matching, directory entries
  rendered as `name/`. UTF-16 cursor handling for international
  characters (covered by `line_prefix` tests).
- ✅ Diagnostics for missing assets are already produced by the
  link checker (see PR #3 above) since image targets flow through
  the same resolver. No new CLI surface needed.
- Pending follow-up: per-feature `oxContent.asset.srcDir` setting
  separate from the workspace root, for repos that keep static
  assets under a `public/` subdirectory.

### 5. `feat(mdc): component name and attribute completion`

- ✅ New `ox_content_mdc_checker::Registry` (de)serializes a JSON
  index of components and their attributes. Deterministic iteration
  order via `BTreeMap`, lenient parsing (unknown fields tolerated).
- ✅ LSP completion: component names after `<Foo|`, attribute names
  inside `<Foo |…>`. Inside-quote and post-`=` positions are skipped
  to avoid noise. Attribute insertion uses snippet syntax
  (`name="$0"`) so the cursor lands inside the value.
- ✅ Registry path is configurable via the `mdcComponents`
  initialization option, `mdc.components` in the workspace config
  file, or `OX_CONTENT_MDC_COMPONENTS` env var (in that order).
- Pending follow-ups:
  - Hover documentation for an MDC tag the cursor sits on
    (registry already exposes the data).
  - Diagnostic for using an unknown component (opt-in only — would
    be noisy for projects with partial registries).
  - Framework-specific auto-discovery (Nuxt content, Astro, etc.)
    that builds the registry without a hand-written JSON file.

### 6. `feat(component-resolver): Vue and React props via corsa_client`

The single largest item on the roadmap. Lands behind a `tsgo` opt-in
setting so users without `typescript-go` available are not affected.
Split into three sequential PRs:

#### 6a. Scaffold (`feat(component-resolver): scaffold crate`)

- ✅ New crate `ox_content_component_resolver` registered in the
  workspace.
- ✅ Public types: `Resolver`, `ResolverConfig`, `ResolvedComponent`,
  `ResolvedProp`, `Location`, `Error`.
- ✅ `corsa_client = "0.10"` wired through and proven to build
  against the workspace.
- ✅ Scaffold returns `Error::NotImplemented` so editor integrations
  can develop against the public types before the implementation
  lands.
- ✅ 5 unit tests pin the scaffold contract (missing-tsgo,
  relative-path, NotImplemented, serde round-trip, config builder).

#### 6b. Resolver implementation (planned follow-up)

- Open `component_file` as a tsgo virtual document.
- Locate the default export and extract the props type (TS `Props`,
  `defineProps<…>()`, React.FC `<Props>`).
- Enumerate prop members → name, type string, optionality, JSDoc,
  declaration location.
- One `tsgo` process shared per workspace; lifecycle owned by the
  resolver, not the editor.
- Integration test gated on `OX_CONTENT_TSGO_PATH` env var.

#### 6c. LSP + CLI integration (planned follow-up)

- LSP wires completion, hover, go-to-definition, and diagnostics on
  top of the resolver for MDC/MDX files.
- New CLI `ox-content-component-check` for CI.
- VS Code: `oxContent.components.tsgoPath`,
  `oxContent.components.tsconfig`, `oxContent.components.enabled`.
- Document the resolution model in `docs/content/component-resolution.md`.

### 7. `feat(textlint): sidecar integration`

- ✅ New `ox_content_lsp::textlint` module spawns
  `<command> --format json --stdin --stdin-filename <path>` and
  parses the per-file message array into LSP diagnostics under
  `source: "textlint"`. Runs **on save only** so the typing path
  stays fast (textlint can take a few hundred ms per file).
- ✅ Opt-in via `oxContent.textlintEnabled` initialization option,
  `textlint.enabled` in the workspace config, or
  `OX_CONTENT_TEXTLINT_ENABLED=1`. Off by default — textlint is
  heavy and noisy for projects that don't use it.
- ✅ Custom command override via `oxContent.textlintCommand` /
  `textlint.command` / `OX_CONTENT_TEXTLINT_COMMAND`. Empty falls
  back to `npx textlint`. Shell-style quoting supported.
- ✅ VS Code: `oxContent.textlint.enabled` /
  `oxContent.textlint.command` settings forward into the
  initialization options.
- ✅ Neovim: `textlint.enabled` / `textlint.command` in
  `require("ox-content").setup(...)` forward the same LSP
  initialization options.
- ✅ Zed: `lsp.ox-content-lsp.initialization_options.textlintEnabled`
  and `textlintCommand` are documented and forwarded by the extension.
- ✅ 12 unit tests cover JSON parsing, severity mapping,
  zero-indexed coordinates, the missing-rule-id and unknown-severity
  cases, shlex-style command splitting, and the disabled /
  missing-binary subprocess paths.
- ✅ Protocol coverage pins the `textDocument/didSave` path that
  publishes textlint diagnostics through LSP.
- ✅ Code actions for textlint JSON `fix` suggestions are exposed as LSP
  quick fixes.
- Pending follow-ups: dedicated `ox-content-textlint` CLI (currently the user
  runs textlint directly), debounce / cancellation between rapid saves.

### 8. `feat(nvim): polish, parity, and busted suite`

- Add user commands for every new feature shipped above
  (`:OxContentLinkCheck`, `:OxContentAssetCheck`, `:OxContentTextlint`,
  `:OxContentComponentCheck`).
- Move client setup into a single composable surface so users can disable
  individual features.
- Add `busted` tests under `editors/neovim/tests/` and a CI job
  `neovim-test` running `nvim --headless -c "PlenaryBustedDirectory ..."`.
- Refresh the README with the new command surface.

### 9. `chore(release): editor extension publish workflow`

Final shipping PR. Does not change runtime behavior.

- ✅ New workflow `.github/workflows/publish-editors.yml` triggered by `v*`
  tags and manual dispatch. It builds platform-specific VSIX files with bundled
  `ox-content-lsp` binaries for `linux-x64`, `darwin-x64`, `darwin-arm64`, and
  `win32-x64`, then publishes via `vsce` and `ovsx` when `VSCE_PAT` /
  `OVSX_PAT` are configured.
- ✅ The same workflow validates the Zed extension WASM build and can open a
  `zed-industries/extensions` registry PR when `ZED_EXTENSIONS_PAT` is
  configured.
- Document the operational setup in
  `npm/vscode-ox-content/PUBLISHING.md`: publisher creation, `VSCE_PAT`,
  `OVSX_PAT`, `ZED_EXTENSIONS_PAT`, versioning policy, and manual dry runs.

## Out of Scope

- IDEA / WebStorm plugin. Not on the roadmap; we expose only the LSP surface
  and let third parties wrap it.
- Bundled `tsgo` binary. The component resolver requires the user to provide
  a `typescript-go` build; we will not vendor it.
- Hosted preview service. Preview HMR runs locally only.

## Tracking

Progress is tracked through the conventional commit log: each PR above maps
1:1 to a `feat:` / `test:` / `chore:` commit on `main`. This document is
updated in the same PR when an item lands.
