Skip to content
Plugins (190)

lsp-plugin

stable

LSP (Language Server Protocol) message building and parsing helpers for constructing JSON-RPC requests, responses, notifications, diagnostics, completions, and hover results.

use plugin lsp
19 functions Utilities
Runs with the native Zolo host

Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.

/ filter jk navigate Esc clear
Functions (19)
  1. create_request Build a JSON-RPC request string
  2. create_response Build a JSON-RPC response string
  3. create_notification Build a JSON-RPC notification string
  4. create_error_response Build a JSON-RPC error response string
  5. encode_lsp_message Wrap JSON body with Content-Length header
  6. decode_lsp_header Parse Content-Length from a raw header
  7. position Create an LSP Position object
  8. range Create an LSP Range object
  9. diagnostic Create an LSP Diagnostic object
  10. completion_item Create an LSP CompletionItem object
  11. hover Create an LSP Hover result object
  12. document_symbol Create an LSP DocumentSymbol object
  13. text_edit Create an LSP TextEdit object
  14. parse_message Parse a JSON string into a table
  15. severity_error Returns diagnostic severity 1 (Error)
  16. severity_warning Returns diagnostic severity 2 (Warning)
  17. severity_info Returns diagnostic severity 3 (Information)
  18. severity_hint Returns diagnostic severity 4 (Hint)
  19. publish_diagnostics Build a publishDiagnostics notification

From import to result

  1. 01Import only what you use
  2. 02Copy the smallest example
  3. 03Run it with the Zolo CLI

Overview

lsp-plugin is a toolkit for speaking the Language Server Protocol from Zolo. It has no connection or server state of its own: every function is a pure builder or parser that turns Zolo values into the JSON-RPC and LSP shapes a client expects, or turns a raw wire message back into a Zolo table. Requests, responses, notifications, and error responses come out as ready-to-send JSON strings, while the structural helpers (position, range, diagnostic, completion_item, hover, document_symbol, text_edit) produce the nested tables those messages carry as their params/result payloads.

The mental model is layered: build a payload table with the structural helpers, wrap it in a JSON-RPC envelope with create_request / create_response / create_notification, then frame it for the wire with encode_lsp_message. On the way in you reverse the flow — decode_lsp_header reads the Content-Length and parse_message decodes the JSON body into a table. The severity_* constants and publish_diagnostics shortcut cover the most common diagnostics path. Reach for this plugin when implementing a language server, an LSP client, or any tooling that exchanges JSON-RPC messages.

Common patterns

Build a hover response payload and frame it for the wire in one pass:

use plugin lsp::{hover, range, create_response, encode_lsp_message}

let r = range(10, 4, 10, 9)
let h = hover("**let** — declares an immutable binding", r)
let wire = encode_lsp_message(create_response(1, h))
print(wire)

Collect diagnostics for a file and publish them as a single notification:

use plugin lsp::{range, diagnostic, severity_error, severity_warning, publish_diagnostics, encode_lsp_message}

let diags = [
  diagnostic(range(2, 0, 2, 5), "Undefined variable 'x'", severity_error()),
  diagnostic(range(7, 4, 7, 9), "Unused binding", severity_warning()),
]
let notif = publish_diagnostics("file:///main.zolo", diags)
print(encode_lsp_message(notif))

Decode an incoming wire message: read the header, then parse the JSON body:

use plugin lsp::{decode_lsp_header, parse_message}

let raw = "Content-Length: 52\r\n\r\n"
let header = decode_lsp_header(raw)
print("expecting {header["content_length"]} bytes")

let msg = parse_message(r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}"#)
print("method: {msg["method"]}")

Build a JSON-RPC request string

Builds a JSON-RPC 2.0 request string with the given id, method, and optional params table. Use this when sending requests that expect a response from the client or server.

use plugin lsp::{create_request}

