Fixed module system
This commit is contained in:
+111
-1
@@ -346,6 +346,8 @@ impl Backend {
|
|||||||
let src_root = ws.join("src");
|
let src_root = ws.join("src");
|
||||||
let package_mist = read_mist_package(&ws);
|
let package_mist = read_mist_package(&ws);
|
||||||
|
|
||||||
|
self.ensure_implicit_packages(&src_root).await;
|
||||||
|
|
||||||
// Collect sources first to avoid holding locks during transpile
|
// Collect sources first to avoid holding locks during transpile
|
||||||
let sources: Vec<(PathBuf, String, String)> = {
|
let sources: Vec<(PathBuf, String, String)> = {
|
||||||
let docs = self.documents.lock().await;
|
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>) {
|
async fn publish_diagnostics(&self, uri: Url, diagnostics: Vec<Diagnostic>) {
|
||||||
let key = uri.clone();
|
let key = uri.clone();
|
||||||
let prev = self.previous_diagnostics.lock().await.get(&key).cloned();
|
let prev = self.previous_diagnostics.lock().await.get(&key).cloned();
|
||||||
@@ -471,6 +480,8 @@ impl LanguageServer for Backend {
|
|||||||
|
|
||||||
eprintln!("Loaded {} mist files", files.len());
|
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
|
// Compute mod_decls before any transpile so the root file gets its
|
||||||
// child module declarations from the very first didOpen.
|
// child module declarations from the very first didOpen.
|
||||||
let package_mist = read_mist_package(root);
|
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() {
|
if let Some(rx) = notification_rx.lock().await.take() {
|
||||||
tokio::spawn(handle_ra_notifications(
|
tokio::spawn(handle_ra_notifications(
|
||||||
rx,
|
rx,
|
||||||
@@ -1099,10 +1145,15 @@ fn compute_mod_decls(
|
|||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
// Skip the root package file itself
|
// Skip the root package entry file (e.g. main.mist)
|
||||||
if *file == package_path {
|
if *file == package_path {
|
||||||
continue;
|
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));
|
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
|
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 {
|
fn clean_completion_item(mut item: CompletionItem) -> CompletionItem {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use mist_codegen::{GetRust, RustCodegen};
|
use mist_codegen::RustCodegen;
|
||||||
use mist_parser::rev_mapper::Mapping;
|
use mist_parser::rev_mapper::Mapping;
|
||||||
use mist_parser::{parse, parse_module};
|
use mist_parser::parse;
|
||||||
|
|
||||||
pub struct TranspiledFile {
|
pub struct TranspiledFile {
|
||||||
pub mist_path: PathBuf,
|
pub mist_path: PathBuf,
|
||||||
@@ -16,8 +16,13 @@ pub fn transpile_mist(
|
|||||||
source: &str,
|
source: &str,
|
||||||
extra_mod_decl: &str,
|
extra_mod_decl: &str,
|
||||||
) -> Result<TranspiledFile, String> {
|
) -> Result<TranspiledFile, String> {
|
||||||
let module_decl = parse_module(source).map_err(|e| format!("parse module error: {e:?}"))?;
|
let mut rust_path = crate::from_mist_to_rust(mist_path.to_path_buf());
|
||||||
let 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:?}"))?;
|
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 mut codegen = RustCodegen::new(mist_path.to_path_buf());
|
||||||
let output = codegen.generate(parsed);
|
let output = codegen.generate(parsed);
|
||||||
|
|
||||||
let self_mod_prefix = module_decl
|
codegen.mapping.shift_rust(extra_mod_decl.lines().count() as isize, 0);
|
||||||
.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);
|
|
||||||
|
|
||||||
Ok(TranspiledFile {
|
Ok(TranspiledFile {
|
||||||
mist_path: mist_path.to_path_buf(),
|
mist_path: mist_path.to_path_buf(),
|
||||||
rust_path,
|
rust_path,
|
||||||
rust_content: format!("{}{}", combined_prefix, output),
|
rust_content: format!("{}{}", extra_mod_decl, output),
|
||||||
mapping: codegen.mapping,
|
mapping: codegen.mapping,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user