cli: Display patches like issues, in `rad patch`
We also make sure not to bail if one of the patches fails to load.
This commit is contained in:
parent
b809bd3d73
commit
3b20d45d80
|
|
@ -41,19 +41,22 @@ It will now be listed as one of the project's open patches.
|
|||
|
||||
```
|
||||
$ rad patch
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Define power requirements 191a14e R0 3e674d1 (flux-capacitor-power) ahead 1, behind 0 │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● opened by did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) [ ... ]│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Head + - Opened │
|
||||
├──────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● 191a14e Define power requirements z6MknSL…StBU8Vi (you) 3e674d1 +0 -0 4 months ago │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
```
|
||||
$ rad patch show 191a14e520f2eeff7c0e3ee0a5523c5217eecb89 -p
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Define power requirements │
|
||||
│ Patch 191a14e520f2eeff7c0e3ee0a5523c5217eecb89 │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ Status open │
|
||||
│ Title Define power requirements │
|
||||
│ Patch 191a14e520f2eeff7c0e3ee0a5523c5217eecb89 │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ Head 3e674d1a1df90807e934f9ae5da2591dd6848a33 │
|
||||
│ Branches flux-capacitor-power │
|
||||
│ Commits ahead 1, behind 0 │
|
||||
│ Status open │
|
||||
│ │
|
||||
│ See details. │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
|
|
@ -120,12 +123,20 @@ $ rad review 191a14e520f2eeff7c0e3ee0a5523c5217eecb89 --accept --no-message --no
|
|||
Showing the patch list now will reveal the favorable verdict:
|
||||
|
||||
```
|
||||
$ rad patch
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Define power requirements 191a14e R1 27857ec (flux-capacitor-power, patch/191a14e) ahead 2, behind 0 │
|
||||
├──────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● opened by did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) [ ... ]│
|
||||
│ ↑ updated to b8f7bfbbb3c6a207b349e9f45bf535c706805871 (27857ec) [ ... ]│
|
||||
│ ✓ accepted by z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) [ ... ]│
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
$ rad patch show 191a14e
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Define power requirements │
|
||||
│ Patch 191a14e520f2eeff7c0e3ee0a5523c5217eecb89 │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ Head 27857ec9eb04c69cacab516e8bf4b5fd36090f66 │
|
||||
│ Branches flux-capacitor-power, patch/191a14e │
|
||||
│ Commits ahead 2, behind 0 │
|
||||
│ Status open │
|
||||
│ │
|
||||
│ See details. │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● opened by did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) [ ... ]│
|
||||
│ ↑ updated to b8f7bfbbb3c6a207b349e9f45bf535c706805871 (27857ec) [ ... ]│
|
||||
│ ✓ accepted by z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) [ ... ]│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
|
|
|||
|
|
@ -41,17 +41,20 @@ It will now be listed as one of the project's open patches.
|
|||
|
||||
```
|
||||
$ rad patch
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Define power requirements a07ef77 R0 3e674d1 (flux-capacitor-power) ahead 1, behind 0 │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● opened by did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (you) [ ... ]│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Head + - Opened │
|
||||
├──────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● a07ef77 Define power requirements z6Mkt67…v4N1tRk (you) 3e674d1 +0 -0 4 months ago │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
$ rad patch show a07ef7743a32a2e902672ea3526d1db6ee08108a
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Define power requirements │
|
||||
│ Patch a07ef7743a32a2e902672ea3526d1db6ee08108a │
|
||||
│ Author did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk │
|
||||
│ Status open │
|
||||
│ Title Define power requirements │
|
||||
│ Patch a07ef7743a32a2e902672ea3526d1db6ee08108a │
|
||||
│ Author did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk │
|
||||
│ Head 3e674d1a1df90807e934f9ae5da2591dd6848a33 │
|
||||
│ Branches flux-capacitor-power │
|
||||
│ Commits ahead 1, behind 0 │
|
||||
│ Status open │
|
||||
│ │
|
||||
│ See details. │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
|
|
|
|||
|
|
@ -26,12 +26,19 @@ $ git branch -r
|
|||
bob/flux-capacitor-power
|
||||
bob/master
|
||||
rad/master
|
||||
$ rad patch
|
||||
$ rad patch show a07ef77
|
||||
╭───────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Define power requirements a07ef77 R1 27857ec ahead 2, behind 0 │
|
||||
│ Title Define power requirements │
|
||||
│ Patch a07ef7743a32a2e902672ea3526d1db6ee08108a │
|
||||
│ Author did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk │
|
||||
│ Head 27857ec9eb04c69cacab516e8bf4b5fd36090f66 │
|
||||
│ Commits ahead 2, behind 0 │
|
||||
│ Status open │
|
||||
│ │
|
||||
│ See details. │
|
||||
├───────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● opened by did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk [ ... ]│
|
||||
│ ↑ updated to 11483929d8714a92992229f65433e06288f3b760 (27857ec) [ ... ]│
|
||||
│ ● opened by did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk [ ... ] │
|
||||
│ ↑ updated to 11483929d8714a92992229f65433e06288f3b760 (27857ec) [ ... ] │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
|
|
@ -76,15 +83,23 @@ Fast-forward
|
|||
The patch is now merged and closed :).
|
||||
|
||||
```
|
||||
$ rad patch --merged
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Define power requirements a07ef77 R2 f6484e0 (flux-capacitor-power, master) ahead 3, behind 0 │
|
||||
├───────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● opened by did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk [ ... ]│
|
||||
│ ↑ updated to 11483929d8714a92992229f65433e06288f3b760 (27857ec) [ ... ]│
|
||||
│ ↑ updated to 0795d619232479e910f95bb9c873ee1ec305c43c (f6484e0) [ ... ]│
|
||||
│ ✓ merged by did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) [ ... ]│
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
$ rad patch show a07ef77
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Define power requirements │
|
||||
│ Patch a07ef7743a32a2e902672ea3526d1db6ee08108a │
|
||||
│ Author did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk │
|
||||
│ Head f6484e0f43e48a8983b9b39bf9bd4cd889f1d520 │
|
||||
│ Branches flux-capacitor-power, master │
|
||||
│ Commits ahead 3, behind 0 │
|
||||
│ Status merged │
|
||||
│ │
|
||||
│ See details. │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● opened by did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk [ ... ] │
|
||||
│ ↑ updated to 11483929d8714a92992229f65433e06288f3b760 (27857ec) [ ... ] │
|
||||
│ ↑ updated to 0795d619232479e910f95bb9c873ee1ec305c43c (f6484e0) [ ... ] │
|
||||
│ ✓ merged by did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) [ ... ] │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
To publish our new state to the network, we simply push:
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
create::run(&repository, &profile, &workdir, message.clone(), options)?;
|
||||
}
|
||||
Operation::List { filter } => {
|
||||
list::run(&repository, &profile, Some(workdir), filter)?;
|
||||
list::run(&repository, &profile, filter)?;
|
||||
}
|
||||
Operation::Show { patch_id, diff } => {
|
||||
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||
|
|
|
|||
|
|
@ -68,8 +68,23 @@ pub fn patch_merge_target_oid(target: MergeTarget, repository: &Repository) -> a
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the diff stats between two commits.
|
||||
pub fn diff_stats(
|
||||
repo: &git::raw::Repository,
|
||||
old: &Oid,
|
||||
new: &Oid,
|
||||
) -> Result<git::raw::DiffStats, git::raw::Error> {
|
||||
let old = repo.find_commit(*old)?;
|
||||
let new = repo.find_commit(*new)?;
|
||||
let old_tree = old.tree()?;
|
||||
let new_tree = new.tree()?;
|
||||
let diff = repo.diff_tree_to_tree(Some(&old_tree), Some(&new_tree), None)?;
|
||||
|
||||
diff.stats()
|
||||
}
|
||||
|
||||
/// Create a human friendly message about git's sync status.
|
||||
pub fn pretty_sync_status(
|
||||
pub fn ahead_behind(
|
||||
repo: &git::raw::Repository,
|
||||
revision_oid: Oid,
|
||||
head_oid: Oid,
|
||||
|
|
@ -89,35 +104,21 @@ pub fn pretty_sync_status(
|
|||
.item(behind))
|
||||
}
|
||||
|
||||
/// Make a human friendly string for commit version information.
|
||||
///
|
||||
/// For example '<oid> (branch1[, branch2])'.
|
||||
pub fn pretty_commit_version(
|
||||
revision_oid: &Oid,
|
||||
repo: Option<&git::raw::Repository>,
|
||||
) -> anyhow::Result<term::Line> {
|
||||
let oid = term::format::secondary(term::format::oid(*revision_oid));
|
||||
let mut line = term::Line::new(oid);
|
||||
/// Get the branches that point to a commit.
|
||||
pub fn branches(target: &Oid, repo: &git::raw::Repository) -> anyhow::Result<Vec<String>> {
|
||||
let mut branches: Vec<String> = vec![];
|
||||
|
||||
if let Some(repo) = repo {
|
||||
for r in repo.references()?.flatten() {
|
||||
if !r.is_branch() {
|
||||
continue;
|
||||
}
|
||||
if let (Some(oid), Some(name)) = (&r.target(), &r.shorthand()) {
|
||||
if oid == revision_oid {
|
||||
branches.push(name.to_string());
|
||||
};
|
||||
};
|
||||
for r in repo.references()?.flatten() {
|
||||
if !r.is_branch() {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if !branches.is_empty() {
|
||||
line.push(term::Label::space());
|
||||
line.push(term::format::yellow(format!("({})", branches.join(", "))));
|
||||
if let (Some(oid), Some(name)) = (&r.target(), &r.shorthand()) {
|
||||
if oid == target {
|
||||
branches.push(name.to_string());
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Ok(line)
|
||||
Ok(branches)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use anyhow::anyhow;
|
||||
|
||||
use radicle::cob::patch;
|
||||
use radicle::cob::patch::{Patch, PatchId, Patches, Revision, Verdict};
|
||||
use radicle::git;
|
||||
use radicle::cob::patch::{Patch, PatchId, Patches, Verdict};
|
||||
use radicle::prelude::*;
|
||||
use radicle::profile::Profile;
|
||||
use radicle::storage::git::Repository;
|
||||
|
||||
use crate::terminal as term;
|
||||
use term::table::{Table, TableOptions};
|
||||
use term::Element as _;
|
||||
|
||||
use super::common;
|
||||
|
|
@ -16,7 +16,6 @@ use super::common;
|
|||
pub fn run(
|
||||
repository: &Repository,
|
||||
profile: &Profile,
|
||||
workdir: Option<git::raw::Repository>,
|
||||
filter: Option<patch::State>,
|
||||
) -> anyhow::Result<()> {
|
||||
let me = *profile.id();
|
||||
|
|
@ -48,62 +47,89 @@ pub fn run(
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let mut table = Table::<9, term::Line>::new(TableOptions {
|
||||
spacing: 2,
|
||||
border: Some(term::colors::FAINT),
|
||||
..TableOptions::default()
|
||||
});
|
||||
|
||||
table.push([
|
||||
term::format::dim(String::from("●")).into(),
|
||||
term::format::bold(String::from("ID")).into(),
|
||||
term::format::bold(String::from("Title")).into(),
|
||||
term::format::bold(String::from("Author")).into(),
|
||||
term::format::bold(String::new()).into(),
|
||||
term::format::bold(String::from("Head")).into(),
|
||||
term::format::bold(String::from("+")).into(),
|
||||
term::format::bold(String::from("-")).into(),
|
||||
term::format::bold(String::from("Opened")).into(),
|
||||
]);
|
||||
table.divider();
|
||||
|
||||
let mut errors = Vec::new();
|
||||
for (id, patch) in &mut own {
|
||||
widget(&me, id, patch, workdir.as_ref(), repository)?.print();
|
||||
match row(&me, id, patch, repository) {
|
||||
Ok(r) => table.push(r),
|
||||
Err(e) => errors.push((patch.title(), id, e.to_string())),
|
||||
}
|
||||
}
|
||||
for (id, patch) in &mut other {
|
||||
widget(profile.id(), id, patch, workdir.as_ref(), repository)?.print();
|
||||
match row(&me, id, patch, repository) {
|
||||
Ok(r) => table.push(r),
|
||||
Err(e) => errors.push((patch.title(), id, e.to_string())),
|
||||
}
|
||||
}
|
||||
table.print();
|
||||
|
||||
if !errors.is_empty() {
|
||||
for (title, id, error) in errors {
|
||||
term::error(format!(
|
||||
"{} Patch {title:?} ({id}) failed to load: {error}",
|
||||
term::format::negative("Error:")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn header(
|
||||
patch_id: &PatchId,
|
||||
patch: &Patch,
|
||||
workdir: Option<&git::raw::Repository>,
|
||||
repository: &Repository,
|
||||
revision: &Revision,
|
||||
) -> anyhow::Result<term::Line> {
|
||||
let target_head = common::patch_merge_target_oid(patch.target(), repository)?;
|
||||
let header = term::Line::spaced([
|
||||
term::format::bold(patch.title()).into(),
|
||||
term::format::highlight(term::format::cob(patch_id)).into(),
|
||||
term::format::dim(format!("R{}", patch.version())).into(),
|
||||
])
|
||||
.space()
|
||||
.extend(common::pretty_commit_version(&revision.head(), workdir)?)
|
||||
.space()
|
||||
.extend(common::pretty_sync_status(
|
||||
repository.raw(),
|
||||
revision.head().into(),
|
||||
target_head,
|
||||
)?);
|
||||
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
/// Patch widget.
|
||||
pub fn widget<'a>(
|
||||
/// Patch row.
|
||||
pub fn row(
|
||||
whoami: &PublicKey,
|
||||
patch_id: &PatchId,
|
||||
id: &PatchId,
|
||||
patch: &Patch,
|
||||
workdir: Option<&git::raw::Repository>,
|
||||
repository: &Repository,
|
||||
) -> anyhow::Result<term::VStack<'a>> {
|
||||
) -> anyhow::Result<[term::Line; 9]> {
|
||||
let state = patch.state();
|
||||
let (_, revision) = patch
|
||||
.latest()
|
||||
.ok_or_else(|| anyhow!("patch is malformed: no revisions found"))?;
|
||||
let header = header(patch_id, patch, workdir, repository, revision)?;
|
||||
let mut widget = term::VStack::default()
|
||||
.child(header)
|
||||
.divider()
|
||||
.border(Some(term::colors::FAINT));
|
||||
let stats = common::diff_stats(repository.raw(), revision.base(), &revision.head())?;
|
||||
let author = patch.author().id;
|
||||
|
||||
for line in timeline(whoami, patch_id, patch, repository)? {
|
||||
widget.push(line);
|
||||
}
|
||||
Ok(widget)
|
||||
Ok([
|
||||
match state {
|
||||
patch::State::Open => term::format::positive("●").into(),
|
||||
patch::State::Archived { .. } => term::format::yellow("●").into(),
|
||||
patch::State::Draft => term::format::dim("●").into(),
|
||||
patch::State::Merged { .. } => term::format::primary("✔").into(),
|
||||
},
|
||||
term::format::tertiary(term::format::cob(id)).into(),
|
||||
term::format::default(patch.title().to_owned()).into(),
|
||||
term::format::did(&author).dim().into(),
|
||||
if author.as_key() == whoami {
|
||||
term::format::primary("(you)".to_owned()).into()
|
||||
} else {
|
||||
term::format::default(String::new()).into()
|
||||
},
|
||||
term::format::secondary(term::format::oid(revision.head())).into(),
|
||||
term::format::positive(format!("+{}", stats.insertions())).into(),
|
||||
term::format::negative(format!("-{}", stats.deletions())).into(),
|
||||
term::format::timestamp(&patch.timestamp())
|
||||
.dim()
|
||||
.italic()
|
||||
.into(),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn timeline(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use radicle::git;
|
|||
use radicle::storage::git::Repository;
|
||||
use radicle_term::{
|
||||
table::{Table, TableOptions},
|
||||
textarea, Element, Paint, VStack,
|
||||
textarea, Element, VStack,
|
||||
};
|
||||
|
||||
use crate::terminal as term;
|
||||
|
|
@ -13,12 +13,7 @@ use crate::terminal as term;
|
|||
use super::common::*;
|
||||
use super::*;
|
||||
|
||||
fn show_patch_diff(
|
||||
patch: &patch::Patch,
|
||||
storage: &Repository,
|
||||
// TODO: Tell user which working copy branches point to the patch.
|
||||
_workdir: &git::raw::Repository,
|
||||
) -> anyhow::Result<()> {
|
||||
fn show_patch_diff(patch: &patch::Patch, storage: &Repository) -> anyhow::Result<()> {
|
||||
let target_head = patch_merge_target_oid(patch.target(), storage)?;
|
||||
let base_oid = storage.raw().merge_base(target_head, **patch.head())?;
|
||||
let diff = format!("{}..{}", base_oid, patch.head());
|
||||
|
|
@ -46,32 +41,53 @@ pub fn run(
|
|||
let Some(patch) = patches.get(patch_id)? else {
|
||||
anyhow::bail!("Patch `{patch_id}` not found");
|
||||
};
|
||||
let (_, revision) = patch
|
||||
.latest()
|
||||
.ok_or_else(|| anyhow!("patch is malformed: no revisions found"))?;
|
||||
let state = patch.state();
|
||||
let branches = common::branches(&revision.head(), workdir)?;
|
||||
let target_head = common::patch_merge_target_oid(patch.target(), stored)?;
|
||||
let ahead_behind = common::ahead_behind(stored.raw(), revision.head().into(), target_head)?;
|
||||
|
||||
let mut attrs = Table::<2, Paint<String>>::new(TableOptions {
|
||||
let mut attrs = Table::<2, term::Line>::new(TableOptions {
|
||||
spacing: 2,
|
||||
..TableOptions::default()
|
||||
});
|
||||
attrs.push([
|
||||
term::format::tertiary("Title".to_owned()),
|
||||
term::format::bold(patch.title().to_owned()),
|
||||
term::format::tertiary("Title".to_owned()).into(),
|
||||
term::format::bold(patch.title().to_owned()).into(),
|
||||
]);
|
||||
attrs.push([
|
||||
term::format::tertiary("Patch".to_owned()),
|
||||
term::format::default(patch_id.to_string()),
|
||||
term::format::tertiary("Patch".to_owned()).into(),
|
||||
term::format::default(patch_id.to_string()).into(),
|
||||
]);
|
||||
attrs.push([
|
||||
term::format::tertiary("Author".to_owned()),
|
||||
term::format::default(patch.author().id().to_string()),
|
||||
term::format::tertiary("Author".to_owned()).into(),
|
||||
term::format::default(patch.author().id().to_string()).into(),
|
||||
]);
|
||||
attrs.push([
|
||||
term::format::tertiary("Status".to_owned()),
|
||||
term::format::tertiary("Head".to_owned()).into(),
|
||||
term::format::secondary(revision.head().to_string()).into(),
|
||||
]);
|
||||
if !branches.is_empty() {
|
||||
attrs.push([
|
||||
term::format::tertiary("Branches".to_owned()).into(),
|
||||
term::format::yellow(branches.join(", ")).into(),
|
||||
]);
|
||||
}
|
||||
attrs.push([
|
||||
term::format::tertiary("Commits".to_owned()).into(),
|
||||
ahead_behind,
|
||||
]);
|
||||
attrs.push([
|
||||
term::format::tertiary("Status".to_owned()).into(),
|
||||
match state {
|
||||
patch::State::Open => term::format::positive(state.to_string()),
|
||||
patch::State::Draft => term::format::dim(state.to_string()),
|
||||
patch::State::Archived => term::format::yellow(state.to_string()),
|
||||
patch::State::Merged => term::format::primary(state.to_string()),
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
]);
|
||||
|
||||
let description = patch.description().trim();
|
||||
|
|
@ -95,7 +111,7 @@ pub fn run(
|
|||
|
||||
if diff {
|
||||
term::blank();
|
||||
show_patch_diff(&patch, stored, workdir)?;
|
||||
show_patch_diff(&patch, stored)?;
|
||||
term::blank();
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use super::Paint;
|
||||
use super::{Element, Line, Paint};
|
||||
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
|
|
@ -14,8 +14,10 @@ pub trait Cell: Display {
|
|||
/// Cell display width in number of terminal columns.
|
||||
fn width(&self) -> usize;
|
||||
/// Truncate cell if longer than given width. Shows the delimiter if truncated.
|
||||
#[must_use]
|
||||
fn truncate(&self, width: usize, delim: &str) -> Self::Truncated;
|
||||
/// Pad the cell so that it is the given width, while keeping the content left-aligned.
|
||||
#[must_use]
|
||||
fn pad(&self, width: usize) -> Self::Padded;
|
||||
}
|
||||
|
||||
|
|
@ -42,6 +44,27 @@ impl Cell for Paint<String> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Cell for Line {
|
||||
type Truncated = Line;
|
||||
type Padded = Line;
|
||||
|
||||
fn width(&self) -> usize {
|
||||
<Self as Element>::size(self).cols
|
||||
}
|
||||
|
||||
fn pad(&self, width: usize) -> Self::Padded {
|
||||
let mut line = self.clone();
|
||||
Line::pad(&mut line, width);
|
||||
line
|
||||
}
|
||||
|
||||
fn truncate(&self, width: usize, delim: &str) -> Self::Truncated {
|
||||
let mut line = self.clone();
|
||||
Line::truncate(&mut line, width, delim);
|
||||
line
|
||||
}
|
||||
}
|
||||
|
||||
impl Cell for Paint<&str> {
|
||||
type Truncated = Paint<String>;
|
||||
type Padded = Paint<String>;
|
||||
|
|
|
|||
|
|
@ -2,3 +2,6 @@ use crate::ansi::Color;
|
|||
|
||||
/// The faintest color; useful for borders and such.
|
||||
pub const FAINT: Color = Color::Fixed(236);
|
||||
|
||||
/// Negative color, useful for errors.
|
||||
pub const NEGATIVE: Color = Color::Red;
|
||||
|
|
|
|||
|
|
@ -157,9 +157,9 @@ impl IntoIterator for Line {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Label> for Line {
|
||||
fn from(label: Label) -> Self {
|
||||
Self { items: vec![label] }
|
||||
impl<T: Into<Label>> From<T> for Line {
|
||||
fn from(value: T) -> Self {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -218,19 +218,19 @@ mod test {
|
|||
let line = Line::default().item("banana").item("peach").item("apple");
|
||||
|
||||
let mut actual = line.clone();
|
||||
actual.truncate(9, "…");
|
||||
actual = actual.truncate(9, "…");
|
||||
assert_eq!(actual.to_string(), "bananape…");
|
||||
|
||||
let mut actual = line.clone();
|
||||
actual.truncate(7, "…");
|
||||
actual = actual.truncate(7, "…");
|
||||
assert_eq!(actual.to_string(), "banana…");
|
||||
|
||||
let mut actual = line.clone();
|
||||
actual.truncate(1, "…");
|
||||
actual = actual.truncate(1, "…");
|
||||
assert_eq!(actual.to_string(), "…");
|
||||
|
||||
let mut actual = line;
|
||||
actual.truncate(0, "…");
|
||||
actual = actual.truncate(0, "…");
|
||||
assert_eq!(actual.to_string(), "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use std::fmt;
|
|||
|
||||
use crate as term;
|
||||
use crate::cell::Cell;
|
||||
use crate::{Color, Label, Line, Max, Paint, Size};
|
||||
use crate::{Color, Line, Max, Paint, Size};
|
||||
|
||||
pub use crate::Element;
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ impl<const W: usize, T> Default for Table<W, T> {
|
|||
|
||||
impl<const W: usize, T: Cell + fmt::Debug> Element for Table<W, T>
|
||||
where
|
||||
T::Padded: Into<Label>,
|
||||
T::Padded: Into<Line>,
|
||||
{
|
||||
fn size(&self) -> Size {
|
||||
Table::size(self)
|
||||
|
|
@ -123,11 +123,11 @@ where
|
|||
} else {
|
||||
self.widths[i] + self.opts.spacing
|
||||
};
|
||||
line.push(cell.pad(pad));
|
||||
line = line.extend(cell.pad(pad).into());
|
||||
}
|
||||
|
||||
if let Some(width) = width {
|
||||
line.truncate(width, "…");
|
||||
line = line.truncate(width, "…");
|
||||
}
|
||||
if let Some(color) = border {
|
||||
line.push(Paint::new(" │").fg(color));
|
||||
|
|
|
|||
Loading…
Reference in New Issue