racket-langserver
The racket-langserver is a Language Server Protocol implementation for Racket. This project seeks to use DrRacket’s public APIs to provide functionality that mimics DrRacket’s code tools as closely as possible.
1 Installation and usage
A Racket runtime is a prerequisite, so before using racket-langserver, ensure that a Racket runtime is installed. You can install from the official download page or install one from your package manager.
First, install an LSP runtime for your editor.
Next, install the package via raco:
raco pkg install racket-langserver
To update the racket-langserver useraco pkg update racket-langserver
Once it is installed, you can configure your editor to use a custom LSP client for Racket (and all installed module languages, e.g. Rhombus) files (usually .rkt), and set the command for the custom client to
racket -l racket-langserver
You may need to restart your LSP runtime or your editor for racket-langserver to start.
1.1 VSCode
Use the Magic Racket extension.
2 Interfaces
| (require racket-langserver/common/interfaces) | |
| package: racket-langserver | |
This module provides the data types used by the LSP protocol layer and the doc library API. Most structs are generated by define-json-struct, which is basically struct with JSON encode/decode support. Shared runtime structs such as SemanticToken and LexerEntry are defined directly when JSON encode/decode support is not needed.
To convert a struct value to a JSON-compatible hasheq, use jsexpr-encode from racket-langserver/json-util. Nested struct values are encoded recursively.
2.1 Position and Range
struct
(struct Pos (line char) #:transparent) line : exact-nonnegative-integer? char : exact-nonnegative-integer?
2.2 Edit Payloads
2.3 Query Responses
Optional Typed Racket type in a racket fence. Labeled Type, or Type (stale) when the type comes from a retained trace.
Optional fenced definition. For a current same-file trace with local declaration detail, this shows code around the bound declaration. Local uses share that stored detail; each use does not rebuild it. Own-line leading comments above the form may also appear. Usually this slot shows the outermost same-line form (a compact binding clause, a one-line header collapsed with ..., or a complete one-line form). When no form on the identifier line fits, the full nearest enclosing form is shown. Without same-file detail, a Scribble bluebox signature may fill this slot instead. Racket-family forms use a racket fence. Rhombus forms use a rhombus fence. A lone definition fence is unlabeled. When a type precedes it, the fence is labeled Source or Signature.
Optional documentation after a —
separator when earlier slots are also present: an online link and/or a locally installed docs excerpt. Link comes before body. Optional check-syntax mouse-over text as a plain unlabeled note, only when it is the sole content (no type, definition, or docs link). The text is unchanged.
When a trace is old, same-file source detail still comes from the current buffer and retained types remain visible as stale. These results can be useful, but the binding link or type may be wrong until expansion finishes. Imports and cross-file bindings do not get same-file source snippets. Their cards use documentation when available, otherwise check-syntax text alone.
The range field comes from the inferred-type interval when present, including literal and expression-delimiter intervals. Otherwise it comes from check-syntax mouse-over status or a kept same-file source-detail range.
struct
(struct DocumentHighlight (range) #:transparent) range : Range?
struct
(struct CompletionItem (label) #:transparent) label : string?
struct
(struct CompletionList (isIncomplete items) #:transparent) isIncomplete : boolean? items : (listof CompletionItem?)
struct
(struct SignatureInformation (label documentation) #:transparent) label : string? documentation : string?
struct
(struct SignatureHelp (signatures) #:transparent) signatures : (listof SignatureInformation?)
value
Name |
| Code |
File |
| 1 |
Module |
| 2 |
Namespace |
| 3 |
Package |
| 4 |
Class |
| 5 |
Method |
| 6 |
Property |
| 7 |
Field |
| 8 |
Constructor |
| 9 |
Enum |
| 10 |
Interface |
| 11 |
Function |
| 12 |
Variable |
| 13 |
Constant |
| 14 |
String |
| 15 |
Number |
| 16 |
Boolean |
| 17 |
Array |
| 18 |
Object |
| 19 |
Key |
| 20 |
Null |
| 21 |
EnumMember |
| 22 |
Struct |
| 23 |
Event |
| 24 |
Operator |
| 25 |
TypeParameter |
| 26 |
Access named constants via SymbolKind-Constant, SymbolKind-String, SymbolKind-Variable, etc.
struct
(struct SymbolInformation (name kind location) #:transparent) name : string? kind : SymbolKind? location : Location?
struct
(struct CodeAction (title kind diagnostics isPreferred edit) #:transparent) title : string? kind : string? diagnostics : (listof Diagnostic?) isPreferred : boolean? edit : WorkspaceEdit?
Name |
| Code |
Error |
| 1 |
Warning |
| 2 |
Information |
| 3 |
Hint |
| 4 |
Access named constants via DiagnosticSeverity-Error, DiagnosticSeverity-Warning, etc.
struct
(struct Diagnostic (range severity source message) #:transparent) range : Range? severity : DiagnosticSeverity? source : string? message : string?
2.4 Resyntax Results
struct
(struct Resyntax-Result (start end message rule-name new-text) #:transparent) start : exact-nonnegative-integer? end : exact-nonnegative-integer? message : string? rule-name : symbol? new-text : string?
2.5 Formatting Options
struct
(struct FormattingOptions ( tab-size insert-spaces trim-trailing-whitespace insert-final-newline trim-final-newlines key) #:transparent) tab-size : exact-nonnegative-integer? insert-spaces : boolean? trim-trailing-whitespace : boolean? insert-final-newline : boolean? trim-final-newlines : boolean? key : (or/c false/c hash?)
The tab-size and insert-spaces fields are required in protocol payloads. The remaining fields (trim-trailing-whitespace, insert-final-newline, trim-final-newlines, key) are optional in the JSON payload; absent fields are represented as (Nothing) rather than #f. Test for an absent optional field with Nothing? from racket-langserver/common/json-util.
The corresponding JSON field names use camelCase: tabSize, insertSpaces, trimTrailingWhitespace, insertFinalNewline, trimFinalNewlines.
Not all generated accessors are exported. Public callers should rely on FormattingOptions-tab-size and FormattingOptions-trim-trailing-whitespace.
2.6 Lexer Entries
struct
(struct LexerEntry (start end text type) #:transparent) start : exact-nonnegative-integer? end : exact-nonnegative-integer? text : string? type : symbol?
2.7 Semantic Tokens
struct
(struct SemanticToken (start end type modifiers) #:transparent) start : exact-nonnegative-integer? end : exact-nonnegative-integer? type : SemanticTokenType? modifiers : SemanticTokenModifier?
value
Name |
| Value |
variable |
| "variable" |
function |
| "function" |
string |
| "string" |
number |
| "number" |
regexp |
| "regexp" |
Name |
| Value |
definition |
| "definition" |
3 Doc Library
| (require racket-langserver/doclib/doc) | |
| package: racket-langserver | |
The doc library provides single-threaded document helpers for representing and querying Racket source documents. All functions operate on document values that satisfy Doc? without touching the network or a thread scheduler, making them suitable for direct testing and reuse.
3.1 Document State
procedure
uri : string? text : string? version : exact-nonnegative-integer? = 0
procedure
(doc-get-text doc) → string?
doc : Doc?
procedure
(doc-apply-edits! doc edits) → void?
doc : Doc? edits : (listof TextEdit?)
procedure
(doc-apply-edit! doc range text) → void?
doc : Doc? range : Range? text : string?
procedure
(doc-reset! doc new-text) → void?
doc : Doc? new-text : string?
procedure
(doc-update-version! doc new-ver) → void?
doc : Doc? new-ver : exact-nonnegative-integer?
procedure
(doc-update-uri! doc new-uri) → void?
doc : Doc? new-uri : string?
procedure
(doc-copy-text-buffer doc) → (is-a?/c lsp-editor%)
doc : Doc?
3.2 Positions and Ranges
All position helpers below work in terms of absolute character offsets (zero-based integer indices into the document text) as well as LSP Pos structs (line/character pairs).
procedure
(doc-pos->abs-pos doc pos) → exact-nonnegative-integer?
doc : Doc? pos : Pos?
procedure
(doc-abs-pos->pos doc abs-pos) → Pos?
doc : Doc? abs-pos : exact-nonnegative-integer?
procedure
(doc-line-start-abs-pos doc line) → exact-nonnegative-integer?
doc : Doc? line : exact-nonnegative-integer?
procedure
(doc-line-end-abs-pos doc line) → exact-nonnegative-integer?
doc : Doc? line : exact-nonnegative-integer?
procedure
doc : Doc?
procedure
(doc-find-containing-paren doc pos)
→ (or/c exact-nonnegative-integer? #f) doc : Doc? pos : exact-nonnegative-integer?
3.3 Trace and Expansion
These functions manage check-syntax expansion and the resulting trace.
procedure
(doc-expand! doc) → boolean?
doc : Doc?
procedure
(doc-update-trace! doc new-trace new-version) → void? doc : Doc? new-trace : (is-a?/c build-trace%) new-version : exact-nonnegative-integer?
procedure
(doc-trace-latest? doc) → boolean?
doc : Doc?
3.4 Resyntax
These functions manage the optional resyntax recommendations attached to a document. They are separate from the check-syntax trace so callers can run resyntax synchronously in-process or compute results elsewhere and write them back later.
procedure
procedure
(doc-resyntax doc) → (listof Resyntax-Result?)
doc : Doc?
procedure
(doc-resyntax! doc) → void?
doc : Doc?
procedure
(doc-get-resyntax-results doc) → (listof Resyntax-Result?)
doc : Doc?
procedure
(doc-update-resyntax-result! doc results) → void?
doc : Doc? results : (listof Resyntax-Result?)
procedure
(resyntax-result->diag doc res) → Diagnostic?
doc : Doc? res : Resyntax-Result?
procedure
(resyntax-result->code-action doc res) → CodeAction?
doc : Doc? res : Resyntax-Result?
3.5 Token and Symbol Utilities
These lexer-derived helper APIs are not yet stable and may change between releases. The API is query-oriented: prefer point lookups and derived responses over bulk snapshot enumeration.
procedure
(doc-range-tokens doc range) → (listof SemanticToken?)
doc : Doc? range : Range?
procedure
(doc-token-at doc pos) → (or/c LexerEntry? #f)
doc : Doc? pos : exact-nonnegative-integer?
procedure
(doc-token-prefix-at doc pos) → string?
doc : Doc? pos : exact-nonnegative-integer?
3.6 Query Functions
These return structured LSP responses. Most require an up-to-date trace; call doc-expand! first or check doc-trace-latest?. Exceptions are noted in individual entries.
Optional Typed Racket type fence, labeled Type or Type (stale)
Optional fenced definition (same-file source, else docs signature). Labeled Source/Signature only when a type precedes it
Optional documentation link and/or excerpt after at most one —
Optional check-syntax mouse-over text only when it is the sole content. The text is unchanged and unlabeled
Source forms are read from the current buffer through kept trace ranges while an old trace refreshes. Retained Typed Racket types remain visible as Type (stale). The shown form or type can be useful, but the binding link or type may be wrong until expansion finishes. Code context is limited to 10 lines and 1000 source characters. Leading comments are limited to 10 lines and 200 source characters per line. Mouse-over status and documentation can also appear while an old trace refreshes.
procedure
(doc-completion doc pos) → CompletionList?
doc : Doc? pos : Pos?
procedure
(doc-references doc uri pos include-decl?)
→ (or/c (listof Location?) #f) doc : Doc? uri : string? pos : Pos? include-decl? : boolean?
The include-decl? parameter is accepted for API compatibility with the LSP protocol but is not currently used in the implementation; the declaration site is always included when the binding is in the same file.
procedure
(doc-highlights doc pos) → (or/c (listof DocumentHighlight?) #f)
doc : Doc? pos : Pos?
procedure
(doc-rename doc uri pos new-name) → (or/c WorkspaceEdit? #f)
doc : Doc? uri : string? pos : Pos? new-name : string?
procedure
(doc-prepare-rename doc pos) → (or/c Range? #f)
doc : Doc? pos : Pos?
procedure
(doc-signature-help doc pos) → (or/c SignatureHelp? #f)
doc : Doc? pos : Pos?
procedure
(doc-code-action doc range) → (listof CodeAction?)
doc : Doc? range : Range?
procedure
(doc-diagnostics doc) → (listof Diagnostic?)
doc : Doc?
procedure
(doc-symbols doc uri) → (listof SymbolInformation?)
doc : Doc? uri : string?
3.7 Formatting
procedure
(doc-format-edits doc fmt-range #:formatting-options opts [ #:on-type? on-type?]) → (or/c (listof TextEdit?) #f) doc : Doc? fmt-range : Range? opts : FormattingOptions? on-type? : boolean? = #f
When on-type? is #t, blank lines are indented too. For LSP on-type formatting requests, prefer doc-on-type-format-edits.
Formatting is performed on an internal copy of the document; the doc is not mutated by this call. Pass the result to doc-apply-edits! to apply the edits.
procedure
(doc-on-type-format-edits doc pos ch #:formatting-options opts) → (or/c (listof TextEdit?) #f) doc : Doc? pos : Pos? ch : string? opts : FormattingOptions?
For recognized s-expression languages, close delimiters format the containing form, and other triggers format the current line. For non-s-expression or unrecognized languages, returns an empty list.
Formatting is performed on an internal copy of the document; the doc is not mutated by this call. Pass the result to doc-apply-edits! to apply the edits.