From 3ff4c2ab082855f745ebf776169ec3090957912a Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 30 Jun 2026 06:24:49 +0200 Subject: [PATCH 1/7] Using FS for saving files #93 --- crates/analyzer/src/lib.rs | 78 +++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/crates/analyzer/src/lib.rs b/crates/analyzer/src/lib.rs index 3ce6236..bbb7e17 100644 --- a/crates/analyzer/src/lib.rs +++ b/crates/analyzer/src/lib.rs @@ -18,7 +18,9 @@ use tower_lsp::lsp_types::{self, *}; use tower_lsp::{Client, LanguageServer, LspService, Server}; use crate::rust_analyzer::RustAnalyzer; -use crate::transpiler::{TranspileError, format_mist, transpile_mist, transpile_mist_no_sem}; +use crate::transpiler::{ + TranspileError, TranspiledFile, format_mist, transpile_mist, transpile_mist_no_sem, +}; static MARKER_COUNTER: AtomicU64 = AtomicU64::new(0); @@ -547,7 +549,7 @@ impl Backend { mist_path: &Path, source: &str, extra_mod_decl: &str, - ) { + ) -> Option { let transpiled = match transpile_mist(mist_path, source, extra_mod_decl) { Ok(t) => t, Err(e) => { @@ -556,7 +558,7 @@ impl Backend { if let Some(uri) = clean_lsp_url(mist_path) { self.publish_diagnostics(uri, diag).await; } - return; + return None; } }; @@ -577,20 +579,22 @@ impl Backend { let _ = ra.did_open(rust_uri, &transpiled.rust_content).await; } - map.insert(transpiled.rust_path, transpiled.mapping); + map.insert(transpiled.rust_path.clone(), transpiled.mapping.clone()); self.last_rust_contents .lock() .await - .insert(mist_path.to_path_buf(), transpiled.rust_content); + .insert(mist_path.to_path_buf(), transpiled.rust_content.clone()); } // Clear any previous diagnostics — transpile succeeded. if let Some(mist_uri) = clean_lsp_url(mist_path) { self.publish_diagnostics(mist_uri, vec![]).await; } + + Some(transpiled) } - async fn rebuild_module_tree(&self) { + async fn rebuild_module_tree(&self, persist: bool) { let ws = match &*self.workspace_folder.lock().await { Some(p) => p.clone(), None => return, @@ -615,8 +619,14 @@ impl Backend { }; for (mist_path, source, decl) in sources { - self.handle_transpile_and_notify(&mist_path, &source, &decl) - .await; + if let Some(transpiled) = self + .handle_transpile_and_notify(&mist_path, &source, &decl) + .await + { + if persist { + Self::write_transpiled_to_disk(&transpiled); + } + } } } @@ -627,6 +637,30 @@ impl Backend { ensure_implicit_packages_impl(&mut *docs, src_root); } + /// Write a transpiled file's `.rs` content and `.map.json` mapping to disk. + /// The output directory is created if it doesn't exist. + fn write_transpiled_to_disk(transpiled: &TranspiledFile) { + if let Some(parent) = transpiled.rust_path.parent() { + if let Err(e) = std::fs::create_dir_all(parent) { + eprintln!("Failed to create output directory {:?}: {e}", parent); + return; + } + } + if let Err(e) = std::fs::write(&transpiled.rust_path, &transpiled.rust_content) { + eprintln!( + "Failed to write transpiled file {:?}: {e}", + transpiled.rust_path + ); + return; + } + let map_path = transpiled.rust_path.with_extension("map.json"); + if let Ok(map_json) = serde_json::to_string(&transpiled.mapping) { + if let Err(e) = std::fs::write(&map_path, &map_json) { + eprintln!("Failed to write mapping file {:?}: {e}", map_path); + } + } + } + async fn publish_diagnostics(&self, uri: Url, diagnostics: Vec) { let key = uri.clone(); let prev = self.previous_diagnostics.lock().await.get(&key).cloned(); @@ -951,7 +985,7 @@ impl LanguageServer for Backend { self.handle_transpile_and_notify(&mist_path, &source, "") .await; - self.rebuild_module_tree().await; + self.rebuild_module_tree(false).await; } async fn did_change(&self, params: DidChangeTextDocumentParams) { @@ -973,7 +1007,7 @@ impl LanguageServer for Backend { self.handle_transpile_and_notify(&mist_path, &source, "") .await; - self.rebuild_module_tree().await; + self.rebuild_module_tree(false).await; } } @@ -986,16 +1020,24 @@ impl LanguageServer for Backend { } }; - if let Some(text) = params.text { - self.documents - .lock() - .await - .insert(mist_path.clone(), Rope::from_str(&text)); + let text = if let Some(text) = ¶ms.text { + text.clone() + } else { + tokio::fs::read_to_string(&mist_path).await.expect("Failed to read file") + }; - self.handle_transpile_and_notify(&mist_path, &text, "") - .await; - self.rebuild_module_tree().await; + self.documents + .lock() + .await + .insert(mist_path.clone(), Rope::from_str(&text)); + + if let Some(transpiled) = self + .handle_transpile_and_notify(&mist_path, &text, "") + .await + { + Self::write_transpiled_to_disk(&transpiled); } + self.rebuild_module_tree(true).await; } async fn did_close(&self, params: DidCloseTextDocumentParams) { From bfaa9eeea5c2535897e86194f280dbb511bc3ce1 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 30 Jun 2026 07:08:39 +0200 Subject: [PATCH 2/7] Fix Diagnostics #93 --- crates/analyzer/src/lib.rs | 30 +++++++++++++++++----------- crates/analyzer/src/rust_analyzer.rs | 17 ++++++++++++++++ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/crates/analyzer/src/lib.rs b/crates/analyzer/src/lib.rs index bbb7e17..5c7a025 100644 --- a/crates/analyzer/src/lib.rs +++ b/crates/analyzer/src/lib.rs @@ -1023,7 +1023,9 @@ impl LanguageServer for Backend { let text = if let Some(text) = ¶ms.text { text.clone() } else { - tokio::fs::read_to_string(&mist_path).await.expect("Failed to read file") + tokio::fs::read_to_string(&mist_path) + .await + .expect("Failed to read file") }; self.documents @@ -1036,6 +1038,14 @@ impl LanguageServer for Backend { .await { Self::write_transpiled_to_disk(&transpiled); + if let Some(rust_uri) = clean_lsp_url(&transpiled.rust_path) { + let _ = self + .rust_analyzer + .lock() + .await + .did_save(rust_uri, &transpiled.rust_content) + .await; + } } self.rebuild_module_tree(true).await; } @@ -1817,17 +1827,13 @@ async fn handle_ra_notifications( }; if let Some(mist_uri) = mist_uri.or(Some(rust_uri.clone())) { - let prev = previous_diagnostics.lock().await.get(&mist_uri).cloned(); - - if prev.as_ref() != Some(&mapped_diagnostics) { - previous_diagnostics - .lock() - .await - .insert(mist_uri.clone(), mapped_diagnostics.clone()); - client - .publish_diagnostics(mist_uri, mapped_diagnostics, None) - .await; - } + previous_diagnostics + .lock() + .await + .insert(mist_uri.clone(), mapped_diagnostics.clone()); + client + .publish_diagnostics(mist_uri, mapped_diagnostics, None) + .await; } } } diff --git a/crates/analyzer/src/rust_analyzer.rs b/crates/analyzer/src/rust_analyzer.rs index 3e55a45..12333d5 100644 --- a/crates/analyzer/src/rust_analyzer.rs +++ b/crates/analyzer/src/rust_analyzer.rs @@ -374,6 +374,23 @@ impl RustAnalyzer { .await } + pub async fn did_save( + &mut self, + uri: Url, + text: &str, + ) -> Result<(), Box> { + self.notify( + "textDocument/didSave", + json!({ + "textDocument": { + "uri": uri, + }, + "text": text, + }), + ) + .await + } + pub async fn did_close( &mut self, uri: Url, From bd938ec3e7aa5d6acf9607e31b2795b8abbcb030 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 30 Jun 2026 18:23:15 +0200 Subject: [PATCH 3/7] Updated transpiler config --- crates/mist-api/src/transpiler.mist | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index a6808c8..0a10669 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -26,7 +26,7 @@ pub struct MistConfig { pub PathBuf package, pub Vec packages, - pub String main_mods, + pub mist_parser::MistFmtConfig fmt, } impl MistConfig @@ -52,7 +52,9 @@ impl Default for MistConfig Self { package: PathBuf::from("main.mist"), packages: Vec::new(), - main_mods: String::new(), + fmt: mist_parser::MistFmtConfig { + allman_bracket_style: true, + }, } } } @@ -71,7 +73,7 @@ pub PathBuf transpile() } config.package = src.join(&*config.package); let master = modules::Module::new(config.package.clone(), &config); - master.transpile(&out, &mut config); + master.transpile(&out, &mut config, &mut String::new()); let elapsed = start.elapsed(); println!("\x1b[32m\nTranspile successful\x1b[0m in \x1b[34m{:.2?}\x1b[0m", elapsed); @@ -80,7 +82,7 @@ pub PathBuf transpile() impl modules::Module { - (bool, String) transpile(&self, PathBuf& parent_dir, MistConfig mut& config) + (bool, String) transpile(&self, PathBuf& parent_dir, MistConfig mut& config, String mut& main_mods) { if !self.path.is_dir() && self.path.extension().and_then((v) => v.to_str()) != Some("mist") { let file_name = self.path.file_name().and_then((s) => s.to_str()).expect("Failed to get file name"); @@ -93,7 +95,7 @@ impl modules::Module let mut child_changed = false; for child in &self.children { if !config.should_ignore(&child.path) { - let out = child.transpile(&dir, config); + let out = child.transpile(&dir, config, main_mods); if out.0 { child_changed = true; } @@ -101,9 +103,9 @@ impl modules::Module } } if self.path == config.package { - config.main_mods = output.clone(); + *main_mods = output.clone(); } else if config.should_ignore(&self.path) { - output.push_str(&config.main_mods) + output.push_str(main_mods) } self.write_transpile(output, parent_dir, child_changed) } From c1d7152992fcaf3f0f77dedb038da14a9c654372 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 30 Jun 2026 18:30:19 +0200 Subject: [PATCH 4/7] Deduped config --- Cargo.lock | 1 + crates/analyzer/Cargo.toml | 1 + crates/analyzer/src/lib.rs | 14 +++----------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5621ceb..5dd1e46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,6 +481,7 @@ dependencies = [ "cargo_metadata", "dashmap 6.1.0", "heck", + "mist-api", "mist-codegen", "mist-parser", "pest", diff --git a/crates/analyzer/Cargo.toml b/crates/analyzer/Cargo.toml index 9516385..7f6449c 100644 --- a/crates/analyzer/Cargo.toml +++ b/crates/analyzer/Cargo.toml @@ -9,6 +9,7 @@ keywords = ["language-server", "language", "mist"] categories = ["compilers"] [dependencies] +mist-api = { workspace = true } mist-parser = { workspace = true } mist-codegen = { workspace = true } pest = "2.8.6" diff --git a/crates/analyzer/src/lib.rs b/crates/analyzer/src/lib.rs index 5c7a025..815ea48 100644 --- a/crates/analyzer/src/lib.rs +++ b/crates/analyzer/src/lib.rs @@ -7,11 +7,12 @@ use std::sync::Arc; use std::sync::atomic::{AtomicU64, Ordering}; use heck::ToSnakeCase; +use mist_api::transpiler::MistConfig; use mist_parser::MistFmtConfig; use mist_parser::error::ParseError; use mist_parser::rev_mapper::{Mapping, MistMap, RustMap}; use ropey::Rope; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use serde_json::Value; use tokio::sync::Mutex; use tower_lsp::lsp_types::{self, *}; @@ -1890,13 +1891,6 @@ fn read_mist_package(workspace_root: &Path) -> String { .unwrap_or_else(|| "main.mist".to_string()) } -#[derive(Debug, Serialize, Deserialize)] -pub struct MistConfig { - pub package: PathBuf, - pub packages: Vec, - pub fmt: Option, -} - fn read_mist_fmt(workspace_root: &Path) -> Option { let toml_path = workspace_root.join("Mist.toml"); let content = match std::fs::read_to_string(&toml_path) { @@ -1908,9 +1902,7 @@ fn read_mist_fmt(workspace_root: &Path) -> Option { } }; - toml::from_str::(&content) - .ok() - .and_then(|v| v.fmt) + toml::from_str::(&content).ok().map(|v| v.fmt) } fn compute_mod_decls( From 4e1bcf36fce6d5be6a6cc8b10173baf90a1ae809 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 30 Jun 2026 19:14:12 +0200 Subject: [PATCH 5/7] Include sub crates #86 --- crates/mist-api/src/transpiler.mist | 30 ++++++++++++++++++++++++++--- src/main.mist | 6 ++++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index 0a10669..1001f91 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -26,6 +26,7 @@ pub struct MistConfig { pub PathBuf package, pub Vec packages, + pub Vec include, pub mist_parser::MistFmtConfig fmt, } @@ -52,6 +53,7 @@ impl Default for MistConfig Self { package: PathBuf::from("main.mist"), packages: Vec::new(), + include: Vec::new(), fmt: mist_parser::MistFmtConfig { allman_bracket_style: true, }, @@ -59,10 +61,9 @@ impl Default for MistConfig } } -pub PathBuf transpile() +pub void transpile(Path& root) { let start = time::Instant::now(); - let root = env::current_dir().expect("Unable to find project root"); let toml_path = root.join("Mist.toml"); let src = root.join("src"); let out = root.join(".mist/src"); @@ -77,7 +78,30 @@ pub PathBuf transpile() let elapsed = start.elapsed(); println!("\x1b[32m\nTranspile successful\x1b[0m in \x1b[34m{:.2?}\x1b[0m", elapsed); - root + for incl in &config.include { + if ends_with_star(&incl) { + let sub_crates = incl.parent().expect("Failed to get parent"); + let entries = match fs::read_dir(sub_crates) { + Ok(entries) => entries, + Err(e) => { + eprintln!("error: failed to read directory {}\n {}", sub_crates.display(), e); + process::exit(1); + } + }; + for sub_crate in entries { + if let Ok(sub_crate) = sub_crate { + transpile(&sub_crate.path()); + } + } + } else { + transpile(incl); + } + } +} + +bool ends_with_star(Path& path) +{ + path.file_name().and_then((name) => name.to_str()).is_some_and((name) => name.ends_with("*")) } impl modules::Module diff --git a/src/main.mist b/src/main.mist index 3994119..3f28e86 100644 --- a/src/main.mist +++ b/src/main.mist @@ -17,7 +17,8 @@ void main() } match args[1].as_str() { "run" | "build" | "check" | "r" | "b" | "c" => { - let root = transpiler::transpile(); + let root = env::current_dir().expect("Unable to find project root"); + transpiler::transpile(&root); println!(""); if builder::build(args, root) { println!("\x1b[32m\nBuild successful\x1b[0m"); @@ -26,7 +27,8 @@ void main() } } "transpile" | "t" => { - transpiler::transpile(); + let root = env::current_dir().expect("Unable to find project root"); + transpiler::transpile(&root); } "init" => { init_project(args); From 310ff6b53c01051c189ffaf1ca73121838c285a3 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 30 Jun 2026 19:28:27 +0200 Subject: [PATCH 6/7] Fix include non mist crates #86 --- crates/mist-api/src/transpiler.mist | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index 1001f91..4359691 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -63,6 +63,7 @@ impl Default for MistConfig pub void transpile(Path& root) { + println!("\x1b[32m\nTranspiling\x1b[0m \x1b[94m{:?}\x1b[0m", root.file_name().unwrap_or_default()); let start = time::Instant::now(); let toml_path = root.join("Mist.toml"); let src = root.join("src"); @@ -76,8 +77,7 @@ pub void transpile(Path& root) let master = modules::Module::new(config.package.clone(), &config); master.transpile(&out, &mut config, &mut String::new()); let elapsed = start.elapsed(); - println!("\x1b[32m\nTranspile successful\x1b[0m in \x1b[34m{:.2?}\x1b[0m", - elapsed); + println!("\x1b[32m\nTranspile successful\x1b[0m in \x1b[34m{:.2?}\x1b[0m", elapsed); for incl in &config.include { if ends_with_star(&incl) { let sub_crates = incl.parent().expect("Failed to get parent"); @@ -90,7 +90,10 @@ pub void transpile(Path& root) }; for sub_crate in entries { if let Ok(sub_crate) = sub_crate { - transpile(&sub_crate.path()); + let sub_crate = sub_crate.path(); + if fs::exists(sub_crate.join("Mist.toml")).unwrap_or_default() { + transpile(&sub_crate); + } } } } else { From a3da192b9ad98567b4cc90c5a8e47dddb5c046e6 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 30 Jun 2026 19:41:34 +0200 Subject: [PATCH 7/7] Analyzer Sub crate support --- crates/analyzer/src/lib.rs | 134 +++++++++++++++++++++++++++++-------- 1 file changed, 107 insertions(+), 27 deletions(-) diff --git a/crates/analyzer/src/lib.rs b/crates/analyzer/src/lib.rs index 815ea48..3c31a01 100644 --- a/crates/analyzer/src/lib.rs +++ b/crates/analyzer/src/lib.rs @@ -522,8 +522,9 @@ impl Backend { Some(p) => p.clone(), None => return String::new(), }; - let src_root = ws.join("src"); - let package_mist = read_mist_package(&ws); + let crate_root = find_crate_root(mist_path).unwrap_or(ws); + let src_root = crate_root.join("src"); + let package_mist = read_mist_package(&crate_root); let docs = self.documents.lock().await; let mod_decls = compute_mod_decls(&docs, &src_root, &package_mist); mod_decls.get(mist_path).cloned().unwrap_or_default() @@ -600,10 +601,8 @@ impl Backend { Some(p) => p.clone(), None => return, }; - let src_root = ws.join("src"); - let package_mist = read_mist_package(&ws); - self.ensure_implicit_packages(&src_root).await; + self.ensure_implicit_packages().await; // Collect sources first to avoid holding locks during transpile let sources: Vec<(PathBuf, String, String)> = { @@ -611,12 +610,21 @@ impl Backend { if docs.is_empty() { return; } - let mod_decls = compute_mod_decls(&docs, &src_root, &package_mist); - mod_decls - .into_iter() - .filter(|(_, decl)| !decl.is_empty()) - .filter_map(|(path, decl)| docs.get(&path).map(|r| (path, r.to_string(), decl))) - .collect() + let crate_roots = collect_crate_roots(&docs, &ws); + let mut result = Vec::new(); + for crate_root in &crate_roots { + let src_root = crate_root.join("src"); + let package_mist = read_mist_package(crate_root); + let mod_decls = compute_mod_decls(&docs, &src_root, &package_mist); + for (path, decl) in mod_decls { + if !decl.is_empty() { + if let Some(source) = docs.get(&path) { + result.push((path.clone(), source.to_string(), decl)); + } + } + } + } + result }; for (mist_path, source, decl) in sources { @@ -633,9 +641,17 @@ impl Backend { /// Create synthetic `package.mist` documents for subdirectories that contain /// .mist files but no real package.mist (implicit packages). - async fn ensure_implicit_packages(&self, src_root: &Path) { + async fn ensure_implicit_packages(&self) { + let ws = match &*self.workspace_folder.lock().await { + Some(p) => p.clone(), + None => return, + }; let mut docs = self.documents.lock().await; - ensure_implicit_packages_impl(&mut *docs, src_root); + let crate_roots = collect_crate_roots(&docs, &ws); + for crate_root in &crate_roots { + let src_root = crate_root.join("src"); + ensure_implicit_packages_impl(&mut *docs, &src_root); + } } /// Write a transpiled file's `.rs` content and `.map.json` mapping to disk. @@ -765,8 +781,6 @@ impl LanguageServer for Backend { tokio::spawn(async move { if let Some(root) = &*ws.lock().await { - let src_root = root.join("src"); - // Hold the lock across both initialize + initialized so the // editor's didOpen cannot race in-between and send notifications // to rust-analyzer before it has received Initialized. @@ -783,7 +797,21 @@ impl LanguageServer for Backend { } let mut files = Vec::new(); - collect_mist_files(&src_root, &mut files); + collect_mist_files(&root.join("src"), &mut files); + + // Discover subcrates by scanning for Mist.toml in immediate + // subdirectories of the workspace root. + let mut subcrate_roots = Vec::new(); + if let Ok(entries) = std::fs::read_dir(root) { + for entry in entries.flatten() { + let entry_path = entry.path(); + if entry_path.is_dir() && entry_path.join("Mist.toml").is_file() { + let sub_src = entry_path.join("src"); + collect_mist_files(&sub_src, &mut files); + subcrate_roots.push(entry_path); + } + } + } for file in &files { if let Ok(text) = std::fs::read_to_string(file) { @@ -796,15 +824,32 @@ impl LanguageServer for Backend { eprintln!("Loaded {} mist files", files.len()); - ensure_implicit_packages_impl(&mut *documents.lock().await, &src_root); + // Create implicit packages per crate root + { + let mut docs = documents.lock().await; + let all_crate_roots: Vec = std::iter::once(root.clone()) + .chain(subcrate_roots.iter().cloned()) + .collect(); + for crate_root in &all_crate_roots { + ensure_implicit_packages_impl(&mut *docs, &crate_root.join("src")); + } + } - // Compute mod_decls before any transpile so the root file gets its - // child module declarations from the very first didOpen. - let package_mist = read_mist_package(root); - let src_root = root.join("src"); + // Compute mod_decls per crate root before any transpile so each + // crate's root file gets its child module declarations. + let all_crate_roots: Vec = std::iter::once(root.clone()) + .chain(subcrate_roots.iter().cloned()) + .collect(); let mod_decls = { let docs = documents.lock().await; - compute_mod_decls(&docs, &src_root, &package_mist) + let mut merged = HashMap::new(); + for crate_root in &all_crate_roots { + let cr_src = crate_root.join("src"); + let pkg = read_mist_package(crate_root); + let decls = compute_mod_decls(&docs, &cr_src, &pkg); + merged.extend(decls); + } + merged }; for file in &files { @@ -854,14 +899,22 @@ impl LanguageServer for Backend { .cloned() .collect(); for syn_path in &synthetic_paths { - let decl = mod_decls.get(syn_path).map(String::as_str).unwrap_or(""); + // Find the crate root for this synthetic file + let syn_crate_root = find_crate_root(syn_path).unwrap_or_else(|| root.clone()); + let pkg = read_mist_package(&syn_crate_root); + let cr_src = syn_crate_root.join("src"); + let decl = { + let docs = documents.lock().await; + let crate_decls = compute_mod_decls(&docs, &cr_src, &pkg); + crate_decls.get(syn_path).cloned().unwrap_or_default() + }; let source = documents .lock() .await .get(syn_path) .map(|r| r.to_string()) .unwrap_or_default(); - if let Ok(transpiled) = transpile_mist(syn_path, &source, decl) { + if let Ok(transpiled) = transpile_mist(syn_path, &source, &decl) { if let Some(rust_uri) = clean_lsp_url(&transpiled.rust_path) { let _ = ra .lock() @@ -984,7 +1037,8 @@ impl LanguageServer for Backend { .await .insert(mist_path.clone(), Rope::from_str(&source)); - self.handle_transpile_and_notify(&mist_path, &source, "") + let extra_decl = self.compute_mod_decl_for_file(&mist_path).await; + self.handle_transpile_and_notify(&mist_path, &source, &extra_decl) .await; self.rebuild_module_tree(false).await; } @@ -1006,7 +1060,8 @@ impl LanguageServer for Backend { .await .insert(mist_path.clone(), Rope::from_str(&source)); - self.handle_transpile_and_notify(&mist_path, &source, "") + let extra_decl = self.compute_mod_decl_for_file(&mist_path).await; + self.handle_transpile_and_notify(&mist_path, &source, &extra_decl) .await; self.rebuild_module_tree(false).await; } @@ -1034,8 +1089,9 @@ impl LanguageServer for Backend { .await .insert(mist_path.clone(), Rope::from_str(&text)); + let extra_decl = self.compute_mod_decl_for_file(&mist_path).await; if let Some(transpiled) = self - .handle_transpile_and_notify(&mist_path, &text, "") + .handle_transpile_and_notify(&mist_path, &text, &extra_decl) .await { Self::write_transpiled_to_disk(&transpiled); @@ -1905,6 +1961,30 @@ fn read_mist_fmt(workspace_root: &Path) -> Option { toml::from_str::(&content).ok().map(|v| v.fmt) } +fn find_crate_root(file_path: &Path) -> Option { + let mut dir = file_path.parent()?; + loop { + if dir.join("Mist.toml").is_file() { + return Some(dir.to_path_buf()); + } + dir = match dir.parent() { + Some(p) => p, + None => return None, + }; + } +} + +fn collect_crate_roots(docs: &HashMap, workspace_root: &Path) -> HashSet { + let mut roots = HashSet::new(); + roots.insert(workspace_root.to_path_buf()); + for path in docs.keys() { + if let Some(crate_root) = find_crate_root(path) { + roots.insert(crate_root); + } + } + roots +} + fn compute_mod_decls( documents: &HashMap, src_root: &Path,