symbolique 0.1.0

Symbol table pipeline for language servers — parse, link, merge, and resolve symbols across files, built on the laburnum LSP framework.
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

//! Merged-level symbol writer extension trait.
//!
//! Generated by [`define_symbol_writer!`] — see `symbol_writer_macro.rs` for
//! the shared implementation.

use {
  crate::{
    core::{Ident, Symbol, SymbolPath, SymbolSortKey, Value},
    partitions::{merged::MergedSymbols, symbols::Symbols},
  },
  laburnum::database::{
    HasPartition, PartitionKey, PartitionWriteContextRef, RecordHandle,
    storage::Partitions,
  },
};

super::symbol_writer_macro::define_symbol_writer! {
  trait_name: MergedSymboliqueWriteExt,
  index_partition: MergedSymbols,
  clear_method: clear_merged_symbols,
  method_prefix: write_merged_,
}

#[cfg(test)]
mod tests {
  use crate::{
    Symbol, SymbolVisibility,
    partitions::{
      MergedSymbols, Symbols,
      test_support::{TestPartitions, TestStores},
    },
    test_helpers::{DV, SI, TP, test_span, test_span_cache},
  };
  use laburnum::database::{
    HasPartition, PartitionWriteContextRef,
    chunk::RecordWriter,
  };
  use super::MergedSymboliqueWriteExt;

  fn make_writer() -> RecordWriter<TestPartitions> {
    RecordWriter::<TestPartitions>::new(laburnum::Ident::new("test"))
  }

  #[test]
  fn write_merged_definition_round_trip() {
    let mut writer = make_writer();
    let mut cache = test_span_cache();
    let span = test_span(&mut cache, 0);

    let handle = {
      let mut ctx = PartitionWriteContextRef::new(&mut writer);
      ctx.write_merged_definition::<DV, SI, TP>(
        "ws|fn|global".to_string(),
        span,
        SI::new("global"),
        Some(DV::Float(2.72)),
        SymbolVisibility::Public,
      )
    };

    let chunk = writer.build();
    let stores = chunk.storage();

    let sym_store =
      <TestStores as HasPartition<Symbols<DV, SI, TP>>>::store(stores);
    let record = sym_store
      .get_by_handle(&handle)
      .as_ref()
      .and_then(|r| r.record())
      .cloned();
    match record {
      Some(Symbol::Definition {
        name, value, visibility,
      }) => {
        assert_eq!(name.as_str(), "global");
        assert_eq!(value, Some(DV::Float(2.72)));
        assert_eq!(visibility, SymbolVisibility::Public);
      }
      _ => panic!("expected Definition"),
    }

    let idx_store =
      <TestStores as HasPartition<MergedSymbols<DV, SI, TP>>>::store(stores);
    assert!(idx_store.index_get("ws|fn|global").is_some());
  }

  #[test]
  fn write_merged_reference_round_trip() {
    let mut writer = make_writer();
    let mut cache = test_span_cache();
    let span = test_span(&mut cache, 1);

    let handle = {
      let mut ctx = PartitionWriteContextRef::new(&mut writer);
      ctx.write_merged_reference::<DV, SI, TP>(
        "ws|ref|call".to_string(),
        span,
        Some(SI::new("call")),
        "ws|fn|target".to_string(),
        false,
        true,
      )
    };

    let chunk = writer.build();
    let stores = chunk.storage();

    let sym_store =
      <TestStores as HasPartition<Symbols<DV, SI, TP>>>::store(stores);
    let record = sym_store
      .get_by_handle(&handle)
      .as_ref()
      .and_then(|r| r.record())
      .cloned();
    match record {
      Some(Symbol::Reference {
        path: _,
        name,
        target_path,
        is_absolute,
        nameable,
      }) => {
        assert_eq!(name.as_ref().map(|n| n.as_str()), Some("call"));
        assert_eq!(target_path, "ws|fn|target");
        assert!(!is_absolute);
        assert!(nameable);
      }
      _ => panic!("expected Reference"),
    }

    let idx_store =
      <TestStores as HasPartition<MergedSymbols<DV, SI, TP>>>::store(stores);
    assert!(idx_store.index_get("ws|ref|call").is_some());
  }

  #[test]
  fn clear_merged_symbols_records_prefix() {
    let mut writer = make_writer();

    {
      let mut ctx = PartitionWriteContextRef::new(&mut writer);
      ctx.clear_merged_symbols::<DV, SI, TP>(
        &"ws|".to_string(),
      );
    }

    let prefixes = writer.clear_prefixes();
    assert_eq!(prefixes.len(), 1);
    assert_eq!(prefixes[0].1, "ws|");
  }
}