A file-based task manager
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Render task body through the rich-text parser in tsk show

The new command_show was printing the raw task body verbatim, ignoring
the existing parser. Pipe the body through task::parse so bold, italic,
underline, strike, highlight, inline code, links, and wiki-links render
as ANSI escapes (auto-suppressed when stdout isn't a tty, per the
colored crate). Adds -R / raw to skip the parser when you need the
unprocessed bytes.

Tests: integration test forces CLICOLOR_FORCE=1 and asserts markup
characters are stripped and ANSI escapes are emitted, plus -R round-
trips the original bytes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+53 -4
+18 -4
src/lib.rs
··· 38 38 39 39 #[derive(Subcommand)] 40 40 enum Commands { 41 - /// Initialize a `.tsk/` marker in the current git repo. (Auto-created on first use.) 41 + /// Bootstrap user-local state in `<git-dir>/tsk/`. (Auto-created on first use.) 42 42 Init, 43 43 /// Create a new task and push it onto the active queue. 44 44 Push { ··· 69 69 }, 70 70 /// Show a task by id. 71 71 Show { 72 + /// Print xattr-style YAML front-matter for the task's properties. 72 73 #[arg(short = 'x', default_value_t = false)] 73 74 show_attrs: bool, 75 + /// Skip the rich-text parser and print the raw bytes verbatim. 76 + #[arg(short = 'R', default_value_t = false)] 77 + raw: bool, 74 78 #[command(flatten)] 75 79 task_id: TaskId, 76 80 }, ··· 294 298 Commands::Show { 295 299 task_id, 296 300 show_attrs, 297 - } => command_show(dir, task_id, show_attrs), 301 + raw, 302 + } => command_show(dir, task_id, show_attrs, raw), 298 303 Commands::Edit { task_id } => command_edit(dir, task_id), 299 304 Commands::Drop { task_id } => command_drop(dir, task_id), 300 305 Commands::Swap => Workspace::from_path(dir)?.swap_top(), ··· 429 434 Ok(()) 430 435 } 431 436 432 - fn command_show(dir: PathBuf, task_id: TaskId, show_attrs: bool) -> Result<()> { 437 + fn command_show(dir: PathBuf, task_id: TaskId, show_attrs: bool, raw: bool) -> Result<()> { 433 438 let task = Workspace::from_path(dir)?.task(task_id.into())?; 434 439 if show_attrs && !task.attributes.is_empty() { 435 440 println!("---"); ··· 440 445 } 441 446 println!("---"); 442 447 } 443 - println!("{task}"); 448 + let plain = task.to_string(); 449 + match (raw, task::parse(&plain)) { 450 + (false, Some(parsed)) => { 451 + // Re-attach the title — the parser is fed the body-side text and 452 + // produces a styled body; the title is rendered as-is on top. 453 + print!("{}", parsed.content); 454 + } 455 + _ => print!("{plain}"), 456 + } 457 + println!(); 444 458 Ok(()) 445 459 } 446 460
+35
tests/multi_user.rs
··· 209 209 } 210 210 211 211 #[test] 212 + fn show_renders_styled_body() { 213 + let (_dir, alice, _bob) = setup_two_clones(); 214 + // Push a body that exercises every inline style. 215 + tsk_ok( 216 + &alice, 217 + &[ 218 + "push", 219 + "rendered\n\nthis is !bold!, *italic*, _under_, ~struck~, =hi=, `code`.", 220 + ], 221 + ); 222 + 223 + // Force colored output even in the test harness's pipe. 224 + let mut cmd = Command::new(tsk_bin()); 225 + cmd.current_dir(&alice) 226 + .env("CLICOLOR_FORCE", "1") 227 + .args(["show", "-r", "0"]); 228 + let (code, stdout, stderr) = run(&mut cmd); 229 + assert_eq!(code, 0, "stderr={stderr}"); 230 + 231 + // Markup characters must be stripped. 232 + assert!(!stdout.contains("!bold!"), "got {stdout:?}"); 233 + assert!(!stdout.contains("*italic*"), "got {stdout:?}"); 234 + assert!(!stdout.contains("=hi="), "got {stdout:?}"); 235 + // ANSI bold escape must be present somewhere. 236 + assert!( 237 + stdout.contains("\x1b["), 238 + "expected ANSI escapes in styled output: {stdout:?}" 239 + ); 240 + 241 + // -R bypasses the parser and prints the raw bytes. 242 + let raw = tsk_ok(&alice, &["show", "-r", "0", "-R"]); 243 + assert!(raw.contains("!bold!"), "raw must keep markup: {raw:?}"); 244 + } 245 + 246 + #[test] 212 247 fn share_into_namespace_round_trip() { 213 248 let (_dir, alice, _bob) = setup_two_clones(); 214 249