laburnum 1.17.1

An LSP framework for building language servers and compilers, powered by an incremental query tree with content-addressed storage, task-based dataflow, and parallel queries.
Documentation
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

use crate::{
    SpanCache,
    Uri,
    database::PartitionWriteContextRef,
    protocol::jsonrpc,
    scheduler::{
      lanes::{
        DEFAULT_LANE,
        Lane,
      },
      task::TaskContext,
    },
    source::SourceKey,
  };

/// Extension points for customizing language server behavior.
///
/// This trait provides hooks that are called at specific points in the LSP
/// lifecycle, allowing you to integrate your parser, analyzer, and other
/// language-specific logic.
///
/// # Hook Methods
///
/// - [`on_file_version`](Self::on_file_version) - Called when a file changes
///   (parse, analyze, write records)
/// - [`get_document_symbols`](Self::get_document_symbols) - Returns symbols for
///   outline view
///
/// # Default Implementations
///
/// All hooks have default implementations that do nothing. Override only what
/// you need.
///
/// # Example
///
/// ```ignore
/// use laburnum::hooks::LaburnumHooks;
///
/// impl LaburnumHooks<MyStorage, MyServer> for MyServer {
///   async fn on_file_version(
///     uri: Uri,
///     source_key: SourceKey,
///     src: &str,
///     span_cache: &mut SpanCache,
///     ctx: TaskContext<MyStorage, MyServer>,
///     writer: &mut PartitionWriteContextRef<'_, MyStorage>,
///   ) -> Result<(), LaburnumError> {
///     let ast = parse(src, span_cache)?;
///
///     writer.write::<MyPartition>(sort_key, ast);
///
///     Ok(())
///   }
/// }
/// ```
#[trait_variant::make(Send)]
pub trait LaburnumHooks<
  P: crate::database::storage::Partitions,
  T: crate::protocol::lsp::LanguageServer<P>,
>: Send + Sync + 'static
{
  /// Called during the language server's Initialize
  ///
  /// Use this to do any work required before additional requests
  /// are accepted.
  ///
  /// Use this to declare what LSP features your server supports. The client
  /// uses this to enable/disable UI features.
  ///
  /// # Example
  ///
  /// ```ignore
  /// fn modify_server_capabilities(capabilities: &mut ServerCapabilities) {
  ///   // Enable formatting
  ///   capabilities.document_formatting_provider = Some(OneOf::Left(true));
  ///
  ///   // Enable completion with trigger characters
  ///   capabilities.completion_provider = Some(CompletionOptions {
  ///     trigger_characters: Some(vec![".".to_string()]),
  ///     ..Default::default()
  ///   });
  ///
  ///   // Enable hover
  ///   capabilities.hover_provider = Some(HoverProviderCapability::Simple(true));
  /// }
  /// ```
  #[allow(unused_variables)]
  async fn on_initialize(
    ctx: &mut TaskContext<P, T>,
    writer: &mut PartitionWriteContextRef<'_, P>,
    capabilities: &mut crate::protocol::lsp::ServerCapabilities,
  ) -> Result<(), jsonrpc::Error> {
    async move { Ok(()) }
  }

  /// Name returned in the LSP `initialize` response as `serverInfo.name`.
  ///
  /// When `None`, `serverInfo` is omitted entirely. Override to identify
  /// your server in client UIs (Zed's "Server Info" panel, etc.).
  const SERVER_NAME: Option<&'static str> = None;

  /// Version returned in the LSP `initialize` response as
  /// `serverInfo.version`. Free-form per the spec — typically semver,
  /// optionally with a build/commit suffix. Ignored when [`SERVER_NAME`]
  /// is `None`.
  ///
  /// [`SERVER_NAME`]: Self::SERVER_NAME
  const SERVER_VERSION: Option<&'static str> = None;

  /// Called when a file's content changes (open, edit, or initial load).
  ///
  /// This is the primary integration point for your parser and analyzer.
  /// Typical implementation:
  /// 1. Lex source into tokens (using `src` and `span_cache`)
  /// 2. Parse tokens into AST
  /// 3. Analyze (build symbol table, type check, etc.)
  /// 4. Write records to multiple partitions
  /// 5. Convert errors to diagnostics
  ///
  /// **Note:** The framework handles SpanCache storage and Source construction
  /// after this hook returns. The `span_cache` you populate here will be
  /// embedded into the final immutable Source.
  ///
  /// # Parameters
  ///
  /// - `uri` - File URI (e.g., `file:///src/main.rs`)
  /// - `source_key` - Content-addressed file version identifier
  /// - `src` - Source content as a string slice
  /// - `span_cache` - Mutable span cache for creating spans during parsing
  /// - `ctx` - Task context for spawning child tasks and querying database
  /// - `writer` - For writing records to this chunk
  ///
  /// # Returns
  ///
  /// Return the `RecordWriter` (possibly with records added). Laburnum will
  /// build the chunk and add it to the database. Return `Err` only for
  /// unrecoverable errors.
  ///
  /// # Example
  ///
  /// See [`implementing-parser.md`](https://github.com/gold-build/laburnum/blob/main/docs/guides/implementing-parser.md)
  /// for complete guide.
  #[allow(unused_variables)]
  async fn on_file_version(
    uri: Uri,
    source_key: SourceKey,
    src: &str,
    span_cache: &mut SpanCache,
    ctx: TaskContext<P, T>,
    writer: &mut PartitionWriteContextRef<'_, P>,
  ) -> Result<(), crate::LaburnumError> {
    async { Ok(()) }
  }

  /// Priority lane for `on_file_version` tasks.
  ///
  /// Default: [`DEFAULT_LANE`]. Override if you want different priority:
  /// - `SYNC_LANE` - Immediate (not recommended, blocks user)
  /// - `INPUT_CONTINUOUS_LANE` - Interactive priority
  /// - `ASYNC_LANE1` - Background work
  const ON_FILE_VERSION_LANE: Lane = DEFAULT_LANE;

  /// Returns document symbols for a specific source file version.
  ///
  /// This is called to populate the outline view / breadcrumbs in editors.
  /// Override this if you want to compute symbols on-demand rather than storing
  /// them in the database.
  ///
  /// # Default Behavior
  ///
  /// The default implementation returns an empty vector. Most implementations
  /// should instead write symbols to `DocumentSymbols::KEY` during
  /// `on_file_version` and let the default DocumentSymbolService query them.
  ///
  /// # When to Override
  ///
  /// Override if symbols are expensive to store but cheap to compute on-demand.
  #[allow(unused_variables)]
  async fn get_document_symbols(
    source_key: SourceKey,
    ctx: &TaskContext<P, T>,
  ) -> Vec<
    std::sync::Arc<
      dyn crate::partitions::document_symbols::DocumentSymbolRecord,
    >,
  > {
    async { Vec::new() }
  }

  /// Priority lane for `get_document_symbols` tasks.
  const GET_DOCUMENT_SYMBOLS_LANE: Lane = DEFAULT_LANE;
}