review(p9-fb-10): 회차 2 지적 반영
- TAGS_COL_W const 추출 (truncate + pad 동시 사용 — drift 방지). - format_doc_row 직접 unit test 2개 (Hangul title / Hangul tag) 가 display column 정렬을 정확히 pin. `<title_w$>` 으로 되돌리는 회귀가 unit-level 에서 catch 됨.
This commit is contained in:
@@ -17,6 +17,11 @@ use ratatui::widgets::{Block, Borders, List, ListItem, ListState, Paragraph};
|
||||
use crate::app::{App, KeyOutcome, Pane};
|
||||
use crate::input::{display_width, truncate_to_display_width};
|
||||
|
||||
/// Width (in display columns) of the `tags` column in the doc-list
|
||||
/// row. Used twice — truncate input + pad calculation — so a const
|
||||
/// keeps them in sync.
|
||||
const TAGS_COL_W: usize = 12;
|
||||
|
||||
/// Internal state owned by `LibraryState`. Public-by-crate so
|
||||
/// `handle_key_library` can mutate it without crossing the
|
||||
/// `pub`-visibility boundary `LibraryState` exposes.
|
||||
@@ -193,7 +198,7 @@ pub(crate) fn format_doc_row(d: &DocSummary, title_w: usize) -> String {
|
||||
} else {
|
||||
d.tags.join(",")
|
||||
};
|
||||
let tags = truncate_to_display_width(&tags, 12);
|
||||
let tags = truncate_to_display_width(&tags, TAGS_COL_W);
|
||||
let updated = d
|
||||
.updated_at
|
||||
.format(&time::format_description::well_known::Rfc3339)
|
||||
@@ -206,7 +211,7 @@ pub(crate) fn format_doc_row(d: &DocSummary, title_w: usize) -> String {
|
||||
// from `display_width`, then concatenate — the truncate above
|
||||
// already guarantees `display_width(title) <= title_w`.
|
||||
let title_pad = title_w.saturating_sub(display_width(&title));
|
||||
let tags_pad = 12usize.saturating_sub(display_width(&tags));
|
||||
let tags_pad = TAGS_COL_W.saturating_sub(display_width(&tags));
|
||||
format!(
|
||||
"{title}{:title_pad$} {tags}{:tags_pad$} {updated_short:<10} {chunk_count}",
|
||||
"",
|
||||
@@ -404,3 +409,56 @@ pub(crate) fn refresh_docs(state: &mut App) -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use kebab_core::{
|
||||
ChunkerVersion, DocSummary, DocumentId, Lang, ParserVersion, SourceType, TrustLevel,
|
||||
WorkspacePath,
|
||||
};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
fn doc(title: &str, tags: &[&str]) -> DocSummary {
|
||||
DocSummary {
|
||||
doc_id: DocumentId("a".repeat(32)),
|
||||
doc_path: WorkspacePath::new("x.md".into()).unwrap(),
|
||||
title: title.into(),
|
||||
lang: Lang("en".into()),
|
||||
tags: tags.iter().map(|s| (*s).into()).collect(),
|
||||
trust_level: TrustLevel::Primary,
|
||||
source_type: SourceType::Note,
|
||||
byte_len: 1,
|
||||
chunk_count: 1,
|
||||
created_at: OffsetDateTime::from_unix_timestamp(1_700_000_000).unwrap(),
|
||||
updated_at: OffsetDateTime::from_unix_timestamp(1_700_000_000).unwrap(),
|
||||
parser_version: ParserVersion("p".into()),
|
||||
chunker_version: ChunkerVersion("c".into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// p9-fb-10: format_doc_row pads by display width (not char
|
||||
/// count) so wide-char titles don't shift downstream columns.
|
||||
/// Regression pin — `<title_w$>` (std::fmt char-count form)
|
||||
/// would fail this for any Hangul title.
|
||||
#[test]
|
||||
fn format_doc_row_pads_by_display_width_for_hangul_title() {
|
||||
let row = format_doc_row(&doc("러스트로 만드는 KB", &["rust"]), 30);
|
||||
// Expected layout (display cols):
|
||||
// title 30 + " "(2) + tags 12 + " "(2) + date 10 + " "(2) + chunk
|
||||
// chunk = "1" → 1 col. Total = 30+2+12+2+10+2+1 = 59.
|
||||
assert_eq!(
|
||||
display_width(&row),
|
||||
59,
|
||||
"row must align to display columns, not char count: {row:?}"
|
||||
);
|
||||
}
|
||||
|
||||
/// p9-fb-10: Hangul tag also pads by display width.
|
||||
#[test]
|
||||
fn format_doc_row_pads_by_display_width_for_hangul_tag() {
|
||||
let row = format_doc_row(&doc("ascii", &["한글"]), 20);
|
||||
// title 20 + " " + tags 12 + " " + date 10 + " " + "1" = 49
|
||||
assert_eq!(display_width(&row), 49, "row: {row:?}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user