Fixed bugs #83

This commit is contained in:
2026-06-24 14:11:57 +02:00
parent ca11866328
commit 1f2e989522
2 changed files with 157 additions and 35 deletions
+120 -30
View File
@@ -17,7 +17,7 @@ use tower_lsp::lsp_types::{self, *};
use tower_lsp::{Client, LanguageServer, LspService, Server}; use tower_lsp::{Client, LanguageServer, LspService, Server};
use crate::rust_analyzer::RustAnalyzer; use crate::rust_analyzer::RustAnalyzer;
use crate::transpiler::transpile_mist; use crate::transpiler::{transpile_mist, transpile_mist_no_sem};
static MARKER_COUNTER: AtomicU64 = AtomicU64::new(0); static MARKER_COUNTER: AtomicU64 = AtomicU64::new(0);
@@ -227,6 +227,16 @@ fn byte_offset_to_lsp_pos(source: &str, offset: usize) -> Position {
} }
} }
/// Holds saved state for restoring rust-analyzer content after a temporary
/// marker-driven request (goto, hover, completion). Only populated when
/// markered content was pushed to rust-analyzer and must be reverted.
struct MarkerRestore {
rust_uri: Url,
mist_path: PathBuf,
original_content: String,
original_version: i32,
}
/// Re-parse the source to extract a proper LSP diagnostic from the error. /// Re-parse the source to extract a proper LSP diagnostic from the error.
/// Returns None when the error is a semantic check failure (can be multiple /// Returns None when the error is a semantic check failure (can be multiple
/// errors) or when re-parsing unexpectedly succeeds. /// errors) or when re-parsing unexpectedly succeeds.
@@ -306,6 +316,10 @@ fn transpile_error_to_diagnostic(source: &str) -> Option<Diagnostic> {
impl Backend { impl Backend {
/// Resolve a Mist cursor position to a Rust position by injecting a unique marker /// Resolve a Mist cursor position to a Rust position by injecting a unique marker
/// into the source at the cursor, transpiling, and finding the marker in the output. /// into the source at the cursor, transpiling, and finding the marker in the output.
///
/// The third return element is an optional `MarkerRestore` that the caller MUST
/// pass to `restore_after_marker` **after** it finishes its rust-analyzer request.
/// This ensures markered content never pollutes the permanent rust-analyzer state.
async fn resolve_mist_via_marker( async fn resolve_mist_via_marker(
&self, &self,
mist_path: &Path, mist_path: &Path,
@@ -313,7 +327,7 @@ impl Backend {
line: u32, line: u32,
character: u32, character: u32,
extra_mod_decl: &str, extra_mod_decl: &str,
) -> Option<(Url, Position)> { ) -> Option<(Url, Position, Option<MarkerRestore>)> {
let id = MARKER_COUNTER.fetch_add(1, Ordering::Relaxed); let id = MARKER_COUNTER.fetch_add(1, Ordering::Relaxed);
let marker = format!("__mist_mk{id:x}__"); let marker = format!("__mist_mk{id:x}__");
@@ -325,7 +339,7 @@ impl Backend {
} }
}; };
let transpiled = match transpile_mist(mist_path, &modified, extra_mod_decl) { let transpiled = match transpile_mist_no_sem(mist_path, &modified, extra_mod_decl) {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
eprintln!("[marker] transpile failed for {marker} at LSP({line},{character}): {e}"); eprintln!("[marker] transpile failed for {marker} at LSP({line},{character}): {e}");
@@ -339,18 +353,18 @@ impl Backend {
rust_path.set_file_name("mod.rs"); rust_path.set_file_name("mod.rs");
} }
let (last_mapping, last_content) = { let (last_mapping, last_content, original_version) = {
let map_guard = self.mapping.lock().await; let map_guard = self.mapping.lock().await;
let content_guard = self.last_rust_contents.lock().await; let content_guard = self.last_rust_contents.lock().await;
let versions = self.doc_versions.lock().await;
let m = map_guard.get(&rust_path)?.clone(); let m = map_guard.get(&rust_path)?.clone();
let c = content_guard.get(mist_path)?.clone(); let c = content_guard.get(mist_path)?.clone();
(m, c) let v = versions.get(mist_path).copied().unwrap_or(0);
(m, c, v)
}; };
let (_rust_before, mist_before) = last_mapping.find_by_mist(&mist_target)?; let (_rust_before, mist_before) = last_mapping.find_by_mist(&mist_target)?;
// Find the next mapping entry after mist_before — the marker goes
// right before it in the Rust output, filling the gap.
let rust_next = last_mapping let rust_next = last_mapping
.map .map
.iter() .iter()
@@ -372,22 +386,30 @@ impl Backend {
let rust_uri = clean_lsp_url(&rust_path)?; let rust_uri = clean_lsp_url(&rust_path)?;
// Save original state, then push markered content temporarily.
{ {
let mut ra = self.rust_analyzer.lock().await; let mut ra = self.rust_analyzer.lock().await;
let mut versions = self.doc_versions.lock().await; let mut versions = self.doc_versions.lock().await;
let version = versions.entry(mist_path.to_path_buf()).or_insert(0); let temp_version = original_version + 1;
*version += 1; versions.insert(mist_path.to_path_buf(), temp_version);
let _ = ra let _ = ra
.did_change(rust_uri.clone(), &modified_rust, *version) .did_change(rust_uri.clone(), &modified_rust, temp_version)
.await; .await;
} }
let restore = MarkerRestore {
rust_uri: rust_uri.clone(),
mist_path: mist_path.to_path_buf(),
original_content: last_content,
original_version,
};
eprintln!( eprintln!(
"[marker] {marker} (fallback): mist LSP({line},{character}) -> rust LSP({},{})", "[marker] {marker} (fallback): mist LSP({line},{character}) -> rust LSP({},{})",
rust_pos.line, rust_pos.character rust_pos.line, rust_pos.character
); );
return Some((rust_uri, rust_pos)); return Some((rust_uri, rust_pos, Some(restore)));
} }
}; };
let rust_pos = match find_marker_position(&transpiled.rust_content, &marker) { let rust_pos = match find_marker_position(&transpiled.rust_content, &marker) {
@@ -413,20 +435,44 @@ impl Backend {
rust_pos.line, rust_pos.character rust_pos.line, rust_pos.character
); );
// If actual source can't be transpiled, rust-analyzer's file is stale. // If the actual source can't be transpiled, rust-analyzer's file is stale.
// Push the markered transpiled content so the upcoming LSP request (completion, // Push the markered transpiled content temporarily — the caller MUST restore.
// hover, goto_def) sees the right context (the marker acts as placeholder). let restore = if transpile_mist(mist_path, source, extra_mod_decl).is_err() {
if transpile_mist(mist_path, source, extra_mod_decl).is_err() { let original_content = self
.last_rust_contents
.lock()
.await
.get(mist_path)
.cloned()?;
let original_version = self
.doc_versions
.lock()
.await
.get(mist_path)
.copied()
.unwrap_or(0);
{
let mut ra = self.rust_analyzer.lock().await; let mut ra = self.rust_analyzer.lock().await;
let mut versions = self.doc_versions.lock().await; let mut versions = self.doc_versions.lock().await;
let version = versions.entry(mist_path.to_path_buf()).or_insert(0); let temp_version = original_version + 1;
*version += 1; versions.insert(mist_path.to_path_buf(), temp_version);
let _ = ra let _ = ra
.did_change(rust_uri.clone(), &transpiled.rust_content, *version) .did_change(rust_uri.clone(), &transpiled.rust_content, temp_version)
.await; .await;
} }
Some((rust_uri, rust_pos)) Some(MarkerRestore {
rust_uri: rust_uri.clone(),
mist_path: mist_path.to_path_buf(),
original_content,
original_version,
})
} else {
None
};
Some((rust_uri, rust_pos, restore))
} }
async fn compute_mod_decl_for_file(&self, mist_path: &Path) -> String { async fn compute_mod_decl_for_file(&self, mist_path: &Path) -> String {
@@ -571,6 +617,32 @@ impl Backend {
.await; .await;
} }
} }
/// Restore rust-analyzer content that was temporarily replaced by a marker.
/// Only restores if no intervening change has bumped the version further.
async fn restore_after_marker(&self, restore: MarkerRestore) {
let current_version = self
.doc_versions
.lock()
.await
.get(&restore.mist_path)
.copied()
.unwrap_or(0);
if current_version != restore.original_version + 1 {
// Something else changed the content in the meantime — don't
// clobber it with stale data.
return;
}
let mut ra = self.rust_analyzer.lock().await;
let mut versions = self.doc_versions.lock().await;
let new_version = current_version + 1;
versions.insert(restore.mist_path.clone(), new_version);
let _ = ra
.did_change(restore.rust_uri, &restore.original_content, new_version)
.await;
}
} }
#[tower_lsp::async_trait] #[tower_lsp::async_trait]
@@ -876,7 +948,7 @@ impl LanguageServer for Backend {
}; };
let mod_decl = self.compute_mod_decl_for_file(&mist_path).await; let mod_decl = self.compute_mod_decl_for_file(&mist_path).await;
let Some((rust_uri, rust_pos)) = self let Some((rust_uri, rust_pos, marker_restore)) = self
.resolve_mist_via_marker( .resolve_mist_via_marker(
&mist_path, &mist_path,
&source, &source,
@@ -907,12 +979,18 @@ impl LanguageServer for Backend {
context: params.context, context: params.context,
}; };
match self let completion_result = self
.rust_analyzer .rust_analyzer
.lock() .lock()
.await .await
.request::<lsp_types::request::Completion>(comp_params) .request::<lsp_types::request::Completion>(comp_params)
.await .await;
if let Some(restore) = marker_restore {
self.restore_after_marker(restore).await;
}
match completion_result
{ {
Ok(Some(CompletionResponse::Array(items))) => { Ok(Some(CompletionResponse::Array(items))) => {
let mut cleaned: Vec<CompletionItem> = let mut cleaned: Vec<CompletionItem> =
@@ -984,7 +1062,7 @@ impl LanguageServer for Backend {
}; };
let mod_decl = self.compute_mod_decl_for_file(&mist_path).await; let mod_decl = self.compute_mod_decl_for_file(&mist_path).await;
let Some((rust_uri, rust_pos)) = self let Some((rust_uri, rust_pos, marker_restore)) = self
.resolve_mist_via_marker( .resolve_mist_via_marker(
&mist_path, &mist_path,
&source, &source,
@@ -1020,13 +1098,20 @@ impl LanguageServer for Backend {
rust_uri, rust_pos.line, rust_pos.character rust_uri, rust_pos.line, rust_pos.character
); );
match self let result = self
.rust_analyzer .rust_analyzer
.lock() .lock()
.await .await
.request::<lsp_types::request::GotoDefinition>(gd_params) .request::<lsp_types::request::GotoDefinition>(gd_params)
.await .await;
{
// Restore original content before processing result — must happen
// even when the request fails to keep rust-analyzer state clean.
if let Some(restore) = marker_restore {
self.restore_after_marker(restore).await;
}
match result {
Ok(Some(GotoDefinitionResponse::Scalar(loc))) => { Ok(Some(GotoDefinitionResponse::Scalar(loc))) => {
eprintln!("[goto_definition] Scalar response"); eprintln!("[goto_definition] Scalar response");
match self.map_rust_to_mist_pos(&loc.uri, &loc.range.start).await { match self.map_rust_to_mist_pos(&loc.uri, &loc.range.start).await {
@@ -1153,7 +1238,7 @@ impl LanguageServer for Backend {
}; };
let mod_decl = self.compute_mod_decl_for_file(&mist_path).await; let mod_decl = self.compute_mod_decl_for_file(&mist_path).await;
let Some((rust_uri, rust_pos)) = self let Some((rust_uri, rust_pos, marker_restore)) = self
.resolve_mist_via_marker( .resolve_mist_via_marker(
&mist_path, &mist_path,
&source, &source,
@@ -1180,13 +1265,18 @@ impl LanguageServer for Backend {
}, },
}; };
match self let result = self
.rust_analyzer .rust_analyzer
.lock() .lock()
.await .await
.request::<lsp_types::request::HoverRequest>(h_params) .request::<lsp_types::request::HoverRequest>(h_params)
.await .await;
{
if let Some(restore) = marker_restore {
self.restore_after_marker(restore).await;
}
match result {
Ok(hover) => Ok(hover), Ok(hover) => Ok(hover),
Err(e) => { Err(e) => {
eprintln!("hover error: {e}"); eprintln!("hover error: {e}");
+32
View File
@@ -46,3 +46,35 @@ pub fn transpile_mist(
mapping: codegen.mapping, mapping: codegen.mapping,
}) })
} }
/// No Semantics
pub fn transpile_mist_no_sem(
mist_path: &Path,
source: &str,
extra_mod_decl: &str,
) -> Result<TranspiledFile, String> {
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:?}"))?;
let mut codegen = RustCodegen::new(mist_path.to_path_buf());
codegen.generate(parsed.mod_attributes);
codegen.add(extra_mod_decl);
let output = codegen.generate(parsed.items);
Ok(TranspiledFile {
mist_path: mist_path.to_path_buf(),
rust_path,
rust_content: output,
mapping: codegen.mapping,
})
}