#![doc = include_str!("../readme.md")]
#![allow(async_fn_in_trait)]
#[cfg(doc)]
pub mod docs;
pub mod source;
#[cfg(feature = "chumsky")]
pub use laburnum_syntax_macro::laburnum_syntax;
pub use source::{
FIRST_UNRESERVED_FILE_ID, FsSegment, FsSegmentKind, KNOWN_IDENTS_FILE_ID,
LABURNUM_INTERNAL_KNOWN_IDENTS_FILE_ID, MaterializedSource,
PATH_INTERN_FILE_ID, Source, SourceCache, SourceKey, SourceResolver,
SourceStale, SourceView, Span, SpanCache, SpanCacheCheckpoint, SpanData,
UNKNOWN_FILE_ID,
};
#[cfg(feature = "test")]
pub use source::{TestSource, TestSourceCacheReader};
use std::sync::{Arc, atomic::AtomicBool};
pub type Spanned<T> = (T, Span);
#[derive(Clone, Copy, Debug)]
pub struct SpannedIdent {
ident: Ident,
span: Span,
}
impl SpannedIdent {
pub const fn new(ident: Ident, span: Span) -> Self {
Self { ident, span }
}
#[inline]
pub const fn ident(&self) -> Ident {
self.ident
}
#[inline]
pub const fn span(&self) -> Span {
self.span
}
#[inline]
pub fn span_eq(&self, other: Self) -> bool {
self.ident == other.ident && self.span == other.span
}
#[cfg(any(test, feature = "ident_constructor"))]
pub const fn for_test(ident: Ident) -> Self {
Self::new(ident, Span::unknown())
}
}
impl PartialEq for SpannedIdent {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.ident == other.ident
}
}
impl Eq for SpannedIdent {}
impl PartialOrd for SpannedIdent {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for SpannedIdent {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.ident.cmp(&other.ident)
}
}
impl std::hash::Hash for SpannedIdent {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.ident.hash(state);
}
}
impl From<Spanned<Ident>> for SpannedIdent {
#[inline]
fn from((ident, span): Spanned<Ident>) -> Self {
Self { ident, span }
}
}
impl From<SpannedIdent> for Spanned<Ident> {
#[inline]
fn from(value: SpannedIdent) -> Self {
(value.ident, value.span)
}
}
pub type SpannedIdentHashMap<V> = std::collections::HashMap<SpannedIdent, V>;
pub mod hooks;
#[cfg(feature = "chumsky")]
pub mod chumsky;
pub mod database;
pub use database::{
DynPartition, HasPartition, HasPartitionAt, Partition, PartitionReader,
PartitionSortKey, PartitionStore, PartitionWriteContextRef, PartitionWriter,
RecordKey, RecordRef,
gc::{GarbageCollector, GcPhase, GcPolicy},
hlist::{HCons, HNil, Here, There},
query::{
PartitionQueryBuilder, PartitionWaitingQueryBuilder, QueryClient,
TypedPartitionQueryBuilder, TypedPartitionWaitingQueryBuilder,
},
storage::{
ContentAddressedStorage, Partitions, PartitionsBuilder, RecordStorage,
},
};
mod known_idents;
pub mod record;
pub use record::{LaburnumRecord, LaburnumRecordRef, Record};
pub mod diagnostics;
pub mod partitions;
pub mod prelude;
pub mod progress;
pub mod builtin_watchers {
pub use crate::diagnostics::diagnostic_watcher;
}
pub mod errors;
pub use errors::LaburnumError;
pub mod hash;
pub use hash::{ContentHash, ContentHasher, Ident, IdentHashMap, IdentHashSet};
#[doc(hidden)]
pub use paste;
pub mod exec;
pub mod fs;
pub mod protocol;
pub mod scheduler;
pub mod server;
pub mod uri;
pub use uri::Uri;
#[cfg(feature = "test")]
pub mod test;
pub mod connect;
pub mod daemon;
pub mod verbose;
pub use protocol::jsonrpc::Message;
otel::tracer!();
pub struct Server {
pub shutdown_flag: Arc<AtomicBool>,
thread_handle: Option<std::thread::JoinHandle<()>>,
#[allow(dead_code)]
runtime: Option<crate::exec::Runtime>,
}
impl Server {
#[cfg(feature = "test")]
pub fn close_test(self) -> Option<std::thread::JoinHandle<()>> {
self
.shutdown_flag
.store(true, std::sync::atomic::Ordering::SeqCst);
self.thread_handle
}
pub fn close(self) -> std::thread::Result<()> {
otel::span!("laburnum.server.close");
self
.shutdown_flag
.store(true, std::sync::atomic::Ordering::SeqCst);
match self.thread_handle {
| Some(h) => h.join().map(|_| ()),
| None => Ok(()),
}
}
}
pub struct Laburnum<
P: database::storage::Partitions,
T: protocol::lsp::LanguageServer<P>,
> {
scheduler: std::sync::Arc<scheduler::Scheduler<P, T>>,
io_threads: Option<connect::ipc::IoThreads>,
}
impl<P: database::storage::Partitions>
Laburnum<P, server::LaburnumLanguageServer>
{
pub fn example() -> LaburnumBuilder<P, server::LaburnumLanguageServer> {
LaburnumBuilder::new(Arc::new(server::LaburnumLanguageServer))
}
}
impl<P: database::storage::Partitions, T: protocol::lsp::LanguageServer<P>>
Laburnum<P, T>
where
T: crate::hooks::LaburnumHooks<P, T>,
{
#[allow(clippy::new_ret_no_self)]
pub fn new(server: T) -> LaburnumBuilder<P, T> {
LaburnumBuilder::new(Arc::new(server))
}
pub fn run(mut self) -> Server {
let shutdown_flag = self.scheduler.shutdown_flag.clone();
let thread_handle = otel::span!("laburnum.run", in |cx|{
std::thread::spawn(move || {
let _guard = cx.attach();
self.run_blocking()
})
});
Server {
shutdown_flag,
thread_handle: Some(thread_handle),
runtime: None,
}
}
pub fn run_blocking(&mut self) {
self.scheduler.run();
}
pub fn shutdown(self) -> std::io::Result<()> {
if let Some(io_threads) = self.io_threads {
io_threads.join()?;
}
Ok(())
}
}
pub struct LaburnumBuilder<
P: database::storage::Partitions,
T: protocol::lsp::LanguageServer<P>,
> {
server: Arc<T>,
filesystems: Vec<crate::fs::FS>,
scheduler_config: scheduler::SchedulerConfiguration,
_phantom: std::marker::PhantomData<P>,
}
impl<P: database::storage::Partitions, T: protocol::lsp::LanguageServer<P>>
LaburnumBuilder<P, T>
{
pub fn new(server: Arc<T>) -> Self {
Self {
server,
filesystems: Vec::new(),
scheduler_config: scheduler::SchedulerConfiguration::default(),
_phantom: std::marker::PhantomData,
}
}
pub fn filesystem(mut self, fs: crate::fs::FS) -> Self {
self.filesystems.push(fs);
self
}
pub fn scheduler_config(
mut self,
config: scheduler::SchedulerConfiguration,
) -> Self {
self.scheduler_config = config;
self
}
pub fn build_stdio(self) -> std::io::Result<Laburnum<P, T>> {
let (connection, io_threads) = connect::ipc::Connection::stdio()?;
let filesystems =
std::sync::Arc::new(parking_lot::RwLock::new(self.filesystems));
let source_cache =
std::sync::Arc::new(parking_lot::RwLock::new(SourceCache::new()));
let worker_count = num_cpus::get().saturating_sub(1).max(1);
Ok(Laburnum {
scheduler: scheduler::Scheduler::new_with_config(
connection,
self.server,
filesystems,
source_cache,
worker_count,
self.scheduler_config,
),
io_threads: Some(io_threads),
})
}
pub fn build_test(self) -> (Server, connect::lsp::LspClient) {
let (server_conn, client_conn) = connect::ipc::Connection::memory();
let runtime =
crate::exec::Runtime::new(crate::exec::DEFAULT_CLIENT_IO_THREADS);
let client =
connect::lsp::LspClient::new_test(client_conn, runtime.spawner());
let filesystems =
std::sync::Arc::new(parking_lot::RwLock::new(self.filesystems));
let source_cache =
std::sync::Arc::new(parking_lot::RwLock::new(SourceCache::new()));
let mut server = Laburnum {
scheduler: scheduler::Scheduler::new_with_config(
server_conn,
self.server,
filesystems,
source_cache,
1,
self.scheduler_config,
),
io_threads: None,
}
.run();
server.runtime = Some(runtime);
(server, client)
}
#[cfg(feature = "test")]
pub fn build_server_inline(
self,
server_conn: connect::ipc::Connection,
) -> std::sync::Arc<scheduler::Scheduler<P, T>> {
otel::span!("laburnum.build_server_inline");
let source_cache =
std::sync::Arc::new(parking_lot::RwLock::new(SourceCache::new()));
scheduler::Scheduler::new_inline(
server_conn,
self.server,
std::sync::Arc::new(parking_lot::RwLock::new(self.filesystems)),
source_cache,
self.scheduler_config,
)
}
pub fn build_server(self, server_conn: connect::ipc::Connection) -> Server {
otel::span!("laburnum.build_server");
let source_cache =
std::sync::Arc::new(parking_lot::RwLock::new(SourceCache::new()));
Laburnum {
scheduler: scheduler::Scheduler::new_with_config(
server_conn,
self.server,
std::sync::Arc::new(parking_lot::RwLock::new(self.filesystems)),
source_cache,
1,
self.scheduler_config,
),
io_threads: None,
}
.run()
}
pub fn build_daemon(
self,
config: daemon::DaemonConfig,
) -> daemon::DaemonServer<P, T>
where
T: hooks::LaburnumHooks<P, T>,
{
otel::span!("laburnum.build_daemon");
let filesystems =
std::sync::Arc::new(parking_lot::RwLock::new(self.filesystems));
let source_cache =
std::sync::Arc::new(parking_lot::RwLock::new(SourceCache::new()));
let worker_count = num_cpus::get().saturating_sub(1).max(1);
daemon::DaemonServer::new(
self.server.clone(),
config,
filesystems,
source_cache,
worker_count,
self.scheduler_config,
)
}
}
pub struct TestHarness {
pub server: Server,
}