styx_lsp_ext/
lib.rs

1#![doc = include_str!("../README.md")]
2//! Protocol types for Styx LSP extensions.
3//!
4//! This crate defines the Roam service traits and types used for communication
5//! between the Styx LSP and external extensions that provide domain-specific
6//! intelligence (completions, hover, diagnostics, etc.).
7//!
8//! # Architecture
9//!
10//! ```text
11//! ┌─────────────┐                      ┌─────────────────┐
12//! │  Styx LSP   │◄────── Roam ────────►│    Extension    │
13//! │             │                      │   (e.g. dibs)   │
14//! │ implements  │                      │   implements    │
15//! │ StyxLspHost │                      │ StyxLspExtension│
16//! └─────────────┘                      └─────────────────┘
17//! ```
18//!
19//! The LSP calls methods on `StyxLspExtension` to request completions, hover, etc.
20//! The extension can call back to `StyxLspHost` to request additional context.
21//!
22//! # Generated Types
23//!
24//! The `#[roam::service]` macro generates:
25//! - `StyxLspExtensionClient` - Call extension methods from the LSP
26//! - `StyxLspExtensionDispatcher` - Dispatch incoming calls on the extension side
27//! - `StyxLspHostClient` - Call LSP methods from the extension
28//! - `StyxLspHostDispatcher` - Dispatch incoming calls on the LSP side
29//!
30use facet::Facet;
31use styx_tree::Value;
32
33// Re-export roam types needed by generated code and consumers
34pub use roam;
35
36// =============================================================================
37// Service traits
38// =============================================================================
39
40/// Service implemented by LSP extensions.
41///
42/// The Styx LSP calls these methods to request domain-specific intelligence.
43#[roam::service]
44pub trait StyxLspExtension {
45    /// Initialize the extension. Called once after spawn.
46    async fn initialize(&self, params: InitializeParams) -> InitializeResult;
47
48    /// Provide completion items at a cursor position.
49    async fn completions(&self, params: CompletionParams) -> Vec<CompletionItem>;
50
51    /// Provide hover information for a symbol.
52    async fn hover(&self, params: HoverParams) -> Option<HoverResult>;
53
54    /// Provide inlay hints for a range.
55    async fn inlay_hints(&self, params: InlayHintParams) -> Vec<InlayHint>;
56
57    /// Validate the document and return diagnostics.
58    async fn diagnostics(&self, params: DiagnosticParams) -> Vec<Diagnostic>;
59
60    /// Provide code actions for a range.
61    async fn code_actions(&self, params: CodeActionParams) -> Vec<CodeAction>;
62
63    /// Go to definition of a symbol.
64    async fn definition(&self, params: DefinitionParams) -> Vec<Location>;
65
66    /// Shutdown the extension gracefully.
67    async fn shutdown(&self);
68}
69
70/// Service implemented by the Styx LSP for extension callbacks.
71///
72/// Extensions can call these methods to request additional context about
73/// the document being edited.
74#[roam::service]
75pub trait StyxLspHost {
76    /// Get a subtree of the document at a path.
77    async fn get_subtree(&self, params: GetSubtreeParams) -> Option<Value>;
78
79    /// Get the full document tree.
80    async fn get_document(&self, params: GetDocumentParams) -> Option<Value>;
81
82    /// Get the raw source text.
83    async fn get_source(&self, params: GetSourceParams) -> Option<String>;
84
85    /// Get the schema source and URI.
86    async fn get_schema(&self, params: GetSchemaParams) -> Option<SchemaInfo>;
87
88    /// Convert byte offset to line/character position.
89    async fn offset_to_position(&self, params: OffsetToPositionParams) -> Option<Position>;
90
91    /// Convert line/character position to byte offset.
92    async fn position_to_offset(&self, params: PositionToOffsetParams) -> Option<u32>;
93}
94
95// =============================================================================
96// Initialization
97// =============================================================================
98
99/// Parameters for extension initialization.
100#[derive(Debug, Clone, Facet)]
101pub struct InitializeParams {
102    /// Version of the Styx LSP.
103    pub styx_version: String,
104    /// URI of the document being edited.
105    pub document_uri: String,
106    /// ID of the schema (from meta.id).
107    pub schema_id: String,
108}
109
110/// Result of extension initialization.
111#[derive(Debug, Clone, Facet)]
112pub struct InitializeResult {
113    /// Name of the extension.
114    pub name: String,
115    /// Version of the extension.
116    pub version: String,
117    /// Capabilities supported by this extension.
118    pub capabilities: Vec<Capability>,
119}
120
121/// Capabilities an extension can support.
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Facet)]
123#[repr(u8)]
124pub enum Capability {
125    Completions = 0,
126    Hover = 1,
127    InlayHints = 2,
128    Diagnostics = 3,
129    CodeActions = 4,
130    Definition = 5,
131}
132
133// =============================================================================
134// Positions and ranges
135// =============================================================================
136
137/// A position in a document (0-indexed line and character).
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Facet)]
139pub struct Position {
140    pub line: u32,
141    pub character: u32,
142}
143
144/// A range in a document.
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Facet)]
146pub struct Range {
147    pub start: Position,
148    pub end: Position,
149}
150
151/// Cursor position with both line/character and byte offset.
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Facet)]
153pub struct Cursor {
154    pub line: u32,
155    pub character: u32,
156    pub offset: u32,
157}
158
159// =============================================================================
160// Completions
161// =============================================================================
162
163/// Parameters for a completion request.
164#[derive(Debug, Clone, Facet)]
165#[facet(skip_all_unless_truthy)]
166pub struct CompletionParams {
167    /// URI of the document.
168    pub document_uri: String,
169    /// Cursor position.
170    pub cursor: Cursor,
171    /// Path to the current location in the document tree.
172    /// e.g., `["AllProducts", "@query", "select"]`
173    pub path: Vec<String>,
174    /// Text the user has typed (for filtering).
175    pub prefix: String,
176    /// The subtree relevant to this completion (innermost object at cursor).
177    pub context: Option<Value>,
178    /// The closest enclosing tagged value (e.g., `@query{...}`).
179    /// Useful for domain-specific context like finding which table a column belongs to.
180    pub tagged_context: Option<Value>,
181}
182
183/// A completion item.
184#[derive(Debug, Clone, Facet)]
185#[facet(skip_all_unless_truthy)]
186pub struct CompletionItem {
187    /// The text to insert.
188    pub label: String,
189    /// Short description (e.g., column type).
190    pub detail: Option<String>,
191    /// Longer description (markdown).
192    pub documentation: Option<String>,
193    /// Item kind for icon selection.
194    pub kind: Option<CompletionKind>,
195    /// Override sort order.
196    pub sort_text: Option<String>,
197    /// Text to insert if different from label.
198    pub insert_text: Option<String>,
199}
200
201/// Kind of completion item.
202#[derive(Debug, Clone, Copy, PartialEq, Eq, Facet)]
203#[repr(u8)]
204pub enum CompletionKind {
205    Field = 0,
206    Value = 1,
207    Keyword = 2,
208    Type = 3,
209}
210
211// =============================================================================
212// Hover
213// =============================================================================
214
215/// Parameters for a hover request.
216#[derive(Debug, Clone, Facet)]
217#[facet(skip_all_unless_truthy)]
218pub struct HoverParams {
219    /// URI of the document.
220    pub document_uri: String,
221    /// Cursor position.
222    pub cursor: Cursor,
223    /// Path to the symbol.
224    pub path: Vec<String>,
225    /// Context subtree (innermost object at cursor).
226    pub context: Option<Value>,
227    /// The closest enclosing tagged value (e.g., `@query{...}`).
228    /// Useful for domain-specific context like finding which table a column belongs to.
229    pub tagged_context: Option<Value>,
230}
231
232/// Result of a hover request.
233#[derive(Debug, Clone, Facet)]
234#[facet(skip_all_unless_truthy)]
235pub struct HoverResult {
236    /// Markdown content to display.
237    pub contents: String,
238    /// Range to highlight (optional).
239    pub range: Option<Range>,
240}
241
242// =============================================================================
243// Inlay hints
244// =============================================================================
245
246/// Parameters for an inlay hints request.
247#[derive(Debug, Clone, Facet)]
248#[facet(skip_all_unless_truthy)]
249pub struct InlayHintParams {
250    /// URI of the document.
251    pub document_uri: String,
252    /// Range to provide hints for.
253    pub range: Range,
254    /// Context subtree.
255    pub context: Option<Value>,
256}
257
258/// An inlay hint.
259#[derive(Debug, Clone, Facet)]
260#[facet(skip_all_unless_truthy)]
261pub struct InlayHint {
262    /// Position to display the hint.
263    pub position: Position,
264    /// Hint text.
265    pub label: String,
266    /// Kind of hint.
267    pub kind: Option<InlayHintKind>,
268    /// Add space before hint.
269    pub padding_left: bool,
270    /// Add space after hint.
271    pub padding_right: bool,
272}
273
274/// Kind of inlay hint.
275#[derive(Debug, Clone, Copy, PartialEq, Eq, Facet)]
276#[repr(u8)]
277pub enum InlayHintKind {
278    Type = 0,
279    Parameter = 1,
280}
281
282// =============================================================================
283// Diagnostics
284// =============================================================================
285
286/// Parameters for a diagnostics request.
287#[derive(Debug, Clone, Facet)]
288#[facet(skip_all_unless_truthy)]
289pub struct DiagnosticParams {
290    /// URI of the document.
291    pub document_uri: String,
292    /// The full document tree.
293    pub tree: Value,
294}
295
296/// A diagnostic (error, warning, etc.).
297#[derive(Debug, Clone, Facet)]
298#[facet(skip_all_unless_truthy)]
299pub struct Diagnostic {
300    /// Range of the diagnostic.
301    pub range: Range,
302    /// Severity level.
303    pub severity: DiagnosticSeverity,
304    /// Human-readable message.
305    pub message: String,
306    /// Source (extension name).
307    pub source: Option<String>,
308    /// Machine-readable error code.
309    pub code: Option<String>,
310    /// Additional data for code actions.
311    pub data: Option<Value>,
312}
313
314/// Severity of a diagnostic.
315#[derive(Debug, Clone, Copy, PartialEq, Eq, Facet)]
316#[repr(u8)]
317pub enum DiagnosticSeverity {
318    Error = 0,
319    Warning = 1,
320    Info = 2,
321    Hint = 3,
322}
323
324// =============================================================================
325// Code actions
326// =============================================================================
327
328/// Parameters for a code actions request.
329#[derive(Debug, Clone, Facet)]
330#[facet(skip_all_unless_truthy)]
331pub struct CodeActionParams {
332    /// URI of the document.
333    pub document_uri: String,
334    /// Range to provide actions for.
335    pub range: Range,
336    /// Diagnostics at this range (for context).
337    pub diagnostics: Vec<Diagnostic>,
338}
339
340/// A code action (quick fix, refactoring, etc.).
341#[derive(Debug, Clone, Facet)]
342#[facet(skip_all_unless_truthy)]
343pub struct CodeAction {
344    /// Title shown to the user.
345    pub title: String,
346    /// Kind of action.
347    pub kind: Option<CodeActionKind>,
348    /// Edit to apply.
349    pub edit: Option<WorkspaceEdit>,
350    /// Whether this is the preferred action.
351    pub is_preferred: bool,
352}
353
354/// Kind of code action.
355#[derive(Debug, Clone, Copy, PartialEq, Eq, Facet)]
356#[repr(u8)]
357pub enum CodeActionKind {
358    QuickFix = 0,
359    Refactor = 1,
360    Source = 2,
361}
362
363/// A workspace edit (changes to one or more documents).
364#[derive(Debug, Clone, Facet)]
365pub struct WorkspaceEdit {
366    pub changes: Vec<DocumentEdit>,
367}
368
369/// Edits to a single document.
370#[derive(Debug, Clone, Facet)]
371pub struct DocumentEdit {
372    pub uri: String,
373    pub edits: Vec<TextEdit>,
374}
375
376/// A text edit (replace a range with new text).
377#[derive(Debug, Clone, Facet)]
378pub struct TextEdit {
379    pub range: Range,
380    pub new_text: String,
381}
382
383// =============================================================================
384// Go to definition
385// =============================================================================
386
387/// Parameters for a go-to-definition request.
388#[derive(Debug, Clone, Facet)]
389#[facet(skip_all_unless_truthy)]
390pub struct DefinitionParams {
391    /// URI of the document.
392    pub document_uri: String,
393    /// Cursor position.
394    pub cursor: Cursor,
395    /// Path to the symbol.
396    pub path: Vec<String>,
397    /// Context subtree (innermost object at cursor).
398    pub context: Option<Value>,
399    /// The closest enclosing tagged value (e.g., `@query{...}`).
400    pub tagged_context: Option<Value>,
401}
402
403/// A location in a document (URI + range).
404#[derive(Debug, Clone, Facet)]
405pub struct Location {
406    /// URI of the target document.
407    pub uri: String,
408    /// Range within the document.
409    pub range: Range,
410}
411
412// =============================================================================
413// Host callbacks
414// =============================================================================
415
416/// Information about the schema.
417#[derive(Debug, Clone, Facet)]
418pub struct SchemaInfo {
419    /// Schema source text.
420    pub source: String,
421    /// Schema URI (file:// or styx-embedded://).
422    pub uri: String,
423}
424
425/// Parameters for get_subtree.
426#[derive(Debug, Clone, Facet)]
427pub struct GetSubtreeParams {
428    /// URI of the document.
429    pub document_uri: String,
430    /// Path to the subtree.
431    pub path: Vec<String>,
432}
433
434/// Parameters for get_document.
435#[derive(Debug, Clone, Facet)]
436pub struct GetDocumentParams {
437    /// URI of the document.
438    pub document_uri: String,
439}
440
441/// Parameters for get_source.
442#[derive(Debug, Clone, Facet)]
443pub struct GetSourceParams {
444    /// URI of the document.
445    pub document_uri: String,
446}
447
448/// Parameters for get_schema.
449#[derive(Debug, Clone, Facet)]
450pub struct GetSchemaParams {
451    /// URI of the document.
452    pub document_uri: String,
453}
454
455/// Parameters for offset_to_position.
456#[derive(Debug, Clone, Facet)]
457pub struct OffsetToPositionParams {
458    /// URI of the document.
459    pub document_uri: String,
460    /// Byte offset.
461    pub offset: u32,
462}
463
464/// Parameters for position_to_offset.
465#[derive(Debug, Clone, Facet)]
466pub struct PositionToOffsetParams {
467    /// URI of the document.
468    pub document_uri: String,
469    /// Position (line/character).
470    pub position: Position,
471}