···1111use std::path::PathBuf;
1212use std::process::exit;
1313use std::{env::current_dir, io::Read};
1414+use task::ParsedLink;
1415use workspace::{Id, TaskIdentifier, Workspace};
15161617//use smol;
···8990 full_id: bool,
9091 },
91929292- /// Prints the contents of a task.
9393+ /// Prints the contents of a task, parsing the body as rich text and formatting it using ANSI
9494+ /// escape sequences.
9395 Show {
9496 /// Shows raw file attributes for the file
9597 #[arg(short = 'x', default_value_t = false)]
···99101 task_id: TaskId,
100102 },
101103104104+ /// Follow a link that is parsed from a task body. It may be an internal or external link (ie.
105105+ /// a url or a wiki-style link using double square brackets). When using the `tsk show`
106106+ /// command, links that are successfully parsed get a numeric superscript that may be used to
107107+ /// address the link. That number should be supplied to the -l/link_index where it will be
108108+ /// subsequently followed opened or shown.
109109+ Follow {
110110+ /// The task whose body will be searched for links.
111111+ #[command(flatten)]
112112+ task_id: TaskId,
113113+ /// The index of the link to open. Must be supplied.
114114+ #[arg(short = 'l')]
115115+ link_index: usize,
116116+ /// When opening an internal link, whether to show or edit the addressed task.
117117+ #[arg(short = 'e', default_value_t = false)]
118118+ edit: bool,
119119+ },
120120+102121 /// Drops the task on the top of the stack and archives it.
103122 Drop {
104123 /// The [TSK-]ID of the task to drop.
···215234 task_id,
216235 show_attrs,
217236 } => command_show(dir, task_id, show_attrs),
237237+ Commands::Follow {
238238+ task_id,
239239+ link_index,
240240+ edit,
241241+ } => command_follow(dir, task_id, link_index, edit),
218242 Commands::Edit { task_id } => command_edit(dir, task_id),
219243 Commands::Completion { shell } => command_completion(shell),
220244 Commands::Drop { task_id } => command_drop(dir, task_id),
···234258 }
235259}
236260261261+fn taskid_from_tsk_id(tsk_id: Id) -> TaskId {
262262+ TaskId {
263263+ tsk_id: Some(tsk_id),
264264+ id: None,
265265+ relative_id: 0,
266266+ find: Find {
267267+ find: false,
268268+ args: FindArgs { search_body: false },
269269+ },
270270+ }
271271+}
272272+237273fn command_init(dir: PathBuf) -> Result<()> {
238274 Workspace::init(dir)
239275}
···366402 }
367403 Ok(())
368404}
405405+406406+fn command_follow(dir: PathBuf, task_id: TaskId, link_index: usize, edit: bool) -> Result<()> {
407407+ let task = Workspace::from_path(dir.clone())?.task(task_id.into())?;
408408+ if let Some(parsed_task) = task::parse(&task.to_string()) {
409409+ if link_index == 0 || link_index > parsed_task.links.len() {
410410+ eprintln!("Link index out of bounds.");
411411+ exit(1);
412412+ }
413413+ let link = &parsed_task.links[link_index - 1];
414414+ match link {
415415+ ParsedLink::External(url) => {
416416+ open::that_detached(url.as_str())?;
417417+ Ok(())
418418+ }
419419+ ParsedLink::Internal(id) => {
420420+ let taskid = taskid_from_tsk_id(*id);
421421+ if edit {
422422+ command_edit(dir, taskid)
423423+ } else {
424424+ command_show(dir, taskid, false)
425425+ }
426426+ }
427427+ }
428428+ } else {
429429+ eprintln!("Unable to parse any links from body.");
430430+ exit(1);
431431+ }
432432+}