racket-langserver
1 Installation and usage
1.1 VSCode
2 Interfaces
2.1 Position and Range
Pos
Range
2.2 Edit Payloads
Text  Edit
Workspace  Edit
2.3 Query Responses
Location
Hover
Document  Highlight
Completion  Item
Completion  List
Signature  Information
Signature  Help
Symbol  Kind?
Symbol  Information
Code  Action
Diagnostic  Severity?
Diagnostic
2.4 Formatting Options
Formatting  Options
2.5 Semantic Tokens
Semantic  Token
Semantic  Token  Type?
Semantic  Token  Modifier?
3 Doc Library
3.1 Document State
Doc
Doc?
Doc-uri
Doc-version
make-doc
doc-get-text
doc-apply-edits!
doc-apply-edit!
doc-reset!
doc-update-version!
doc-update-uri!
doc-copy-text-buffer
3.2 Positions and Ranges
doc-pos->abs-pos
doc-abs-pos->pos
doc-line-start-abs-pos
doc-line-end-abs-pos
doc-end-abs-pos
doc-find-containing-paren
3.3 Trace and Expansion
doc-expand!
doc-update-trace!
doc-trace-latest?
doc-walk-text
3.4 Token and Symbol Utilities
doc-get-symbols
doc-range-tokens
doc-guess-token
3.5 Query Functions
doc-hover
doc-completion
doc-definition
doc-references
doc-highlights
doc-rename
doc-prepare-rename
doc-signature-help
doc-code-action
doc-diagnostics
doc-symbols
3.6 Formatting
doc-format-edits
9.1.0.9

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 use
  raco 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/interfaces)
  package: racket-langserver

This module provides the data types used by the LSP protocol layer and the doc library API. Structs defined here are generated by define-json-struct, which is basically struct with JSON encode/decode support.

Each define-json-struct type exports:
  • A keyword constructor / match expander (e.g. Pos) that also accepts positional arguments.

  • A predicate (e.g. Pos?).

  • Accessors for each field (e.g. Pos-line, Pos-char).

  • A JSON-hash match expander Name-js (e.g. Pos-js) that matches immutable hasheq tables using the JSON field names.

  • A JSON-hash predicate Name-js? (e.g. Pos-js?).

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?
LSP position (zero-based line and character offset). The JSON field for char is character.

struct

