Skip to main content

perl_lsp/
lib.rs

1//! Perl Language Server Protocol Runtime
2//!
3//! This crate provides the production-ready LSP server runtime for Perl, implementing the
4//! Language Server Protocol specification for seamless integration with editors like VSCode,
5//! Neovim, Emacs, and any LSP-compatible client.
6//!
7//! The runtime handles all protocol communication, message framing, state management, and
8//! feature dispatching, while delegating parsing and analysis to the `perl_parser` engine.
9//!
10//! # Quick Start
11//!
12//! ## Running the Server
13//!
14//! The simplest way to start the server is via the binary:
15//!
16//! ```bash
17//! # Install from source
18//! cargo install --path crates/perllsp
19//!
20//! # Run in stdio mode (default)
21//! perllsp --stdio
22//!
23//! # Check health status
24//! perllsp --health
25//!
26//! # Show version information
27//! perllsp --version
28//! ```
29//!
30//! ## Programmatic Usage
31//!
32//! Use the runtime as a library to embed LSP support:
33//!
34//! ```no_run
35//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
36//! perl_lsp::run_stdio()?;
37//! # Ok(())
38//! # }
39//! ```
40//!
41//! ## Custom Server Configuration
42//!
43//! For advanced use cases, create a server instance directly:
44//!
45//! ```no_run
46//! use perl_lsp::LspServer;
47//!
48//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
49//! let mut server = LspServer::new();
50//! server.run()?;
51//! # Ok(())
52//! # }
53//! ```
54//!
55//! # Architecture
56//!
57//! The runtime is organized into distinct layers for maintainability and extensibility:
58//!
59//! ## Protocol Layer
60//!
61//! - **[`protocol`]**: JSON-RPC message types and LSP protocol definitions
62//! - **[`transport`]**: Message framing and transport (stdio, TCP, WebSocket)
63//! - **[`dispatch`]**: Request routing and method dispatch logic
64//!
65//! ## State Management
66//!
67//! - **[`state`]**: Document and workspace state management
68//! - **[`textdoc`]**: Text document synchronization and version tracking
69//! - **[`runtime`]**: Server lifecycle and initialization management
70//!
71//! ## Feature Providers
72//!
73//! The [`features`] module contains all LSP capability implementations:
74//!
75//! - **Completion**: Context-aware code completion with type inference
76//! - **Hover**: Symbol information and documentation on hover
77//! - **Definition**: Go-to-definition with cross-file support
78//! - **References**: Find all references across workspace
79//! - **Rename**: Symbol renaming with conflict detection
80//! - **Diagnostics**: Real-time syntax and semantic validation
81//! - **Formatting**: Code formatting via perltidy integration
82//! - **Semantic Tokens**: Fine-grained syntax highlighting
83//! - **Code Actions**: Quick fixes and refactoring suggestions
84//! - **Code Lens**: Inline actionable metadata
85//! - **Inlay Hints**: Parameter names and type information
86//! - **Call Hierarchy**: Function call navigation
87//! - **Type Hierarchy**: Class inheritance navigation
88//! - **Document Links**: File and URL navigation
89//! - **Folding**: Code folding for blocks and regions
90//! - **Selection Range**: Smart text selection expansion
91//! - **Signature Help**: Function signature assistance
92//!
93//! ## Utilities
94//!
95//! - **[`convert`]**: Conversions between [`perl_parser`] types and [`lsp_types`]
96//! - **[`util`]**: URI handling, UTF-16 conversion, and position mapping
97//! - **[`fallback`]**: Text-based fallback when parsing fails
98//! - **[`cancellation`]**: Request cancellation infrastructure
99//! - **[`diagnostics_catalog`]**: Stable diagnostic codes
100//!
101//! ## Request Handling
102//!
103//! - **[`handlers`]**: Request and notification handlers
104//! - **[`server`]**: Public server API and message processing loop
105//!
106//! # Protocol Support
107//!
108//! The server implements LSP 3.17 specification features:
109//!
110//! ## Lifecycle Methods
111//!
112//! - `initialize` - Server initialization and capability negotiation
113//! - `initialized` - Post-initialization notification
114//! - `shutdown` - Graceful server shutdown
115//! - `exit` - Server process termination
116//!
117//! ## Document Synchronization
118//!
119//! - `textDocument/didOpen` - Document opened in editor
120//! - `textDocument/didChange` - Document content changed
121//! - `textDocument/didSave` - Document saved to disk
122//! - `textDocument/didClose` - Document closed in editor
123//!
124//! ## Language Features
125//!
126//! See [`features`] module documentation for complete feature list and capabilities.
127//!
128//! # Communication Modes
129//!
130//! ## Stdio Mode (Default)
131//!
132//! Standard input/output transport for editor integration:
133//!
134//! ```bash
135//! perllsp --stdio
136//! ```
137//!
138//! Editors configure this mode in their LSP client settings:
139//!
140//! ### VSCode Configuration
141//!
142//! ```json
143//! {
144//!   "perl.lsp.command": "perllsp",
145//!   "perl.lsp.args": ["--stdio"]
146//! }
147//! ```
148//!
149//! ### Neovim Configuration
150//!
151//! ```lua
152//! require'lspconfig'.perl.setup{
153//!   cmd = { "perllsp", "--stdio" }
154//! }
155//! ```
156//!
157//! ## Socket Mode
158//!
159//! TCP socket transport for remote or debugging scenarios:
160//!
161//! ```bash
162//! perllsp --socket --port 9257
163//! ```
164//!
165//! Connect via TCP socket from any LSP client supporting network transport.
166//!
167//! # Features Module
168//!
169//! All LSP capabilities are implemented in the [`features`] module, organized by category:
170//!
171//! ```text
172//! features/
173//! ├── completion.rs           # Code completion
174//! ├── diagnostics.rs          # Real-time validation
175//! ├── formatting.rs           # Code formatting
176//! ├── hover.rs                # Hover information
177//! ├── references.rs           # Find references
178//! ├── rename.rs               # Symbol renaming
179//! ├── semantic_tokens.rs      # Syntax highlighting
180//! ├── code_actions.rs         # Quick fixes
181//! ├── code_lens_provider.rs   # Code lens
182//! ├── inlay_hints.rs          # Inline hints
183//! └── ...
184//! ```
185//!
186//! # State Management
187//!
188//! The server maintains state across requests:
189//!
190//! - **Document State**: Parsed ASTs, symbol tables, and version tracking
191//! - **Workspace Index**: Cross-file symbol resolution and references
192//! - **Configuration**: Client-provided settings and capabilities
193//! - **Diagnostics**: Cached validation results for incremental updates
194//!
195//! # Error Handling
196//!
197//! The runtime implements comprehensive error recovery:
198//!
199//! - **Parse Errors**: Graceful degradation to text-based features
200//! - **Protocol Errors**: JSON-RPC error responses with diagnostic codes
201//! - **Cancellation**: Request cancellation for long-running operations
202//! - **Fallback**: Text-based completion/hover when parsing fails
203//!
204//! # Performance Optimizations
205//!
206//! - **Incremental Parsing**: Reuse unchanged AST nodes on edits
207//! - **Lazy Indexing**: Index files on-demand rather than at startup
208//! - **Caching**: Cache parse results and symbol tables
209//! - **Cancellation**: Cancel stale requests when new ones arrive
210//! - **Streaming**: Stream large responses to avoid blocking
211//!
212//! # Integration with perl-parser
213//!
214//! The runtime delegates all parsing and analysis to `perl_parser`:
215//!
216//! ```text
217//! use perl_parser::Parser;
218//! use perl_lsp::convert::to_lsp_diagnostic;
219//!
220//! let code = "my $x = 1;";
221//! let mut parser = Parser::new(code);
222//! let ast = parser.parse()?;
223//!
224//! // Convert parser errors to LSP diagnostics
225//! let diagnostics: Vec<_> = parser.errors()
226//!     .iter()
227//!     .map(|e| to_lsp_diagnostic(e, &ast))
228//!     .collect();
229//! ```
230//!
231//! # Testing
232//!
233//! The runtime includes comprehensive test coverage:
234//!
235//! ```bash
236//! # Run all LSP runtime tests
237//! cargo test -p perl-lsp-rs
238//!
239//! # Run with adaptive threading for resource-constrained environments
240//! RUST_TEST_THREADS=2 cargo test -p perl-lsp-rs
241//!
242//! # Test specific feature
243//! cargo test -p perl-lsp-rs completion
244//! ```
245//!
246//! # Logging and Diagnostics
247//!
248//! Enable logging for debugging:
249//!
250//! ```bash
251//! perllsp --stdio --log
252//! ```
253//!
254//! Logs are written to stderr, separate from LSP protocol communication on stdout/stdin.
255//!
256//! # Client Capabilities
257//!
258//! The server adapts to client capabilities negotiated during initialization:
259//!
260//! - **Dynamic Registration**: Supports dynamic capability registration
261//! - **Workspace Folders**: Multi-root workspace support
262//! - **Configuration**: Client-side configuration changes
263//! - **Pull Diagnostics**: Diagnostic pull model (LSP 3.17+)
264//!
265//! # Security Considerations
266//!
267//! - **Sandboxed Execution**: No arbitrary code execution
268//! - **Path Validation**: URI validation and path sanitization
269//! - **Resource Limits**: Memory and time budgets for operations
270//! - **Input Validation**: Strict protocol message validation
271//!
272//! # Migration from perl-parser
273//!
274//! This crate was extracted from `perl_parser` to separate LSP runtime concerns
275//! from parsing engine logic. The migration maintains API compatibility while
276//! improving modularity.
277//!
278//! Legacy code can continue using `perl_parser::lsp_server::LspServer` with
279//! deprecation warnings pointing to this crate.
280//!
281//! # Related Crates
282//!
283//! - `perl_parser`: Parsing engine and analysis infrastructure
284//! - `perl_lexer`: Context-aware Perl tokenizer
285//! - `perl_corpus`: Comprehensive test corpus
286//! - `perl_dap`: Debug Adapter Protocol implementation
287//!
288//! # Documentation
289//!
290//! - **LSP Guide**: See `docs/reference/LSP_IMPLEMENTATION_GUIDE.md` in the repository
291//! - **Capability Policy**: See `docs/reference/LSP_CAPABILITY_POLICY.md`
292//! - **Commands Reference**: See `docs/reference/COMMANDS_REFERENCE.md`
293//!
294//! # Usage Example
295//!
296//! Complete example of running the LSP server:
297//!
298//! ```no_run
299//! use perl_lsp::LspServer;
300//!
301//! let mut server = LspServer::new();
302//!
303//! match server.run() {
304//!     Ok(()) => println!("Server exited cleanly"),
305//!     Err(e) => eprintln!("Server error: {}", e),
306//! }
307//! ```
308
309#![deny(unsafe_code)]
310// Lint enforcement: library modules must use tracing, not direct stderr/stdout prints.
311// cli.rs is exempt — it provides user-facing CLI output that intentionally uses stderr/stdout.
312#![deny(clippy::print_stderr, clippy::print_stdout)]
313#![cfg_attr(test, allow(clippy::print_stderr, clippy::print_stdout))]
314#![warn(missing_docs)]
315#![allow(
316    // Migrated from perl-parser - these patterns are acceptable in LSP runtime code
317    clippy::collapsible_match,
318    clippy::only_used_in_recursion,
319    clippy::while_let_loop,
320    clippy::needless_range_loop,
321    clippy::for_kv_map,
322    clippy::arc_with_non_send_sync,
323    clippy::mutable_key_type,
324    clippy::new_without_default,
325    clippy::if_same_then_else
326)]
327
328// Module declarations - migrated from perl-parser
329pub mod cancellation;
330pub mod cli;
331pub mod convert;
332pub mod diagnostics_catalog;
333pub mod dispatch;
334pub mod execute_command;
335pub mod fallback;
336pub mod features;
337pub mod handlers;
338pub mod protocol;
339pub mod runtime;
340pub mod security;
341pub mod server;
342pub mod state;
343pub mod textdoc;
344pub mod transport;
345pub mod util;
346
347// Re-exports for key types
348pub use cli::run_cli;
349pub use protocol::{JsonRpcError, JsonRpcRequest, JsonRpcResponse};
350pub use server::LspServer;
351
352/// DAP bridge adapter re-export
353#[cfg(feature = "dap-phase1")]
354pub use perl_dap::BridgeAdapter;
355
356// =============================================================================
357// Internal compatibility re-exports (crate-internal, not API surface)
358// =============================================================================
359// These re-exports allow migrated code to use `crate::...` paths for engine
360// pieces while we incrementally update paths to `perl_parser::...`
361
362/// Parser re-export for migrated code
363pub(crate) use perl_parser::Parser;
364
365/// Position utilities re-export
366pub(crate) mod position {
367    pub use perl_parser::position::*;
368}
369
370/// Declaration types re-export
371pub(crate) mod declaration {
372    pub use perl_parser::declaration::*;
373}
374
375/// Workspace index re-export
376pub(crate) mod workspace_index {
377    pub use perl_parser::workspace_index::*;
378}
379
380/// Symbol types re-export
381pub(crate) mod symbol {
382    pub use perl_parser::symbol::*;
383}
384
385/// AST types re-export
386pub(crate) mod ast {
387    pub use perl_parser::ast::*;
388}
389
390/// Feature re-exports for old intra-crate paths
391pub(crate) mod code_actions_enhanced {
392    #[allow(unused_imports)]
393    pub use crate::features::code_actions_enhanced::*;
394}
395
396pub(crate) mod code_lens_provider {
397    pub use crate::features::code_lens_provider::*;
398}
399
400pub(crate) mod diagnostics {
401    #[allow(unused_imports)]
402    pub use crate::features::diagnostics::*;
403}
404
405// More feature re-exports for runtime imports
406pub(crate) mod inlay_hints {
407    pub use crate::features::inlay_hints::*;
408}
409
410pub(crate) mod document_links {
411    pub use crate::features::document_links::*;
412}
413
414pub(crate) mod lsp_document_link {
415    pub use crate::features::lsp_document_link::*;
416}
417
418pub(crate) mod linked_editing {
419    pub use crate::features::linked_editing::*;
420}
421
422pub(crate) mod code_actions_pragmas {
423    pub use crate::features::code_actions_pragmas::*;
424}
425
426// Engine re-exports for runtime
427pub(crate) mod perl_critic {
428    pub use perl_parser::perl_critic::*;
429}
430
431pub(crate) mod semantic {
432    pub use perl_parser::semantic::*;
433}
434
435pub(crate) mod error {
436    pub use perl_parser::error::*;
437}
438
439pub(crate) mod completion {
440    pub use crate::features::completion::*;
441}
442
443pub(crate) mod on_type_formatting {
444    pub use crate::features::on_type_formatting::*;
445}
446
447pub(crate) mod inline_completions {
448    pub use crate::features::inline_completions::*;
449}
450
451pub(crate) mod type_hierarchy {
452    pub use crate::features::type_hierarchy::*;
453}
454
455// Re-export SourceLocation at crate root for convenience
456pub(crate) use perl_parser::ast::SourceLocation;
457
458// Engine modules needed by runtime
459pub(crate) mod type_inference {
460    pub use perl_parser::type_inference::*;
461}
462
463pub(crate) mod builtin_signatures {
464    pub use perl_parser::builtin_signatures::*;
465}
466
467pub(crate) mod workspace_rename {
468    pub use crate::features::workspace_rename::*;
469}
470
471pub(crate) mod semantic_tokens {
472    pub use crate::features::semantic_tokens::*;
473}
474
475pub(crate) mod call_hierarchy_provider;
476
477// Parser module re-export for tests using crate::parser::Parser
478pub(crate) mod parser {
479    #[allow(unused_imports)]
480    pub use perl_parser::parser::*;
481}
482
483// Folding re-export
484pub(crate) mod folding {
485    pub use crate::features::folding::*;
486}
487
488// References re-export
489pub(crate) mod references {
490    #[allow(unused_imports)]
491    pub use crate::features::references::*;
492}
493
494// Rename re-export
495pub(crate) mod rename {
496    #[allow(unused_imports)]
497    pub use crate::features::rename::*;
498}
499
500// Signature help re-export
501pub(crate) mod signature_help {
502    #[allow(unused_imports)]
503    pub use crate::features::signature_help::*;
504}
505
506/// Run the LSP server in stdio mode.
507///
508/// This is the main entry point for the LSP server. It reads JSON-RPC messages from stdin
509/// and writes responses to stdout, following the Language Server Protocol specification.
510///
511/// # Errors
512///
513/// Returns an error if:
514/// - The transport layer fails to initialize
515/// - Message framing or parsing fails
516/// - The server encounters an unrecoverable error
517///
518/// # Example
519///
520/// ```no_run
521/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
522/// perl_lsp::run_stdio()?;
523/// # Ok(())
524/// # }
525/// ```
526pub fn run_stdio() -> Result<(), Box<dyn std::error::Error>> {
527    let server = LspServer::new();
528    server.run().map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
529}