Fixed module system
This commit is contained in:
+111
-1
@@ -346,6 +346,8 @@ impl Backend {
|
||||
let src_root = ws.join("src");
|
||||
let package_mist = read_mist_package(&ws);
|
||||
|
||||
self.ensure_implicit_packages(&src_root).await;
|
||||
|
||||
// Collect sources first to avoid holding locks during transpile
|
||||
let sources: Vec<(PathBuf, String, String)> = {
|
||||
let docs = self.documents.lock().await;
|
||||
@@ -366,6 +368,13 @@ 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) {
|
||||
let mut docs = self.documents.lock().await;
|
||||
ensure_implicit_packages_impl(&mut *docs, src_root);
|
||||
}
|
||||
|
||||
async fn publish_diagnostics(&self, uri: Url, diagnostics: Vec<Diagnostic>) {
|
||||
let key = uri.clone();
|
||||
let prev = self.previous_diagnostics.lock().await.get(&key).cloned();
|
||||
@@ -471,6 +480,8 @@ impl LanguageServer for Backend {
|
||||
|
||||
eprintln!("Loaded {} mist files", files.len());
|
||||
|
||||
ensure_implicit_packages_impl(&mut *documents.lock().await, &src_root);
|
||||
|
||||
// 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);
|
||||
@@ -510,6 +521,41 @@ impl LanguageServer for Backend {
|
||||
}
|
||||
}
|
||||
|
||||
// Also transpile synthetic package.mist files (implicit packages)
|
||||
// so rust-analyzer has their content from the start.
|
||||
let synthetic_paths: Vec<PathBuf> = documents
|
||||
.lock()
|
||||
.await
|
||||
.keys()
|
||||
.filter(|p| {
|
||||
!files.contains(p)
|
||||
&& p.file_name().and_then(|n| n.to_str()) == Some("package.mist")
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
for syn_path in &synthetic_paths {
|
||||
let decl = mod_decls.get(syn_path).map(String::as_str).unwrap_or("");
|
||||
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 Some(rust_uri) = clean_lsp_url(&transpiled.rust_path) {
|
||||
let _ = ra
|
||||
.lock()
|
||||
.await
|
||||
.did_open(rust_uri, &transpiled.rust_content)
|
||||
.await;
|
||||
mapping
|
||||
.lock()
|
||||
.await
|
||||
.insert(transpiled.rust_path, transpiled.mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(rx) = notification_rx.lock().await.take() {
|
||||
tokio::spawn(handle_ra_notifications(
|
||||
rx,
|
||||
@@ -1099,10 +1145,15 @@ fn compute_mod_decls(
|
||||
Some(s) => s,
|
||||
None => continue,
|
||||
};
|
||||
// Skip the root package file itself
|
||||
// Skip the root package entry file (e.g. main.mist)
|
||||
if *file == package_path {
|
||||
continue;
|
||||
}
|
||||
// Skip any subdirectory package.mist — it's the directory's own
|
||||
// entry file, not a sibling submodule.
|
||||
if stem == "package" {
|
||||
continue;
|
||||
}
|
||||
mod_decl.push_str(&format!("pub mod {};\n", stem));
|
||||
}
|
||||
|
||||
@@ -1118,7 +1169,66 @@ fn compute_mod_decls(
|
||||
}
|
||||
}
|
||||
|
||||
// For each subdirectory that is a Mist module, add pub mod <dirname>;
|
||||
// to the parent directory's declarations so the parent's transpiled Rust
|
||||
// output includes the child module declaration.
|
||||
for dir in dirs.keys() {
|
||||
if dir == src_root {
|
||||
continue;
|
||||
}
|
||||
let dirname = match dir.file_name().and_then(|n| n.to_str()) {
|
||||
Some(n) => n,
|
||||
None => continue,
|
||||
};
|
||||
let parent = dir.parent().unwrap();
|
||||
let parent_target = if parent == src_root {
|
||||
package_path.clone()
|
||||
} else {
|
||||
let pkg = parent.join("package.mist");
|
||||
if !documents.contains_key(&pkg) {
|
||||
continue;
|
||||
}
|
||||
pkg
|
||||
};
|
||||
result
|
||||
.entry(parent_target)
|
||||
.or_default()
|
||||
.push_str(&format!("pub mod {};\n", dirname));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn ensure_implicit_packages_impl(
|
||||
docs: &mut HashMap<PathBuf, Rope>,
|
||||
src_root: &Path,
|
||||
) {
|
||||
let mut dirs_with_mist: HashMap<PathBuf, Vec<PathBuf>> = HashMap::new();
|
||||
for mist_path in docs.keys() {
|
||||
if let Ok(rel) = mist_path.strip_prefix(src_root) {
|
||||
if let Some(parent) = rel.parent() {
|
||||
if !parent.as_os_str().is_empty() {
|
||||
dirs_with_mist
|
||||
.entry(src_root.join(parent))
|
||||
.or_default()
|
||||
.push(mist_path.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for dir in dirs_with_mist.keys() {
|
||||
let pkg_path = dir.join("package.mist");
|
||||
if !docs.contains_key(&pkg_path) {
|
||||
let dirname = dir
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("module")
|
||||
.to_string();
|
||||
let content = format!("pub module {dirname};\n");
|
||||
docs.insert(pkg_path, Rope::from_str(&content));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clean_completion_item(mut item: CompletionItem) -> CompletionItem {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use mist_codegen::{GetRust, RustCodegen};
|
||||
use mist_codegen::RustCodegen;
|
||||
use mist_parser::rev_mapper::Mapping;
|
||||
use mist_parser::{parse, parse_module};
|
||||
use mist_parser::parse;
|
||||
|
||||
pub struct TranspiledFile {
|
||||
pub mist_path: PathBuf,
|
||||
@@ -16,8 +16,13 @@ pub fn transpile_mist(
|
||||
source: &str,
|
||||
extra_mod_decl: &str,
|
||||
) -> Result<TranspiledFile, String> {
|
||||
let module_decl = parse_module(source).map_err(|e| format!("parse module error: {e:?}"))?;
|
||||
let rust_path = crate::from_mist_to_rust(mist_path.to_path_buf());
|
||||
let mut rust_path = crate::from_mist_to_rust(mist_path.to_path_buf());
|
||||
// Package files (package.mist) must output as <dir>/mod.rs so the Rust module
|
||||
// hierarchy resolves correctly (pub mod <child>; declarations look for sibling
|
||||
// .rs files, and the parent module declaration looks for <dir>/mod.rs).
|
||||
if mist_path.file_name().and_then(|n| n.to_str()) == Some("package.mist") {
|
||||
rust_path.set_file_name("mod.rs");
|
||||
}
|
||||
|
||||
let parsed = parse(source).map_err(|e| format!("parse error: {e:?}"))?;
|
||||
|
||||
@@ -29,19 +34,12 @@ pub fn transpile_mist(
|
||||
let mut codegen = RustCodegen::new(mist_path.to_path_buf());
|
||||
let output = codegen.generate(parsed);
|
||||
|
||||
let self_mod_prefix = module_decl
|
||||
.as_ref()
|
||||
.map(|(vis, name)| format!("{}mod {};\n", vis.get_rust(), name.get_rust()))
|
||||
.unwrap_or_default();
|
||||
|
||||
let combined_prefix = format!("{}{}", extra_mod_decl, self_mod_prefix);
|
||||
|
||||
codegen.mapping.shift_rust(combined_prefix.lines().count() as isize, 0);
|
||||
codegen.mapping.shift_rust(extra_mod_decl.lines().count() as isize, 0);
|
||||
|
||||
Ok(TranspiledFile {
|
||||
mist_path: mist_path.to_path_buf(),
|
||||
rust_path,
|
||||
rust_content: format!("{}{}", combined_prefix, output),
|
||||
rust_content: format!("{}{}", extra_mod_decl, output),
|
||||
mapping: codegen.mapping,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user