Skip to main content

increparse_lsp/
lib.rs

1//! LSP adapter for [`increparse`]: a framework-agnostic bridge between the
2//! Language Server Protocol and incremental multi-pass parsing.
3//!
4//! The crate depends only on [`lsp_types`] — no server framework, no async
5//! runtime, no I/O. You keep your server loop (`lsp-server`, `tower-lsp`,
6//! `async-lsp`, a hand-rolled loop) and use this crate for the plumbing that
7//! is easy to get wrong:
8//!
9//! * **Position encoding** — LSP positions are `(line, character)` pairs in
10//!   UTF-8, UTF-16, or UTF-32 code units (negotiated via the
11//!   `positionEncoding` capability); increparse spans are byte offsets.
12//!   [`LineIndex`] converts between the two, correctly, across multibyte
13//!   text.
14//! * **[`Document`]** — one open file: its text, its [`Session`], the client
15//!   version, and the negotiated encoding. `didOpen`/`didChange` events go
16//!   in; a run report comes out. Incremental change events are translated
17//!   into byte-range [`Edit`]s so the tree reuses everything the edit did
18//!   not touch.
19//! * **Diagnostics** — walk the settled tree, hand [`Failed`](increparse::Status)
20//!   regions to your language-specific hook, and get back publishable
21//!   `Diagnostic`s with correctly converted ranges.
22//!
23//! # Examples
24//!
25//! ```
26//! use increparse::{Engine, Outcome, Pass, Schedule, SerialExecutor, Span, Status};
27//! use increparse_lsp::{Document, PositionEncoding};
28//! use lsp_types::Uri;
29//!
30//! #[derive(Clone, Debug, PartialEq, Eq)]
31//! enum Ctx { File }
32//!
33//! struct Accept;
34//! impl Pass for Accept {
35//!     type Ctx = Ctx;
36//!     fn parse(&self, _source: &str, _span: Span, _ctx: &Ctx) -> Outcome<Ctx> {
37//!         Outcome::Done
38//!     }
39//! }
40//!
41//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
42//! let uri: Uri = "file:///hello.txt".parse()?;
43//! let mut doc = Document::open(uri, 0, "".into(), "héllo wörld".into(), PositionEncoding::Utf16, Ctx::File);
44//!
45//! let engine = Engine::with((Accept,));
46//!
47//! // The client edits "héllo" -> "héy": a same-length replace at byte 3.
48//! // Translate the client's change events into `Edit`s via
49//! // `Document::apply_changes`, then inspect the report:
50//! let text = doc.text().to_string();
51//! let report = engine.run(
52//!     &text,
53//!     doc.session_mut().tree_mut(),
54//!     &SerialExecutor,
55//!     &increparse::CancelToken::new(),
56//! );
57//! assert!(report.reached_fixpoint);
58//! # Ok(())
59//! # }
60//! ```
61//!
62//! A complete (small) server lives in `examples/mini_lang_server.rs`.
63
64#![forbid(unsafe_code)]
65#![deny(missing_docs)]
66
67mod diagnostics;
68mod document;
69mod encoding;
70mod line_index;
71mod server;
72mod simple;
73
74pub use diagnostics::{diagnostics, DiagnosticsOptions, FailedNode};
75pub use document::Document;
76pub use encoding::PositionEncoding;
77pub use line_index::LineIndex;
78pub use server::{serve, serve_on, Documents, Language};
79pub use simple::{
80    CompletionFn, DefinitionFn, DescribeFn, ExtraDiagnosticsFn, HoverFn, LabelFn, NodeLabel,
81    SimpleLanguage, SymbolsFn,
82};
83
84/// The types you almost always want, in one glob (includes the core
85/// prelude).
86pub mod prelude {
87    pub use crate::{
88        diagnostics, serve, serve_on, CompletionFn, DefinitionFn, DescribeFn, DiagnosticsOptions,
89        Document, Documents, ExtraDiagnosticsFn, FailedNode, HoverFn, LabelFn, Language, LineIndex,
90        NodeLabel, PositionEncoding, SimpleLanguage, SymbolsFn,
91    };
92    pub use increparse::prelude::*;
93}