1use std::sync::Arc;
4
5use parking_lot::Mutex;
6use tinymist_query::analysis::Analysis;
7use tokio::sync::mpsc;
8
9use crate::project::*;
10use crate::{actor::editor::EditorRequest, Config};
11
12#[derive(Default)]
14pub struct ProjectOpts {
15 pub handle: Option<tokio::runtime::Handle>,
17 pub analysis: Arc<Analysis>,
19 pub config: Config,
21 #[cfg(feature = "preview")]
23 pub preview: ProjectPreviewState,
24 pub export_target: ExportTarget,
26}
27
28pub struct StartProjectResult<F> {
30 pub service: WatchService<F>,
32 pub intr_tx: mpsc::UnboundedSender<LspInterrupt>,
34 pub editor_rx: mpsc::UnboundedReceiver<EditorRequest>,
36}
37
38pub fn start_project<F>(
42 verse: LspUniverse,
43 opts: Option<ProjectOpts>,
44 intr_handler: F,
45) -> StartProjectResult<F>
46where
47 F: FnMut(
48 &mut LspProjectCompiler,
49 Interrupt<LspCompilerFeat>,
50 fn(&mut LspProjectCompiler, Interrupt<LspCompilerFeat>),
51 ),
52{
53 let opts = opts.unwrap_or_default();
54 #[cfg(any(feature = "export", feature = "system"))]
55 let handle = opts.handle.unwrap_or_else(tokio::runtime::Handle::current);
56
57 let _ = opts.config;
58
59 let (editor_tx, editor_rx) = mpsc::unbounded_channel();
61 let (intr_tx, intr_rx) = tokio::sync::mpsc::unbounded_channel();
62
63 let (dep_tx, dep_rx) = mpsc::unbounded_channel();
65 #[cfg(feature = "system")]
67 {
68 let fs_intr_tx = intr_tx.clone();
69 handle.spawn(watch_deps(dep_rx, move |event| {
70 fs_intr_tx.interrupt(LspInterrupt::Fs(event));
71 }));
72 }
73 #[cfg(not(feature = "system"))]
74 {
75 let _ = dep_rx;
76 log::warn!("Project: system watcher is not enabled, file changes will not be watched");
77 }
78
79 let compile_handle = Arc::new(CompileHandlerImpl {
81 #[cfg(feature = "preview")]
82 preview: opts.preview,
83 is_standalone: true,
84 #[cfg(feature = "export")]
85 export: crate::task::ExportTask::new(handle, Some(editor_tx.clone()), opts.config.export()),
86 editor_tx,
87 client: Arc::new(intr_tx.clone()),
88
89 analysis: opts.analysis,
90 status_revision: Mutex::default(),
91 notified_revision: Mutex::default(),
92 });
93
94 let mut compiler = ProjectCompiler::new(
95 verse,
96 dep_tx,
97 CompileServerOpts {
98 handler: compile_handle,
99 export_target: opts.export_target,
100 ignore_first_sync: true,
101 },
102 );
103
104 compiler.primary.reason.by_entry_update = true;
105
106 StartProjectResult {
107 service: WatchService {
108 compiler,
109 intr_rx,
110 intr_handler,
111 },
112 intr_tx,
113 editor_rx,
114 }
115}
116
117pub struct WatchService<F> {
119 pub compiler: LspProjectCompiler,
121 intr_rx: tokio::sync::mpsc::UnboundedReceiver<LspInterrupt>,
122 intr_handler: F,
123}
124
125impl<F> WatchService<F>
126where
127 F: FnMut(
128 &mut LspProjectCompiler,
129 Interrupt<LspCompilerFeat>,
130 fn(&mut LspProjectCompiler, Interrupt<LspCompilerFeat>),
131 ) + Send
132 + 'static,
133{
134 pub async fn run(self) {
136 let Self {
137 mut compiler,
138 mut intr_rx,
139 mut intr_handler,
140 } = self;
141
142 let handler = compiler.handler.clone();
143 handler.on_any_compile_reason(&mut compiler);
144
145 while let Some(intr) = intr_rx.recv().await {
146 log::debug!("Project compiler received: {intr:?}");
147 intr_handler(&mut compiler, intr, ProjectState::do_interrupt);
148 }
149
150 log::info!("Project compiler exited");
151 }
152}