From 2c4e93b3d213cbbe257ce96c8830432cbe206cf1 Mon Sep 17 00:00:00 2001 From: cloudhead Date: Mon, 11 Sep 2023 15:39:20 +0200 Subject: [PATCH] cli: Show issue comments When `rad issue show` is run, the issue comments are shown by default. This can be tweaked using the `--format` option. A value of "header" only shows the header, while the default which is "full", shows the header and comments. --- radicle-cli/examples/rad-issue.md | 20 ++++++++ radicle-cli/examples/rad-label.md | 4 +- radicle-cli/src/commands/issue.rs | 79 +++++++++++++++++++++++++------ 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/radicle-cli/examples/rad-issue.md b/radicle-cli/examples/rad-issue.md index 788acbd9..3a4f7ddb 100644 --- a/radicle-cli/examples/rad-issue.md +++ b/radicle-cli/examples/rad-issue.md @@ -80,3 +80,23 @@ $ rad comment 42028af21fabc09bfac2f25490f119f7c7e11542 --message 'The flux capac $ rad comment 42028af21fabc09bfac2f25490f119f7c7e11542 --reply-to 84492237dc0908b1e5b728d1a4e5f1343b6ffe9b --message 'More power!' dd679552a15e2db73bbedf3084f5f7c62bb0d724 ``` + +We can see our comments by showing the issue: + +``` +$ rad issue show 42028af21fabc09bfac2f25490f119f7c7e11542 +╭─────────────────────────────────────────────────────────╮ +│ Title flux capacitor underpowered │ +│ Issue 42028af21fabc09bfac2f25490f119f7c7e11542 │ +│ Author z6MknSL…StBU8Vi (you) │ +│ Status open │ +│ │ +│ Flux capacitor power requirements exceed current supply │ +├─────────────────────────────────────────────────────────┤ +│ z6MknSL…StBU8Vi (you) [ ... ] 8449223 │ +│ The flux capacitor needs 1.21 Gigawatts │ +├─────────────────────────────────────────────────────────┤ +│ z6MknSL…StBU8Vi (you) [ ... ] dd67955 │ +│ More power! │ +╰─────────────────────────────────────────────────────────╯ +``` diff --git a/radicle-cli/examples/rad-label.md b/radicle-cli/examples/rad-label.md index 9a51ca55..7b9f2f4a 100644 --- a/radicle-cli/examples/rad-label.md +++ b/radicle-cli/examples/rad-label.md @@ -8,7 +8,7 @@ $ rad label 42028af21fabc09bfac2f25490f119f7c7e11542 bug good-first-issue We can now show the issue to check whether those labels were added: ``` -$ rad issue show 42028af21fabc09bfac2f25490f119f7c7e11542 +$ rad issue show 42028af21fabc09bfac2f25490f119f7c7e11542 --format header ╭─────────────────────────────────────────────────────────╮ │ Title flux capacitor underpowered │ │ Issue 42028af21fabc09bfac2f25490f119f7c7e11542 │ @@ -29,7 +29,7 @@ $ rad unlabel 42028af21fabc09bfac2f25490f119f7c7e11542 good-first-issue Notice that the `good-first-issue` label has disappeared: ``` -$ rad issue show 42028af21fabc09bfac2f25490f119f7c7e11542 +$ rad issue show 42028af21fabc09bfac2f25490f119f7c7e11542 --format header ╭─────────────────────────────────────────────────────────╮ │ Title flux capacitor underpowered │ │ Issue 42028af21fabc09bfac2f25490f119f7c7e11542 │ diff --git a/radicle-cli/src/commands/issue.rs b/radicle-cli/src/commands/issue.rs index e88f0068..9886c098 100644 --- a/radicle-cli/src/commands/issue.rs +++ b/radicle-cli/src/commands/issue.rs @@ -45,6 +45,7 @@ Usage Options --no-announce Don't announce issue to peers + --header Show only the issue header, hiding the comments --quiet, -q Don't print anything --help Print help "#, @@ -77,6 +78,14 @@ pub enum Assigned { Peer(Did), } +/// Display format. +#[derive(Default, Debug, PartialEq, Eq)] +pub enum Format { + #[default] + Full, + Header, +} + #[derive(Debug, PartialEq, Eq)] pub enum Operation { Edit { @@ -91,6 +100,7 @@ pub enum Operation { }, Show { id: Rev, + format: Format, }, State { id: Rev, @@ -131,6 +141,7 @@ impl Args for Options { let mut description: Option = None; let mut state: Option = Some(State::Open); let mut labels = Vec::new(); + let mut format = Format::default(); let mut announce = true; let mut quiet = false; @@ -199,6 +210,16 @@ impl Args for Options { assigned = Some(Assigned::Me); } } + Long("format") if op == Some(OperationName::Show) => { + let val = parser.value()?; + let val = term::args::string(&val); + + match val.as_str() { + "header" => format = Format::Header, + "full" => format = Format::Full, + _ => anyhow::bail!("unknown format '{val}'"), + } + } Long("no-announce") => { announce = false; } @@ -239,6 +260,7 @@ impl Args for Options { }, OperationName::Show => Operation::Show { id: id.ok_or_else(|| anyhow!("an issue must be provided"))?, + format, }, OperationName::State => Operation::State { id: id.ok_or_else(|| anyhow!("an issue must be provided"))?, @@ -306,15 +328,15 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { } => { let issue = issues.create(title, description, labels.as_slice(), &[], [], &signer)?; if !options.quiet { - show_issue(&issue, issue.id(), &profile)?; + show_issue(&issue, issue.id(), Format::Header, &profile)?; } } - Operation::Show { id } => { + Operation::Show { id, format } => { let id = id.resolve(&repo.backend)?; let issue = issues .get(&id)? .context("No issue with the given ID exists")?; - show_issue(&issue, &id, &profile)?; + show_issue(&issue, &id, format, &profile)?; } Operation::State { id, state } => { let id = id.resolve(&repo.backend)?; @@ -579,7 +601,7 @@ fn open( signer, )?; if !options.quiet { - show_issue(&issue, issue.id(), profile)?; + show_issue(&issue, issue.id(), Format::Header, profile)?; } Ok(()) @@ -636,12 +658,17 @@ fn edit( Ok(()) })?; - show_issue(&issue, &id, profile)?; + show_issue(&issue, &id, Format::Header, profile)?; Ok(()) } -fn show_issue(issue: &issue::Issue, id: &cob::ObjectId, profile: &Profile) -> anyhow::Result<()> { +fn show_issue( + issue: &issue::Issue, + id: &cob::ObjectId, + format: Format, + profile: &Profile, +) -> anyhow::Result<()> { let labels: Vec = issue.labels().cloned().map(|t| t.into()).collect(); let assignees: Vec = issue .assigned() @@ -689,28 +716,52 @@ fn show_issue(issue: &issue::Issue, id: &cob::ObjectId, profile: &Profile) -> an term::format::tertiary("Status".to_owned()).into(), match issue.state() { issue::State::Open => term::format::positive("open".to_owned()).into(), - issue::State::Closed { reason } => term::format::default(format!( - "{} {}", - term::format::negative("closed"), - term::format::default(format!("as {reason}")) - )) - .into(), + issue::State::Closed { + reason: CloseReason::Solved, + } => term::Line::spaced([ + term::format::negative("closed").into(), + term::format::negative("(solved)").italic().dim().into(), + ]), + issue::State::Closed { + reason: CloseReason::Other, + } => term::Line::spaced([term::format::negative("closed").into()]), }, ]); let (_, description) = issue.description(); - let widget = VStack::default() + let mut widget = VStack::default() .border(Some(term::colors::FAINT)) .child(attrs) .children(if !description.is_empty() { vec![ term::Label::blank().boxed(), - term::textarea(description).boxed(), + term::textarea(description.trim()).wrap(60).boxed(), ] } else { vec![] }); + if format == Format::Full { + for (id, comment) in issue.comments().skip(1) { + let author = comment.author(); + let author = Author::new(&author, profile); + let (alias, nid) = author.labels(); + let hstack = term::hstack::HStack::default() + .child(term::Line::spaced([ + alias, + nid, + term::format::timestamp(&comment.timestamp()).dim().into(), + ])) + .child(term::Line::new(term::Label::space())) + .child(term::Line::spaced([term::format::oid(*id) + .fg(term::Color::Cyan) + .into()])); + + widget = widget.divider(); + widget.push(hstack); + widget.push(term::textarea(comment.body()).wrap(60)); + } + } widget.print(); Ok(())