Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
radicle-cli/issue: Improve comment templating
Lorenz Leutgeb committed 10 months ago
commit 78ba263d0a946c4b90235d562b56ceb7badfc965
parent af35e6f4d04735c33cc10777d6421a61555db754
1 file changed +103 -21
modified crates/radicle-cli/src/commands/issue.rs
@@ -11,6 +11,7 @@ use radicle::cob::common::{Label, Reaction};
use radicle::cob::issue::{CloseReason, State};
use radicle::cob::{issue, thread};
use radicle::crypto;
+
use radicle::git::Oid;
use radicle::issue::cache::Issues as _;
use radicle::node::device::Device;
use radicle::node::NodeId;
@@ -521,11 +522,18 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
            message,
            reply_to,
        } => {
+
            let reply_to = reply_to
+
                .map(|rev| rev.resolve::<radicle::git::Oid>(repo.raw()))
+
                .transpose()?;
+

            let signer = term::signer(&profile)?;
            let issue_id = id.resolve::<cob::ObjectId>(&repo.backend)?;
            let mut issue = issues.get_mut(&issue_id)?;
-
            let (body, reply_to) = prompt_comment(message, reply_to, &issue, &repo)?;
-
            let comment_id = issue.comment(body, reply_to, vec![], &signer)?;
+

+
            let (root_comment_id, _) = issue.root();
+
            let body = prompt_comment(message, issue.thread(), reply_to, None)?;
+
            let comment_id =
+
                issue.comment(body, reply_to.unwrap_or(*root_comment_id), vec![], &signer)?;

            if options.quiet {
                term::print(comment_id);
@@ -543,7 +551,18 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
            let issue_id = id.resolve::<cob::ObjectId>(&repo.backend)?;
            let comment_id = comment_id.resolve(&repo.backend)?;
            let mut issue = issues.get_mut(&issue_id)?;
-
            let (body, _) = prompt_comment(message, None, &issue, &repo)?;
+

+
            let comment = issue
+
                .thread()
+
                .comment(&comment_id)
+
                .ok_or(anyhow::anyhow!("comment '{comment_id}' not found"))?;
+

+
            let body = prompt_comment(
+
                message,
+
                issue.thread(),
+
                comment.reply_to(),
+
                Some(comment.body()),
+
            )?;
            issue.edit_comment(comment_id, body, vec![], &signer)?;

            if options.quiet {
@@ -892,29 +911,92 @@ where
}

/// Get a comment from the user, by prompting.
-
pub fn prompt_comment<R: WriteRepository + radicle::cob::Store<Namespace = NodeId>>(
+
pub fn prompt_comment(
    message: Message,
-
    reply_to: Option<Rev>,
-
    issue: &issue::Issue,
-
    repo: &R,
-
) -> anyhow::Result<(String, thread::CommentId)> {
-
    let (root, r) = issue.root();
-
    let (reply_to, help) = if let Some(rev) = reply_to {
-
        let id = rev.resolve::<radicle::git::Oid>(repo.raw())?;
-
        let parent = issue
-
            .thread()
-
            .comment(&id)
-
            .ok_or(anyhow::anyhow!("comment '{rev}' not found"))?;
-

-
        (id, parent.body().trim())
+
    thread: &thread::Thread,
+
    mut reply_to: Option<Oid>,
+
    edit: Option<&str>,
+
) -> anyhow::Result<String> {
+
    let (chase, missing) = {
+
        let mut chase = Vec::with_capacity(thread.len());
+
        let mut missing = None;
+

+
        while let Some(id) = reply_to {
+
            if let Some(comment) = thread.comment(&id) {
+
                chase.push(comment);
+
                reply_to = comment.reply_to();
+
            } else {
+
                missing = reply_to;
+
                break;
+
            }
+
        }
+

+
        (chase, missing)
+
    };
+

+
    let quotes = if chase.is_empty() {
+
        ""
    } else {
-
        (*root, r.body().trim())
+
        "Quotes (lines starting with '>') will be preserved. Please remove those that you do not intend to keep.\n"
    };
-
    let help = format!("\n{}\n", term::format::html::commented(help));
-
    let body = message.get(&help)?;
+

+
    let mut buffer = term::format::html::commented(format!("HTML comments, such as this one, are deleted before posting.\n{quotes}Saving an empty file aborts the operation.").as_str());
+
    buffer.push('\n');
+

+
    for comment in chase.iter().rev() {
+
        buffer.reserve(2);
+
        buffer.push('\n');
+
        comment_quoted(comment, &mut buffer);
+
    }
+

+
    if let Some(id) = missing {
+
        buffer.push('\n');
+
        buffer.push_str(
+
            term::format::html::commented(
+
                format!("The comment with ID {id} that was replied to could not be found.")
+
                    .as_str(),
+
            )
+
            .as_str(),
+
        );
+
    }
+

+
    if let Some(edit) = edit {
+
        if !chase.is_empty() {
+
            buffer.push_str(
+
                "\n<!-- The contents of the comment you are editing follow below this line. -->\n",
+
            );
+
        }
+
        buffer.reserve(2 + edit.len());
+
        buffer.push('\n');
+
        buffer.push_str(edit);
+
    }
+

+
    let body = message.get(&buffer)?;

    if body.is_empty() {
        anyhow::bail!("aborting operation due to empty comment");
    }
-
    Ok((body, reply_to))
+
    Ok(body)
+
}
+

+
fn comment_quoted(comment: &thread::Comment, buffer: &mut String) {
+
    let body = comment.body();
+
    let lines = body.lines();
+

+
    let hint = {
+
        let (lower, upper) = lines.size_hint();
+
        upper.unwrap_or(lower)
+
    };
+

+
    buffer.push_str(format!("{} wrote:\n", comment.author()).as_str());
+
    buffer.reserve(body.len() + hint * 2);
+

+
    for line in lines {
+
        buffer.push('>');
+
        if !line.is_empty() {
+
            buffer.push(' ');
+
        }
+
        buffer.push_str(line);
+
        buffer.push('\n');
+
    }
}