use std::sync::Arc;
use parking_lot::Mutex;
use tinymist_query::analysis::Analysis;
use tokio::sync::mpsc;
use crate::project::*;
use crate::{actor::editor::EditorRequest, Config};
#[derive(Default)]
pub struct ProjectOpts {
pub handle: Option<tokio::runtime::Handle>,
pub analysis: Arc<Analysis>,
pub config: Config,
#[cfg(feature = "preview")]
pub preview: ProjectPreviewState,
pub export_target: ExportTarget,
}
pub struct StartProjectResult<F> {
pub service: WatchService<F>,
pub intr_tx: mpsc::UnboundedSender<LspInterrupt>,
pub editor_rx: mpsc::UnboundedReceiver<EditorRequest>,
}
pub fn start_project<F>(
verse: LspUniverse,
opts: Option<ProjectOpts>,
intr_handler: F,
) -> StartProjectResult<F>
where
F: FnMut(
&mut LspProjectCompiler,
Interrupt<LspCompilerFeat>,
fn(&mut LspProjectCompiler, Interrupt<LspCompilerFeat>),
),
{
let opts = opts.unwrap_or_default();
#[cfg(any(feature = "export", feature = "system"))]
let handle = opts.handle.unwrap_or_else(tokio::runtime::Handle::current);
let _ = opts.config;
let (editor_tx, editor_rx) = mpsc::unbounded_channel();
let (intr_tx, intr_rx) = tokio::sync::mpsc::unbounded_channel();
let (dep_tx, dep_rx) = mpsc::unbounded_channel();
#[cfg(feature = "system")]
{
let fs_intr_tx = intr_tx.clone();
handle.spawn(watch_deps(dep_rx, move |event| {
fs_intr_tx.interrupt(LspInterrupt::Fs(event));
}));
}
#[cfg(not(feature = "system"))]
{
let _ = dep_rx;
log::warn!("Project: system watcher is not enabled, file changes will not be watched");
}
let compile_handle = Arc::new(CompileHandlerImpl {
#[cfg(feature = "preview")]
preview: opts.preview,
is_standalone: true,
#[cfg(feature = "export")]
export: crate::task::ExportTask::new(handle, Some(editor_tx.clone()), opts.config.export()),
editor_tx,
client: Arc::new(intr_tx.clone()),
analysis: opts.analysis,
status_revision: Mutex::default(),
notified_revision: Mutex::default(),
});
let mut compiler = ProjectCompiler::new(
verse,
dep_tx,
CompileServerOpts {
handler: compile_handle,
export_target: opts.export_target,
syntax_only: opts.config.syntax_only,
ignore_first_sync: true,
},
);
compiler.primary.reason.by_entry_update = true;
StartProjectResult {
service: WatchService {
compiler,
intr_rx,
intr_handler,
},
intr_tx,
editor_rx,
}
}
pub struct WatchService<F> {
pub compiler: LspProjectCompiler,
intr_rx: tokio::sync::mpsc::UnboundedReceiver<LspInterrupt>,
intr_handler: F,
}
impl<F> WatchService<F>
where
F: FnMut(
&mut LspProjectCompiler,
Interrupt<LspCompilerFeat>,
fn(&mut LspProjectCompiler, Interrupt<LspCompilerFeat>),
) + Send
+ 'static,
{
pub async fn run(self) {
let Self {
mut compiler,
mut intr_rx,
mut intr_handler,
} = self;
let handler = compiler.handler.clone();
handler.on_any_compile_reason(&mut compiler);
while let Some(intr) = intr_rx.recv().await {
log::debug!("Project compiler received: {intr:?}");
intr_handler(&mut compiler, intr, ProjectState::do_interrupt);
}
log::info!("Project compiler exited");
}
}