graphix_shell/
lsp_backend.rs1use crate::deps;
5use ahash::AHashMap;
6use anyhow::{Context, Result};
7use arcstr::ArcStr;
8use enumflags2::BitFlags;
9use graphix_compiler::{
10 env::Env,
11 expr::{BufferOverrides, ModuleResolver, Source},
12 CFlag, ExecCtx,
13};
14use graphix_lsp::{LspBackend, TypecheckResult};
15use graphix_rt::{CheckResult, GXConfig, GXEvent, GXHandle, GXRt, NoExt};
16use lsp_types::{InitializeParams, Uri};
17use netidx::InternalOnly;
18use parking_lot::Mutex;
19use poolshark::global::GPooled;
20use std::{
21 path::{Path, PathBuf},
22 sync::Arc as StdArc,
23};
24use tokio::{
25 runtime::{Handle, Runtime},
26 sync::mpsc,
27 task,
28};
29use triomphe::Arc;
30
31pub fn run() -> Result<()> {
35 let rt = Runtime::new().context("building tokio runtime")?;
36 let result = graphix_lsp::serve(|init| {
37 let roots = project_roots(init);
38 rt.block_on(build_backend(roots))
39 });
40 drop(rt);
41 result
42}
43
44async fn build_backend(roots: Vec<PathBuf>) -> Result<StdArc<dyn LspBackend>> {
45 let netidx = InternalOnly::new().await.context("starting internal netidx")?;
46 let publisher = netidx.publisher().clone();
47 let subscriber = netidx.subscriber().clone();
48 let mut ctx = ExecCtx::new(GXRt::<NoExt>::new(publisher, subscriber))
49 .context("creating graphix context")?;
50 let mut vfs = AHashMap::default();
51 let res = deps::register::<NoExt>(&mut ctx, &mut vfs)
52 .context("registering stdlib modules")?;
53 let mut resolvers: Vec<ModuleResolver> = vec![ModuleResolver::VFS(vfs)];
54 let base_resolvers = resolvers.clone();
57 for root in roots {
58 resolvers.push(ModuleResolver::Files { base: root, overrides: None });
59 }
60 let flags = CFlag::WarnUnhandled | CFlag::WarnUnused;
61 let (tx, rx) = mpsc::channel(100);
64 task::spawn(drain(rx));
65 let gx = GXConfig::builder(ctx, tx)
66 .flags(BitFlags::from(flags))
67 .root(res.root)
68 .resolvers(resolvers)
69 .lsp_mode(true)
70 .build()
71 .context("building runtime config")?
72 .start()
73 .await
74 .context("loading stdlib")?;
75 let _keep_netidx = StdArc::new(netidx);
76 let buffer_overrides: BufferOverrides = Arc::new(Mutex::new(AHashMap::default()));
77 Ok(StdArc::new(ShellLspBackend {
78 gx,
79 rt_handle: Handle::current(),
80 _keep_netidx,
81 base_resolvers,
82 buffer_overrides,
83 }))
84}
85
86async fn drain(mut rx: mpsc::Receiver<GPooled<Vec<GXEvent>>>) {
87 while rx.recv().await.is_some() {}
88}
89
90fn project_roots(init: &InitializeParams) -> Vec<PathBuf> {
94 let mut roots = Vec::new();
95 if let Some(folders) = &init.workspace_folders {
96 for folder in folders {
97 if let Some(p) = file_uri_to_path(&folder.uri) {
98 roots.push(p);
99 }
100 }
101 }
102 if roots.is_empty() {
103 #[allow(deprecated)]
104 if let Some(uri) = &init.root_uri {
105 if let Some(p) = file_uri_to_path(uri) {
106 roots.push(p);
107 }
108 }
109 }
110 if roots.is_empty() {
111 #[allow(deprecated)]
112 if let Some(p) = init.root_path.as_ref() {
113 roots.push(PathBuf::from(p));
114 }
115 }
116 roots
117}
118
119fn file_uri_to_path(uri: &Uri) -> Option<PathBuf> {
120 graphix_lsp::uri::uri_to_path(uri)
121}
122
123struct ShellLspBackend {
124 gx: GXHandle<NoExt>,
125 rt_handle: Handle,
126 _keep_netidx: StdArc<InternalOnly>,
127 base_resolvers: Vec<ModuleResolver>,
132 buffer_overrides: BufferOverrides,
137}
138
139impl ShellLspBackend {
140 fn resolvers_for(&self, file: &Path) -> Vec<ModuleResolver> {
147 let mut resolvers = self.base_resolvers.clone();
148 if let Some(parent) = file.parent() {
149 resolvers.push(ModuleResolver::Files {
150 base: parent.to_path_buf(),
151 overrides: Some(self.buffer_overrides.clone()),
152 });
153 }
154 resolvers
155 }
156}
157
158impl LspBackend for ShellLspBackend {
159 fn env(&self) -> Env {
160 self.rt_handle.block_on(self.gx.get_env()).unwrap_or_default()
161 }
162
163 fn buffer_overrides(&self) -> BufferOverrides {
164 self.buffer_overrides.clone()
165 }
166
167 fn typecheck_project(
168 &self,
169 root: &Path,
170 initial_scope: Option<ArcStr>,
171 ) -> Result<TypecheckResult> {
172 let CheckResult { env, references, module_references, scope_map, lsp } =
173 self.rt_handle.block_on(self.gx.check_with_resolvers(
174 Source::File(root.to_path_buf()),
175 self.resolvers_for(root),
176 initial_scope,
177 ))?;
178 Ok(TypecheckResult { env, references, module_references, scope_map, lsp })
179 }
180}