diff --git a/man/man1/sk.1 b/man/man1/sk.1 index 0d72896f..8535bdac 100644 --- a/man/man1/sk.1 +++ b/man/man1/sk.1 @@ -4,7 +4,7 @@ .SH NAME sk \- Fuzzy Finder in rust! .SH SYNOPSIS -\fBsk\fR [\fB\-\-tac\fR] [\fB\-\-min\-query\-length\fR] [\fB\-\-no\-sort\fR] [\fB\-t\fR|\fB\-\-tiebreak\fR] [\fB\-n\fR|\fB\-\-nth\fR] [\fB\-\-with\-nth\fR] [\fB\-d\fR|\fB\-\-delimiter\fR] [\fB\-e\fR|\fB\-\-exact\fR] [\fB\-\-regex\fR] [\fB\-\-algo\fR] [\fB\-\-case\fR] [\fB\-b\fR|\fB\-\-bind\fR] [\fB\-m\fR|\fB\-\-multi\fR] [\fB\-\-no\-multi\fR] [\fB\-\-no\-mouse\fR] [\fB\-c\fR|\fB\-\-cmd\fR] [\fB\-i\fR|\fB\-\-interactive\fR] [\fB\-I \fR] [\fB\-\-color\fR] [\fB\-\-no\-hscroll\fR] [\fB\-\-keep\-right\fR] [\fB\-\-skip\-to\-pattern\fR] [\fB\-\-no\-clear\-if\-empty\fR] [\fB\-\-no\-clear\-start\fR] [\fB\-\-no\-clear\fR] [\fB\-\-show\-cmd\-error\fR] [\fB\-\-cycle\fR] [\fB\-\-disabled\fR] [\fB\-\-layout\fR] [\fB\-\-reverse\fR] [\fB\-\-height\fR] [\fB\-\-no\-height\fR] [\fB\-\-min\-height\fR] [\fB\-\-margin\fR] [\fB\-p\fR|\fB\-\-prompt\fR] [\fB\-\-cmd\-prompt\fR] [\fB\-\-selector\fR] [\fB\-\-multi\-selector\fR] [\fB\-\-ansi\fR] [\fB\-\-tabstop\fR] [\fB\-\-info\fR] [\fB\-\-no\-info\fR] [\fB\-\-inline\-info\fR] [\fB\-\-header\fR] [\fB\-\-header\-lines\fR] [\fB\-\-border\fR] [\fB\-\-history\fR] [\fB\-\-history\-size\fR] [\fB\-\-cmd\-history\fR] [\fB\-\-cmd\-history\-size\fR] [\fB\-\-preview\fR] [\fB\-\-preview\-window\fR] [\fB\-q\fR|\fB\-\-query\fR] [\fB\-\-cmd\-query\fR] [\fB\-\-read0\fR] [\fB\-\-print0\fR] [\fB\-\-print\-query\fR] [\fB\-\-print\-cmd\fR] [\fB\-\-print\-score\fR] [\fB\-1\fR|\fB\-\-select\-1\fR] [\fB\-0\fR|\fB\-\-exit\-0\fR] [\fB\-\-sync\fR] [\fB\-\-pre\-select\-n\fR] [\fB\-\-pre\-select\-pat\fR] [\fB\-\-pre\-select\-items\fR] [\fB\-\-pre\-select\-file\fR] [\fB\-f\fR|\fB\-\-filter\fR] [\fB\-\-shell\fR] [\fB\-\-shell\-bindings\fR] [\fB\-\-man\fR] [\fB\-\-listen\fR] [\fB\-\-tmux\fR] [\fB\-\-log\-file\fR] [\fB\-\-expect\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] +\fBsk\fR [\fB\-\-tac\fR] [\fB\-\-min\-query\-length\fR] [\fB\-\-no\-sort\fR] [\fB\-t\fR|\fB\-\-tiebreak\fR] [\fB\-n\fR|\fB\-\-nth\fR] [\fB\-\-with\-nth\fR] [\fB\-d\fR|\fB\-\-delimiter\fR] [\fB\-e\fR|\fB\-\-exact\fR] [\fB\-\-regex\fR] [\fB\-\-algo\fR] [\fB\-\-case\fR] [\fB\-b\fR|\fB\-\-bind\fR] [\fB\-m\fR|\fB\-\-multi\fR] [\fB\-\-no\-multi\fR] [\fB\-\-no\-mouse\fR] [\fB\-c\fR|\fB\-\-cmd\fR] [\fB\-i\fR|\fB\-\-interactive\fR] [\fB\-I \fR] [\fB\-\-color\fR] [\fB\-\-no\-hscroll\fR] [\fB\-\-keep\-right\fR] [\fB\-\-skip\-to\-pattern\fR] [\fB\-\-no\-clear\-if\-empty\fR] [\fB\-\-no\-clear\-start\fR] [\fB\-\-no\-clear\fR] [\fB\-\-show\-cmd\-error\fR] [\fB\-\-cycle\fR] [\fB\-\-disabled\fR] [\fB\-\-layout\fR] [\fB\-\-reverse\fR] [\fB\-\-height\fR] [\fB\-\-no\-height\fR] [\fB\-\-min\-height\fR] [\fB\-\-margin\fR] [\fB\-p\fR|\fB\-\-prompt\fR] [\fB\-\-cmd\-prompt\fR] [\fB\-\-selector\fR] [\fB\-\-multi\-selector\fR] [\fB\-\-ansi\fR] [\fB\-\-tabstop\fR] [\fB\-\-info\fR] [\fB\-\-no\-info\fR] [\fB\-\-inline\-info\fR] [\fB\-\-header\fR] [\fB\-\-header\-lines\fR] [\fB\-\-border\fR] [\fB\-\-history\fR] [\fB\-\-history\-size\fR] [\fB\-\-cmd\-history\fR] [\fB\-\-cmd\-history\-size\fR] [\fB\-\-preview\fR] [\fB\-\-preview\-window\fR] [\fB\-q\fR|\fB\-\-query\fR] [\fB\-\-cmd\-query\fR] [\fB\-\-read0\fR] [\fB\-\-print0\fR] [\fB\-\-print\-query\fR] [\fB\-\-print\-cmd\fR] [\fB\-\-print\-score\fR] [\fB\-\-print\-header\fR] [\fB\-1\fR|\fB\-\-select\-1\fR] [\fB\-0\fR|\fB\-\-exit\-0\fR] [\fB\-\-sync\fR] [\fB\-\-pre\-select\-n\fR] [\fB\-\-pre\-select\-pat\fR] [\fB\-\-pre\-select\-items\fR] [\fB\-\-pre\-select\-file\fR] [\fB\-f\fR|\fB\-\-filter\fR] [\fB\-\-shell\fR] [\fB\-\-shell\-bindings\fR] [\fB\-\-man\fR] [\fB\-\-listen\fR] [\fB\-\-tmux\fR] [\fB\-\-log\-file\fR] [\fB\-\-expect\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] .SH DESCRIPTION sk \- fuzzy finder in Rust .PP @@ -459,29 +459,49 @@ Replace replstr with the selected item in commands Set color theme Format: [BASE][,COLOR:ANSI[:ATTR1:ATTR2:..]] + Example: Available themes: + * molokai: molokai 256color + * light: light 256color + * 16: dark base16 theme + * bw: black & white theme + * none: base color scheme + * dark | default: dark 256color, default value Available color names: + * normal (or "" empty string): normal text + * matched (or hl): matched text + * current (or fg+): current line foreground + * bg+: current line background (special case, always sets background) + * current_match (or hl+): matched text in current line + * query: query text + * spinner: spinner character + * info: info text (match count) + * prompt: prompt text + * cursor (or pointer): cursor/pointer + * selected (or marker): selected item marker + * header: header text + * border: border lines Adding `\-fg`, `_fg`, `\-bg`, `_bg`, `\-underline`, `_underline` sets the corresponding part of @@ -763,7 +783,10 @@ Print the query as the first line Print the command as the first line (after print\-query) .TP \fB\-\-print\-score\fR -Print the command as the first line (after print\-cmd) +Print the score after each item +.TP +\fB\-\-print\-header\fR +Print the header as the first line (after print\-score) .TP \fB\-1\fR, \fB\-\-select\-1\fR Automatically select the match if there is only one diff --git a/shell/completion.bash b/shell/completion.bash index 4cc3af75..0d94b4f3 100644 --- a/shell/completion.bash +++ b/shell/completion.bash @@ -23,7 +23,7 @@ _sk() { case "${cmd}" in sk) - opts="-t -n -d -e -b -m -c -i -I -p -q -1 -0 -f -x -h -V --tac --min-query-length --no-sort --tiebreak --nth --with-nth --delimiter --exact --regex --algo --case --bind --multi --no-multi --no-mouse --cmd --interactive --color --no-hscroll --keep-right --skip-to-pattern --no-clear-if-empty --no-clear-start --no-clear --show-cmd-error --cycle --disabled --layout --reverse --height --no-height --min-height --margin --prompt --cmd-prompt --selector --multi-selector --ansi --tabstop --info --no-info --inline-info --header --header-lines --border --history --history-size --cmd-history --cmd-history-size --preview --preview-window --query --cmd-query --read0 --print0 --print-query --print-cmd --print-score --select-1 --exit-0 --sync --pre-select-n --pre-select-pat --pre-select-items --pre-select-file --filter --shell --shell-bindings --man --listen --tmux --log-file --extended --literal --hscroll-off --filepath-word --jump-labels --no-bold --pointer --marker --phony --expect --help --version" + opts="-t -n -d -e -b -m -c -i -I -p -q -1 -0 -f -x -h -V --tac --min-query-length --no-sort --tiebreak --nth --with-nth --delimiter --exact --regex --algo --case --bind --multi --no-multi --no-mouse --cmd --interactive --color --no-hscroll --keep-right --skip-to-pattern --no-clear-if-empty --no-clear-start --no-clear --show-cmd-error --cycle --disabled --layout --reverse --height --no-height --min-height --margin --prompt --cmd-prompt --selector --multi-selector --ansi --tabstop --info --no-info --inline-info --header --header-lines --border --history --history-size --cmd-history --cmd-history-size --preview --preview-window --query --cmd-query --read0 --print0 --print-query --print-cmd --print-score --print-header --select-1 --exit-0 --sync --pre-select-n --pre-select-pat --pre-select-items --pre-select-file --filter --shell --shell-bindings --man --listen --tmux --log-file --extended --literal --hscroll-off --filepath-word --jump-labels --no-bold --pointer --marker --phony --expect --help --version" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/shell/completion.fish b/shell/completion.fish index 6936ff02..25db6c43 100644 --- a/shell/completion.fish +++ b/shell/completion.fish @@ -90,7 +90,8 @@ complete -c sk -l read0 -d 'Read input delimited by ASCII NUL(\\0) characters' complete -c sk -l print0 -d 'Print output delimited by ASCII NUL(\\0) characters' complete -c sk -l print-query -d 'Print the query as the first line' complete -c sk -l print-cmd -d 'Print the command as the first line (after print-query)' -complete -c sk -l print-score -d 'Print the command as the first line (after print-cmd)' +complete -c sk -l print-score -d 'Print the score after each item' +complete -c sk -l print-header -d 'Print the header as the first line (after print-score)' complete -c sk -s 1 -l select-1 -d 'Automatically select the match if there is only one' complete -c sk -s 0 -l exit-0 -d 'Automatically exit when no match is left' complete -c sk -l sync -d 'Synchronous search for multi-staged filtering' diff --git a/shell/completion.nu b/shell/completion.nu index abc79a3b..673e3280 100644 --- a/shell/completion.nu +++ b/shell/completion.nu @@ -84,7 +84,8 @@ module completions { --print0 # Print output delimited by ASCII NUL(\0) characters --print-query # Print the query as the first line --print-cmd # Print the command as the first line (after print-query) - --print-score # Print the command as the first line (after print-cmd) + --print-score # Print the score after each item + --print-header # Print the header as the first line (after print-score) --select-1(-1) # Automatically select the match if there is only one --exit-0(-0) # Automatically exit when no match is left --sync # Synchronous search for multi-staged filtering diff --git a/shell/completion.zsh b/shell/completion.zsh index bbfc6156..3075d324 100644 --- a/shell/completion.zsh +++ b/shell/completion.zsh @@ -107,7 +107,8 @@ zsh\:"Zsh"))' \ '--print0[Print output delimited by ASCII NUL(\\0) characters]' \ '--print-query[Print the query as the first line]' \ '--print-cmd[Print the command as the first line (after print-query)]' \ -'--print-score[Print the command as the first line (after print-cmd)]' \ +'--print-score[Print the score after each item]' \ +'--print-header[Print the header as the first line (after print-score)]' \ '-1[Automatically select the match if there is only one]' \ '--select-1[Automatically select the match if there is only one]' \ '-0[Automatically exit when no match is left]' \ diff --git a/src/bin/main.rs b/src/bin/main.rs index e47a5a84..7cdf4296 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -144,6 +144,8 @@ fn sk_main(mut opts: SkimOptions) -> Result { filter: opts.filter.clone(), print_query: opts.print_query, print_cmd: opts.print_cmd, + print_score: opts.print_score, + print_header: opts.print_header, output_ending: String::from(if opts.print0 { "\0" } else { "\n" }), }; @@ -182,12 +184,19 @@ fn sk_main(mut opts: SkimOptions) -> Result { print!("{}{}", result.cmd, bin_options.output_ending); } + if bin_options.print_header { + print!("{}{}", result.header, bin_options.output_ending); + } + if let Event::Action(Action::Accept(Some(accept_key))) = result.final_event { print!("{}{}", accept_key, bin_options.output_ending); } for item in &result.selected_items { print!("{}{}", item.output(), bin_options.output_ending); + if bin_options.print_score { + print!("{}{}", item.rank[0], bin_options.output_ending); + } } std::io::stdout().flush()?; @@ -241,6 +250,8 @@ pub struct BinOptions { output_ending: String, print_query: bool, print_cmd: bool, + print_score: bool, + print_header: bool, } /// Runs skim in filter mode, matching items against a fixed query without interactive UI diff --git a/src/lib.rs b/src/lib.rs index 68bf584a..2c7a044e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -525,6 +525,7 @@ impl Skim { query: app.input.to_string(), is_abort, selected_items: app.results(), + header: app.header.header, }) } } diff --git a/src/options.rs b/src/options.rs index ec60c463..5fdfca0c 100644 --- a/src/options.rs +++ b/src/options.rs @@ -900,10 +900,14 @@ pub struct SkimOptions { #[cfg_attr(feature = "cli", arg(long, help_heading = "Scripting"))] pub print_cmd: bool, - /// Print the command as the first line (after print-cmd) + /// Print the score after each item #[cfg_attr(feature = "cli", arg(long, help_heading = "Scripting"))] pub print_score: bool, + /// Print the header as the first line (after print-score) + #[cfg_attr(feature = "cli", arg(long, help_heading = "Scripting"))] + pub print_header: bool, + /// Automatically select the match if there is only one #[cfg_attr(feature = "cli", arg(long, short = '1', help_heading = "Scripting"))] pub select_1: bool, @@ -1082,6 +1086,7 @@ impl Default for SkimOptions { fn default() -> Self { Self { listen: None, + print_header: false, disabled: false, tac: Default::default(), min_query_length: Default::default(), diff --git a/src/output.rs b/src/output.rs index 8c45c823..54b9e427 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,4 +1,4 @@ -use crate::SkimItem; +use crate::item::MatchedItem; use crate::tui::Event; use std::sync::Arc; @@ -23,5 +23,8 @@ pub struct SkimOutput { pub cmd: String, /// The selected items. - pub selected_items: Vec>, + pub selected_items: Vec>, + + /// The header + pub header: String, } diff --git a/src/tmux.rs b/src/tmux.rs index d74e3eda..811a7525 100644 --- a/src/tmux.rs +++ b/src/tmux.rs @@ -25,6 +25,7 @@ use which::which; use crate::{ SkimItem, SkimOptions, SkimOutput, + item::MatchedItem, tui::{Event, event::Action}, }; @@ -266,10 +267,31 @@ pub fn run_with(opts: &SkimOptions) -> Option { "" }; - let mut output_lines: Vec> = vec![]; - for line in stdout { + let header = if opts.print_header && status.success() { + stdout.next().expect("Not enough lines to unpack in downstream result") + } else { + "" + } + .to_string(); + + let mut output_lines: Vec> = vec![]; + while let Some(line) = stdout.next() { debug!("Adding output line: {line}"); - output_lines.push(Arc::new(SkimTmuxOutput { line: line.to_string() })); + let mut item = MatchedItem { + item: Arc::new(SkimTmuxOutput { line: line.to_string() }), + rank: [0; 5], + matched_range: None, + }; + if opts.print_score { + item.rank = [ + stdout.next().unwrap_or_default().parse().unwrap_or_default(), + 0, + 0, + 0, + 0, + ]; + } + output_lines.push(Arc::new(item)); } let is_abort = !status.success(); @@ -289,6 +311,7 @@ pub fn run_with(opts: &SkimOptions) -> Option { query: query_str.to_string(), cmd: command_str.to_string(), selected_items: output_lines, + header, }; Some(skim_output) } diff --git a/src/tui/app.rs b/src/tui/app.rs index a9f9b0bf..2e312eee 100644 --- a/src/tui/app.rs +++ b/src/tui/app.rs @@ -1059,18 +1059,15 @@ impl<'a> App<'a> { } /// Returns the selected items as results - pub fn results(&self) -> Vec> { + pub fn results(&self) -> Vec> { if self.options.multi && !self.item_list.selection.is_empty() { self.item_list .selection .iter() - .map(|item| { - debug!("res index: {}", item.get_index()); - item.item.clone() - }) + .map(|item| Arc::new(item.clone())) .collect() - } else if let Some(sel) = self.item_list.selected() { - vec![sel] + } else if let Some(sel) = self.item_list.items.get(self.item_list.current) { + vec![Arc::new(sel.clone())] } else { vec![] } diff --git a/src/tui/header.rs b/src/tui/header.rs index d55865fb..7f02d797 100644 --- a/src/tui/header.rs +++ b/src/tui/header.rs @@ -17,7 +17,8 @@ use unicode_width::UnicodeWidthChar; /// Header widget for displaying static text above the item list #[derive(Clone)] pub struct Header { - header: String, + /// The header string with expanded tabstop, as will be displayed on screen + pub header: String, theme: Arc, } diff --git a/tests/options.rs b/tests/options.rs index 8f16e787..eeebeeae 100644 --- a/tests/options.rs +++ b/tests/options.rs @@ -303,6 +303,40 @@ sk_test!(opt_print_cmd_and_query, "10\\n20\\n30", &["--cmd-query", "cmd", "--pri @output[2] trim().eq("20"); }); +sk_test!(opt_print_header, "x", &["--header", "foo", "--print-header"], { + @capture[0] starts_with(">"); + @keys Enter; + @output[0] trim().eq("foo"); + @output[1] trim().eq("x"); +}); + +sk_test!(opt_print_score, "x\\nyx\\nyz", &["--print-score"], { + @capture[0] starts_with(">"); + @keys Key('x'); + @capture[0] starts_with("> x"); + @capture[1] trim().starts_with("2/3"); + @keys Enter; + @output[0] trim().eq("x"); + @output[1] trim().eq("-31"); +}); + +sk_test!(opt_print_score_multi, "x\\nyx\\nyz", &["--print-score", "-m"], { + @capture[0] starts_with(">"); + @keys Key('x'); + @capture[0] starts_with("> x"); + @capture[1] trim().starts_with("2/3"); + @keys BTab; + @capture[2] trim().eq(">x"); + @capture[3] trim().eq("> yx"); + @keys BTab; + @capture[3] trim().eq(">>yx"); + @keys Enter; + @output[0] trim().eq("x"); + @output[1] trim().eq("-31"); + @output[2] trim().eq("yx"); + @output[3] trim().eq("-15"); +}); + sk_test!(opt_hscroll_begin, &format!("b{}", &["a"; 1000].join("")), &["-q", "b"], { @capture[2] ends_with(".."); });