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
72 changes: 72 additions & 0 deletions lsp-export-protocol/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# lsp-export-protocol

The `codedown/render` wire types, shared by `lsp-export-server` and its clients. Depends only on
`aeson`, `containers` and `text`, so a client needs no LSP dependency to speak the protocol.

A *task* is one document rendered to one format. The client picks its `taskId`; every message
carries it.

Field names are the record field with its prefix dropped, first letter lower-cased: `startTaskId`
is `taskId`. Enums drop the constructor prefix and lower-case: `StatusRendering` is `"rendering"`.

## Messages

The document arrives as ordinary `textDocument/didOpen` and `didChange`, so renders come from the
buffer rather than from disk. Commands are `workspace/executeCommand` requests; a rejected one
comes back as an `InvalidParams` error response. Renders report progress as custom notifications,
since they outlive the command that started them.

| client → server | arguments | result |
| --- | --- | --- |
| `codedown.render.start` | `[StartParams]` | `StartResult` |
| `codedown.render.stop` | `[taskId]` | `null` |
| `codedown.render.rebuild` | `[taskId, RebuildParams]` | `null` |
| `codedown.render.setPaused` | `[taskId, bool]` | `null` |
| `codedown.render.resync` | `[taskId]` | `null` |

| server → client | params |
| --- | --- |
| `codedown/render/status` | `StatusParams` |
| `codedown/render/output` | `OutputParams` |
| `codedown/render/log` | `LogParams` |

`StartResult` only echoes the `taskId` the client chose, so a client that tracks its own ids can
treat every command as fire-and-forget and watch for the error response.

`rebuild` says a file the last render read has changed; the server re-hashes the deps it knows and
renders only if one differs. `setPaused` suspends rendering without ending the task. `resync` makes
the next output a full one.

The notifications are externally tagged: the variant is the method, and the params carry no
discriminator of their own, so a client dispatches on the method and then parses the params.

## The cycle

1. An edit marks the task dirty. The worker sleeps `debounceMs`; another edit restarts the wait.
2. Quick pass: `status` `rendering` (`phase: "quick"`), a `log` with `clear: true`, then the render
program runs with its output streaming as `log`.
3. `output` if the bytes changed, then `status` `ready` with `renderMs` and `timings`.
4. If the program has a settle pass and edits stay quiet for 1500ms, it runs the same way with
`phase: "settle"`, its logs flushed only if it failed or changed the output.
5. An edit mid-render cancels it, with no `status` for that pass.

A failure sends `status` `errored` with the program's `exitCode`. Both passes of one edit report
`ready`, so a client tracking "is it rendering" should let `phase: "settle"` leave its state alone.

## Output

`OutputFile` has the base64 `bytes`, an `encoding`, and `decodedSize` — the size of the
reconstructed file, not of this message.

The first output is `full`; later ones are `xdelta3` patches against `baseRenderId`, the `renderId`
of the last output sent. So a client keeps the bytes it last reconstructed and:

- `baseRenderId` matches → apply, store, remember `renderId`.
- it doesn't, or the patch won't apply → send `resync`.
- bytes identical to the last render → nothing is sent at all.

If xdelta3 fails the server falls back to `full`.

`deps` lists files the render read from disk, minus the entry document and any buffer the client
sends over `didChange`; watch them and send `rebuild`. `documentVersion` is the entry buffer's LSP
version, absent when the server read from disk.
39 changes: 39 additions & 0 deletions lsp-export-protocol/lsp-export-protocol.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
cabal-version: 2.2

-- This file has been generated from package.yaml by hpack version 0.39.1.
--
-- see: https://github.com/sol/hpack

name: lsp-export-protocol
version: 0.1.0.0
synopsis: The codedown/render wire protocol spoken by lsp-export-server
homepage: https://github.com/codedownio/lsp-export-server#readme
bug-reports: https://github.com/codedownio/lsp-export-server/issues
license: BSD-3-Clause
build-type: Simple
extra-source-files:
README.md

source-repository head
type: git
location: https://github.com/codedownio/lsp-export-server

library
exposed-modules:
Render.Protocol
other-modules:
Paths_lsp_export_protocol
autogen-modules:
Paths_lsp_export_protocol
hs-source-dirs:
src
default-extensions:
LambdaCase
OverloadedStrings
ghc-options: -Wunused-packages -Wall -fno-warn-name-shadowing
build-depends:
aeson
, base >=4.7 && <5
, containers
, text
default-language: Haskell2010
26 changes: 26 additions & 0 deletions lsp-export-protocol/package.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: lsp-export-protocol
version: 0.1.0.0
synopsis: The codedown/render wire protocol spoken by lsp-export-server
license: BSD-3-Clause
github: codedownio/lsp-export-server

extra-source-files:
- README.md

dependencies:
- base >= 4.7 && < 5
- aeson
- containers
- text

default-extensions:
- LambdaCase
- OverloadedStrings

ghc-options:
- -Wunused-packages
- -Wall
- -fno-warn-name-shadowing

library:
source-dirs: src
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,4 @@ data LogParams = LogParams {
} deriving (Show, Eq, Generic)
instance ToJSON LogParams where toJSON = genericToJSON (fieldOptions 3)
instance FromJSON LogParams where parseJSON = genericParseJSON (fieldOptions 3)

5 changes: 4 additions & 1 deletion lsp-export-server.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ library
exposed-modules:
Render.Command
Render.Process
Render.Protocol
Render.Server
Render.Xdelta
other-modules:
Expand Down Expand Up @@ -81,6 +80,7 @@ library
, filepath
, lens
, lsp
, lsp-export-protocol
, lsp-types
, process
, stm
Expand Down Expand Up @@ -128,6 +128,7 @@ library test-lib
, directory
, filepath
, lens
, lsp-export-protocol
, lsp-export-server
, lsp-test
, lsp-types
Expand Down Expand Up @@ -169,6 +170,7 @@ executable lsp-export-server
, containers
, directory
, filepath
, lsp-export-protocol
, lsp-export-server
, optparse-applicative
, temporary
Expand Down Expand Up @@ -217,6 +219,7 @@ test-suite lsp-export-server-test
, bytestring
, containers
, filepath
, lsp-export-protocol
, lsp-export-server
, lsp-test
, sandwich
Expand Down
4 changes: 4 additions & 0 deletions package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ library:
- filepath
- lens
- lsp
- lsp-export-protocol
- lsp-types
- process
- stm
Expand All @@ -74,6 +75,7 @@ executables:
- directory
- filepath
- optparse-applicative
- lsp-export-protocol
- lsp-export-server
- temporary
- time
Expand All @@ -88,6 +90,7 @@ internal-libraries:
- directory
- filepath
- lens
- lsp-export-protocol
- lsp-export-server
- lsp-test
- lsp-types
Expand All @@ -112,6 +115,7 @@ tests:
dependencies:
- aeson
- filepath
- lsp-export-protocol
- lsp-export-server
- lsp-test
- sandwich
Expand Down
1 change: 1 addition & 0 deletions stack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ resolver: nightly-2026-04-19

packages:
- .
- ./lsp-export-protocol

nix:
enable: false
Expand Down
Loading