···11use std::path::PathBuf;
12use std::process::exit;
13use std::{env::current_dir, io::Read};
014use workspace::{Id, TaskIdentifier, Workspace};
1516//use smol;
···89 full_id: bool,
90 },
9192- /// Prints the contents of a task.
093 Show {
94 /// Shows raw file attributes for the file
95 #[arg(short = 'x', default_value_t = false)]
···99 task_id: TaskId,
100 },
10100000000000000000102 /// Drops the task on the top of the stack and archives it.
103 Drop {
104 /// The [TSK-]ID of the task to drop.
···215 task_id,
216 show_attrs,
217 } => command_show(dir, task_id, show_attrs),
00000218 Commands::Edit { task_id } => command_edit(dir, task_id),
219 Commands::Completion { shell } => command_completion(shell),
220 Commands::Drop { task_id } => command_drop(dir, task_id),
···234 }
235}
236000000000000237fn command_init(dir: PathBuf) -> Result<()> {
238 Workspace::init(dir)
239}
···366 }
367 Ok(())
368}
0000000000000000000000000000
···11use std::path::PathBuf;
12use std::process::exit;
13use std::{env::current_dir, io::Read};
14+use task::ParsedLink;
15use workspace::{Id, TaskIdentifier, Workspace};
1617//use smol;
···90 full_id: bool,
91 },
9293+ /// Prints the contents of a task, parsing the body as rich text and formatting it using ANSI
94+ /// escape sequences.
95 Show {
96 /// Shows raw file attributes for the file
97 #[arg(short = 'x', default_value_t = false)]
···101 task_id: TaskId,
102 },
103104+ /// Follow a link that is parsed from a task body. It may be an internal or external link (ie.
105+ /// a url or a wiki-style link using double square brackets). When using the `tsk show`
106+ /// command, links that are successfully parsed get a numeric superscript that may be used to
107+ /// address the link. That number should be supplied to the -l/link_index where it will be
108+ /// subsequently followed opened or shown.
109+ Follow {
110+ /// The task whose body will be searched for links.
111+ #[command(flatten)]
112+ task_id: TaskId,
113+ /// The index of the link to open. Must be supplied.
114+ #[arg(short = 'l')]
115+ link_index: usize,
116+ /// When opening an internal link, whether to show or edit the addressed task.
117+ #[arg(short = 'e', default_value_t = false)]
118+ edit: bool,
119+ },
120+121 /// Drops the task on the top of the stack and archives it.
122 Drop {
123 /// The [TSK-]ID of the task to drop.
···234 task_id,
235 show_attrs,
236 } => command_show(dir, task_id, show_attrs),
237+ Commands::Follow {
238+ task_id,
239+ link_index,
240+ edit,
241+ } => command_follow(dir, task_id, link_index, edit),
242 Commands::Edit { task_id } => command_edit(dir, task_id),
243 Commands::Completion { shell } => command_completion(shell),
244 Commands::Drop { task_id } => command_drop(dir, task_id),
···258 }
259}
260261+fn taskid_from_tsk_id(tsk_id: Id) -> TaskId {
262+ TaskId {
263+ tsk_id: Some(tsk_id),
264+ id: None,
265+ relative_id: 0,
266+ find: Find {
267+ find: false,
268+ args: FindArgs { search_body: false },
269+ },
270+ }
271+}
272+273fn command_init(dir: PathBuf) -> Result<()> {
274 Workspace::init(dir)
275}
···402 }
403 Ok(())
404}
405+406+fn command_follow(dir: PathBuf, task_id: TaskId, link_index: usize, edit: bool) -> Result<()> {
407+ let task = Workspace::from_path(dir.clone())?.task(task_id.into())?;
408+ if let Some(parsed_task) = task::parse(&task.to_string()) {
409+ if link_index == 0 || link_index > parsed_task.links.len() {
410+ eprintln!("Link index out of bounds.");
411+ exit(1);
412+ }
413+ let link = &parsed_task.links[link_index - 1];
414+ match link {
415+ ParsedLink::External(url) => {
416+ open::that_detached(url.as_str())?;
417+ Ok(())
418+ }
419+ ParsedLink::Internal(id) => {
420+ let taskid = taskid_from_tsk_id(*id);
421+ if edit {
422+ command_edit(dir, taskid)
423+ } else {
424+ command_show(dir, taskid, false)
425+ }
426+ }
427+ }
428+ } else {
429+ eprintln!("Unable to parse any links from body.");
430+ exit(1);
431+ }
432+}