lex_lsp/lib.rs
1//! Language Server Protocol (LSP) implementation for Lex
2//!
3//! This crate provides language server capabilities for the Lex format, enabling rich editor
4//! support in any LSP-compatible editor (VSCode, Neovim, Emacs, Sublime, etc.).
5//!
6//! Design Decision: tower-lsp
7//!
8//! After evaluating the Rust LSP ecosystem, we chose tower-lsp as our framework:
9//!
10//! Considered Options:
11//! 1. tower-lsp: High-level async framework built on Tower (~204K monthly downloads)
12//! 2. lsp-server: Low-level sync library from rust-analyzer (~309K monthly downloads)
13//! 3. async-lsp: Low-level async with full Tower integration (~135 GitHub stars)
14//!
15//! Why tower-lsp:
16//! - Best balance of ease-of-use and functionality for a new LSP project
17//! - Strong ecosystem support with extensive documentation and examples
18//! - Modern async/await patterns ideal for Lex's structured parsing needs
19//! - Built-in LSP 3.18 support with proposed features
20//! - Active community with production usage in many language servers
21//! - Good integration with Rust async ecosystem (tokio, futures)
22//!
23//! Trade-offs:
24//! - Less flexible than async-lsp for custom Tower layers (acceptable for our needs)
25//! - Requires &self for trait methods, forcing Arc<Mutex<>> for mutable state (standard pattern)
26//! - Notification ordering is async (not an issue for initial feature set)
27//!
28//! Future Migration Path:
29//! If we later need precise notification ordering or custom middleware, we can migrate
30//! to async-lsp with minimal disruption as both use similar async patterns.
31//!
32//! Feature Set
33//!
34//! Lex is a structured document format, not a programming language. LSP features are selected
35//! to optimize document authoring and navigation workflows:
36//!
37//! Core Features:
38//!
39//! a. Syntax Highlighting
40
41//! 1. Semantic Tokens (textDocument/semanticTokens/*):
42//! - Syntax highlighting for sessions, lists, definitions, annotations
43//! - Inline formatting: bold, italic, code, math
44//! - References, footnotes, citations
45//! - Verbatim blocks with language-specific highlighting
46//! 2. Document Symbols (textDocument/documentSymbol):
47//! - Hierarchical outline view of document structure
48//! - Sessions with nesting (1., 1.1., 1.1.1., etc.)
49//! - Definitions, annotations, lists as navigable symbols
50//! 3. Hover Information (textDocument/hover):
51//! - Preview footnote/citation content on hover
52//! - Show annotation metadata
53//! - Preview definition content when hovering over reference
54//! 4. Folding Ranges (textDocument/foldingRange):
55//! - Fold sessions and nested content
56//! - Fold list items with children
57//! - Fold annotations, definitions, verbatim blocks
58//!
59//! b. Navigation
60//!
61//! 5. Go to Definition / Find References (textDocument/definition, textDocument/references):
62//! - Find all references to footnotes/citations
63//! - Jump from footnote reference [42] to annotation
64//! - Jump from citation [@spec2025] to bibliography entry
65//! - Jump from internal reference [TK-rootlist] to target
66//! 6. Document Links (textDocument/documentLink):
67//! - Clickable links in text
68//! - Verbatim block src parameters (images, includes)
69//! - External references
70//!
71//! c. Editing
72//!
73//! 7. Document Formatting (textDocument/formatting, textDocument/rangeFormatting):
74//! - Fix indentation issues
75//! - Normalize blank lines
76//! - Align list markers //!
77//!
78//!
79//!
80//! Architecture
81//!
82//! The server follows a layered architecture:
83//!
84//! LSP Layer (tower-lsp):
85//! - Handles JSON-RPC communication
86//! - Protocol handshaking and capability negotiation
87//! - Request/response routing
88//!
89//! Server Layer (this crate):
90//! - Implements LanguageServer trait
91//! - Manages document state and parsing
92//! - Coordinates feature implementations
93//! - Very thing, mostly calls the the feature layers over lex-parser
94//! - Thin tests just asserting the right things are being called and returned
95//!
96//! Feature Layer:
97//! - Each feature operates on Lex AST
98//! - Stateless transformations where possible
99//! - All logic and dense unit tests
100//!
101//!
102//! Testing Strategy
103//!
104//! Following Lex project conventions:
105//! - Use official sample files from specs/ for all tests
106//! - Use lexplore loader for consistent test data
107//! - Use ast_assertions library for AST validation
108//! - Test each feature in isolation and integration
109//! - Test against kitchensink and trifecta fixtures
110//!
111//! Non-Features
112//!
113//! The following LSP features are intentionally excluded as they don't apply to document formats:
114//! - Code Lens: Not applicable to documents
115//! - Type Hierarchy: No type system
116//! - Implementation: No interfaces/implementations
117//! - Moniker: For cross-repo linking, not needed
118//! - Linked Editing Range: For paired tags (HTML/XML)
119//! - Diagnostics: we don't have a clear vision for how that would work.
120//!
121//! Error Handling and Robustness
122//!
123//! The server is designed to be highly robust and crash-resistant, following these principles:
124//!
125//! 1. No Panics:
126//! - We strictly avoid `unwrap()` and `expect()` in production code paths.
127//! - All potential failure points (parsing, serialization, IO) return `Result`.
128//! - Errors are propagated up the stack and handled gracefully.
129//!
130//! 2. Graceful Degradation:
131//! - If a feature fails (e.g., semantic tokens calculation), we log the error and return
132//! an empty result or `None` rather than crashing the server.
133//! - This ensures that a bug in one feature doesn't bring down the entire editor experience.
134//!
135//! 3. Error Propagation:
136//! - The `lex-parser` crate returns `Result` types for all parsing operations.
137//! - The `lex-lsp` server maps these internal errors to appropriate LSP error codes
138//! (e.g., `InternalError`, `InvalidRequest`) when communicating with the client.
139//!
140//! 4. Property-Based Testing:
141//! - We use `proptest` to fuzz the server with random inputs (commands, document text)
142//! to uncover edge cases and ensure stability under unexpected conditions.
143//!
144//! Usage
145//!
146//! This crate provides both a library and binary:
147//!
148//! Library:
149//! ```rust
150//! use lex_lsp::LexLanguageServer;
151//! use tower_lsp::Server;
152//!
153//! #[tokio::main]
154//! async fn main() {
155//! let stdin = tokio::io::stdin();
156//! let stdout = tokio::io::stdout();
157//!
158//! let (service, socket) = LspService::new(|client| LexLanguageServer::new(client));
159//! Server::new(stdin, stdout, socket).serve(service).await;
160//! }
161//! ```
162//!
163//! Binary:
164//! $ lex-lsp
165//! Starts the language server on stdin/stdout for editor integration.
166//!
167
168pub mod features;
169pub mod server;
170
171pub use server::LexLanguageServer;