(struct Range (start end)
    #:transparent)
  start : Pos?
  end : Pos?
LSP half-open range, the character at end is excluded.

2.2 Edit Payloads🔗ℹ

struct

(struct TextEdit (range newText)
    #:transparent)
  range : Range?
  newText : string?
A single text replacement operation. Apply by replacing the text covered by the range field with newText. Apply edits with doc-apply-edits! (including single-edit lists). This avoids range-shift pitfalls when multiple edits target nearby offsets.

struct

(struct WorkspaceEdit (changes)
    #:transparent)
  changes : (hash/c symbol? (listof TextEdit?))
Collection of document edits keyed by document URI.

2.3 Query Responses🔗ℹ

struct

(struct Location (uri range)
    #:transparent)
  uri : string?
  range : Range?
Identifies a source location in a document. The uri field names the target document, and range gives the span within that document.

struct

(struct Hover (contents range)
    #:transparent)
  contents : string?
  range : Range?
Hover information for a symbol occurrence. The contents field is Markdown text (typically a type signature plus, when available, a link to online documentation). The range field identifies the source span the hover applies to.

struct

(struct DocumentHighlight (range)
    #:transparent)
  range : Range?
Highlight entry for one symbol occurrence. The range field is the span to highlight in the current document.

struct

(struct CompletionItem (label)
    #:transparent)
  label : string?
One completion candidate shown by the client. The label field is the display text for the candidate.

struct

(struct CompletionList (isIncomplete items)
    #:transparent)
  isIncomplete : boolean?
  items : (listof CompletionItem?)
Completion response payload. The items field contains candidate entries. The isIncomplete field is always #t, indicating clients should continue filtering, sorting and may request updated results as the prefix changes.

struct

(struct SignatureInformation (label documentation)
    #:transparent)
  label : string?
  documentation : string?
One call-signature entry in a signature help response. The label field is the signature text shown to the user. The documentation field provides additional description for that signature.

struct

(struct SignatureHelp (signatures)
    #:transparent)
  signatures : (listof SignatureInformation?)
Signature help response payload. The signatures field contains the candidate call signatures for the call site, usually one entry per overload.

Predicate for the SymbolKind JSON enum. Each variant maps a symbolic name to an LSP integer code:

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?
One symbol entry in document/workspace symbol results. The name field is the symbol display name. The kind field is a SymbolKind enum value (an LSP symbol kind integer code). The location field gives the symbol’s source location.

struct

(struct CodeAction (title kind diagnostics isPreferred edit)
    #:transparent)
  title : string?
  kind : string?
  diagnostics : (listof Diagnostic?)
  isPreferred : boolean?
  edit : WorkspaceEdit?
One code action returned by a code-action request. The title field is the user-visible action label. The kind field is the LSP action kind string (for example quick-fix or refactor). The diagnostics field lists diagnostics this action addresses. The isPreferred field marks the preferred action among alternatives. The edit field is the workspace edit to apply when the action is chosen.

Predicate for the DiagnosticSeverity JSON enum.

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?
One diagnostic reported to the editor. The range field identifies the affected source span. The severity field is a DiagnosticSeverity enum value: 1 = error, 2 = warning, 3 = information, 4 = hint. The source field names the producer (for example racket-langserver). The message field is the user-facing diagnostic text.

2.4 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?)
Formatting options accepted by doc-format-edits.

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 an internal undef sentinel rather than #f. Test for an absent optional field with undef? from interfaces.rkt.

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.5 Semantic Tokens🔗ℹ

struct

(struct SemanticToken (start end type modifiers)
    #:transparent)
  start : exact-nonnegative-integer?
  end : exact-nonnegative-integer?
  type : SemanticTokenType?
  modifiers : SemanticTokenModifier?
One semantic token with absolute character offsets. The start and end fields are zero-based character positions. The type field is a SemanticTokenType enum value. The modifiers field is a SemanticTokenModifier enum value. Returned by doc-range-tokens; the caller encodes these into the LSP 3.17 relative-delta wire format.

Predicate for the SemanticTokenType JSON enum.

Name

  

Value

variable

  

"variable"

function

  

"function"

string

  

"string"

number

  

"number"

regexp

  

"regexp"

Predicate for the SemanticTokenModifier JSON enum.

Name

  

Value

definition

  

"definition"

3 Doc Library🔗ℹ

 (require racket-langserver/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🔗ℹ

syntax

Doc

Documentation placeholder for the opaque document struct type name. Use Doc? to test values and make-doc to construct them.

procedure

(Doc? v)  boolean?

  v : any/c

procedure

(Doc-uri doc)  string?

  doc : Doc?

procedure

(Doc-version doc)  exact-nonnegative-integer?

  doc : Doc?
Predicates and accessors for the opaque Doc document value. Doc? tests whether a value is a document. Doc-uri returns the document URI string. Doc-version returns the current nonnegative edit version.

procedure

(make-doc uri text [version])  Doc?

  uri : string?
  text : string?
  version : exact-nonnegative-integer? = 0
Creates a new document state for the given uri and initial text. version defaults to 0 and tracks the edit sequence number. An initial (empty) build-trace% is allocated; call doc-expand! to populate it.

procedure

(doc-get-text doc)  string?

  doc : Doc?
Returns the full current text content of the document.

procedure

(doc-apply-edits! doc edits)  void?

  doc : Doc?
  edits : (listof TextEdit?)
Applies a list of TextEdit values to doc. Edits are sorted and applied in descending start-position order so earlier edits do not shift the offsets of later ones. Raises an error if any two edits overlap. The output of doc-format-edits can be passed directly to this function if not #f.

procedure

(doc-apply-edit! doc range text)  void?

  doc : Doc?
  range : Range?
  text : string?
Applies a single text replacement to doc: replaces the content covered by range with text and adjusts the internal trace offsets accordingly. Prefer doc-apply-edits! when applying multiple edits so that offset ordering is handled automatically.

procedure

(doc-reset! doc new-text)  void?

  doc : Doc?
  new-text : string?
Replaces the full text with new-text and resets internal trace state. Use this for whole-file replacements.

procedure

(doc-update-version! doc new-ver)  void?

  doc : Doc?
  new-ver : exact-nonnegative-integer?
Updates the document’s tracked version number.

procedure

(doc-update-uri! doc new-uri)  void?

  doc : Doc?
  new-uri : string?
Updates the URI associated with the document.

procedure

(doc-copy-text-buffer doc)  (is-a?/c lsp-editor%)

  doc : Doc?
Returns a mutable copy of the internal editor buffer.

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?
Converts an LSP Pos to an absolute character offset.

procedure

(doc-abs-pos->pos doc abs-pos)  Pos?

  doc : Doc?
  abs-pos : exact-nonnegative-integer?
Converts an absolute character offset to an LSP Pos.

procedure

(doc-line-start-abs-pos doc line)  exact-nonnegative-integer?

  doc : Doc?
  line : exact-nonnegative-integer?
Returns the absolute character offset at the start of line (zero-based).

procedure

(doc-line-end-abs-pos doc line)  exact-nonnegative-integer?

  doc : Doc?
  line : exact-nonnegative-integer?
Returns the absolute character offset at the end of line, not including the newline character.

procedure

(doc-end-abs-pos doc)  exact-nonnegative-integer?

  doc : Doc?
Returns the absolute character offset one past the last character.

procedure

(doc-find-containing-paren doc pos)

  (or/c exact-nonnegative-integer? #f)
  doc : Doc?
  pos : exact-nonnegative-integer?
Scans backward from pos and returns the absolute offset of the nearest unmatched opening parenthesis or bracket (( or [), or #f if none is found. This is a character-level heuristic, not a full parse.

3.3 Trace and Expansion🔗ℹ

These functions manage check-syntax expansion and the resulting trace.

procedure

(doc-expand! doc)  boolean?

  doc : Doc?
Expands the document in-place, updates its trace to the current version, and walks the expanded text. Returns #t on success, #f if check-syntax expansion failed (e.g., the file has syntax errors). Trace-dependent query functions return fully accurate results only after a successful expansion. Lexer-only queries (such as doc-symbols and doc-get-symbols) still work without expansion, but hover, definition, and reference queries will be stale or empty.

procedure

(doc-update-trace! doc    
  new-trace    
  new-version)  void?
  doc : Doc?
  new-trace : (is-a?/c build-trace%)
  new-version : exact-nonnegative-integer?
Replaces the document’s trace with a freshly computed one. Called internally by doc-expand! but also useful when a scheduler provides a new trace externally.

procedure

(doc-trace-latest? doc)  boolean?

  doc : Doc?
Returns #t if the trace version matches the current document version, meaning the trace is up to date and query results are reliable.

procedure

(doc-walk-text trace text)  void?

  trace : (is-a?/c build-trace%)
  text : string?
Feeds text into trace for incremental token and hover collection. Called automatically by doc-expand!.

3.4 Token and Symbol Utilities🔗ℹ

Lexer-derived token and symbol helpers. These do not require an up-to-date trace except where noted.

procedure

(doc-get-symbols doc)

  (interval-map-of (list/c string? SymbolKind?))
  doc : Doc?
Returns a lexer-derived interval map of symbol, string, and constant token intervals for the current document. Each entry maps a [start, end) range to a (list text kind) pair where text is the token’s string representation and kind is a SymbolKind enum value (SymbolKind-Constant, SymbolKind-String, or SymbolKind-Variable). Positions are absolute character offsets. Does not require an up-to-date trace.

procedure

(doc-range-tokens doc range)  (listof SemanticToken?)

  doc : Doc?
  range : Range?
Returns semantic tokens that intersect range. Each SemanticToken struct has fields start, end, type, and modifiers (all absolute character offsets or enum values). The caller is responsible for encoding these into the LSP 3.17 delta format before sending them on the wire. Requires an up-to-date trace.

procedure

(doc-guess-token doc pos)  string?

  doc : Doc?
  pos : exact-nonnegative-integer?
Heuristically extracts a token-like prefix ending at absolute offset pos by scanning backward until a quote or whitespace character is encountered. Used for completion prefix matching. This is a character-level approximation; it does not use the lexer.

3.5 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.

procedure

(doc-hover doc pos)  (or/c Hover? #f)

  doc : Doc?
  pos : Pos?
Returns hover info at pos, including the identifier’s type signature and a link to online documentation, or #f if nothing is found.

procedure

(doc-completion doc pos)  CompletionList?

  doc : Doc?
  pos : Pos?
Returns completion candidates at pos. The list is always marked incomplete (isIncomplete is #t) since filtering is delegated to the client.

procedure

(doc-definition doc uri pos)  (or/c Location? #f)

  doc : Doc?
  uri : string?
  pos : Pos?
Resolves the definition location for the identifier at pos. Returns a Location in the same file for local bindings, a Location in the external file for required identifiers (triggering a cross-file check-syntax expansion), or #f if not found.

procedure

(doc-references doc uri pos include-decl?)

  (or/c (listof Location?) #f)
  doc : Doc?
  uri : string?
  pos : Pos?
  include-decl? : boolean?
Returns all reference locations for the identifier at pos, or #f if no binding is found.

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?
Returns document highlight entries for all occurrences of the symbol at pos, or #f if no binding is found.

procedure

(doc-rename doc uri pos new-name)  (or/c WorkspaceEdit? #f)

  doc : Doc?
  uri : string?
  pos : Pos?
  new-name : string?
Builds a workspace edit that renames all occurrences of the identifier at pos to new-name. Returns #f if the identifier cannot be renamed (e.g., it is imported from another module).

procedure

(doc-prepare-rename doc pos)  (or/c Range? #f)

  doc : Doc?
  pos : Pos?
Returns the range of the renameable identifier at pos, or #f if the identifier cannot be renamed (e.g., it is an external binding).

procedure

(doc-signature-help doc pos)  (or/c SignatureHelp? #f)

  doc : Doc?
  pos : Pos?
Returns signature help for the function call enclosing pos by scanning backward for the nearest opening parenthesis, or #f if not inside a call.

procedure

(doc-code-action doc range)  (listof CodeAction?)

  doc : Doc?
  range : Range?
Returns quick-fix code actions available at the start of range. Returns an empty list when no actions are available.

procedure

(doc-diagnostics doc)  (listof Diagnostic?)

  doc : Doc?
Returns the list of diagnostics (errors, warnings) collected in the document’s current trace. The result reflects the most recent doc-expand! run.

procedure

(doc-symbols doc uri)  (listof SymbolInformation?)

  doc : Doc?
  uri : string?
Returns all lexer-visible symbol, string, and constant occurrences in the document as SymbolInformation values with their source locations. Does not require an up-to-date trace; runs the lexer directly over the current text.

3.6 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
Computes formatting edits for the lines covered by fmt-range. Returns a list of TextEdit values to apply, or #f if no indenter is available (e.g., the document lacks a #lang line).

When on-type? is #t, blank lines are indented too. This mode is intended for on-type formatting triggered by pressing Enter.

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.