Skip to content

Commit

Permalink
fix skim-rs#90 escape single quote in preview command
Browse files Browse the repository at this point in the history
The placeholder in preview command will be replaced by single quoted
selected item. However if the preview command itself includes single
quote, the result won't work.

e.g. command `echo {}` given item `'` would results in `echo '''` that
is not a valid shell command. Unfortunately there is no easy way to
escape single quote in shell.

So we apply the hack: turn single quote `'` to `'\''` that terminate the
first quote and insert an escaped quote and another starting quote.
  • Loading branch information
lotabout committed Oct 1, 2018
1 parent b276700 commit e51da55
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod query;
mod reader;
mod score;
mod sender;
mod util;

use curses::Curses;
use event::Event::*;
Expand Down
19 changes: 14 additions & 5 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ use std::sync::Arc;
use std::time::{Duration, Instant};
use unicode_width::UnicodeWidthChar;
use std::env;
use util::escape_single_quote;

pub type ClosureType = Box<Fn(&mut Window) + Send>;

const SPINNER_DURATION: u32 = 200;
const SPINNERS: [char; 8] = ['-', '\\', '|', '/', '-', '\\', '|', '/'];

lazy_static! {
static ref RE_FILEDS: Regex = Regex::new(r"(\{-?[0-9.,q]*?})").unwrap();
static ref RE_FILEDS: Regex = Regex::new(r"\\?(\{-?[0-9.,q]*?})").unwrap();
static ref REFRESH_DURATION: Duration = Duration::from_millis(200);
}

Expand Down Expand Up @@ -643,13 +644,21 @@ impl Model {
.expect("model:inject_preview_command: invalid preview command");
debug!("replace: {:?}, text: {:?}", cmd, text);
RE_FILEDS.replace_all(cmd, |caps: &Captures| {
// \{...
if &caps[0][0..1] == "\\" {
return caps[0].to_string();
}

// {1..} and other variant
assert!(caps[1].len() >= 2);
let range = &caps[1][1..caps[1].len() - 1];
if range == "" {
format!("'{}'", text)
let replacement = if range == "" {
text
} else {
format!("'{}'", get_string_by_range(&self.delimiter, text, range).unwrap_or(""))
}
get_string_by_range(&self.delimiter, text, range).unwrap_or("")
};

format!("'{}'", escape_single_quote(replacement))
})
}

Expand Down
3 changes: 3 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub fn escape_single_quote(text: &str) -> String {
text.replace("'", "'\\''")
}
19 changes: 19 additions & 0 deletions test/test_skim.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ def test_key_bindings(self):

def test_read0(self):
self.tmux.send_keys(f"find . | wc -l", Key('Enter'))
self.tmux.until(lambda lines: re.search('\d', lines[-1]) is not None)
lines = self.tmux.capture()
num_of_files = int(lines[-1])

Expand Down Expand Up @@ -750,5 +751,23 @@ def test_multiple_option_values_should_be_accepted(self):
self.tmux.until(lambda lines: lines[-1].startswith('>'))
self.tmux.send_keys(Key('Enter'))

def test_single_quote_of_preview_command(self):
# echo "'\"ABC\"'" | sk --preview="echo X{}X" => X'"ABC"'X
echo_command = '''echo "'\\"ABC\\"'" | '''
sk_command = self.sk('--preview=\"echo X{}X\"')
command = echo_command + sk_command
self.tmux.paste(command)
self.tmux.send_keys(Key('Enter'))
self.tmux.until(lambda lines: lines.any_include('''X'"ABC"'X'''))

# echo "'\"ABC\"'" | sk --preview="echo X\{}X" => X{}X
echo_command = '''echo "'\\"ABC\\"'" | '''
sk_command = self.sk('--preview=\"echo X\\{}X\"')
command = echo_command + sk_command
self.tmux.paste(command)
self.tmux.send_keys(Key('Enter'))
self.tmux.until(lambda lines: lines.any_include('''X{}X'''))


if __name__ == '__main__':
unittest.main()

0 comments on commit e51da55

Please sign in to comment.