let req = create_request(1, "textDocument/hover", #{"textDocument": #{"uri": "file:///main.zolo"}, "position": #{"line": 10, "character": 5}})
print(req)

The id accepts any value, and the structural helpers compose into the params:

use plugin lsp::{create_request, position, encode_lsp_message}

let pos = position(3, 12)
let req = create_request("req-7", "textDocument/definition", #{"position": pos})
print(encode_lsp_message(req))

Build a JSON-RPC response string

Builds a JSON-RPC 2.0 success response string with the given id and result table.

use plugin lsp::{create_response, hover}

let h = hover("**string** — built-in type")
let resp = create_response(1, h)
print(resp)

Build a JSON-RPC notification string

Builds a JSON-RPC 2.0 notification string (no id field). Notifications do not expect a response.

use plugin lsp::{create_notification}

let notif = create_notification("textDocument/didOpen", #{"textDocument": #{"uri": "file:///main.zolo", "languageId": "zolo", "version": 1, "text": ""}})
print(notif)

Build a JSON-RPC error response string

Builds a JSON-RPC 2.0 error response with the given request id, error code integer, and human-readable message.

use plugin lsp::{create_error_response}

let err = create_error_response(1, -32601, "Method not found")
print(err)

Wrap JSON body with Content-Length header

Wraps a JSON string with the LSP wire-format header Content-Length: N\r\n\r\n. Use this before writing messages to stdout or a socket.

use plugin lsp::{create_notification, encode_lsp_message}

let body = create_notification("initialized", #{})
let wire = encode_lsp_message(body)
print(wire)

Parse Content-Length from a raw header

Parses the Content-Length value from a raw LSP header string. Returns a table with a content_length integer field.

use plugin lsp::{decode_lsp_header}

let header = "Content-Length: 128\r\n\r\n"
let parsed = decode_lsp_header(header)
print("Body length: {parsed["content_length"]}")

Create an LSP Position object

Creates an LSP Position object with zero-based line and character fields.

use plugin lsp::{position}

let pos = position(10, 5)

Create an LSP Range object

Creates an LSP Range object with start and end position fields.

use plugin lsp::{range}

let r = range(10, 0, 10, 20)

A range is just two positions, so it slots directly into a text_edit:

use plugin lsp::{range, text_edit}

let edit = text_edit(range(0, 0, 0, 3), "let")
print(edit["newText"])

Create an LSP Diagnostic object

Creates an LSP Diagnostic object. Use severity_error(), severity_warning(), etc. to get the correct severity integer.

use plugin lsp::{range, diagnostic, severity_error}

let r = range(5, 0, 5, 10)
let diag = diagnostic(r, "Undefined variable 'x'", severity_error())

Create an LSP CompletionItem object

Creates an LSP CompletionItem object. kind is an LSP completion kind integer (e.g. 1=Text, 2=Method, 3=Function, 6=Variable). detail and insert_text are optional.

use plugin lsp::{completion_item}

let item = completion_item("print", 3, "fn print(value: any)", "print($1)")

The trailing two arguments are optional — omit them for a bare label:

use plugin lsp::{completion_item, create_response}

let items = [
  completion_item("let", 14),
  completion_item("match", 14),
]
let resp = create_response(5, items)
print(resp)

Create an LSP Hover result object

Creates an LSP Hover result with markdown contents. The optional range highlights the hovered token in the editor.

use plugin lsp::{hover, range, create_response}

let r = range(2, 4, 2, 9)
let h = hover("**let** — declares an immutable binding", r)
let resp = create_response(42, h)

Create an LSP DocumentSymbol object

Creates an LSP DocumentSymbol object. kind is an LSP symbol kind integer (e.g. 12=Function, 13=Variable). Both range and selection_range are LSP Range tables.

use plugin lsp::{range, document_symbol}

let full = range(0, 0, 10, 1)
let name_range = range(0, 3, 0, 8)
let sym = document_symbol("myFn", 12, full, name_range)

Create an LSP TextEdit object

Creates an LSP TextEdit object describing a text replacement at range with new_text.

use plugin lsp::{range, text_edit}

let r = range(3, 0, 3, 5)
let edit = text_edit(r, "hello")

Parse a JSON string into a table

Parses a raw JSON string into a Zolo table. Use this to decode incoming LSP messages received over stdin or a socket.

use plugin lsp::{parse_message}

let msg = parse_message(r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}"#)
print("Method: {msg["method"]}")

Nested objects decode into nested tables you can dig into by key:

use plugin lsp::{parse_message}

let msg = parse_message(r#"{"method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///a.zolo"}}}"#)
print("opened: {msg["params"]["textDocument"]["uri"]}")

Returns diagnostic severity 1 (Error)

Returns the LSP diagnostic severity constant for Error (value 1).

use plugin lsp::{severity_error}

print(severity_error())

Returns diagnostic severity 2 (Warning)

Returns the LSP diagnostic severity constant for Warning (value 2).

use plugin lsp::{severity_warning}

print(severity_warning())

Returns diagnostic severity 3 (Information)

Returns the LSP diagnostic severity constant for Information (value 3).

use plugin lsp::{severity_info}

print(severity_info())

Returns diagnostic severity 4 (Hint)

Returns the LSP diagnostic severity constant for Hint (value 4).

use plugin lsp::{severity_hint}

print(severity_hint())

Build a publishDiagnostics notification

Builds a textDocument/publishDiagnostics notification JSON string containing the given uri and array of diagnostic tables.

use plugin lsp::{range, diagnostic, severity_warning, publish_diagnostics, encode_lsp_message}

let r = range(0, 0, 0, 5)
let diag = diagnostic(r, "Unused variable", severity_warning())
let notif = publish_diagnostics("file:///main.zolo", [diag])
let wire = encode_lsp_message(notif)
print(wire)

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

en