diff --git a/crates/analyzer/src/lib.rs b/crates/analyzer/src/lib.rs index df5aba9..d37e889 100644 --- a/crates/analyzer/src/lib.rs +++ b/crates/analyzer/src/lib.rs @@ -21,7 +21,8 @@ use tower_lsp::{Client, LanguageServer, LspService, Server}; use crate::rust_analyzer::RustAnalyzer; use crate::transpiler::{ - TranspileError, TranspiledFile, format_mist, transpile_mist, transpile_mist_no_sem, + TranspileError, TranspiledFile, format_mist, resolve_rust_path, transpile_mist, + transpile_mist_no_sem, }; static MARKER_COUNTER: AtomicU64 = AtomicU64::new(0); @@ -392,10 +393,13 @@ impl Backend { // Use the last known working Rust content + mapping to inject the // marker at the nearest valid Rust position. let mist_target = MistMap(line as usize, character as usize); - let mut rust_path = crate::from_mist_to_rust(mist_path.to_path_buf()); - if mist_path.file_name().and_then(|n| n.to_str()) == Some("package.mist") { - rust_path.set_file_name("mod.rs"); - } + let source_for_path = self + .documents + .lock() + .await + .get(mist_path) + .map(|r| r.to_string()); + let rust_path = resolve_rust_path(mist_path, source_for_path.as_deref()); let (last_mapping, last_content, original_version) = { let map_guard = self.mapping.lock().await; @@ -617,9 +621,10 @@ impl Backend { .cloned() .collect(); for path in &stale { + let source_for_path = docs.get(path).map(|r| r.to_string()); docs.remove(path); if path.extension().and_then(|e| e.to_str()) == Some("mist") { - let rust_path = mist_to_rust_path(path); + let rust_path = resolve_rust_path(path, source_for_path.as_deref()); let _ = std::fs::remove_file(&rust_path); let map_path = rust_path.with_extension("map.json"); let _ = std::fs::remove_file(&map_path); @@ -1175,9 +1180,13 @@ impl LanguageServer for Backend { } }; + let source_for_path = { + let docs = self.documents.lock().await; + docs.get(&mist_path).map(|r| r.to_string()) + }; self.documents.lock().await.remove(&mist_path); - let rust_path = mist_to_rust_path(&mist_path); + let rust_path = resolve_rust_path(&mist_path, source_for_path.as_deref()); if let Some(rust_uri) = clean_lsp_url(&rust_path) { let _ = self.rust_analyzer.lock().await.did_close(rust_uri).await; diff --git a/crates/analyzer/src/transpiler.rs b/crates/analyzer/src/transpiler.rs index d771ccc..8b455c8 100644 --- a/crates/analyzer/src/transpiler.rs +++ b/crates/analyzer/src/transpiler.rs @@ -17,33 +17,43 @@ pub enum TranspileError<'a> { Semantic(Vec), } +/// Resolve the output `.rs` path for a `.mist` source file. +/// +/// Always checks for a `pub module x;` declaration via `parse_module` and +/// uses the declared module name as the filename when present. Falls back +/// to the literal file stem when parsing fails or the file has no module +/// declaration. +pub fn resolve_rust_path(mist_path: &Path, source: Option<&str>) -> PathBuf { + let mut rust_path = crate::from_mist_to_rust(mist_path.to_path_buf()); + // Package files (package.mist) must output as /mod.rs so the Rust + // module hierarchy resolves correctly. + if mist_path.file_name().and_then(|n| n.to_str()) == Some("package.mist") { + rust_path.set_file_name("mod.rs"); + } else if let Some(src) = source { + // Prioritize the module declaration from the source over the file stem. + // Every caller re-parses so we always pick up the current declaration. + if let Ok(Some((_, ref name))) = parse_module(src) { + if rust_path.file_name().map(|v| v.to_str()).unwrap_or_default() != Some("mod.rs") { + if let Some(ext) = rust_path.extension().map(|e| e.to_owned()) { + let mut new_name = std::ffi::OsString::from(&name.0); + new_name.push("."); + new_name.push(ext); + rust_path.set_file_name(new_name); + } else { + rust_path.set_file_name(&name.0); + } + } + } + } + rust_path +} + pub fn transpile_mist<'a>( mist_path: &Path, source: &'a str, extra_mod_decl: &str, ) -> Result> { - let mut rust_path = crate::from_mist_to_rust(mist_path.to_path_buf()); - // Package files (package.mist) must output as /mod.rs so the Rust module - // hierarchy resolves correctly (pub mod ; declarations look for sibling - // .rs files, and the parent module declaration looks for /mod.rs). - if mist_path.file_name().and_then(|n| n.to_str()) == Some("package.mist") { - rust_path.set_file_name("mod.rs"); - } - - // Prioritize the module declaration from the source over the file stem. - // Every transpile re-parses so we always pick up the current declaration. - if let Ok(Some((_, ref name))) = parse_module(source) { - if rust_path.file_name().map(|v| v.to_str()).unwrap_or_default() != Some("mod.rs") { - if let Some(ext) = rust_path.extension().map(|e| e.to_owned()) { - let mut new_name = std::ffi::OsString::from(&name.0); - new_name.push("."); - new_name.push(ext); - rust_path.set_file_name(new_name); - } else { - rust_path.set_file_name(&name.0); - } - } - } + let rust_path = resolve_rust_path(mist_path, Some(source)); let parsed = parse(source).map_err(TranspileError::Parse)?; @@ -73,28 +83,7 @@ pub fn transpile_mist_no_sem( source: &str, extra_mod_decl: &str, ) -> Result { - let mut rust_path = crate::from_mist_to_rust(mist_path.to_path_buf()); - // Package files (package.mist) must output as /mod.rs so the Rust module - // hierarchy resolves correctly (pub mod ; declarations look for sibling - // .rs files, and the parent module declaration looks for /mod.rs). - if mist_path.file_name().and_then(|n| n.to_str()) == Some("package.mist") { - rust_path.set_file_name("mod.rs"); - } - - // Prioritize the module declaration from the source over the file stem. - // Every transpile re-parses so we always pick up the current declaration. - if let Ok(Some((_, ref name))) = parse_module(source) { - if rust_path.file_name().map(|v| v.to_str()).unwrap_or_default() != Some("mod.rs") { - if let Some(ext) = rust_path.extension().map(|e| e.to_owned()) { - let mut new_name = std::ffi::OsString::from(&name.0); - new_name.push("."); - new_name.push(ext); - rust_path.set_file_name(new_name); - } else { - rust_path.set_file_name(&name.0); - } - } - } + let rust_path = resolve_rust_path(mist_path, Some(source)); let parsed = parse(source).map_err(|e| format!("parse error: {e:?}"))?;