Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions src/cli/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,11 @@ fn open_all_dbs(cli: &Cli) -> Result<Vec<(String, Database)>> {
let label = source_label_from_path(&path);
// Only add if not already present (avoid duplicates)
if !dbs.iter().any(|(l, _)| l == &label) {
dbs.push((label, Database::open(&path)?));
// Silently skip databases that fail to open (e.g., locked, corrupted)
// This prevents flakiness when tests run in parallel
if let Ok(db) = Database::open(&path) {
dbs.push((label, db));
}
}
}
}
Expand Down Expand Up @@ -1528,8 +1532,13 @@ fn handle_list(cli: &Cli, mode: &OutputMode, args: &ListArgs) -> Result<()> {
output::write_bookmarks(mode, &bookmarks, args.line_format.as_deref())?;
}
} else {
// Multi-database case with line number support
let mut all = Vec::new();
// Keep track of which database each bookmark belongs to
let mut db_map: std::collections::HashMap<String, &Database> =
std::collections::HashMap::new();
for (label, db) in &dbs {
db_map.insert(label.clone(), db);
let bookmarks = db.list_bookmarks(&filter)?;
for bm in bookmarks {
all.push((label.clone(), bm));
Expand All @@ -1539,7 +1548,26 @@ fn handle_list(cli: &Cli, mode: &OutputMode, args: &ListArgs) -> Result<()> {
.iter()
.map(|(label, bm)| output::AnnotatedBookmark { source: label, bookmark: bm })
.collect();
output::write_annotated_bookmarks(mode, &annotated, args.line_format.as_deref())?;

// Create a line fetcher that looks up the line from the correct database
let get_line_fn = |short_id: &str| -> Option<usize> {
// Find the bookmark with this short_id
for (label, bm) in &all {
if crate::cli::output::short_id(&bm.id) == short_id
&& let Some(db) = db_map.get(label)
{
return get_bookmark_line(db, &bm.id, &bm.file_path);
}
}
None
};

output::write_annotated_bookmarks(
mode,
&annotated,
args.line_format.as_deref(),
Some(&get_line_fn),
)?;
}
Ok(())
}
Expand Down Expand Up @@ -1713,7 +1741,12 @@ fn handle_search(cli: &Cli, mode: &OutputMode, args: &SearchArgs) -> Result<()>
.iter()
.map(|(label, bm)| output::AnnotatedBookmark { source: label, bookmark: bm })
.collect();
output::write_annotated_bookmarks(mode, &annotated, None)?;
output::write_annotated_bookmarks(
mode,
&annotated,
None,
None as Option<&fn(&str) -> Option<usize>>,
)?;
}
Ok(())
}
Expand Down
30 changes: 23 additions & 7 deletions src/cli/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,15 @@ pub struct AnnotatedBookmark<'a> {
}

/// Write annotated bookmarks using a line format template.
fn write_annotated_line_format(bookmarks: &[AnnotatedBookmark], template: &str) -> io::Result<()> {
/// Accepts an optional function to fetch line numbers for each bookmark.
fn write_annotated_line_format<F>(
bookmarks: &[AnnotatedBookmark],
template: &str,
get_line_fn: Option<&F>,
) -> io::Result<()>
where
F: Fn(&str) -> Option<usize>,
{
let mut stdout = io::stdout().lock();
for ab in bookmarks {
let bm = ab.bookmark;
Expand All @@ -386,14 +394,16 @@ fn write_annotated_line_format(bookmarks: &[AnnotatedBookmark], template: &str)
let tags = bm.tags.iter().map(|t| format!("#{t}")).collect::<Vec<_>>().join(" ");
let note = get_first_note(bm);
let context = get_first_context(bm);
let line =
if let Some(ref fn_line) = get_line_fn { fn_line(short).unwrap_or(0) } else { 0 };
let status = bm.status.to_string();

let ctx = LineFormatContext {
id: short,
file: &bm.file_path,
filename,
line: 0, // Line numbers not supported in multi-db mode
offset: 0,
line,
offset: line,
status: status.as_str(),
tags: &tags,
note,
Expand All @@ -409,11 +419,15 @@ fn write_annotated_line_format(bookmarks: &[AnnotatedBookmark], template: &str)
}

/// Write annotated bookmarks (multi-db results with source labels).
pub fn write_annotated_bookmarks(
pub fn write_annotated_bookmarks<F>(
mode: &OutputMode,
bookmarks: &[AnnotatedBookmark],
line_format: Option<&str>,
) -> io::Result<()> {
get_line_fn: Option<&F>,
) -> io::Result<()>
where
F: Fn(&str) -> Option<usize>,
{
match mode {
OutputMode::Json => {
let items: Vec<serde_json::Value> = bookmarks
Expand Down Expand Up @@ -453,7 +467,7 @@ pub fn write_annotated_bookmarks(
}
OutputMode::Line => {
if let Some(fmt) = line_format {
write_annotated_line_format(bookmarks, fmt)
write_annotated_line_format(bookmarks, fmt, get_line_fn)
} else {
let mut stdout = io::stdout().lock();
for ab in bookmarks {
Expand All @@ -475,7 +489,9 @@ pub fn write_annotated_bookmarks(
Ok(())
}
}
OutputMode::Custom(template) => write_annotated_line_format(bookmarks, template),
OutputMode::Custom(template) => {
write_annotated_line_format(bookmarks, template, get_line_fn)
}
OutputMode::Markdown => {
// For multi-db markdown output, fall back to table format
let mut table = Table::new();
Expand Down
Loading