laburnum 1.17.0

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::{
  SourceKey,
  protocol::lsp,
};

crate::define_dyn_partition!(
  /// To write diagnostics, create a wrapper partition with [`impl_partition_for_dyn!`]:
  ///
  /// ```rust,ignore
  /// use laburnum::{impl_partition_for_dyn, partitions::Diagnostics};
  ///
  /// // Your record type must implement DiagnosticRecord + Record
  /// impl_partition_for_dyn!(MyDiagnosticsPartition, Diagnostics, MyDiagnostic);
  ///
  /// // Then write using the wrapper partition
  /// let sort_key = DiagnosticSortKey::Diagnostic { source_key, severity, sequence };
  /// writer.write::<MyDiagnosticsPartition>(sort_key, diagnostic.into());
  /// ```
  ///
  /// # Clearing Old Diagnostics
  ///
  /// When re-processing a file, clear old diagnostics before writing new ones using
  /// [`PartitionWriteContext::clear_prefix`]. Use [`DiagnosticSortKey::FilePrefix`]
  /// to clear all diagnostics for a file regardless of version:
  ///
  /// ```rust,ignore
  /// // Clear all diagnostics for this file before writing new ones
  /// writer.clear_prefix(
  ///   Diagnostics::KEY,
  ///   DiagnosticSortKey::FilePrefix(source_key.file_id()),
  /// );
  ///
  /// // Then write new diagnostics (or none if the file is valid)
  /// for error in errors {
  ///   writer.write::<MyDiagnosticsPartition>(sort_key, diagnostic);
  /// }
  /// ```
  ///
  /// This ensures that when a file is fixed (no more errors), the old diagnostics
  /// are cleared and watchers are notified to send empty diagnostics to the client.
  ///
  /// [`impl_partition_for_dyn!`]: crate::impl_partition_for_dyn
  /// [`PartitionWriteContext::clear_prefix`]: crate::database::PartitionWriteContext::clear_prefix
  Diagnostics,
  "laburnum::diagnostics",
  DiagnosticSortKey,
  DiagnosticRecord
);

#[derive(Debug, Clone)]
pub enum DiagnosticSortKey {
  Diagnostic {
    source_key: SourceKey,
    severity:   lsp::DiagnosticSeverity,
    sequence:   u16,
  },
  All,
  FilePrefix(u16),
  SourcePrefix(SourceKey),
  SourceSeverityPrefix(SourceKey, lsp::DiagnosticSeverity),
}

impl std::fmt::Display for DiagnosticSortKey {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    match self {
      | DiagnosticSortKey::Diagnostic {
        source_key,
        severity,
        sequence,
      } => write!(f, "{}|{}|{:04}", source_key, severity, sequence),
      | DiagnosticSortKey::All => Ok(()),
      | DiagnosticSortKey::FilePrefix(file_id) => {
        write!(f, "{}v", file_id)
      },
      | DiagnosticSortKey::SourcePrefix(source_key) => {
        write!(f, "{}|", source_key)
      },
      | DiagnosticSortKey::SourceSeverityPrefix(source_key, severity) => {
        write!(f, "{}|{}|", source_key, severity)
      },
    }
  }
}

pub trait DiagnosticRecord: Send + Sync + std::fmt::Debug {
  fn to_lsp_diagnostic(
    &self,
    source_cache: &crate::source::cache::reporter::SourceCacheReader,
    encoding: &crate::protocol::lsp::PositionEncodingKind,
  ) -> lsp::Diagnostic;

  fn to_ariadne_report(
    &self,
    source_cache: &crate::source::cache::reporter::SourceCacheReader,
  ) -> ariadne::Report<'static, (SourceKey, std::ops::Range<usize>)>;

  fn source_key(&self) -> Option<SourceKey>;

  fn severity(&self) -> Option<lsp::DiagnosticSeverity>;

  fn message(&self) -> Option<&str>;
}