Integrate diagnostic decorations and current-source Text input boundaries

This commit is contained in:
2026-09-13 04:47:30 -07:00
parent 16904398db
commit 92ff99eb54
98 changed files with 35959 additions and 208 deletions
+280
View File
@@ -3436,6 +3436,17 @@ impl App {
if version == self.source_version {
match result {
Ok(disk) => {
// Discovery must include the exact overlays retained
// by this workspace. A current request version alone
// does not prove arbitrary reply spans match them.
if disk.bundle.entry != self.source_workspace.entry
|| self.source_workspace.files.iter().any(|(path, buffer)| {
disk.bundle.files.get(path) != Some(&buffer.text)
})
{
self.status = "Included-file results no longer match the retained drafts. Load includes again.".into();
return self.request_lint();
}
if let Some(target) = recovery_target(&disk) {
self.pending_recovery = Some(target);
}
@@ -5340,6 +5351,275 @@ mod source_project_tests {
);
assert!(!app.dirty());
}
fn open_missing_function_file(directory: &Directory) -> App {
let entry = format!(
"arcadia {}; project p \"P\" {{ include \"work.arcadia\"; }}\n",
oa_core::SCHEMA_VERSION
);
let path = directory.0.join("project.arcadia");
std::fs::write(&path, entry).unwrap();
let mut app = App::with_project(Project::demo());
send(
&mut app,
Message::Loaded(Ok(Some(read_snapshot(path).unwrap()))),
);
assert!(!app.source_workspace.applied);
assert!(!app.source_workspace.files.contains_key("work.arcadia"));
app
}
fn assert_raw_second_warning(app: &App, raw: &str) {
let issue = app
.source_diagnostics
.iter()
.find(|issue| {
issue.path == "work.arcadia"
&& issue.diagnostic.code == "unallocated_leaf"
&& issue.diagnostic.message.contains("Second")
})
.unwrap();
let span = issue.diagnostic.span;
assert_eq!(span.start, raw.find("sa_function second").unwrap());
assert_eq!(
&raw[span.start..span.end],
"sa_function second \"Second\" {}"
);
assert_eq!(
app.text_workspace
.diagnostic_publication()
.unwrap()
.source(),
app.source_key()
);
}
#[test]
fn includes_loaded_diagnostics_use_raw_newly_discovered_bytes() {
let directory = Directory::new();
let mut app = open_missing_function_file(&directory);
let model = app.document.snapshot();
let publication = app.text_workspace.diagnostic_publication();
let raw = "sa_function first \"First\" {}\nsa_function second \"Second\" {}\n";
std::fs::write(directory.0.join("work.arcadia"), raw).unwrap();
let disk = source_files::discover_missing(
app.disk_project.as_ref().unwrap(),
&app.source_workspace.bundle().files,
LIMITS,
)
.unwrap();
// Real producer control: compiled diagnostics would refer to inserted
// IDs, while IncludesLoaded must leave the raw draft unapplied.
let compiled = source_project::compile_bundle(&disk.reachable_bundle(), LIMITS).unwrap();
assert_ne!(compiled.bundle, disk.bundle);
let version = app.source_version;
send(&mut app, Message::IncludesLoaded(version, Ok(disk)));
assert_raw_second_warning(&app, raw);
assert_eq!(app.source_workspace.files["work.arcadia"].text, raw);
assert!(!app.source_workspace.applied);
assert!(Arc::ptr_eq(&model, &app.document.snapshot()));
assert_ne!(app.text_workspace.diagnostic_publication(), publication);
assert_eq!(
std::fs::read_to_string(directory.0.join("work.arcadia")).unwrap(),
raw
);
}
#[test]
fn includes_loaded_diagnostics_use_retained_overlay_and_ignore_old_version() {
let directory = Directory::new();
let mut app = open_missing_function_file(&directory);
let raw = "sa_function first \"First\" {}\nsa_function second \"Second\" {}\n";
std::fs::write(directory.0.join("work.arcadia"), raw).unwrap();
let first = source_files::discover_missing(
app.disk_project.as_ref().unwrap(),
&app.source_workspace.bundle().files,
LIMITS,
)
.unwrap();
let version = app.source_version;
send(&mut app, Message::IncludesLoaded(version, Ok(first)));
let model = app.document.snapshot();
let previous = source_files::discover_missing(
app.disk_project.as_ref().unwrap(),
&app.source_workspace.bundle().files,
LIMITS,
)
.unwrap();
let previous_version = app.source_version;
send(&mut app, Message::SourceFile("work.arcadia".into()));
let edited = format!("// Retained Unicode draft: 航海\n{raw}");
edit(&mut app, &edited);
let current = source_workspace::lint(&app.source_workspace.bundle());
app.publish_source_diagnostics(current);
let publication = app.text_workspace.diagnostic_publication();
let key = app.source_key();
send(
&mut app,
Message::IncludesLoaded(previous_version, Ok(previous)),
);
assert_eq!(app.source_key(), key);
assert_eq!(app.text_workspace.diagnostic_publication(), publication);
assert_raw_second_warning(&app, &edited);
std::fs::write(directory.0.join("work.arcadia"), "// external disk edit\n").unwrap();
let discovered = source_files::discover_missing(
app.disk_project.as_ref().unwrap(),
&app.source_workspace.bundle().files,
LIMITS,
)
.unwrap();
assert_eq!(discovered.bundle.files["work.arcadia"], edited);
let version = app.source_version;
send(&mut app, Message::IncludesLoaded(version, Ok(discovered)));
assert_raw_second_warning(&app, &edited);
assert_ne!(app.text_workspace.diagnostic_publication(), publication);
assert_eq!(app.source_editor.text(), edited);
assert_eq!(app.source_workspace.files["work.arcadia"].text, edited);
assert!(Arc::ptr_eq(&model, &app.document.snapshot()));
assert_eq!(
std::fs::read_to_string(directory.0.join("work.arcadia")).unwrap(),
"// external disk edit\n"
);
}
#[test]
fn includes_loaded_rejects_mismatched_retained_bytes_before_publication() {
let directory = Directory::new();
let mut app = open_missing_function_file(&directory);
std::fs::write(
directory.0.join("work.arcadia"),
"sa_function first \"First\" {}\nsa_function second \"Second\" {}\n",
)
.unwrap();
// Discovery can also report a pending recovery target. A rejected
// reply must not install that metadata either.
std::fs::create_dir(directory.0.join(".arcadia-save-project.arcadia.journal")).unwrap();
let reply = source_files::discover_missing(
app.disk_project.as_ref().unwrap(),
&app.source_workspace.bundle().files,
LIMITS,
)
.unwrap();
assert!(recovery_target(&reply).is_some());
let edited = format!("// retained entry draft\n{}", app.source_editor.text());
edit(&mut app, &edited);
let current = source_workspace::lint(&app.source_workspace.bundle());
app.publish_source_diagnostics(current);
assert!(!app.source_diagnostics.is_empty());
let publication = app.text_workspace.diagnostic_publication();
let diagnostics = app.source_diagnostics.clone();
let before = app.source_workspace.bundle();
let key = app.source_key();
let reachable = app.source_workspace.reachable.clone();
let disk = app.disk_project.clone().unwrap();
let model = app.document.snapshot();
let recovery = app.pending_recovery.clone();
// Deliberately stamp an older real producer reply with the current
// version: this tests byte admission independently of version refusal.
let version = app.source_version;
send(&mut app, Message::IncludesLoaded(version, Ok(reply)));
assert_eq!(app.source_version, version);
assert_eq!(app.source_key(), key);
assert_eq!(app.source_workspace.bundle(), before);
assert_eq!(app.source_workspace.reachable, reachable);
assert!(!app.source_workspace.files.contains_key("work.arcadia"));
assert_eq!(app.source_editor.text(), edited);
assert_eq!(app.pending_recovery, recovery);
assert_eq!(app.text_workspace.diagnostic_publication(), publication);
assert_eq!(app.source_diagnostics, diagnostics);
let after = app.disk_project.as_ref().unwrap();
assert_eq!(after.root, disk.root);
assert_eq!(after.bundle, disk.bundle);
assert_eq!(after.expectations, disk.expectations);
assert_eq!(after.reachable, disk.reachable);
assert_eq!(after.diagnostics, disk.diagnostics);
assert!(Arc::ptr_eq(&model, &app.document.snapshot()));
}
#[test]
fn includes_loaded_requires_each_inactive_detached_overlay() {
let directory = Directory::new();
let mut app = open_missing_function_file(&directory);
let original = "sa_function first \"First\" {}\n";
std::fs::write(directory.0.join("work.arcadia"), original).unwrap();
let discovered = source_files::discover_missing(
app.disk_project.as_ref().unwrap(),
&app.source_workspace.bundle().files,
LIMITS,
)
.unwrap();
let version = app.source_version;
send(&mut app, Message::IncludesLoaded(version, Ok(discovered)));
send(&mut app, Message::SourceFile("work.arcadia".into()));
let retained = format!("// retained detached draft\n{original}");
edit(&mut app, &retained);
send(&mut app, Message::SourceFile("project.arcadia".into()));
let entry = format!(
"arcadia {}; project p \"P\" {{}}\n",
oa_core::SCHEMA_VERSION
);
edit(&mut app, &entry);
assert!(app.apply_source(), "{}", app.status);
assert_eq!(app.source_workspace.active, app.source_workspace.entry);
assert!(!app.source_workspace.reachable.contains("work.arcadia"));
assert_eq!(app.source_workspace.files["work.arcadia"].text, retained);
let valid = source_files::discover_missing(
app.disk_project.as_ref().unwrap(),
&app.source_workspace.bundle().files,
LIMITS,
)
.unwrap();
let before = app.source_workspace.bundle();
let key = app.source_key();
let publication = app.text_workspace.diagnostic_publication();
let diagnostics = app.source_diagnostics.clone();
let disk = app.disk_project.clone().unwrap();
let model = app.document.snapshot();
for missing in [false, true] {
let mut reply = valid.clone();
if missing {
reply.bundle.files.remove("work.arcadia");
} else {
reply
.bundle
.files
.insert("work.arcadia".into(), original.into());
}
// Entry and active text match exactly. Only an inactive, detached
// overlay is wrong or absent; checking active/entry alone would pass.
assert_eq!(
reply.bundle.files[&app.source_workspace.entry],
app.source_editor.text()
);
let version = app.source_version;
send(&mut app, Message::IncludesLoaded(version, Ok(reply)));
assert_eq!(app.source_version, version);
assert_eq!(app.source_key(), key);
assert_eq!(app.source_workspace.bundle(), before);
assert!(!app.source_workspace.reachable.contains("work.arcadia"));
assert_eq!(app.text_workspace.diagnostic_publication(), publication);
assert_eq!(app.source_diagnostics, diagnostics);
let after = app.disk_project.as_ref().unwrap();
assert_eq!(after.bundle, disk.bundle);
assert_eq!(after.expectations, disk.expectations);
assert_eq!(after.reachable, disk.reachable);
assert_eq!(after.diagnostics, disk.diagnostics);
assert!(Arc::ptr_eq(&model, &app.document.snapshot()));
}
// Exact overlays remain admissible even when the retained draft is
// detached and its external file still contains the original bytes.
let version = app.source_version;
send(&mut app, Message::IncludesLoaded(version, Ok(valid)));
assert_eq!(app.source_version, version + 1);
assert_ne!(app.text_workspace.diagnostic_publication(), publication);
assert_eq!(app.source_workspace.bundle(), before);
assert_eq!(app.source_workspace.files["work.arcadia"].text, retained);
assert_eq!(
std::fs::read_to_string(directory.0.join("work.arcadia")).unwrap(),
original
);
}
#[test]
fn closed_detached_file_keeps_first_save_conflict_then_can_be_reloaded_fresh() {
let directory = Directory::new();