Skip to content

Commit

Permalink
fix lotabout#248: Implement '{n}' placeholder expression
Browse files Browse the repository at this point in the history
  • Loading branch information
lotabout authored and paul committed May 28, 2020
1 parent 5b0e95e commit 43342fa
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 14 deletions.
13 changes: 12 additions & 1 deletion man/man1/sk.1
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,21 @@ e.g. \fBsk --preview='head -$LINES {}'\fR
sk overrides \fB$LINES\fR and \fB$COLUMNS\fR so that they represent the exact
size of the preview window.

A placeholder expression starting with \fB+\fR flag will be replaced to the
space-separated list of the selected lines (or the current line if no selection
was made) individually quoted.

e.g.
\fBsk --multi --preview='head -10 {+}'
git log --oneline | sk --multi --preview 'git show {+1}'\fR


Note that you can escape a placeholder pattern by prepending a backslash.

Also, \fB{q}\fR is replaced to the current query string. \fB{cq}\fR is
replaced to the current command query string.
replaced to the current command query string. \fB{n}\fR is replaced to
zero-based ordinal index of the line. Use \fB{+n}\fR if you want all index
numbers when multiple lines are selected

Preview window will be updated even when there is no match for the current
query if any of the placeholder expressions evaluates to a non-empty string.
Expand Down
8 changes: 7 additions & 1 deletion src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,13 @@ pub fn filter(options: &SkimOptions, source: Option<SkimItemReceiver>) -> Result
.try_for_each(|matched| {
num_matched += 1;
if options.print_score {
write!(stdout, "{}\t{}{}", -matched.rank.score, matched.item.output(), output_ending)
write!(
stdout,
"{}\t{}{}",
-matched.rank.score,
matched.item.output(),
output_ending
)
} else {
write!(stdout, "{}{}", matched.item.output(), output_ending)
}
Expand Down
9 changes: 6 additions & 3 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,14 +366,17 @@ impl Model {
let query = self.query.get_fz_query();
let cmd_query = self.query.get_cmd_query();

let tmp = self.selection.get_selected_items();
let tmp: Vec<Cow<str>> = tmp.iter().map(|item| item.text()).collect();
let selections = self.selection.get_selected_wrapped_items();
let tmp: Vec<Cow<str>> = selections.iter().map(|item| item.text()).collect();
let selected_texts: Vec<&str> = tmp.iter().map(|cow| cow.as_ref()).collect();
let indices: Vec<usize> = selections.iter().map(|x| x.get_index()).collect();

let context = InjectContext {
current_index: item.as_ref().map(|x| x.get_index()).unwrap_or(0),
delimiter: &self.delimiter,
current_selection: &current_selection,
selections: &selected_texts,
indices: &indices,
query: &query,
cmd_query: &cmd_query,
};
Expand Down Expand Up @@ -552,7 +555,7 @@ impl Model {
let item = self.selection.get_current_item();
if let Some(previewer) = self.previewer.as_mut() {
let selections = &self.selection;
let get_selected_items = || selections.get_selected_items();
let get_selected_items = || selections.get_selected_wrapped_items();
previewer.on_item_change(
item,
env.query.to_string(),
Expand Down
9 changes: 6 additions & 3 deletions src/previewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl Previewer {
new_query: impl Into<Option<String>>,
new_cmd_query: impl Into<Option<String>>,
num_selected: usize,
get_selected_items: impl Fn() -> Vec<Arc<dyn SkimItem>>, // lazy get
get_selected_items: impl Fn() -> Vec<Arc<ItemWrapper>>, // lazy get
) {
let new_item = new_item.into();
let new_query = new_query.into();
Expand Down Expand Up @@ -156,14 +156,17 @@ impl Previewer {
let query = self.prev_query.as_ref().map(|s| &**s).unwrap_or("");
let cmd_query = self.prev_cmd_query.as_ref().map(|s| &**s).unwrap_or("");

let tmp = get_selected_items();
let tmp: Vec<Cow<str>> = tmp.iter().map(|item| item.text()).collect();
let selections = get_selected_items();
let tmp: Vec<Cow<str>> = selections.iter().map(|item| item.text()).collect();
let selected_texts: Vec<&str> = tmp.iter().map(|cow| cow.as_ref()).collect();
let indices: Vec<usize> = selections.iter().map(|x| x.get_index()).collect();

let context = InjectContext {
current_index: item.get_index(),
delimiter: &self.delimiter,
current_selection: &current_selection,
selections: &selected_texts,
indices: &indices,
query: &query,
cmd_query: &cmd_query,
};
Expand Down
11 changes: 9 additions & 2 deletions src/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ impl Selection {
self.hscroll_offset = hscroll_offset as usize;
}

pub fn get_selected_items(&self) -> Vec<Arc<dyn SkimItem>> {
pub fn get_selected_wrapped_items(&self) -> Vec<Arc<ItemWrapper>> {
// select the current one
let select_cursor = !self.multi_selection || self.selected.is_empty();
let mut selected: Vec<Arc<ItemWrapper>> = self.selected.values().cloned().collect();
Expand All @@ -271,7 +271,14 @@ impl Selection {
}

selected.sort_by_key(|item| item.get_id());
selected.into_iter().map(|wrapped| wrapped.get_inner()).collect()
selected
}

pub fn get_selected_items(&self) -> Vec<Arc<dyn SkimItem>> {
self.get_selected_wrapped_items()
.into_iter()
.map(|wrapped| wrapped.get_inner())
.collect()
}

pub fn get_num_of_selected_exclude_current(&self) -> usize {
Expand Down
34 changes: 30 additions & 4 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,15 +301,17 @@ pub fn parse_margin(margin_option: &str) -> (Size, Size, Size, Size) {
#[derive(Copy, Clone)]
pub struct InjectContext<'a> {
pub delimiter: &'a Regex,
pub current_index: usize,
pub current_selection: &'a str,
pub indices: &'a [usize],
pub selections: &'a [&'a str],
pub query: &'a str,
pub cmd_query: &'a str,
}

lazy_static! {
static ref RE_ITEMS: Regex = Regex::new(r"\\?(\{ *-?[0-9.+]*? *})").unwrap();
static ref RE_FIELDS: Regex = Regex::new(r"\\?(\{ *-?[0-9.,cq+]*? *})").unwrap();
static ref RE_FIELDS: Regex = Regex::new(r"\\?(\{ *-?[0-9.,cq+n]*? *})").unwrap();
}

/// Check if a command depends on item
Expand Down Expand Up @@ -340,24 +342,42 @@ pub fn inject_command<'a>(cmd: &'a str, context: InjectContext<'a>) -> Cow<'a, s
let range = &range[1..range.len() - 1];
let range = range.trim();

if range == "+" {
if range.starts_with("+") {
let current_selection = vec![context.current_selection];
let selections = if context.selections.is_empty() {
&current_selection
} else {
context.selections
};
let current_index = vec![context.current_index];
let indices = if context.indices.is_empty() {
&current_index
} else {
context.indices
};

return selections
.iter()
.map(|&s| format!("'{}'", escape_single_quote(s)))
.zip(indices.iter())
.map(|(&s, &i)| {
let rest = &range[1..];
let index_str = format!("{}", i);
let replacement = match rest {
"" => s,
"n" => &index_str,
_ => get_string_by_range(context.delimiter, s, rest).unwrap_or(""),
};
format!("'{}'", escape_single_quote(replacement))
})
.collect::<Vec<_>>()
.join(" ");
}

let index_str = format!("{}", context.current_index);
let replacement = match range {
"" => context.current_selection,
"+" => unreachable!(),
x if x.starts_with("+") => unreachable!(),
"n" => &index_str,
"q" => context.query,
"cq" => context.cmd_query,
_ => get_string_by_range(context.delimiter, context.current_selection, range).unwrap_or(""),
Expand Down Expand Up @@ -398,9 +418,11 @@ mod tests {
let cmd_query = "cmd_query";

let default_context = InjectContext {
current_index: 0,
delimiter: &delimiter,
current_selection,
selections: &selections,
indices: &[0, 1],
query,
cmd_query,
};
Expand All @@ -422,6 +444,10 @@ mod tests {
assert_eq!("'query'", inject_command("{q}", default_context));
assert_eq!("'cmd_query'", inject_command("{cq}", default_context));
assert_eq!("'a,b,c' 'x,y,z'", inject_command("{+}", default_context));
assert_eq!("'0'", inject_command("{n}", default_context));
assert_eq!("'a' 'x'", inject_command("{+1}", default_context));
assert_eq!("'b' 'y'", inject_command("{+2}", default_context));
assert_eq!("'0' '1'", inject_command("{+n}", default_context));
}

#[test]
Expand Down

0 comments on commit 43342fa

Please sign in to comment.