From ba726a92b0e0cabe7877d06ef71895de97584066 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 10 Aug 2021 11:17:40 -0700 Subject: [PATCH 1/5] Move markdown formatting from derive to runtime This eliminates the newly added `edgedb-cli-md` local crate. It also allows to disable styling if stdout isn't tty and makes formatting more flexible overall. --- Cargo.lock | 12 +--------- Cargo.toml | 2 +- edgedb-cli-derive/Cargo.toml | 1 - edgedb-cli-derive/src/attrib.rs | 20 ----------------- edgedb-cli-derive/src/into_app.rs | 25 ++++++++++++++------- edgedb-cli-md/.gitignore | 3 --- edgedb-cli-md/Cargo.toml | 11 --------- src/main.rs | 1 + edgedb-cli-md/src/lib.rs => src/markdown.rs | 19 ++++++++++++++++ src/options.rs | 19 ++++++---------- 10 files changed, 46 insertions(+), 67 deletions(-) delete mode 100644 edgedb-cli-md/.gitignore delete mode 100644 edgedb-cli-md/Cargo.toml rename edgedb-cli-md/src/lib.rs => src/markdown.rs (86%) diff --git a/Cargo.lock b/Cargo.lock index 05f556d79..a5b802f78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -948,10 +948,10 @@ dependencies = [ "codespan-reporting", "colorful", "combine", + "crossterm", "dirs 3.0.2", "downcast-rs", "edgedb-cli-derive", - "edgedb-cli-md", "edgedb-client", "edgedb-derive", "edgedb-protocol", @@ -1029,7 +1029,6 @@ version = "0.3.0" dependencies = [ "clap", "clap_generate", - "edgedb-cli-md", "heck", "linked-hash-map", "proc-macro-error", @@ -1040,15 +1039,6 @@ dependencies = [ "trybuild", ] -[[package]] -name = "edgedb-cli-md" -version = "0.3.0" -dependencies = [ - "crossterm", - "minimad", - "termimad", -] - [[package]] name = "edgedb-client" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index f2549bdca..f8496aebe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ prettytable-rs = {version="0.8.0", default-features=false} tempfile = "3.1.0" codespan-reporting = "0.11" termcolor = "1.1.0" +crossterm = "0.19.0" async-listen = "0.2.0" sha1 = "0.6.0" hex = "0.4.3" @@ -79,7 +80,6 @@ toml = "0.5.8" termimad = "0.10.2" minimad = "0.7.0" edgedb-cli-derive = { path="edgedb-cli-derive" } -edgedb-cli-md = { path="edgedb-cli-md" } fs-err = "2.6.0" pem = "0.8" rustls = {version="0.19.1", features=["dangerous_configuration"]} diff --git a/edgedb-cli-derive/Cargo.toml b/edgedb-cli-derive/Cargo.toml index 60820c48b..503ce9578 100644 --- a/edgedb-cli-derive/Cargo.toml +++ b/edgedb-cli-derive/Cargo.toml @@ -6,7 +6,6 @@ authors = ["EdgeDB Inc. "] edition = "2018" [dependencies] -edgedb-cli-md = { path="../edgedb-cli-md" } clap = { git="https://github.com/clap-rs/clap" } clap_generate = { git="https://github.com/clap-rs/clap" } termimad = "0.10.2" diff --git a/edgedb-cli-derive/src/attrib.rs b/edgedb-cli-derive/src/attrib.rs index 2e6bd6238..e2f5e1671 100644 --- a/edgedb-cli-derive/src/attrib.rs +++ b/edgedb-cli-derive/src/attrib.rs @@ -8,7 +8,6 @@ use syn::parse::{Parse, Parser, ParseStream}; use syn::punctuated::Punctuated; use syn::token::Paren; -use edgedb_cli_md as mdstyle; use crate::kw; @@ -591,25 +590,6 @@ impl Markdown { }; parser.parse2(attr.tokens.clone()).unwrap_or_abort(); } - pub fn clap_text(&self) -> syn::LitStr { - let text = self.source.value(); - syn::LitStr::new(&mdstyle::format_markdown(&text), self.source.span()) - } - pub fn formatted_title(&self) -> syn::LitStr { - let text = self.source.value(); - let text = mdstyle::prepare_markdown(&text); - let mut text = mdstyle::parse_markdown(&text); - if !text.lines.is_empty() { - text.lines.drain(1..); - } - let skin = mdstyle::make_skin(); - let fmt = termimad::FmtText::from_text( - &skin, - text, - None, - ); - syn::LitStr::new(fmt.to_string().trim(), self.source.span()) - } } impl TryFrom for Case { diff --git a/edgedb-cli-derive/src/into_app.rs b/edgedb-cli-derive/src/into_app.rs index cafc0d9b5..c07a57019 100644 --- a/edgedb-cli-derive/src/into_app.rs +++ b/edgedb-cli-derive/src/into_app.rs @@ -26,9 +26,9 @@ pub fn structure(s: &types::Struct) -> TokenStream { let propagate_args = mk_struct_propagate(&s, &dest, &matches); let help = s.attrs.help.as_ref().or(s.attrs.doc.as_ref()) - .map(|text| text.clap_text().value()).unwrap_or_else(String::new); + .map(|text| text.source.value().to_string()).unwrap_or_else(String::new); let help_title = s.attrs.help.as_ref().or(s.attrs.doc.as_ref()) - .map(|text| text.formatted_title().value()) + .map(|text| text.source.value().to_string()) .unwrap_or_else(String::new); let subcmds = if let Some(sub) = s.fields.iter().find(|s| s.attrs.subcommand) @@ -227,9 +227,10 @@ fn mk_arg(field: &types::Field, case: &Case) -> TokenStream { } if let Some(text) = field.attrs.help.as_ref().or(field.attrs.doc.as_ref()) { - let formatted = text.clap_text(); + let source = &text.source; modifiers.extend(quote! { - #arg = #arg.about(#formatted); + let about = Box::new(crate::markdown::format_markdown(#source)); + #arg = #arg.about(Box::leak(about).as_str()); }); } if let Some(name) = field.attrs.name.as_ref() { @@ -294,6 +295,13 @@ fn mk_struct(s: &types::Struct, app: &syn::Ident, #app = #app.#name(#value); }); } + if let Some(doc) = &s.attrs.doc { + let source = &doc.source; + output.extend(quote! { + let about = Box::new(crate::markdown::format_markdown(#source)); + #app = #app.about(Box::leak(about).as_str()); + }); + } let (subcmd_interface, flat_interface) = if inheritance { (quote!(clap::Subcommand), quote!(clap::IntoApp)) } else { @@ -424,9 +432,10 @@ fn mk_subcommand(s: &types::Subcommand, sub: &syn::Ident) let mut modifiers = TokenStream::new(); if let Some(text) = s.attrs.about.as_ref().or(s.attrs.doc.as_ref()) { - let formatted = text.clap_text(); + let source = &text.source; modifiers.extend(quote! { - #sub = #sub.about(#formatted); + let about = Box::new(crate::markdown::format_markdown(#source)); + #sub = #sub.about(Box::leak(about).as_str()); }); } for (name, value) in &s.attrs.options { @@ -830,12 +839,12 @@ fn subcmd_to_desc(sub: &types::Subcommand, e: &types::Enum) -> TokenStream { }); let about = sub.attrs.about.as_ref() .or(sub.attrs.doc.as_ref()) - .map(|a| a.clap_text()) + .map(|a| a.source.clone()) .map(|v| quote!(Some(#v))) .unwrap_or_else(|| quote!(None)); let title = sub.attrs.about.as_ref() .or(sub.attrs.doc.as_ref()) - .map(|a| a.formatted_title()) + .map(|a| a.source.value().to_string()) .map(|v| quote!(Some(#v))) .unwrap_or_else(|| quote!(None)); let hidden = sub.attrs.hidden; diff --git a/edgedb-cli-md/.gitignore b/edgedb-cli-md/.gitignore deleted file mode 100644 index 2a66ba770..000000000 --- a/edgedb-cli-md/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/Cargo.lock -/target - diff --git a/edgedb-cli-md/Cargo.toml b/edgedb-cli-md/Cargo.toml deleted file mode 100644 index f1472ecdb..000000000 --- a/edgedb-cli-md/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "edgedb-cli-md" -license = "MIT/Apache-2.0" -version = "0.3.0" -authors = ["EdgeDB Inc. "] -edition = "2018" - -[dependencies] -termimad = "0.10.2" -minimad = "0.7.0" -crossterm = "0.19.0" diff --git a/src/main.rs b/src/main.rs index 638359d73..3dcf1c8f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,7 @@ mod highlight; mod hint; mod interactive; mod log_levels; +mod markdown; mod migrations; mod non_interactive; mod options; diff --git a/edgedb-cli-md/src/lib.rs b/src/markdown.rs similarity index 86% rename from edgedb-cli-md/src/lib.rs rename to src/markdown.rs index 80adce171..315e793e7 100644 --- a/edgedb-cli-md/src/lib.rs +++ b/src/markdown.rs @@ -24,6 +24,10 @@ pub fn prepare_markdown(text: &str) -> String { } pub fn make_skin() -> termimad::MadSkin { + if !atty::is(atty::Stream::Stdout) { + return termimad::MadSkin::no_style(); + } + use crossterm::style::{Color, Attribute}; let mut skin = termimad::MadSkin::default(); skin.bold.set_fg(Color::Reset); @@ -89,3 +93,18 @@ pub fn format_markdown(text: &str) -> String { ); fmt.to_string() } + +pub fn format_title(text: &str) -> String { + let text = prepare_markdown(&text); + let mut text = parse_markdown(&text); + if !text.lines.is_empty() { + text.lines.drain(1..); + } + let skin = make_skin(); + let fmt = termimad::FmtText::from_text( + &skin, + text, + None, + ); + fmt.to_string().trim().to_string() +} diff --git a/src/options.rs b/src/options.rs index 9c6f5f81a..ca19dbb59 100644 --- a/src/options.rs +++ b/src/options.rs @@ -13,8 +13,6 @@ use edgedb_client::Builder; use edgedb_cli_derive::EdbClap; use fs_err as fs; -use edgedb_cli_md as mdstyle; - use crate::cli; use crate::cli::options::CliCommand; use crate::commands::parser::Common; @@ -25,6 +23,7 @@ use crate::hint::HintExt; use crate::project; use crate::repl::OutputFormat; use crate::server; +use crate::markdown; pub mod describe; @@ -38,12 +37,6 @@ static CONNECTION_ARG_HINT: &str = "\ const CONN_OPTIONS_GROUP: &str = "CONNECTION OPTIONS (`edgedb --help-connect` to see the full list)"; -const EDGEDB_ABOUT: &str = "\ - Use the `edgedb` command-line tool to spin up local instances, \ - manage EdgeDB projects, create and apply migrations, and more. \ - \n\n\ - Running `edgedb` without a subcommand opens an interactive shell."; - pub trait PropagateArgs { fn propagate_args(&self, dest: &mut AnyMap, matches: &clap::ArgMatches); } @@ -150,6 +143,10 @@ pub struct ConnectionOptions { pub connect_timeout: Option, } +/// Use the `edgedb` command-line tool to spin up local instances, +/// manage EdgeDB projects, create and apply migrations, and more. +/// +/// Running `edgedb` without a subcommand opens an interactive shell. #[derive(EdbClap, Debug)] #[edb(main)] #[clap(setting=clap::AppSettings::DisableVersionFlag)] @@ -338,7 +335,7 @@ fn make_subcommand_help() -> String { } writeln!(&mut buf, " {:padding$} {}", format!("{} {}", cmd.name, subcmd.name), - wrap(sdescr.help_title), + wrap(&markdown::format_title(sdescr.help_title)), padding=padding ).unwrap(); } @@ -346,7 +343,7 @@ fn make_subcommand_help() -> String { empty_line = true; } else { writeln!(&mut buf, " {:padding$} {}", - cmd.name, wrap(cdescr.help_title), + cmd.name, wrap(&markdown::format_title(cdescr.help_title)), padding=padding ).unwrap(); empty_line = false; @@ -502,10 +499,8 @@ fn term_width() -> usize { impl Options { pub fn from_args_and_env() -> anyhow::Result { - let about = mdstyle::format_markdown(&EDGEDB_ABOUT); let app = ::into_app() .name("edgedb") - .about(about.as_str()) .term_width(term_width()); let app = update_main_help(app); let matches = get_matches(app); From 05e328e69619486c1aea3d42e2b348199f13a9c3 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 10 Aug 2021 19:49:25 -0700 Subject: [PATCH 2/5] markdown: update visibility of functions --- src/markdown.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/markdown.rs b/src/markdown.rs index 315e793e7..4a2faf30e 100644 --- a/src/markdown.rs +++ b/src/markdown.rs @@ -1,4 +1,4 @@ -pub fn prepare_markdown(text: &str) -> String { +fn prepare_markdown(text: &str) -> String { let mut min_indent = text.len(); for line in text.lines() { let stripped = line.trim_start(); @@ -23,7 +23,7 @@ pub fn prepare_markdown(text: &str) -> String { return buf; } -pub fn make_skin() -> termimad::MadSkin { +fn make_skin() -> termimad::MadSkin { if !atty::is(atty::Stream::Stdout) { return termimad::MadSkin::no_style(); } @@ -31,14 +31,16 @@ pub fn make_skin() -> termimad::MadSkin { use crossterm::style::{Color, Attribute}; let mut skin = termimad::MadSkin::default(); skin.bold.set_fg(Color::Reset); + skin.inline_code.set_fg(Color::Reset); skin.inline_code.set_bg(Color::Reset); skin.inline_code.add_attr(Attribute::Bold); + skin.code_block.set_fg(Color::Reset); skin.code_block.set_bg(Color::Reset); skin.code_block.add_attr(Attribute::Bold); skin } -pub fn parse_markdown(text: &str) -> minimad::Text { +fn parse_markdown(text: &str) -> minimad::Text { use minimad::{Text, Composite}; use minimad::Line::*; use minimad::CompositeStyle::*; From e04a2bf3d7c4d36d46265f0a55448572d5b3b804 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 10 Aug 2021 19:49:58 -0700 Subject: [PATCH 3/5] Apply markdown formatting to the CONNECTION OPTIONS group --- src/options.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/options.rs b/src/options.rs index ca19dbb59..d5d494513 100644 --- a/src/options.rs +++ b/src/options.rs @@ -365,6 +365,12 @@ fn update_main_help(mut app: clap::App) -> clap::App { let mut help = help[..subcmd_index].replacen("edgedb", "EdgeDB CLI", 1); help.push_str(&sub_cmd); + help = help.replacen( + CONN_OPTIONS_GROUP, + &markdown::format_markdown(CONN_OPTIONS_GROUP).trim(), + 1 + ); + let help = std::str::from_utf8(Vec::leak(help.into())).unwrap(); return app.override_help(help); } From 39d4036db7a600f973f09370eaad7b1f7b2255ee Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Wed, 11 Aug 2021 08:22:26 -0700 Subject: [PATCH 4/5] derive: get rid of Box::leak() --- edgedb-cli-derive/src/into_app.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/edgedb-cli-derive/src/into_app.rs b/edgedb-cli-derive/src/into_app.rs index c07a57019..aa0174a0b 100644 --- a/edgedb-cli-derive/src/into_app.rs +++ b/edgedb-cli-derive/src/into_app.rs @@ -229,8 +229,10 @@ fn mk_arg(field: &types::Field, case: &Case) -> TokenStream { if let Some(text) = field.attrs.help.as_ref().or(field.attrs.doc.as_ref()) { let source = &text.source; modifiers.extend(quote! { - let about = Box::new(crate::markdown::format_markdown(#source)); - #arg = #arg.about(Box::leak(about).as_str()); + static ABOUT: ::once_cell::sync::Lazy = + ::once_cell::sync::Lazy::new( + || crate::markdown::format_markdown(#source)); + #arg = #arg.about((&ABOUT).as_str()); }); } if let Some(name) = field.attrs.name.as_ref() { @@ -298,8 +300,10 @@ fn mk_struct(s: &types::Struct, app: &syn::Ident, if let Some(doc) = &s.attrs.doc { let source = &doc.source; output.extend(quote! { - let about = Box::new(crate::markdown::format_markdown(#source)); - #app = #app.about(Box::leak(about).as_str()); + static ABOUT: ::once_cell::sync::Lazy = + ::once_cell::sync::Lazy::new( + || crate::markdown::format_markdown(#source)); + #app = #app.about((&ABOUT).as_str()); }); } let (subcmd_interface, flat_interface) = if inheritance { @@ -434,8 +438,12 @@ fn mk_subcommand(s: &types::Subcommand, sub: &syn::Ident) if let Some(text) = s.attrs.about.as_ref().or(s.attrs.doc.as_ref()) { let source = &text.source; modifiers.extend(quote! { - let about = Box::new(crate::markdown::format_markdown(#source)); - #sub = #sub.about(Box::leak(about).as_str()); + { + static ABOUT: ::once_cell::sync::Lazy = + ::once_cell::sync::Lazy::new( + || crate::markdown::format_markdown(#source)); + #sub = #sub.about((&ABOUT).as_str()); + } }); } for (name, value) in &s.attrs.options { From 428f8c5799e3be722855a441676158633e30d939 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Wed, 11 Aug 2021 08:31:29 -0700 Subject: [PATCH 5/5] Memoize madskin init --- src/markdown.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/markdown.rs b/src/markdown.rs index 4a2faf30e..2b7abc59d 100644 --- a/src/markdown.rs +++ b/src/markdown.rs @@ -1,3 +1,5 @@ +use once_cell::sync::Lazy; + fn prepare_markdown(text: &str) -> String { let mut min_indent = text.len(); for line in text.lines() { @@ -23,12 +25,13 @@ fn prepare_markdown(text: &str) -> String { return buf; } -fn make_skin() -> termimad::MadSkin { +static MADSKIN: Lazy = Lazy::new(|| { + use crossterm::style::{Color, Attribute}; + if !atty::is(atty::Stream::Stdout) { return termimad::MadSkin::no_style(); } - use crossterm::style::{Color, Attribute}; let mut skin = termimad::MadSkin::default(); skin.bold.set_fg(Color::Reset); skin.inline_code.set_fg(Color::Reset); @@ -38,7 +41,7 @@ fn make_skin() -> termimad::MadSkin { skin.code_block.set_bg(Color::Reset); skin.code_block.add_attr(Attribute::Bold); skin -} +}); fn parse_markdown(text: &str) -> minimad::Text { use minimad::{Text, Composite}; @@ -87,9 +90,8 @@ fn parse_markdown(text: &str) -> minimad::Text { pub fn format_markdown(text: &str) -> String { let text = prepare_markdown(&text); let text = parse_markdown(&text); - let skin = make_skin(); let fmt = termimad::FmtText::from_text( - &skin, + &MADSKIN, text, None, ); @@ -102,9 +104,8 @@ pub fn format_title(text: &str) -> String { if !text.lines.is_empty() { text.lines.drain(1..); } - let skin = make_skin(); let fmt = termimad::FmtText::from_text( - &skin, + &MADSKIN, text, None, );