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}