From 60c73d3c4ad141e2fd1fd4687a53905a766a5e99 Mon Sep 17 00:00:00 2001 From: Jinzhou Zhang Date: Thu, 22 Oct 2020 21:12:29 +0800 Subject: [PATCH] fix #361: support liberal space by `\ ` --- src/engine/factory.rs | 27 ++++++++++++++++++++------- test/test_skim.py | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/engine/factory.rs b/src/engine/factory.rs index 40a79c37..37c5cb95 100644 --- a/src/engine/factory.rs +++ b/src/engine/factory.rs @@ -142,15 +142,18 @@ impl AndOrEngineFactory { } } + // we want to treat `\ ` as plain white space + // regex crate doesn't support look around, so I use a lazy workaround + // that replace `\ ` with `\0` ahead of split and replace it back afterwards fn parse_or(&self, query: &str, case: CaseMatching) -> Box { if query.trim().is_empty() { self.inner.create_engine_with_case(query, case) } else { - Box::new( - OrEngine::builder() - .engines(RE_OR.split(query).map(|q| self.parse_and(q, case)).collect()) - .build(), - ) + let engines = RE_OR + .split(&self.mask_escape_space(query)) + .map(|q| self.parse_and(q, case)) + .collect(); + Box::new(OrEngine::builder().engines(engines).build()) } } @@ -161,8 +164,9 @@ impl AndOrEngineFactory { for mat in RE_AND.find_iter(query_trim) { let (start, end) = (mat.start(), mat.end()); let term = query_trim[last..start].trim_matches(|c| c == ' ' || c == '|'); + let term = self.unmask_escape_space(term); if !term.is_empty() { - engines.push(self.inner.create_engine_with_case(term, case)); + engines.push(self.inner.create_engine_with_case(&term, case)); } if !mat.as_str().trim().is_empty() { @@ -172,11 +176,20 @@ impl AndOrEngineFactory { } let term = query_trim[last..].trim_matches(|c| c == ' ' || c == '|'); + let term = self.unmask_escape_space(term); if !term.is_empty() { - engines.push(self.inner.create_engine_with_case(term, case)); + engines.push(self.inner.create_engine_with_case(&term, case)); } Box::new(AndEngine::builder().engines(engines).build()) } + + fn mask_escape_space(&self, string: &str) -> String { + string.replace("\\ ", "\0") + } + + fn unmask_escape_space(&self, string: &str) -> String { + string.replace("\0", " ") + } } impl MatchEngineFactory for AndOrEngineFactory { diff --git a/test/test_skim.py b/test/test_skim.py index 5f1e0a37..90167497 100644 --- a/test/test_skim.py +++ b/test/test_skim.py @@ -1067,6 +1067,20 @@ def test_issue_359_multi_byte_and_regex(self): self.tmux.send_keys(f"""echo 'ああa' | {self.sk("--regex -q 'a'")}""", Key('Enter')) self.tmux.until(lambda lines: lines[-3].startswith('> ああa')) + def test_issue_361_literal_space(self): + args = '''-q "'foo\\ bar"''' + self.tmux.send_keys(f"""echo 'foo bar\nfoo bar' | {self.sk(args)}""", Key('Enter')) + self.tmux.until(lambda lines: lines.ready_with_matches(1)) + self.tmux.until(lambda lines: lines[-3].startswith('> foo bar')) + self.tmux.send_keys(Key('Enter')) + + args = '''-q "!foo\\ bar"''' + self.tmux.send_keys(f"""echo 'foo bar\nfoo bar' | {self.sk(args)}""", Key('Enter')) + self.tmux.until(lambda lines: lines.ready_with_matches(1)) + self.tmux.until(lambda lines: lines[-3].startswith('> foo bar')) + self.tmux.send_keys(Key('Enter')) + + def find_prompt(lines, interactive=False, reverse=False): linen = -1 prompt = ">"