-
-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathracket-langserver.scrbl
More file actions
711 lines (594 loc) · 27.9 KB
/
racket-langserver.scrbl
File metadata and controls
711 lines (594 loc) · 27.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
#lang scribble/manual
@(require scribble/extract
(for-label racket
(file "../doclib/doc.rkt")
(file "../common/interfaces.rkt")))
@title{racket-langserver}
The @tt{racket-langserver} is a @hyperlink["https://langserver.org/"]{Language Server Protocol}
implementation for Racket. This project seeks to use
@seclink[#:indirect? #t #:doc '(lib "scribblings/drracket-tools/drracket-tools.scrbl") "top"]{DrRacket's public APIs}
to provide functionality that mimics DrRacket's code tools as closely as possible.
@section{Installation and usage}
A Racket runtime is a prerequisite, so before using @tt{racket-langserver}, ensure that a Racket runtime
is installed. You can install from the @hyperlink["https://download.racket-lang.org"]{official download page}
or install one from your package manager.
First, install an LSP runtime for your editor.
Next, install the package via @tt{raco}:
@commandline{raco pkg install racket-langserver}
@margin-note{To update the @tt{racket-langserver} use
@commandline{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 @tt{.rkt}),
and set the command for the custom client to
@commandline{racket -l racket-langserver}
You may need to restart your LSP runtime or your editor for @tt{racket-langserver} to start.
@subsection{VSCode}
Use the @hyperlink["https://marketplace.visualstudio.com/items?itemName=evzen-wybitul.magic-racket"]{Magic Racket} extension.
@section{Interfaces}
@defmodule[racket-langserver/common/interfaces]
This module provides the data types used by the LSP protocol layer and the doc library API.
Most structs are generated by @racket[define-json-struct], which is basically
@racket[struct] with JSON encode/decode support. Shared runtime structs such as
@racket[SemanticToken] and @racket[LexerEntry] are defined directly when JSON
encode/decode support is not needed.
Each @racket[define-json-struct] type exports:
@itemlist[
@item{A keyword constructor / match expander (e.g. @racket[Pos])
that also accepts positional arguments.}
@item{A predicate (e.g. @racket[Pos?]).}
@item{Accessors for each field (e.g. @racket[Pos-line], @racket[Pos-char]).}
@item{A JSON-hash match expander @tt{Name-js} (e.g. @tt{Pos-js}) that matches
immutable @racket[hasheq] tables using the JSON field names.}
@item{A JSON-hash predicate @tt{Name-js?} (e.g. @tt{Pos-js?}).}
]
To convert a struct value to a JSON-compatible @racket[hasheq], use @tt{jsexpr-encode}
from @tt{racket-langserver/json-util}. Nested struct values are encoded recursively.
@subsection{Position and Range}
@defstruct*[Pos ([line exact-nonnegative-integer?]
[char exact-nonnegative-integer?])
#:transparent]{
LSP position (zero-based line and character offset).
The JSON field for @tt{char} is @tt{character}.
}
@defstruct*[Range ([start Pos?]
[end Pos?])
#:transparent]{
LSP half-open range, the character at @tt{end} is excluded.
}
@subsection{Edit Payloads}
@defstruct*[TextEdit ([range Range?]
[newText string?])
#:transparent]{
A single text replacement operation.
Apply by replacing the text covered by the @tt{range} field with @tt{newText}.
Apply edits with @racket[doc-apply-edits!] (including single-edit lists).
This avoids range-shift pitfalls when multiple edits target nearby offsets.
}
@defstruct*[WorkspaceEdit ([changes (hash/c symbol? (listof TextEdit?))])
#:transparent]{
Collection of document edits keyed by document URI.
}
@subsection{Query Responses}
@defstruct*[Location ([uri string?]
[range Range?])
#:transparent]{
Identifies a source location in a document.
The @tt{uri} field names the target document, and @tt{range} gives the span
within that document.
}
@defstruct*[Hover ([contents string?]
[range Range?])
#:transparent]{
Hover information for a symbol occurrence.
The @tt{contents} field is Markdown text (typically a type signature plus,
when available, a link to online documentation).
The @tt{range} field identifies the source span the hover applies to.
}
@defstruct*[DocumentHighlight ([range Range?])
#:transparent]{
Highlight entry for one symbol occurrence.
The @tt{range} field is the span to highlight in the current document.
}
@defstruct*[CompletionItem ([label string?])
#:transparent]{
One completion candidate shown by the client.
The @tt{label} field is the display text for the candidate.
}
@defstruct*[CompletionList ([isIncomplete boolean?]
[items (listof CompletionItem?)])
#:transparent]{
Completion response payload.
The @tt{items} field contains candidate entries.
The @tt{isIncomplete} field is always @racket[#t], indicating clients should
continue filtering, sorting and may request updated results as the prefix changes.
}
@defstruct*[SignatureInformation ([label string?]
[documentation string?])
#:transparent]{
One call-signature entry in a signature help response.
The @tt{label} field is the signature text shown to the user.
The @tt{documentation} field provides additional description for that signature.
}
@defstruct*[SignatureHelp ([signatures (listof SignatureInformation?)])
#:transparent]{
Signature help response payload.
The @tt{signatures} field contains the candidate call signatures for the
call site, usually one entry per overload.
}
@defthing[SymbolKind? flat-contract?]{
Predicate for the @tt{SymbolKind} JSON enum.
Each variant maps a symbolic name to an LSP integer code:
@tabular[#:sep @hspace[2]
(list (list @bold{Name} @bold{Code})
(list @tt{File} @racket[1])
(list @tt{Module} @racket[2])
(list @tt{Namespace} @racket[3])
(list @tt{Package} @racket[4])
(list @tt{Class} @racket[5])
(list @tt{Method} @racket[6])
(list @tt{Property} @racket[7])
(list @tt{Field} @racket[8])
(list @tt{Constructor} @racket[9])
(list @tt{Enum} @racket[10])
(list @tt{Interface} @racket[11])
(list @tt{Function} @racket[12])
(list @tt{Variable} @racket[13])
(list @tt{Constant} @racket[14])
(list @tt{String} @racket[15])
(list @tt{Number} @racket[16])
(list @tt{Boolean} @racket[17])
(list @tt{Array} @racket[18])
(list @tt{Object} @racket[19])
(list @tt{Key} @racket[20])
(list @tt{Null} @racket[21])
(list @tt{EnumMember} @racket[22])
(list @tt{Struct} @racket[23])
(list @tt{Event} @racket[24])
(list @tt{Operator} @racket[25])
(list @tt{TypeParameter} @racket[26]))]
Access named constants via @tt{SymbolKind-Constant}, @tt{SymbolKind-String},
@tt{SymbolKind-Variable}, etc.
}
@defstruct*[SymbolInformation ([name string?]
[kind SymbolKind?]
[location Location?])
#:transparent]{
One symbol entry in document/workspace symbol results.
The @tt{name} field is the symbol display name.
The @tt{kind} field is a @tt{SymbolKind} enum value (an LSP symbol kind integer code).
The @tt{location} field gives the symbol's source location.
}
@defstruct*[CodeAction ([title string?]
[kind string?]
[diagnostics (listof Diagnostic?)]
[isPreferred boolean?]
[edit WorkspaceEdit?])
#:transparent]{
One code action returned by a code-action request.
The @tt{title} field is the user-visible action label.
The @tt{kind} field is the LSP action kind string (for example quick-fix or refactor).
The @tt{diagnostics} field lists diagnostics this action addresses.
The @tt{isPreferred} field marks the preferred action among alternatives.
The @tt{edit} field is the workspace edit to apply when the action is chosen.
}
@defthing[DiagnosticSeverity? flat-contract?]{
Predicate for the @tt{DiagnosticSeverity} JSON enum.
@tabular[#:sep @hspace[2]
(list (list @bold{Name} @bold{Code})
(list @tt{Error} @racket[1])
(list @tt{Warning} @racket[2])
(list @tt{Information} @racket[3])
(list @tt{Hint} @racket[4]))]
Access named constants via @tt{DiagnosticSeverity-Error},
@tt{DiagnosticSeverity-Warning}, etc.
}
@defstruct*[Diagnostic ([range Range?]
[severity DiagnosticSeverity?]
[source string?]
[message string?])
#:transparent]{
One diagnostic reported to the editor.
The @tt{range} field identifies the affected source span.
The @tt{severity} field is a @tt{DiagnosticSeverity} enum value:
@racket[1] = error, @racket[2] = warning,
@racket[3] = information, @racket[4] = hint.
The @tt{source} field names the producer (for example @tt{racket-langserver}).
The @tt{message} field is the user-facing diagnostic text.
}
@subsection{Resyntax Results}
@defstruct*[Resyntax-Result ([start exact-nonnegative-integer?]
[end exact-nonnegative-integer?]
[message string?]
[rule-name symbol?]
[new-text string?])
#:transparent]{
One refactoring recommendation produced by the optional @tt{resyntax}
integration.
The @tt{start} and @tt{end} fields are absolute character offsets into the
document text.
The @tt{message} field is the user-facing explanation for the recommendation.
The @tt{rule-name} field identifies the resyntax rule that fired.
The @tt{new-text} field is the replacement text suggested for the covered
range.
}
@subsection{Formatting Options}
@defstruct*[FormattingOptions ([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?)])
#:transparent]{
Formatting options accepted by @racket[doc-format-edits].
The @tt{tab-size} and @tt{insert-spaces} fields are required in protocol payloads.
The remaining fields (@tt{trim-trailing-whitespace}, @tt{insert-final-newline},
@tt{trim-final-newlines}, @tt{key}) are optional in the JSON payload;
absent fields are represented as @racket[(Nothing)] rather than @racket[#f].
Test for an absent optional field with @tt{Nothing?} from @tt{racket-langserver/common/json-util}.
The corresponding JSON field names use camelCase:
@tt{tabSize}, @tt{insertSpaces}, @tt{trimTrailingWhitespace}, @tt{insertFinalNewline},
@tt{trimFinalNewlines}.
Not all generated accessors are exported. Public callers should rely on
@racket[FormattingOptions-tab-size] and @racket[FormattingOptions-trim-trailing-whitespace].
}
@subsection{Lexer Entries}
@defstruct*[LexerEntry ([start exact-nonnegative-integer?]
[end exact-nonnegative-integer?]
[text string?]
[type symbol?])
#:transparent]{
One cached lexer token entry returned by @racket[doc-token-at].
The @tt{start} and @tt{end} fields are zero-based absolute character offsets.
The @tt{text} field is the exact token text.
The @tt{type} field names the lexer token class (for example @tt{symbol},
@tt{string}, or @tt{constant}).
}
@subsection{Semantic Tokens}
@defstruct*[SemanticToken ([start exact-nonnegative-integer?]
[end exact-nonnegative-integer?]
[type SemanticTokenType?]
[modifiers SemanticTokenModifier?])
#:transparent]{
One semantic token with absolute character offsets.
The @tt{start} and @tt{end} fields are zero-based character positions.
The @tt{type} field is a @tt{SemanticTokenType} enum value.
The @tt{modifiers} field is a @tt{SemanticTokenModifier} enum value.
Returned by @racket[doc-range-tokens]; the caller encodes these into
the LSP 3.17 relative-delta wire format.
}
@defthing[SemanticTokenType? flat-contract?]{
Predicate for the @tt{SemanticTokenType} JSON enum.
@tabular[#:sep @hspace[2]
(list (list @bold{Name} @bold{Value})
(list @tt{variable} @racket["variable"])
(list @tt{function} @racket["function"])
(list @tt{string} @racket["string"])
(list @tt{number} @racket["number"])
(list @tt{regexp} @racket["regexp"]))]
}
@defthing[SemanticTokenModifier? flat-contract?]{
Predicate for the @tt{SemanticTokenModifier} JSON enum.
@tabular[#:sep @hspace[2]
(list (list @bold{Name} @bold{Value})
(list @tt{definition} @racket["definition"]))]
}
@section{Doc Library}
@defmodule[racket-langserver/doclib/doc]
The doc library provides single-threaded document helpers for representing and querying
Racket source documents. All functions operate on document values that satisfy @racket[Doc?]
without touching the network or a thread scheduler, making them suitable for direct testing and reuse.
@subsection[#:tag "doc-state"]{Document State}
@elemtag["doc-type"]{}
@defidform[Doc]{
Documentation placeholder for the opaque document struct type name.
Use @racket[Doc?] to test values and @racket[make-doc] to construct them.
}
@deftogether[
(@defproc[(Doc? [v any/c]) boolean?]
@defproc[(Doc-uri [doc Doc?]) string?]
@defproc[(Doc-version [doc Doc?]) exact-nonnegative-integer?])]{
Predicates and accessors for the opaque @tt{Doc} document value.
@racket[Doc?] tests whether a value is a document.
@racket[Doc-uri] returns the document URI string.
@racket[Doc-version] returns the current nonnegative edit version.
}
@defproc[(make-doc [uri string?]
[text string?]
[version exact-nonnegative-integer? 0])
Doc?]{
Creates a new document state for the given @tt{uri} and initial @tt{text}.
@tt{version} defaults to @racket[0] and tracks the edit sequence number.
An initial (empty) @tt{build-trace%} is allocated; call @racket[doc-expand!] to populate it.
}
@defproc[(doc-get-text [doc Doc?]) string?]{
Returns the full current text content of the document.
}
@defproc[(doc-apply-edits! [doc Doc?]
[edits (listof TextEdit?)])
void?]{
Applies a list of @racket[TextEdit] values to @tt{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 @racket[doc-format-edits] can be passed directly to this function if not @racket[#f].
}
@defproc[(doc-apply-edit! [doc Doc?]
[range Range?]
[text string?])
void?]{
Applies a single text replacement to @tt{doc}: replaces the content covered by
@tt{range} with @tt{text} and adjusts the internal trace offsets accordingly.
Prefer @racket[doc-apply-edits!] when applying multiple edits so that offset
ordering is handled automatically.
}
@defproc[(doc-reset! [doc Doc?]
[new-text string?])
void?]{
Replaces the full text with @tt{new-text} and resets internal trace state.
Use this for whole-file replacements.
}
@defproc[(doc-update-version! [doc Doc?]
[new-ver exact-nonnegative-integer?])
void?]{
Updates the document's tracked version number.
}
@defproc[(doc-update-uri! [doc Doc?]
[new-uri string?])
void?]{
Updates the URI associated with the document.
}
@defproc[(doc-copy-text-buffer [doc Doc?]) (is-a?/c lsp-editor%)]{
Returns a mutable copy of the internal editor buffer.
}
@subsection{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 @racket[Pos] structs (line/character pairs).
@defproc[(doc-pos->abs-pos [doc Doc?]
[pos Pos?])
exact-nonnegative-integer?]{
Converts an LSP @racket[Pos] to an absolute character offset.
}
@defproc[(doc-abs-pos->pos [doc Doc?]
[abs-pos exact-nonnegative-integer?])
Pos?]{
Converts an absolute character offset to an LSP @racket[Pos].
}
@defproc[(doc-line-start-abs-pos [doc Doc?]
[line exact-nonnegative-integer?])
exact-nonnegative-integer?]{
Returns the absolute character offset at the start of @tt{line} (zero-based).
}
@defproc[(doc-line-end-abs-pos [doc Doc?]
[line exact-nonnegative-integer?])
exact-nonnegative-integer?]{
Returns the absolute character offset at the end of @tt{line},
not including the newline character.
}
@defproc[(doc-end-abs-pos [doc Doc?]) exact-nonnegative-integer?]{
Returns the absolute character offset one past the last character.
}
@defproc[(doc-find-containing-paren [doc Doc?]
[pos exact-nonnegative-integer?])
(or/c exact-nonnegative-integer? #f)]{
Scans backward from @tt{pos} and returns the absolute offset of the nearest
unmatched opening parenthesis or bracket (@tt{(} or @tt{[}), or @racket[#f] if
none is found.
This is a character-level heuristic, not a full parse.
}
@subsection{Trace and Expansion}
These functions manage check-syntax expansion and the resulting trace.
@defproc[(doc-expand! [doc Doc?]) boolean?]{
Expands the document in-place, updates its trace to the current version,
and walks the expanded text. Returns @racket[#t] on success,
@racket[#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 @racket[doc-symbols] and
@racket[doc-token-at] and @racket[doc-token-prefix-at]) still work without
expansion, but hover, definition, and reference queries will be stale or
empty.
This call does not compute or refresh stored resyntax results; use
@racket[doc-resyntax!], or update them explicitly with
@racket[doc-update-resyntax-result!], when you want resyntax-backed
diagnostics and code actions to match the current text.
}
@defproc[(doc-update-trace! [doc Doc?]
[new-trace (is-a?/c build-trace%)]
[new-version exact-nonnegative-integer?])
void?]{
Replaces the document's trace with a freshly computed one. Called internally by
@racket[doc-expand!] but also useful when a scheduler provides a new trace externally.
}
@defproc[(doc-trace-latest? [doc Doc?]) boolean?]{
Returns @racket[#t] if the trace version matches the current document version,
meaning the trace is up to date and query results are reliable.
}
@defproc[(doc-walk-text [trace (is-a?/c build-trace%)]
[text string?])
void?]{
Feeds @tt{text} into @racket[trace] for incremental token and hover collection.
Called automatically by @racket[doc-expand!].
}
@subsection{Resyntax}
These functions manage the optional @tt{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.
@defproc[(doc-resyntax-available?) boolean?]{
Returns @racket[#t] when the optional @tt{resyntax} package is available in
the current process.
}
@defproc[(doc-resyntax [doc Doc?])
(listof Resyntax-Result?)]{
Analyzes the current text of @tt{doc} with @tt{resyntax} and returns the
resulting recommendations without mutating the document.
Returns an empty list when no recommendations are found or when resyntax is
unavailable.
}
@defproc[(doc-resyntax! [doc Doc?]) void?]{
Runs @racket[doc-resyntax] and stores the resulting recommendations on
@tt{doc}.
This is the synchronous single-threaded convenience API for keeping
@racket[doc-diagnostics] and @racket[doc-code-action] aligned with the
document's current text.
}
@defproc[(doc-get-resyntax-results [doc Doc?])
(listof Resyntax-Result?)]{
Returns the currently stored resyntax recommendations for @tt{doc}.
Editing or resetting the document clears previously stored results until
resyntax is run again or new results are written with
@racket[doc-update-resyntax-result!].
}
@defproc[(doc-update-resyntax-result! [doc Doc?]
[results (listof Resyntax-Result?)])
void?]{
Replaces the stored resyntax recommendations on @tt{doc} with @tt{results}.
This is useful when resyntax analysis runs outside the main document thread,
such as the language-server path that computes resyntax asynchronously and
writes the finished results back later.
The written results become the current resyntax state used by
@racket[doc-diagnostics] and @racket[doc-code-action] until the document text
changes.
}
@defproc[(resyntax-result->diag [doc Doc?]
[res Resyntax-Result?])
Diagnostic?]{
Converts @tt{res} into an informational @racket[Diagnostic].
The diagnostic range is derived from the result's absolute offsets, and the
diagnostic message is formatted as @tt{[rule-name] message}.
}
@defproc[(resyntax-result->code-action [doc Doc?]
[res Resyntax-Result?])
CodeAction?]{
Converts @tt{res} into a quick-fix @racket[CodeAction] titled
@tt{Apply rule [rule-name]}.
The action replaces the result range in @tt{doc} with the result's
@tt{new-text}.
}
@subsection{Token and Symbol Utilities}
Lexer-derived token and symbol helpers. These do not require an up-to-date
trace except where noted.
@margin-note{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.}
@defproc[(doc-range-tokens [doc Doc?]
[range Range?])
(listof SemanticToken?)]{
Returns semantic tokens that intersect @tt{range}.
Each @tt{SemanticToken} struct has fields @tt{start}, @tt{end}, @tt{type},
and @tt{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.
}
@defproc[(doc-token-at [doc Doc?]
[pos exact-nonnegative-integer?])
(or/c LexerEntry? #f)]{
Returns the full lexer token covering absolute offset @tt{pos} as a
@racket[LexerEntry], or @racket[#f] when @tt{pos} does not fall within a token
tracked by the cached lexer snapshot.
}
@defproc[(doc-token-prefix-at [doc Doc?]
[pos exact-nonnegative-integer?])
string?]{
Returns the prefix of the lexer token covering absolute offset @tt{pos}.
When no token is found, returns the empty string.
}
@subsection{Query Functions}
These return structured LSP responses. Most require an up-to-date trace;
call @racket[doc-expand!] first or check @racket[doc-trace-latest?].
Exceptions are noted in individual entries.
@defproc[(doc-hover [doc Doc?]
[pos Pos?])
(or/c Hover? #f)]{
Returns hover info at @tt{pos}, including the identifier's type signature
and a link to online documentation, or @racket[#f] if nothing is found.
}
@defproc[(doc-completion [doc Doc?]
[pos Pos?])
CompletionList?]{
Returns completion candidates at @tt{pos}. The list is always marked incomplete
(@tt{isIncomplete} is @racket[#t]) since filtering is delegated to the client.
}
@defproc[(doc-definition [doc Doc?]
[uri string?]
[pos Pos?])
(or/c Location? #f)]{
Resolves the definition location for the identifier at @tt{pos}.
Returns a @racket[Location] in the same file for local bindings, a @racket[Location]
in the external file for required identifiers (triggering a cross-file check-syntax
expansion), or @racket[#f] if not found.
}
@defproc[(doc-references [doc Doc?]
[uri string?]
[pos Pos?]
[include-decl? boolean?])
(or/c (listof Location?) #f)]{
Returns all reference locations for the identifier at @tt{pos}, or @racket[#f]
if no binding is found.
@margin-note{The @tt{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.}
}
@defproc[(doc-highlights [doc Doc?]
[pos Pos?])
(or/c (listof DocumentHighlight?) #f)]{
Returns document highlight entries for all occurrences of the symbol at @tt{pos},
or @racket[#f] if no binding is found.
}
@defproc[(doc-rename [doc Doc?]
[uri string?]
[pos Pos?]
[new-name string?])
(or/c WorkspaceEdit? #f)]{
Builds a workspace edit that renames all occurrences of the identifier at @tt{pos}
to @tt{new-name}. Returns @racket[#f] if the identifier cannot be renamed (e.g.,
it is imported from another module).
}
@defproc[(doc-prepare-rename [doc Doc?]
[pos Pos?])
(or/c Range? #f)]{
Returns the range of the renameable identifier at @tt{pos}, or @racket[#f] if the
identifier cannot be renamed (e.g., it is an external binding).
}
@defproc[(doc-signature-help [doc Doc?]
[pos Pos?])
(or/c SignatureHelp? #f)]{
Returns signature help for the function call enclosing @tt{pos} by scanning
backward for the nearest opening parenthesis, or @racket[#f] if not inside a call.
}
@defproc[(doc-code-action [doc Doc?]
[range Range?])
(listof CodeAction?)]{
Returns quick-fix code actions whose edits intersect @tt{range}.
This includes any quick fix stored in the current trace plus stored resyntax
quick fixes produced by @racket[doc-resyntax!] or
@racket[doc-update-resyntax-result!].
Returns an empty list when no actions are available.
}
@defproc[(doc-diagnostics [doc Doc?])
(listof Diagnostic?)]{
Returns the list of diagnostics for the document, including both trace
diagnostics from the most recent @racket[doc-expand!] run and any stored
current resyntax diagnostics derived from @racket[doc-get-resyntax-results].
}
@defproc[(doc-symbols [doc Doc?]
[uri string?])
(listof SymbolInformation?)]{
Returns all lexer-visible symbol, string, and constant occurrences in the document
as @racket[SymbolInformation] values with their source locations.
Does not require an up-to-date trace; runs the lexer directly over the current text.
}
@subsection{Formatting}
@defproc[(doc-format-edits [doc Doc?]
[fmt-range Range?]
[#:formatting-options opts FormattingOptions?]
[#:on-type? on-type? boolean? #f])
(or/c (listof TextEdit?) #f)]{
Computes formatting edits for the lines covered by @tt{fmt-range}.
Returns a list of @racket[TextEdit] values to apply, or @racket[#f] if no
indenter is available (e.g., the document lacks a @tt{#lang} line).
When @tt{on-type?} is @racket[#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 @racket[doc-apply-edits!] to apply
the edits.
}