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

//! Linked-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::{linked::LinkedSymbols, symbols::Symbols},
  },
  laburnum::database::{
    HasPartition, PartitionKey, PartitionWriteContextRef, RecordHandle,
    storage::Partitions,
  },
};

super::symbol_writer_macro::define_symbol_writer! {
  trait_name: LinkedSymboliqueWriteExt,
  index_partition: LinkedSymbols,
  clear_method: clear_linked_symbols,
  method_prefix: write_linked_,
}

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

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

  #[test]
  fn write_linked_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_linked_definition::<DV, SI, TP>(
        "linked|fn|func_a".to_string(),
        span,
        SI::new("func_a"),
        Some(DV::Integer(10)),
        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(), "func_a");
        assert_eq!(value, Some(DV::Integer(10)));
        assert_eq!(visibility, SymbolVisibility::Public);
      }
      _ => panic!("expected Definition"),
    }

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

  #[test]
  fn write_linked_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_linked_reference::<DV, SI, TP>(
        "linked|ref|target".to_string(),
        span,
        None,
        "linked|fn|target".to_string(),
        true,
        false,
      )
    };

    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!(name.is_none());
        assert_eq!(target_path, "linked|fn|target");
        assert!(is_absolute);
        assert!(!nameable);
      }
      _ => panic!("expected Reference"),
    }

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

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

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

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