Allow multiline commands (#262)

Fixes #260
This commit is contained in:
Denis Isidoro 2020-03-14 18:08:57 -03:00 committed by GitHub
parent 5423cd8ccb
commit 3fa141846b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 37 deletions

View file

@ -157,6 +157,15 @@ $ branch: git branch | awk '{print $NF}'
It's irrelevant how many files are used to store cheatsheets. They can be all in a single file if you wish, as long as you split them accordingly with lines starting with `%`.
Commands may be multiline:
```sh
# This will output foo\nyes
echo foo
echo bar | grep q -b \
&& echo yes \
|| echo no
```
### Variables
The interface prompts for variable names inside brackets (eg `<branch>`).

View file

@ -31,14 +31,6 @@ pub enum SuggestionType {
pub type Suggestion = (String, Option<SuggestionOpts>);
fn gen_snippet(snippet: &str, line: &str) -> String {
if snippet.is_empty() {
line.to_string()
} else {
format!("{}{}", &snippet[..snippet.len() - 2], line)
}
}
fn remove_quote(txt: &str) -> String {
txt.replace('"', "").replace('\'', "")
}
@ -87,6 +79,32 @@ fn parse_variable_line(line: &str) -> (&str, &str, Option<SuggestionOpts>) {
(variable, command, command_options)
}
fn write_cmd(
tags: &str,
comment: &str,
snippet: &str,
tag_width: usize,
comment_width: usize,
stdin: &mut std::process::ChildStdin,
) -> bool {
if snippet.is_empty() {
true
} else {
stdin
.write_all(
display::format_line(
&tags[..],
&comment[..],
&snippet[3..],
tag_width,
comment_width,
)
.as_bytes(),
)
.is_ok()
}
}
fn read_file(
path: &str,
variables: &mut HashMap<String, Suggestion>,
@ -104,6 +122,10 @@ fn read_file(
// tag
if line.starts_with('%') {
if !write_cmd(&tags, &comment, &snippet, tag_width, comment_width, stdin) {
break;
}
snippet = String::from("");
tags = String::from(&line[2..]);
}
// metacomment
@ -111,46 +133,36 @@ fn read_file(
}
// comment
else if line.starts_with('#') {
if !write_cmd(&tags, &comment, &snippet, tag_width, comment_width, stdin) {
break;
}
snippet = String::from("");
comment = String::from(&line[2..]);
}
// variable
else if line.starts_with('$') {
if !write_cmd(&tags, &comment, &snippet, tag_width, comment_width, stdin) {
break;
}
snippet = String::from("");
let (variable, command, opts) = parse_variable_line(&line);
variables.insert(
format!("{};{}", tags, variable),
(String::from(command), opts),
);
}
// snippet with line break
else if line.ends_with('\\') {
snippet = if !snippet.is_empty() {
format!("{}{}", &snippet[..snippet.len() - 2], line)
} else {
line
}
}
// blank
else if line.is_empty() {
}
// snippet
else {
let full_snippet = gen_snippet(&snippet, &line);
match stdin.write_all(
display::format_line(
&tags[..],
&comment[..],
&full_snippet[..],
tag_width,
comment_width,
)
.as_bytes(),
) {
Ok(_) => snippet = String::from(""),
Err(_) => break,
}
snippet.push_str(display::LINE_SEPARATOR);
snippet.push_str(&line);
}
}
}
write_cmd(&tags, &comment, &snippet, tag_width, comment_width, stdin);
}
pub fn read_all(

View file

@ -173,6 +173,10 @@ fn replace_variables_from_snippet(
interpolated_snippet
}
fn with_new_lines(txt: String) -> String {
txt.replace(display::LINE_SEPARATOR, "\n")
}
pub fn main(variant: Variant, config: Config, contains_key: bool) -> Result<(), Box<dyn Error>> {
let _ = display::WIDTHS;
@ -181,8 +185,12 @@ pub fn main(variant: Variant, config: Config, contains_key: bool) -> Result<(),
});
let (key, tags, snippet) = extract_from_selections(&raw_selection[..], contains_key);
let interpolated_snippet =
replace_variables_from_snippet(snippet, tags, variables.unwrap(), &config);
let interpolated_snippet = with_new_lines(replace_variables_from_snippet(
snippet,
tags,
variables.unwrap(),
&config,
));
if key == "ctrl-y" {
cmds::aux::abort("copying snippets to the clipboard", 201)?

View file

@ -1,5 +1,6 @@
use crate::terminal;
use regex::Regex;
use std::cmp::max;
use termion::color;
@ -7,6 +8,8 @@ static COMMENT_COLOR: color::LightCyan = color::LightCyan;
static TAG_COLOR: color::Blue = color::Blue;
static SNIPPET_COLOR: color::White = color::White;
static NEWLINE_ESCAPE_CHAR: char = '\x15';
pub static LINE_SEPARATOR: &str = " \x15 ";
pub static DELIMITER: &str = r" ";
lazy_static! {
@ -24,12 +27,22 @@ pub fn variable_prompt(varname: &str) -> String {
format!("{}: ", varname)
}
fn fix_newlines(txt: &str) -> String {
if txt.contains(NEWLINE_ESCAPE_CHAR) {
let re = Regex::new(r"\\\s+").unwrap();
re.replace_all(txt.replace(LINE_SEPARATOR, " ").as_str(), "")
.to_string()
} else {
txt.to_string()
}
}
pub fn preview(comment: &str, tags: &str, snippet: &str) {
println!(
"{comment_color}{comment} {tag_color}{tags} \n{snippet_color}{snippet}",
comment = format!("# {}", comment),
tags = format!("[{}]", tags),
snippet = snippet,
snippet = fix_newlines(snippet),
comment_color = color::Fg(COMMENT_COLOR),
tag_color = color::Fg(TAG_COLOR),
snippet_color = color::Fg(SNIPPET_COLOR),
@ -47,7 +60,7 @@ fn limit_str(text: &str, length: usize) -> String {
pub fn format_line(
tags: &str,
comment: &str,
full_snippet: &str,
snippet: &str,
tag_width: usize,
comment_width: usize,
) -> String {
@ -55,12 +68,12 @@ pub fn format_line(
"{tag_color}{tags_short}{delimiter}{comment_color}{comment_short}{delimiter}{snippet_color}{snippet_short}{delimiter}{tags}{delimiter}{comment}{delimiter}{snippet}{delimiter}\n",
tags_short = limit_str(tags, tag_width),
comment_short = limit_str(comment, comment_width),
snippet_short = full_snippet,
snippet_short = fix_newlines(snippet),
comment_color = color::Fg(COMMENT_COLOR),
tag_color = color::Fg(TAG_COLOR),
snippet_color = color::Fg(SNIPPET_COLOR),
tags = tags,
comment = comment,
delimiter = DELIMITER,
snippet = &full_snippet)
snippet = &snippet)
}

View file

@ -7,4 +7,14 @@ echo -ne "\033]0;$(hostname)\007"
echo "8.8.8.8 via 172.17.0.1 dev eth0 src 172.17.0.2" | sed -E 's/.*src ([0-9.]+).*/\1/p' | head -n1
# simple echo
echo "foo"
echo "foo"
# multiline command without backslash
echo "foo"
echo "bar"
# multiline command with backslash
echo "foo" \
| grep -q "bar" \
&& echo "match" \
|| echo "no match"