cli: Show repo summary on `rad clone`
After cloning, we show a cool little summary with some repo stats. We had to make some adjustments to the way visual width is calculated due to a bug surfaced around emoji width calculation. Unfortunately there isn't a perfect solution that works across terminals yet, so the "variation selector 16" code-point is filtered out for now.
This commit is contained in:
parent
0c43b05066
commit
e59e196da8
|
|
@ -10,6 +10,12 @@ $ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope all
|
||||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||||
✓ Repository successfully cloned under [..]/heartwood/
|
✓ Repository successfully cloned under [..]/heartwood/
|
||||||
|
╭────────────────────────────────────╮
|
||||||
|
│ heartwood │
|
||||||
|
│ Radicle Heartwood Protocol & Stack │
|
||||||
|
│ 0 issues · 0 patches │
|
||||||
|
╰────────────────────────────────────╯
|
||||||
|
Run `cd ./heartwood` to go to the project directory.
|
||||||
```
|
```
|
||||||
|
|
||||||
We can now have a look at the new working copy that was created from the cloned
|
We can now have a look at the new working copy that was created from the cloned
|
||||||
|
|
|
||||||
|
|
@ -13,4 +13,10 @@ $ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||||
✓ Repository successfully cloned under [..]/heartwood/
|
✓ Repository successfully cloned under [..]/heartwood/
|
||||||
|
╭────────────────────────────────────╮
|
||||||
|
│ heartwood │
|
||||||
|
│ Radicle Heartwood Protocol & Stack │
|
||||||
|
│ 0 issues · 0 patches │
|
||||||
|
╰────────────────────────────────────╯
|
||||||
|
Run `cd ./heartwood` to go to the project directory.
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,12 @@ $ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope trusted
|
||||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||||
✓ Repository successfully cloned under [..]/heartwood/
|
✓ Repository successfully cloned under [..]/heartwood/
|
||||||
|
╭────────────────────────────────────╮
|
||||||
|
│ heartwood │
|
||||||
|
│ Radicle Heartwood Protocol & Stack │
|
||||||
|
│ 0 issues · 0 patches │
|
||||||
|
╰────────────────────────────────────╯
|
||||||
|
Run `cd ./heartwood` to go to the project directory.
|
||||||
```
|
```
|
||||||
|
|
||||||
We can now have a look at the new working copy that was created from the cloned
|
We can now have a look at the new working copy that was created from the cloned
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,12 @@ $ rad clone rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK
|
||||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||||
✓ Repository successfully cloned under [..]/heartwood/
|
✓ Repository successfully cloned under [..]/heartwood/
|
||||||
|
╭────────────────────────────────────╮
|
||||||
|
│ heartwood │
|
||||||
|
│ radicle heartwood protocol & stack │
|
||||||
|
│ 0 issues · 0 patches │
|
||||||
|
╰────────────────────────────────────╯
|
||||||
|
Run `cd ./heartwood` to go to the project directory.
|
||||||
```
|
```
|
||||||
|
|
||||||
We wait for Alice to sync our fork.
|
We wait for Alice to sync our fork.
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use std::time;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use radicle::cob;
|
||||||
use radicle::git::raw;
|
use radicle::git::raw;
|
||||||
use radicle::identity::doc::{DocError, Id};
|
use radicle::identity::doc::{DocError, Id};
|
||||||
use radicle::identity::{doc, IdentityError};
|
use radicle::identity::{doc, IdentityError};
|
||||||
|
|
@ -23,6 +24,7 @@ use crate::commands::rad_sync as sync;
|
||||||
use crate::project;
|
use crate::project;
|
||||||
use crate::terminal as term;
|
use crate::terminal as term;
|
||||||
use crate::terminal::args::{Args, Error, Help};
|
use crate::terminal::args::{Args, Error, Help};
|
||||||
|
use crate::terminal::Element as _;
|
||||||
|
|
||||||
pub const HELP: Help = Help {
|
pub const HELP: Help = Help {
|
||||||
name: "clone",
|
name: "clone",
|
||||||
|
|
@ -113,7 +115,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (working, doc, proj) = clone(
|
let (working, repo, doc, proj) = clone(
|
||||||
options.id,
|
options.id,
|
||||||
options.announce,
|
options.announce,
|
||||||
options.scope,
|
options.scope,
|
||||||
|
|
@ -145,7 +147,28 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
|
|
||||||
term::success!(
|
term::success!(
|
||||||
"Repository successfully cloned under {}",
|
"Repository successfully cloned under {}",
|
||||||
term::format::highlight(Path::new(".").join(path).display())
|
term::format::dim(Path::new(".").join(path).display())
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut info: term::Table<1, term::Line> = term::Table::new(term::TableOptions::bordered());
|
||||||
|
info.push([term::format::bold(proj.name()).into()]);
|
||||||
|
info.push([term::format::italic(proj.description()).into()]);
|
||||||
|
|
||||||
|
let issues = cob::issue::Issues::open(&repo)?.counts()?;
|
||||||
|
let patches = cob::patch::Patches::open(&repo)?.counts()?;
|
||||||
|
|
||||||
|
info.push([term::Line::spaced([
|
||||||
|
term::format::tertiary(issues.open).into(),
|
||||||
|
term::format::default("issues").into(),
|
||||||
|
term::format::dim("·").into(),
|
||||||
|
term::format::tertiary(patches.open).into(),
|
||||||
|
term::format::default("patches").into(),
|
||||||
|
])]);
|
||||||
|
info.print();
|
||||||
|
|
||||||
|
term::info!(
|
||||||
|
"Run {} to go to the project directory.",
|
||||||
|
term::format::command(format!("cd ./{}", proj.name())),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -180,7 +203,15 @@ pub fn clone<G: Signer>(
|
||||||
node: &mut Node,
|
node: &mut Node,
|
||||||
signer: &G,
|
signer: &G,
|
||||||
storage: &Storage,
|
storage: &Storage,
|
||||||
) -> Result<(raw::Repository, Doc<Verified>, Project), CloneError> {
|
) -> Result<
|
||||||
|
(
|
||||||
|
raw::Repository,
|
||||||
|
storage::git::Repository,
|
||||||
|
Doc<Verified>,
|
||||||
|
Project,
|
||||||
|
),
|
||||||
|
CloneError,
|
||||||
|
> {
|
||||||
let me = *signer.public_key();
|
let me = *signer.public_key();
|
||||||
|
|
||||||
// Track.
|
// Track.
|
||||||
|
|
@ -244,9 +275,9 @@ pub fn clone<G: Signer>(
|
||||||
"Creating checkout in ./{}..",
|
"Creating checkout in ./{}..",
|
||||||
term::format::tertiary(path.display())
|
term::format::tertiary(path.display())
|
||||||
));
|
));
|
||||||
let repo = rad::checkout(id, &me, path, &storage)?;
|
let working = rad::checkout(id, &me, path, &storage)?;
|
||||||
|
|
||||||
spinner.finish();
|
spinner.finish();
|
||||||
|
|
||||||
Ok((repo, doc, proj))
|
Ok((working, repository, doc, proj))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use std::fmt::Display;
|
||||||
|
|
||||||
use super::{Element, Line, Paint};
|
use super::{Element, Line, Paint};
|
||||||
|
|
||||||
|
use unicode_segmentation::UnicodeSegmentation as _;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
/// Text that can be displayed on the terminal, measured, truncated and padded.
|
/// Text that can be displayed on the terminal, measured, truncated and padded.
|
||||||
|
|
@ -26,7 +27,7 @@ impl Cell for Paint<String> {
|
||||||
type Padded = Self;
|
type Padded = Self;
|
||||||
|
|
||||||
fn width(&self) -> usize {
|
fn width(&self) -> usize {
|
||||||
UnicodeWidthStr::width(self.content())
|
Cell::width(self.content())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn truncate(&self, width: usize, delim: &str) -> Self {
|
fn truncate(&self, width: usize, delim: &str) -> Self {
|
||||||
|
|
@ -110,12 +111,10 @@ impl Cell for str {
|
||||||
type Padded = String;
|
type Padded = String;
|
||||||
|
|
||||||
fn width(&self) -> usize {
|
fn width(&self) -> usize {
|
||||||
UnicodeWidthStr::width(self)
|
self.graphemes(true).map(UnicodeWidthStr::width).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn truncate(&self, width: usize, delim: &str) -> String {
|
fn truncate(&self, width: usize, delim: &str) -> String {
|
||||||
use unicode_segmentation::UnicodeSegmentation as _;
|
|
||||||
|
|
||||||
if width < Cell::width(self) {
|
if width < Cell::width(self) {
|
||||||
let d = Cell::width(delim);
|
let d = Cell::width(delim);
|
||||||
if width < d {
|
if width < d {
|
||||||
|
|
|
||||||
|
|
@ -237,4 +237,12 @@ mod test {
|
||||||
actual = actual.truncate(0, "…");
|
actual = actual.truncate(0, "…");
|
||||||
assert_eq!(actual.to_string(), "");
|
assert_eq!(actual.to_string(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_width() {
|
||||||
|
let line = Line::new("Radicle Heartwood Protocol & Stack ❤️ 🪵");
|
||||||
|
assert_eq!(line.width(), 39, "{line}");
|
||||||
|
let line = Line::new("❤\u{fe0f}");
|
||||||
|
assert_eq!(line.width(), 1, "{line}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ pub struct Label(Paint<String>);
|
||||||
impl Label {
|
impl Label {
|
||||||
/// Create a new label.
|
/// Create a new label.
|
||||||
pub fn new(s: &str) -> Self {
|
pub fn new(s: &str) -> Self {
|
||||||
Self(Paint::new(s.replace('\n', " ")))
|
Self(Paint::new(cleanup(s)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a blank label.
|
/// Create a blank label.
|
||||||
|
|
@ -64,7 +64,7 @@ impl Cell for Label {
|
||||||
impl<D: fmt::Display> From<Paint<D>> for Label {
|
impl<D: fmt::Display> From<Paint<D>> for Label {
|
||||||
fn from(paint: Paint<D>) -> Self {
|
fn from(paint: Paint<D>) -> Self {
|
||||||
Self(Paint {
|
Self(Paint {
|
||||||
item: paint.item.to_string().replace('\n', " "),
|
item: cleanup(paint.item.to_string().as_str()),
|
||||||
style: paint.style,
|
style: paint.style,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -72,13 +72,13 @@ impl<D: fmt::Display> From<Paint<D>> for Label {
|
||||||
|
|
||||||
impl From<String> for Label {
|
impl From<String> for Label {
|
||||||
fn from(value: String) -> Self {
|
fn from(value: String) -> Self {
|
||||||
Label::from(value.as_str())
|
Self::new(value.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for Label {
|
impl From<&str> for Label {
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
Self(Paint::new(value.replace('\n', " ")))
|
Self::new(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,3 +86,11 @@ impl From<&str> for Label {
|
||||||
pub fn label(s: impl Into<Paint<String>>) -> Label {
|
pub fn label(s: impl Into<Paint<String>>) -> Label {
|
||||||
Label(s.into())
|
Label(s.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cleanup the input string for display as a label.
|
||||||
|
fn cleanup(input: &str) -> String {
|
||||||
|
input
|
||||||
|
.chars()
|
||||||
|
.filter(|c| *c != '\u{fe0f}' && *c != '\n' && *c != '\r')
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ pub use inquire::ui::Styled;
|
||||||
pub use io::*;
|
pub use io::*;
|
||||||
pub use label::{label, Label};
|
pub use label::{label, Label};
|
||||||
pub use spinner::{spinner, spinner_to, Spinner};
|
pub use spinner::{spinner, spinner_to, Spinner};
|
||||||
pub use table::Table;
|
pub use table::{Table, TableOptions};
|
||||||
pub use textarea::{textarea, TextArea};
|
pub use textarea::{textarea, TextArea};
|
||||||
pub use vstack::{VStack, VStackOptions};
|
pub use vstack::{VStack, VStackOptions};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue