laburnum 1.17.2

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::{
  Ident,
  SourceKey,
  Uri,
  database::{
    DynPartition,
    PartitionKey,
  },
  protocol::lsp::SymbolKind,
};

pub struct WorkspaceSymbols;

impl PartitionKey for WorkspaceSymbols {
  const KEY: Ident = Ident::new("laburnum::workspace_symbols");
}
/// To write workspace symbols, create a wrapper partition with
/// [`impl_partition_for_dyn!`]:
///
/// ```rust,ignore
/// use laburnum::{impl_partition_for_dyn, partitions::WorkspaceSymbols};
///
/// // Your record type must implement WorkspaceSymbolRecord + Record
/// impl_partition_for_dyn!(MyWorkspaceSymbolsPartition, WorkspaceSymbols, MySymbol);
///
/// // Then write using the wrapper partition
/// let sort_key = WorkspaceSymbolSortKey::Symbol { name_lowercase, kind, source_key };
/// writer.write::<MyWorkspaceSymbolsPartition>(sort_key, symbol.into());
/// ```
///
/// [`impl_partition_for_dyn!`]: crate::impl_partition_for_dyn
impl DynPartition for WorkspaceSymbols {
  type DynSortKey = WorkspaceSymbolSortKey;
  type RecordConstraint = dyn WorkspaceSymbolRecord;
}

impl<R: WorkspaceSymbolRecord + crate::record::Record>
  crate::database::DynPartitionRecord<WorkspaceSymbols> for R
{
}

#[derive(Debug, Clone)]
pub enum WorkspaceSymbolSortKey {
  Symbol {
    name_lowercase: String,
    kind:           SymbolKind,
    source_key:     SourceKey,
  },
  All,
  NamePrefix {
    name_lowercase: String,
  },
}

impl std::fmt::Display for WorkspaceSymbolSortKey {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    match self {
      | WorkspaceSymbolSortKey::Symbol {
        name_lowercase,
        kind,
        source_key,
      } => write!(f, "{}|{}|{}", name_lowercase, kind, source_key),
      | WorkspaceSymbolSortKey::All => Ok(()),
      | WorkspaceSymbolSortKey::NamePrefix { name_lowercase } => {
        write!(f, "{}", name_lowercase)
      },
    }
  }
}

// -- Sort key ordering (ADR0011) ----------------------------------------------

/// Workspace symbol search is a genuine *string-prefix* query on the name, so
/// `is_prefix_of` uses `str::starts_with` on the name (not token equality).
/// `Ord` is token-based with the name first, which keeps all symbols sharing a
/// name prefix contiguous — so `range(NamePrefix(p)..).take_while(is_prefix_of)`
/// enumerates exactly the matches.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
enum WsSymToken {
  Name(String),
  Kind(SymbolKind),
  Source(SourceKey),
}

impl WorkspaceSymbolSortKey {
  fn tokens(&self) -> Vec<WsSymToken> {
    match self {
      | WorkspaceSymbolSortKey::All => Vec::new(),
      | WorkspaceSymbolSortKey::NamePrefix { name_lowercase } => {
        vec![WsSymToken::Name(name_lowercase.clone())]
      },
      | WorkspaceSymbolSortKey::Symbol {
        name_lowercase,
        kind,
        source_key,
      } => vec![
        WsSymToken::Name(name_lowercase.clone()),
        WsSymToken::Kind(*kind),
        WsSymToken::Source(*source_key),
      ],
    }
  }

  fn name(&self) -> Option<&str> {
    match self {
      | WorkspaceSymbolSortKey::Symbol { name_lowercase, .. }
      | WorkspaceSymbolSortKey::NamePrefix { name_lowercase } => {
        Some(name_lowercase.as_str())
      },
      | WorkspaceSymbolSortKey::All => None,
    }
  }
}

impl PartialEq for WorkspaceSymbolSortKey {
  fn eq(&self, other: &Self) -> bool {
    self.tokens() == other.tokens()
  }
}
impl Eq for WorkspaceSymbolSortKey {}
impl std::hash::Hash for WorkspaceSymbolSortKey {
  fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
    self.tokens().hash(state);
  }
}
impl PartialOrd for WorkspaceSymbolSortKey {
  fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
    Some(self.cmp(other))
  }
}
impl Ord for WorkspaceSymbolSortKey {
  fn cmp(&self, other: &Self) -> std::cmp::Ordering {
    self.tokens().cmp(&other.tokens())
  }
}

impl crate::database::PartitionSortKey for WorkspaceSymbolSortKey {
  fn is_prefix_of(&self, other: &Self) -> bool {
    match self {
      | WorkspaceSymbolSortKey::All => true,
      | WorkspaceSymbolSortKey::NamePrefix { name_lowercase } => {
        other.name().is_some_and(|n| n.starts_with(name_lowercase.as_str()))
      },
      | WorkspaceSymbolSortKey::Symbol { .. } => self == other,
    }
  }

  fn resolve(&self, _resolver: &dyn crate::SourceResolver) -> String {
    self.to_string()
  }
}

pub trait WorkspaceSymbolRecord: Send + Sync + std::fmt::Debug {
  fn name(&self) -> &str;

  fn kind(&self) -> SymbolKind;

  fn uri(&self) -> &Uri;

  fn source_key(&self) -> SourceKey;

  fn range(&self) -> crate::protocol::lsp::Range;

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