git-ents.gitmain
⌘K
foforge
commit 7c2c4c9
lens: return unreadable comment refs from the shared listings

list_projected and list_for_document now report what they cannot read back, as list_all already does (lens.parity: no caller may drop them silently). git ents comment list prints a trailing note per unreadable ref; porcelain stays rows-only for format stability; the lens drops them deliberately and says so, since they name no document.

Assisted-by: Claude:claude-fable-5

Joseph D. Carpinelli · 1 month ago

Reviews

No reviews of this commit yet — record a verdict below.

Start a review

verdict

crates/cli/ents-lens/src/lens.rs @@ -127,14 +127,21 @@ state: Some("open".to_owned()), context: None, }; - Ok(comment::list_for_document( + let (rows, unreadable) = comment::list_for_document( self.refs.as_ref(), &self.objects, &self.path, &rel, buffer, &filter, - )?) + )?; + // An unreadable comment ref names no document, so it has no lens, + // diagnostic, or hover to appear in -- the web listing's + // disclosure and `git ents comment list`'s trailing note are the + // surfaces that report it; here it is dropped deliberately, not + // silently (the library returns it either way, `lens.parity`). + let _ = unreadable; + Ok(rows) } /// The code lenses for `uri` (`lens.lenses`): three per open comment
crates/cli/git-ents/src/exe.rs @@ -215,8 +215,11 @@ } }; let filter = ents_forge::comment::ListFilter { state, context }; - let rows = commands::comment::list_projected(&root, worktree, &filter)?; + let (rows, unreadable) = commands::comment::list_projected(&root, worktree, &filter)?; if porcelain { + // Porcelain stays rows-only for format stability; a tool + // that wants the unreadable refs takes them from + // `comment::list_projected` itself. let _ = write!(out, "{}", commands::comment::porcelain(&rows)); } else { for row in rows { @@ -228,6 +231,9 @@ row.comment.body ); } + for entry in unreadable { + let _ = writeln!(out, "! {}\tunreadable: {}", entry.refname, entry.error); + } } } CommentAction::Add {
crates/cli/git-ents/tests/comment.rs @@ -113,7 +113,7 @@ state: Some("open".to_owned()), context: None, }; - let rows = comment::list_projected(&root, true, &open).expect("lists"); + let (rows, _unreadable) = comment::list_projected(&root, true, &open).expect("lists"); let rendered = comment::porcelain(&rows); let expected = format!( "{id} open current file.txt:5-5\ncontext issues/42\n\tthis new line looks wrong\n\t\n\tsecond paragraph\n" @@ -123,9 +123,10 @@ // Resolve through the CLI surface; the open listing no longer shows // it, the unfiltered one shows it resolved. comment::set_state(&root, &id, true, Some(fixture.key_path.clone())).expect("resolves"); - let rows = comment::list_projected(&root, true, &open).expect("lists"); + let (rows, _unreadable) = comment::list_projected(&root, true, &open).expect("lists"); assert!(rows.is_empty(), "a resolved comment is not open"); - let all = comment::list_projected(&root, true, &ListFilter::default()).expect("lists"); + let (all, _unreadable) = + comment::list_projected(&root, true, &ListFilter::default()).expect("lists"); assert_eq!(all.len(), 1); assert_eq!(all[0].comment.state, "resolved"); } @@ -149,7 +150,8 @@ ) .expect("replies"); - let rows = comment::list_projected(&root, false, &ListFilter::default()).expect("lists"); + let (rows, _unreadable) = + comment::list_projected(&root, false, &ListFilter::default()).expect("lists"); let rendered = comment::porcelain(&rows); let records: Vec<&str> = rendered.split("\n\n").collect(); assert_eq!(records.len(), 2);
crates/forge/ents-forge/tests/conversations.rs @@ -320,7 +320,7 @@ .collect(); std::fs::write(fixture.path().join("file.txt"), dirty).unwrap(); - let open_only = comment::list_projected( + let (open_only, _unreadable) = comment::list_projected( &fixture.refs, &fixture.objects, fixture.path(), @@ -341,7 +341,7 @@ }) ); - let by_context = comment::list_projected( + let (by_context, _unreadable) = comment::list_projected( &fixture.refs, &fixture.objects, fixture.path(),
crates/cli/git-ents/src/commands/comment.rs @@ -26,7 +26,9 @@ /// `git ents comment list [--worktree] [--state ...] [--context ...]`: /// matching comments with each anchor projected onto the working tree -/// (with `worktree`) or `HEAD`. +/// (with `worktree`) or `HEAD`, plus the refs whose stored tree this +/// build could not read back (reported after the listing, never +/// silently dropped). /// /// # Errors /// @@ -35,7 +37,7 @@ root: &LocalRoot, worktree: bool, filter: &ListFilter, -) -> Result<Vec<Listed>> { +) -> Result<(Vec<Listed>, Vec<ents_forge::Unreadable>)> { Ok(comment::list_projected( &root.refs, &root.objects,
crates/forge/ents-forge/src/comment/command.rs @@ -161,7 +161,10 @@ /// every matching comment, each anchor projected onto the working tree /// (`anchor.working-tree`) when `worktree` is set, onto `HEAD` otherwise — /// the listing `lens.parity` requires to be one library call shared by the -/// CLI's machine-readable form, the web UI, and the editor lens. +/// CLI's machine-readable form, the web UI, and the editor lens. Refs +/// whose stored tree this build cannot read back come back as the second +/// element, exactly as [`list_all`] reports them, so no caller can drop +/// them silently. /// /// # Errors /// @@ -174,10 +177,11 @@ repo_path: &std::path::Path, worktree: bool, filter: &ListFilter, -) -> Result<Vec<Listed>> { +) -> Result<(Vec<Listed>, Vec<crate::Unreadable>)> { let repo = gix::open(repo_path)?; let mut out = Vec::new(); - for (id, comment) in list(refs, objects)? { + let (all, unreadable) = list_all(refs, objects)?; + for (id, comment) in all { if let Some(state) = &filter.state && comment.state != *state { @@ -207,7 +211,7 @@ projection, }); } - Ok(out) + Ok((out, unreadable)) } /// What `git ents comment add` writes, before the mechanism-side @@ -549,10 +553,11 @@ target_path: &str, buffer: Option<&[u8]>, filter: &ListFilter, -) -> Result<Vec<Listed>> { +) -> Result<(Vec<Listed>, Vec<crate::Unreadable>)> { let repo = gix::open(repo_path)?; let mut out = Vec::new(); - for (id, comment) in list(refs, objects)? { + let (all, unreadable) = list_all(refs, objects)?; + for (id, comment) in all { if let Some(state) = &filter.state && comment.state != *state { @@ -578,7 +583,7 @@ projection: Some(projection), }); } - Ok(out) + Ok((out, unreadable)) } /// The thread rooted at `root_id` (`model.comment-thread`): the comment