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::{
    Ident,
    database::{
      Partition,
      PartitionKey,
      storage::{
        PartitionsBuilder,
        RecordStorage,
      },
    },
    hash::ContentHasher,
    record::{
      LaburnumRecord,
      Record,
    },
    scheduler::Scheduler,
  },
  std::{
    sync::Arc,
    time::Duration,
  },
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize)]
pub struct TestIndex(usize);

#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
pub struct TestRecordData {
  pub value: String,
  pub count: usize,
}

impl TestRecordData {
  pub fn new(value: String, count: usize) -> Self {
    Self { value, count }
  }
}

impl Record for TestRecordData {
  fn content_hash(&self) -> crate::ContentHash {
    crate::record::hash_record(self)
  }
}

impl bluegum::Bluegum for TestRecordData {
  fn node(&self, b: &mut bluegum::Builder) {
    b.name("TestRecordData")
      .field("value", &self.value)
      .field("count", self.count);
  }
}

impl bluegum::BluegumWithState<dyn crate::SpanResolver> for TestRecordData {}

impl<P: crate::database::storage::Partitions> crate::record::CollectReferences<P>
  for TestRecordData
{
}

impl crate::record::LaburnumRecordRef for &TestRecordData {
  fn as_laburnum_record(&self) -> Option<&LaburnumRecord> {
    None
  }

  fn serialize_with_source_cache<P, T, Ser>(
    &self,
    _source_cache: &crate::source::SourceCache<P, T>,
    serializer: Ser,
  ) -> Result<Ser::Ok, Ser::Error>
  where
    P: crate::database::storage::Partitions,
    T: crate::protocol::lsp::LanguageServer<P>,
    Ser: serde::Serializer,
  {
    use serde::Serialize;
    self.serialize(serializer)
  }
}

impl From<LaburnumRecord> for TestRecordData {
  fn from(_record: LaburnumRecord) -> Self {
    panic!("TestRecordData does not support LaburnumRecord conversion")
  }
}

#[derive(Debug)]
pub struct TestStorage {
  records: Vec<TestRecordData>,
}

impl RecordStorage for TestStorage {
  type Builder = TestStorageBuilder;
  type Index = TestIndex;
  type RecordRef<'a> = &'a TestRecordData;

  fn get(&self, idx: &Self::Index) -> Option<Self::RecordRef<'_>> {
    self.records.get(idx.0)
  }

  fn hash_contents(&self, hasher: &mut ContentHasher) {
    for record in &self.records {
      hasher.update(record.value.as_bytes());
      hasher.update(&record.count.to_le_bytes());
    }
  }
}

#[derive(Default)]
pub struct TestStorageBuilder {
  records: Vec<TestRecordData>,
}

impl PartitionsBuilder for TestStorageBuilder {
  type Record = TestRecordData;
  type Storage = TestStorage;

  fn push(&mut self, record: Self::Record) -> TestIndex {
    let idx = TestIndex(self.records.len());
    self.records.push(record);
    idx
  }

  fn build(self) -> Self::Storage {
    TestStorage {
      records: self.records,
    }
  }
}

pub struct TestPartition;

impl PartitionKey for TestPartition {
  const KEY: Ident = Ident::new("scheduler_test::records");
}

impl Partition for TestPartition {
  type Record = TestRecordData;
  type IndexEntry = crate::database::partitions::HandleEntry<Self>;
  type SortKey = String;

  fn index_entry_from_handle(
    handle: crate::database::RecordHandle<Self>,
  ) -> Self::IndexEntry {
    crate::database::partitions::HandleEntry::new(handle)
  }
}

crate::define_partitions! {
  Test,
  partitions = [Test,],
}

pub fn test_scheduler() -> (
  Arc<Scheduler<TestPartitions, crate::server::LaburnumLanguageServer>>,
  crate::connect::ipc::Connection,
) {
  let (server_conn, client_conn) = crate::connect::ipc::Connection::memory();
  let filesystems = std::sync::Arc::new(parking_lot::RwLock::new(Vec::new()));
  let source_cache =
    std::sync::Arc::new(parking_lot::RwLock::new(crate::SourceCache::new()));
  let config = crate::scheduler::SchedulerConfiguration {
    rpc_response_capacity: 100,
    enable_periodic_gc:    false,
  };
  let scheduler = Scheduler::new_with_config(
    server_conn,
    std::sync::Arc::new(crate::server::LaburnumLanguageServer),
    filesystems,
    source_cache,
    1,
    config,
  );
  (scheduler, client_conn)
}

pub async fn wait_for<F: Fn() -> bool>(
  condition: F,
  timeout: Duration,
) -> bool {
  let start = std::time::Instant::now();
  while start.elapsed() < timeout {
    if condition() {
      return true;
    }
    futures_lite::future::yield_now().await;
  }
  false
}

pub async fn wait_for_value<T, F: Fn() -> Option<T>>(
  condition: F,
  timeout: Duration,
) -> Option<T> {
  let start = std::time::Instant::now();
  while start.elapsed() < timeout {
    if let Some(value) = condition() {
      return Some(value);
    }
    futures_lite::future::yield_now().await;
  }
  None
}