refactor: rename Match -> Span

This commit is contained in:
graelo 2021-03-22 23:56:35 +01:00
parent 4fc7a75046
commit 9fbfff70f3
16 changed files with 316 additions and 330 deletions

View file

@ -15,7 +15,7 @@ fn main() {
let lines = buffer.split('\n').collect::<Vec<_>>();
// Execute copyrat over the buffer (will take control over stdout).
// This returns the selected matche.
// This returns the selected span of text.
let selection: Option<Selection> = run(&lines, &opt);
// Early exit, signaling no selections were found.

View file

@ -40,18 +40,18 @@ pub struct Config {
#[clap(short, long)]
pub reverse: bool,
/// Keep the same hint for identical matches.
/// Keep the same hint for identical spans.
#[clap(short, long)]
pub unique_hint: bool,
/// Move focus back to first/last match.
/// Move focus back to first/last span.
#[clap(short = 'w', long)]
pub focus_wrap_around: bool,
#[clap(flatten)]
pub colors: ui::colors::UiColors,
/// Align hint with its match.
/// Align hint with its span.
#[clap(long, arg_enum, default_value = "leading")]
pub hint_alignment: ui::HintAlignment,

View file

@ -80,12 +80,8 @@ impl ConfigExt {
wrapped.unique_hint = value.parse::<bool>()?;
}
"@copyrat-match-fg" => {
wrapped.colors.match_fg = ui::colors::parse_color(value)?
}
"@copyrat-match-bg" => {
wrapped.colors.match_bg = ui::colors::parse_color(value)?
}
"@copyrat-span-fg" => wrapped.colors.span_fg = ui::colors::parse_color(value)?,
"@copyrat-span-bg" => wrapped.colors.span_bg = ui::colors::parse_color(value)?,
"@copyrat-focused-fg" => {
wrapped.colors.focused_fg = ui::colors::parse_color(value)?
}

View file

@ -20,7 +20,7 @@ pub fn run(lines: &[&str], opt: &config::basic::Config) -> Option<ui::Selection>
opt.unique_hint,
);
if model.matches.is_empty() {
if model.spans.is_empty() {
return None;
}

View file

@ -139,35 +139,35 @@ mod tests {
use super::*;
#[test]
fn simple_matches() {
fn simple_hints() {
let alphabet = Alphabet("abcd".to_string());
let hints = alphabet.make_hints(3);
assert_eq!(hints, ["a", "b", "c"]);
}
#[test]
fn composed_matches() {
fn composed_hints() {
let alphabet = Alphabet("abcd".to_string());
let hints = alphabet.make_hints(6);
assert_eq!(hints, ["a", "b", "c", "da", "db", "dc"]);
}
#[test]
fn composed_matches_multiple() {
fn composed_hints_multiple() {
let alphabet = Alphabet("abcd".to_string());
let hints = alphabet.make_hints(8);
assert_eq!(hints, ["a", "b", "ca", "cb", "da", "db", "dc", "dd"]);
}
#[test]
fn composed_matches_max_2() {
fn composed_hints_max_2() {
let alphabet = Alphabet("ab".to_string());
let hints = alphabet.make_hints(4);
assert_eq!(hints, ["aa", "ab", "ba", "bb"]);
}
#[test]
fn composed_matches_max_4() {
fn composed_hints_max_4() {
let alphabet = Alphabet("abcd".to_string());
let hints = alphabet.make_hints(13);
assert_eq!(
@ -177,7 +177,7 @@ mod tests {
}
#[test]
fn matches_with_longest_alphabet() {
fn hints_with_longest_alphabet() {
let alphabet = Alphabet("ab".to_string());
let hints = alphabet.make_hints(2500);
assert_eq!(hints.len(), 2500);
@ -186,7 +186,7 @@ mod tests {
}
#[test]
fn matches_exceed_longest_alphabet() {
fn hints_exceed_longest_alphabet() {
let alphabet = Alphabet("ab".to_string());
let hints = alphabet.make_hints(10000);
// 2500 unique hints are produced from the longest alphabet

View file

@ -1,10 +0,0 @@
/// Represents matched text, its location on screen, the pattern that created
/// it, and the associated hint.
#[derive(Debug)]
pub struct Match<'a> {
pub x: i32,
pub y: i32,
pub pattern: &'a str,
pub text: &'a str,
pub hint: String,
}

View file

@ -1,11 +1,11 @@
pub(crate) mod alphabet;
mod matches;
mod model;
mod raw_match;
mod raw_span;
pub(crate) mod regexes;
mod span;
pub use matches::Match;
pub use model::Model;
pub use span::Span;
#[cfg(test)]
mod tests {
@ -22,7 +22,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -31,11 +31,11 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 3);
assert_eq!(results.first().unwrap().hint, "a");
assert_eq!(results.last().unwrap().hint, "c");
assert_eq!(spans.len(), 3);
assert_eq!(spans.first().unwrap().hint, "a");
assert_eq!(spans.last().unwrap().hint, "c");
}
#[test]
@ -48,7 +48,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = true;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -57,11 +57,11 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 3);
assert_eq!(results.first().unwrap().hint, "a");
assert_eq!(results.last().unwrap().hint, "a");
assert_eq!(spans.len(), 3);
assert_eq!(spans.first().unwrap().hint, "a");
assert_eq!(spans.last().unwrap().hint, "a");
}
#[test]
@ -74,7 +74,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -83,11 +83,11 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 1);
assert_eq!(spans.len(), 1);
assert_eq!(
results.get(0).unwrap().text,
spans.get(0).unwrap().text,
"30557a29d5abc51e5f1d5b472e79b7e296f595abcf19fe6b9199dbbc809c6ff4"
);
}
@ -103,7 +103,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = true;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -112,12 +112,12 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 3);
assert_eq!(results.get(0).unwrap().text, "/var/log/nginx.log");
assert_eq!(results.get(1).unwrap().text, "test/log/nginx-2.log");
assert_eq!(results.get(2).unwrap().text, "folder/.nginx@4df2.log");
assert_eq!(spans.len(), 3);
assert_eq!(spans.get(0).unwrap().text, "/var/log/nginx.log");
assert_eq!(spans.get(1).unwrap().text, "test/log/nginx-2.log");
assert_eq!(spans.get(2).unwrap().text, "folder/.nginx@4df2.log");
}
#[test]
@ -131,7 +131,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -140,12 +140,12 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 3);
assert_eq!(results.get(0).unwrap().text, "/tmp/foo/bar_lol");
assert_eq!(results.get(1).unwrap().text, "/var/log/boot-strap.log");
assert_eq!(results.get(2).unwrap().text, "../log/kern.log");
assert_eq!(spans.len(), 3);
assert_eq!(spans.get(0).unwrap().text, "/tmp/foo/bar_lol");
assert_eq!(spans.get(1).unwrap().text, "/var/log/boot-strap.log");
assert_eq!(spans.get(2).unwrap().text, "../log/kern.log");
}
#[test]
@ -158,7 +158,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -167,10 +167,10 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 1);
assert_eq!(results.get(0).unwrap().text, "~/.gnu/.config.txt");
assert_eq!(spans.len(), 1);
assert_eq!(spans.get(0).unwrap().text, "~/.gnu/.config.txt");
}
#[test]
@ -183,7 +183,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -192,9 +192,9 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 1);
assert_eq!(spans.len(), 1);
}
#[test]
@ -207,7 +207,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -216,14 +216,14 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 4);
assert_eq!(results.get(0).unwrap().text, "fd70b5695");
assert_eq!(results.get(1).unwrap().text, "5246ddf");
assert_eq!(results.get(2).unwrap().text, "f924213");
assert_eq!(spans.len(), 4);
assert_eq!(spans.get(0).unwrap().text, "fd70b5695");
assert_eq!(spans.get(1).unwrap().text, "5246ddf");
assert_eq!(spans.get(2).unwrap().text, "f924213");
assert_eq!(
results.get(3).unwrap().text,
spans.get(3).unwrap().text,
"973113963b491874ab2e372ee60d4b4cb75f717c"
);
}
@ -238,7 +238,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -247,15 +247,15 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 3);
assert_eq!(results.get(0).unwrap().pattern, "ipv4");
assert_eq!(results.get(0).unwrap().text, "127.0.0.1");
assert_eq!(results.get(1).unwrap().pattern, "ipv4");
assert_eq!(results.get(1).unwrap().text, "255.255.10.255");
assert_eq!(results.get(2).unwrap().pattern, "ipv4");
assert_eq!(results.get(2).unwrap().text, "127.0.0.1");
assert_eq!(spans.len(), 3);
assert_eq!(spans.get(0).unwrap().pattern, "ipv4");
assert_eq!(spans.get(0).unwrap().text, "127.0.0.1");
assert_eq!(spans.get(1).unwrap().pattern, "ipv4");
assert_eq!(spans.get(1).unwrap().text, "255.255.10.255");
assert_eq!(spans.get(2).unwrap().pattern, "ipv4");
assert_eq!(spans.get(2).unwrap().text, "127.0.0.1");
}
#[test]
@ -268,7 +268,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -277,16 +277,16 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 4);
assert_eq!(results.get(0).unwrap().text, "fe80::2:202:fe4");
assert_eq!(spans.len(), 4);
assert_eq!(spans.get(0).unwrap().text, "fe80::2:202:fe4");
assert_eq!(
results.get(1).unwrap().text,
spans.get(1).unwrap().text,
"2001:67c:670:202:7ba8:5e41:1591:d723"
);
assert_eq!(results.get(2).unwrap().text, "fe80::2:1");
assert_eq!(results.get(3).unwrap().text, "fe80:22:312:fe::1%eth0");
assert_eq!(spans.get(2).unwrap().text, "fe80::2:1");
assert_eq!(spans.get(3).unwrap().text, "fe80:22:312:fe::1%eth0");
}
#[test]
@ -300,7 +300,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -309,13 +309,13 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 2);
assert_eq!(results.get(0).unwrap().pattern, "markdown-url");
assert_eq!(results.get(0).unwrap().text, "https://github.io?foo=bar");
assert_eq!(results.get(1).unwrap().pattern, "markdown-url");
assert_eq!(results.get(1).unwrap().text, "http://cdn.com/img.jpg");
assert_eq!(spans.len(), 2);
assert_eq!(spans.get(0).unwrap().pattern, "markdown-url");
assert_eq!(spans.get(0).unwrap().text, "https://github.io?foo=bar");
assert_eq!(spans.get(1).unwrap().pattern, "markdown-url");
assert_eq!(spans.get(1).unwrap().text, "http://cdn.com/img.jpg");
}
#[test]
@ -328,7 +328,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -337,20 +337,20 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 4);
assert_eq!(spans.len(), 4);
assert_eq!(
results.get(0).unwrap().text,
spans.get(0).unwrap().text,
"https://www.rust-lang.org/tools"
);
assert_eq!(results.get(0).unwrap().pattern, "url");
assert_eq!(results.get(1).unwrap().text, "https://crates.io");
assert_eq!(results.get(1).unwrap().pattern, "url");
assert_eq!(results.get(2).unwrap().text, "https://github.io?foo=bar");
assert_eq!(results.get(2).unwrap().pattern, "url");
assert_eq!(results.get(3).unwrap().text, "ssh://github.io");
assert_eq!(results.get(3).unwrap().pattern, "url");
assert_eq!(spans.get(0).unwrap().pattern, "url");
assert_eq!(spans.get(1).unwrap().text, "https://crates.io");
assert_eq!(spans.get(1).unwrap().pattern, "url");
assert_eq!(spans.get(2).unwrap().text, "https://github.io?foo=bar");
assert_eq!(spans.get(2).unwrap().pattern, "url");
assert_eq!(spans.get(3).unwrap().text, "ssh://github.io");
assert_eq!(spans.get(3).unwrap().pattern, "url");
}
#[test]
@ -364,7 +364,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -373,17 +373,14 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 2);
assert_eq!(results.get(0).unwrap().pattern, "email");
assert_eq!(spans.len(), 2);
assert_eq!(spans.get(0).unwrap().pattern, "email");
assert_eq!(spans.get(0).unwrap().text, "first.last+social@example.com");
assert_eq!(spans.get(1).unwrap().pattern, "email");
assert_eq!(
results.get(0).unwrap().text,
"first.last+social@example.com"
);
assert_eq!(results.get(1).unwrap().pattern, "email");
assert_eq!(
results.get(1).unwrap().text,
spans.get(1).unwrap().text,
"john@server.department.company.com"
);
}
@ -398,7 +395,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -407,15 +404,15 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 3);
assert_eq!(results.get(0).unwrap().pattern, "mem-address");
assert_eq!(results.get(0).unwrap().text, "0xfd70b5695");
assert_eq!(results.get(1).unwrap().pattern, "mem-address");
assert_eq!(results.get(1).unwrap().text, "0x5246ddf");
assert_eq!(results.get(2).unwrap().pattern, "mem-address");
assert_eq!(results.get(2).unwrap().text, "0x973113");
assert_eq!(spans.len(), 3);
assert_eq!(spans.get(0).unwrap().pattern, "mem-address");
assert_eq!(spans.get(0).unwrap().text, "0xfd70b5695");
assert_eq!(spans.get(1).unwrap().pattern, "mem-address");
assert_eq!(spans.get(1).unwrap().text, "0x5246ddf");
assert_eq!(spans.get(2).unwrap().pattern, "mem-address");
assert_eq!(spans.get(2).unwrap().text, "0x973113");
}
#[test]
@ -428,7 +425,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -437,13 +434,13 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 4);
assert_eq!(results.get(0).unwrap().text, "#fd7b56");
assert_eq!(results.get(1).unwrap().text, "#FF00FF");
assert_eq!(results.get(2).unwrap().text, "#00fF05");
assert_eq!(results.get(3).unwrap().text, "#abcd00");
assert_eq!(spans.len(), 4);
assert_eq!(spans.get(0).unwrap().text, "#fd7b56");
assert_eq!(spans.get(1).unwrap().text, "#FF00FF");
assert_eq!(spans.get(2).unwrap().text, "#00fF05");
assert_eq!(spans.get(3).unwrap().text, "#abcd00");
}
#[test]
@ -456,7 +453,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -465,11 +462,11 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 1);
assert_eq!(spans.len(), 1);
assert_eq!(
results.get(0).unwrap().text,
spans.get(0).unwrap().text,
"QmRdbNSxDJBXmssAc9fvTtux4duptMvfSGiGuq6yHAQVKQ"
);
}
@ -484,7 +481,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -493,9 +490,9 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 8);
assert_eq!(spans.len(), 8);
}
#[test]
@ -508,7 +505,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -517,11 +514,11 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 1);
assert_eq!(results.get(0).unwrap().pattern, "diff-a");
assert_eq!(results.get(0).unwrap().text, "src/main.rs");
assert_eq!(spans.len(), 1);
assert_eq!(spans.get(0).unwrap().pattern, "diff-a");
assert_eq!(spans.get(0).unwrap().text, "src/main.rs");
}
#[test]
@ -534,7 +531,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -543,11 +540,11 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 1);
assert_eq!(results.get(0).unwrap().pattern, "diff-b");
assert_eq!(results.get(0).unwrap().text, "src/main.rs");
assert_eq!(spans.len(), 1);
assert_eq!(spans.get(0).unwrap().pattern, "diff-b");
assert_eq!(spans.get(0).unwrap().text, "src/main.rs");
}
#[test]
@ -563,7 +560,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -572,22 +569,22 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 9);
assert_eq!(results.get(0).unwrap().text, "http://foo.bar");
assert_eq!(results.get(1).unwrap().text, "CUSTOM-52463");
assert_eq!(results.get(2).unwrap().text, "ISSUE-123");
assert_eq!(results.get(3).unwrap().text, "/var/fd70b569/9999.log");
assert_eq!(results.get(4).unwrap().text, "52463");
assert_eq!(results.get(5).unwrap().text, "973113");
assert_eq!(spans.len(), 9);
assert_eq!(spans.get(0).unwrap().text, "http://foo.bar");
assert_eq!(spans.get(1).unwrap().text, "CUSTOM-52463");
assert_eq!(spans.get(2).unwrap().text, "ISSUE-123");
assert_eq!(spans.get(3).unwrap().text, "/var/fd70b569/9999.log");
assert_eq!(spans.get(4).unwrap().text, "52463");
assert_eq!(spans.get(5).unwrap().text, "973113");
assert_eq!(
results.get(6).unwrap().text,
spans.get(6).unwrap().text,
"123e4567-e89b-12d3-a456-426655440000"
);
assert_eq!(results.get(7).unwrap().text, "8888");
assert_eq!(spans.get(7).unwrap().text, "8888");
assert_eq!(
results.get(8).unwrap().text,
spans.get(8).unwrap().text,
"https://crates.io/23456/fd70b569"
);
}
@ -605,7 +602,7 @@ mod tests {
let alphabet = Alphabet("abcd".to_string());
let reverse = false;
let unique_hint = false;
let results = Model::new(
let spans = Model::new(
&lines,
&alphabet,
use_all_patterns,
@ -614,12 +611,12 @@ mod tests {
reverse,
unique_hint,
)
.matches;
.spans;
assert_eq!(results.len(), 2);
assert_eq!(results.get(0).unwrap().text, "http://foo.bar");
assert_eq!(spans.len(), 2);
assert_eq!(spans.get(0).unwrap().text, "http://foo.bar");
assert_eq!(
results.get(1).unwrap().text,
spans.get(1).unwrap().text,
"https://crates.io/23456/fd70b569"
);
}

View file

@ -4,22 +4,20 @@ use regex::Regex;
use sequence_trie::SequenceTrie;
use super::alphabet::Alphabet;
use super::matches::Match;
use super::raw_match::RawMatch;
use super::raw_span::RawSpan;
use super::regexes::{NamedPattern, EXCLUDE_PATTERNS, PATTERNS};
use super::span::Span;
/// Holds data for the `Ui`.
pub struct Model<'a> {
// buffer: &'a str,
pub lines: &'a [&'a str],
pub reverse: bool,
pub matches: Vec<Match<'a>>,
pub spans: Vec<Span<'a>>,
pub lookup_trie: SequenceTrie<char, usize>,
}
impl<'a> Model<'a> {
pub fn new(
// buffer: &'a str,
lines: &'a [&'a str],
alphabet: &'a Alphabet,
use_all_patterns: bool,
@ -28,36 +26,34 @@ impl<'a> Model<'a> {
reverse: bool,
unique_hint: bool,
) -> Model<'a> {
// let lines = buffer.split('\n').collect::<Vec<_>>();
let mut raw_matches =
raw_matches(&lines, named_patterns, custom_patterns, use_all_patterns);
let mut raw_spans =
find_raw_spans(&lines, named_patterns, custom_patterns, use_all_patterns);
if reverse {
raw_matches.reverse();
raw_spans.reverse();
}
let mut matches = associate_hints(&raw_matches, alphabet, unique_hint);
let mut spans = associate_hints(&raw_spans, alphabet, unique_hint);
if reverse {
matches.reverse();
spans.reverse();
}
let lookup_trie = build_lookup_trie(&matches);
let lookup_trie = build_lookup_trie(&spans);
Model {
// buffer,
lines,
reverse,
matches,
spans,
lookup_trie,
}
}
}
/// Internal function that searches the model's lines for pattern matches.
/// Returns a vector of `RawMatch`es (text, location, pattern id) without
/// an associated hint. The hint is attached to `Match`, not to `RawMatch`.
/// Returns a vector of `RawSpan` (text, location, pattern id) without
/// an associated hint. The hint is attached to `Span`, not to `RawSpan`.
///
/// # Notes
///
@ -65,12 +61,12 @@ impl<'a> Model<'a> {
///
/// If no named patterns were specified, it will search for all available
/// patterns from the `PATTERNS` catalog.
fn raw_matches<'a>(
fn find_raw_spans<'a>(
lines: &'a [&'a str],
named_patterns: &'a [NamedPattern],
custom_patterns: &'a [String],
use_all_patterns: bool,
) -> Vec<RawMatch<'a>> {
) -> Vec<RawSpan<'a>> {
let exclude_regexes = EXCLUDE_PATTERNS
.iter()
.map(|&(name, pattern)| (name, Regex::new(pattern).unwrap()))
@ -100,7 +96,7 @@ fn raw_matches<'a>(
let all_regexes = [exclude_regexes, custom_regexes, regexes].concat();
let mut raw_matches = Vec::new();
let mut raw_spans = Vec::new();
for (index, line) in lines.iter().enumerate() {
// Chunk is the remainder of the line to be searched for matches.
@ -110,7 +106,7 @@ fn raw_matches<'a>(
// Use all avail regexes to match the chunk and select the match
// occuring the earliest on the chunk. Save its matched text and
// position in a `RawMatch` struct.
// position in a `RawSpan` struct.
loop {
// For each avalable regex, use the `find_iter` iterator to
// get the first non-overlapping match in the chunk, returning
@ -149,7 +145,7 @@ fn raw_matches<'a>(
None => (text, 0),
};
raw_matches.push(RawMatch {
raw_spans.push(RawSpan {
x: offset + reg_match.start() as i32 + substart as i32,
y: index as i32,
pattern: pat_name,
@ -164,54 +160,54 @@ fn raw_matches<'a>(
}
}
raw_matches
raw_spans
}
/// Associate a hint to each `RawMatch`, returning a vector of `Match`es.
/// Associate a hint to each `RawSpan`, returning a vector of `Span`.
///
/// If `unique` is `true`, all duplicate matches will have the same hint.
/// For copying matched text, this seems easier and more natural.
/// If `unique` is `false`, duplicate matches will have their own hint.
/// If `unique` is `true`, all duplicate spans will have the same hint.
/// For copying text spans, this seems easier and more natural.
/// If `unique` is `false`, duplicate spans will have their own hint.
fn associate_hints<'a>(
raw_matches: &[RawMatch<'a>],
raw_spans: &[RawSpan<'a>],
alphabet: &'a Alphabet,
unique: bool,
) -> Vec<Match<'a>> {
let hints = alphabet.make_hints(raw_matches.len());
) -> Vec<Span<'a>> {
let hints = alphabet.make_hints(raw_spans.len());
let mut hints_iter = hints.iter();
let mut result: Vec<Match<'a>> = vec![];
let mut result: Vec<Span<'a>> = vec![];
if unique {
// Map (text, hint)
let mut known: collections::HashMap<&str, &str> = collections::HashMap::new();
for raw_mat in raw_matches {
let hint: &str = known.entry(raw_mat.text).or_insert_with(|| {
for raw_span in raw_spans {
let hint: &str = known.entry(raw_span.text).or_insert_with(|| {
hints_iter
.next()
.expect("We should have as many hints as necessary, even invisible ones.")
});
result.push(Match {
x: raw_mat.x,
y: raw_mat.y,
pattern: raw_mat.pattern,
text: raw_mat.text,
result.push(Span {
x: raw_span.x,
y: raw_span.y,
pattern: raw_span.pattern,
text: raw_span.text,
hint: hint.to_string(),
});
}
} else {
for raw_mat in raw_matches {
for raw_span in raw_spans {
let hint = hints_iter
.next()
.expect("We should have as many hints as necessary, even invisible ones.");
result.push(Match {
x: raw_mat.x,
y: raw_mat.y,
pattern: raw_mat.pattern,
text: raw_mat.text,
result.push(Span {
x: raw_span.x,
y: raw_span.y,
pattern: raw_span.pattern,
text: raw_span.text,
hint: hint.to_string(),
});
}
@ -222,12 +218,12 @@ fn associate_hints<'a>(
/// Builds a `SequenceTrie` that helps determine if a sequence of keys
/// entered by the user corresponds to a match. This kind of lookup
/// directly returns a reference to the corresponding `Match` if any.
fn build_lookup_trie<'a>(matches: &'a [Match<'a>]) -> SequenceTrie<char, usize> {
/// directly returns a reference to the corresponding `Span` if any.
fn build_lookup_trie<'a>(spans: &'a [Span<'a>]) -> SequenceTrie<char, usize> {
let mut trie = SequenceTrie::new();
for (index, mat) in matches.iter().enumerate() {
let hint_chars = mat.hint.chars().collect::<Vec<char>>();
for (index, span) in spans.iter().enumerate() {
let hint_chars = span.hint.chars().collect::<Vec<char>>();
// no need to insert twice the same hint
if trie.get(&hint_chars).is_none() {

View file

@ -1,8 +0,0 @@
/// Internal surrogate for `Match`, before a Hint has been associated.
#[derive(Debug)]
pub(super) struct RawMatch<'a> {
pub x: i32,
pub y: i32,
pub pattern: &'a str,
pub text: &'a str,
}

8
src/textbuf/raw_span.rs Normal file
View file

@ -0,0 +1,8 @@
/// Internal surrogate for `Span`, before a Hint has been associated.
#[derive(Debug)]
pub(super) struct RawSpan<'a> {
pub x: i32,
pub y: i32,
pub pattern: &'a str,
pub text: &'a str,
}

10
src/textbuf/span.rs Normal file
View file

@ -0,0 +1,10 @@
/// Represents some span of text, its location on screen, the pattern that
/// created it, and the associated hint.
#[derive(Debug)]
pub struct Span<'a> {
pub x: i32,
pub y: i32,
pub pattern: &'a str,
pub text: &'a str,
pub hint: String,
}

View file

@ -30,7 +30,7 @@ mod tests {
use super::*;
#[test]
fn match_color() {
fn span_color() {
let text1 = format!(
"{}{}",
color::Fg(parse_color("green").unwrap().as_ref()),
@ -42,15 +42,15 @@ mod tests {
}
#[test]
fn no_match_color() {
fn no_span_color() {
assert!(parse_color("wat").is_err(), "this color should not exist");
}
}
/// Holds color-related data.
///
/// - `focus_*` colors are used to render the currently focused matched text.
/// - `normal_*` colors are used to render other matched text.
/// - `focus_*` colors are used to render the currently focused text span.
/// - `normal_*` colors are used to render other text spans.
/// - `hint_*` colors are used to render the hints.
#[derive(Clap, Debug)]
#[clap(about)] // Needed to avoid this doc comment to be used as overall `about`.
@ -63,22 +63,22 @@ pub struct UiColors {
#[clap(long, default_value = "bright-white", parse(try_from_str = parse_color))]
pub text_bg: Box<dyn color::Color>,
/// Foreground color for matches.
/// Foreground color for spans.
#[clap(long, default_value = "yellow",
parse(try_from_str = parse_color))]
pub match_fg: Box<dyn color::Color>,
pub span_fg: Box<dyn color::Color>,
/// Background color for matches.
/// Background color for spans.
#[clap(long, default_value = "bright-white",
parse(try_from_str = parse_color))]
pub match_bg: Box<dyn color::Color>,
pub span_bg: Box<dyn color::Color>,
/// Foreground color for the focused match.
/// Foreground color for the focused span.
#[clap(long, default_value = "magenta",
parse(try_from_str = parse_color))]
pub focused_fg: Box<dyn color::Color>,
/// Background color for the focused match.
/// Background color for the focused span.
#[clap(long, default_value = "bright-white",
parse(try_from_str = parse_color))]
pub focused_bg: Box<dyn color::Color>,

View file

@ -3,7 +3,7 @@
//!
//! In particular, the `Ui` struct
//!
//! - renders text, matched text and hints from the structured buffer content
//! - renders text, spans and hints from the structured buffer content
//! to the screen,
//! - listens for keypress events,
//! - and returns the user selection in the form of a `Selection` struct.
@ -12,8 +12,8 @@
//!
//! - navigate the buffer (in case it is larger than the number of lines in
//! the terminal)
//! - move the focus from one match to another
//! - select one of the matches
//! - move the focus from one span to another
//! - select one of the available spans
//! - toggle the output destination (tmux buffer or clipboard)
//!

View file

@ -33,7 +33,7 @@ impl<'a> ViewController<'a> {
hint_style: Option<HintStyle>,
) -> ViewController<'a> {
let focus_index = if model.reverse {
model.matches.len() - 1
model.spans.len() - 1
} else {
0
};
@ -57,10 +57,10 @@ impl<'a> ViewController<'a> {
// }}}
// Coordinates {{{1
/// Convert the `Match` text into the coordinates of the wrapped lines.
/// Convert the `Span` text into the coordinates of the wrapped lines.
///
/// Compute the new x offset of the text as the remainder of the line width
/// (e.g. the match could start at offset 120 in a 80-width terminal, the new
/// (e.g. the Span could start at offset 120 in a 80-width terminal, the new
/// offset being 40).
///
/// Compute the new y offset of the text as the initial y offset plus any
@ -76,20 +76,20 @@ impl<'a> ViewController<'a> {
(new_offset_x, new_offset_y)
}
/// Returns screen offset of a given `Match`.
/// Returns screen offset of a given `Span`.
///
/// If multibyte characters occur before the hint (in the "prefix"), then
/// their compouding takes less space on screen when printed: for
/// instance ´ + e = é. Consequently the hint offset has to be adjusted
/// to the left.
fn match_offsets(&self, mat: &textbuf::Match<'a>) -> (usize, usize) {
fn span_offsets(&self, span: &textbuf::Span<'a>) -> (usize, usize) {
let offset_x = {
let line = &self.model.lines[mat.y as usize];
let prefix = &line[0..mat.x as usize];
let line = &self.model.lines[span.y as usize];
let prefix = &line[0..span.x as usize];
let adjust = prefix.len() - prefix.chars().count();
(mat.x as usize) - (adjust)
(span.x as usize) - (adjust)
};
let offset_y = mat.y as usize;
let offset_y = span.y as usize;
(offset_x, offset_y)
}
@ -98,12 +98,12 @@ impl<'a> ViewController<'a> {
// Focus management {{{1
/// Move focus onto the previous hint, returning both the index of the
/// previously focused match, and the index of the newly focused one.
/// previously focused Span, and the index of the newly focused one.
fn prev_focus_index(&mut self) -> (usize, usize) {
let old_index = self.focus_index;
if self.focus_wrap_around {
if self.focus_index == 0 {
self.focus_index = self.model.matches.len() - 1;
self.focus_index = self.model.spans.len() - 1;
} else {
self.focus_index -= 1;
}
@ -115,16 +115,16 @@ impl<'a> ViewController<'a> {
}
/// Move focus onto the next hint, returning both the index of the
/// previously focused match, and the index of the newly focused one.
/// previously focused Span, and the index of the newly focused one.
fn next_focus_index(&mut self) -> (usize, usize) {
let old_index = self.focus_index;
if self.focus_wrap_around {
if self.focus_index == self.model.matches.len() - 1 {
if self.focus_index == self.model.spans.len() - 1 {
self.focus_index = 0;
} else {
self.focus_index += 1;
}
} else if self.focus_index < self.model.matches.len() - 1 {
} else if self.focus_index < self.model.spans.len() - 1 {
self.focus_index += 1;
}
let new_index = self.focus_index;
@ -136,7 +136,7 @@ impl<'a> ViewController<'a> {
/// Render entire model lines on provided writer.
///
/// This renders the basic content on which matches and hints can be rendered.
/// This renders the basic content on which spans and hints can be rendered.
///
/// # Notes
/// - All trailing whitespaces are trimmed, empty lines are skipped.
@ -181,7 +181,7 @@ impl<'a> ViewController<'a> {
.unwrap();
}
/// Render the Match's `text` field on provided writer using the `match_*g` color.
/// Render the Span's `text` field on provided writer using the `span_*g` color.
///
/// If a Mach is "focused", it is then rendered with the `focused_*g` colors.
///
@ -195,14 +195,14 @@ impl<'a> ViewController<'a> {
offset: (usize, usize),
colors: &UiColors,
) {
// To help identify it, the match thas has focus is rendered with a dedicated color.
// To help identify it, the span thas has focus is rendered with a dedicated color.
let (fg_color, bg_color) = if focused {
(&colors.focused_fg, &colors.focused_bg)
} else {
(&colors.match_fg, &colors.match_bg)
(&colors.span_fg, &colors.span_bg)
};
// Render just the Match's text on top of existing content.
// Render just the Span's text on top of existing content.
write!(
stdout,
"{goto}{bg_color}{fg_color}{text}{fg_reset}{bg_reset}",
@ -216,7 +216,7 @@ impl<'a> ViewController<'a> {
.unwrap();
}
/// Render a Match's `hint` field on the provided writer.
/// Render a Span's `hint` field on the provided writer.
///
/// This renders the hint according to some provided style:
/// - just colors
@ -318,12 +318,12 @@ impl<'a> ViewController<'a> {
}
}
/// Convenience function that renders both the matched text and its hint,
/// Convenience function that renders both the text span and its hint,
/// if focused.
fn render_match(&self, stdout: &mut dyn io::Write, mat: &textbuf::Match<'a>, focused: bool) {
let text = mat.text;
fn render_span(&self, stdout: &mut dyn io::Write, span: &textbuf::Span<'a>, focused: bool) {
let text = span.text;
let (offset_x, offset_y) = self.match_offsets(mat);
let (offset_x, offset_y) = self.span_offsets(span);
let (offset_x, offset_y) = self.map_coords_to_wrapped_space(offset_x, offset_y);
ViewController::render_matched_text(
@ -336,16 +336,16 @@ impl<'a> ViewController<'a> {
if !focused {
// If not focused, render the hint (e.g. "eo") as an overlay on
// top of the rendered matched text, aligned at its leading or the
// top of the rendered text span, aligned at its leading or the
// trailing edge.
let extra_offset = match self.hint_alignment {
HintAlignment::Leading => 0,
HintAlignment::Trailing => text.len() - mat.hint.len(),
HintAlignment::Trailing => text.len() - span.hint.len(),
};
ViewController::render_matched_hint(
stdout,
&mat.hint,
&span.hint,
(offset_x + extra_offset, offset_y),
&self.rendering_colors,
&self.hint_style,
@ -357,15 +357,15 @@ impl<'a> ViewController<'a> {
///
/// This renders in 3 phases:
/// - all lines are rendered verbatim
/// - each Match's `text` is rendered as an overlay on top of it
/// - each Match's `hint` text is rendered as a final overlay
/// - each Span's `text` is rendered as an overlay on top of it
/// - each Span's `hint` text is rendered as a final overlay
///
/// Depending on the value of `self.hint_alignment`, the hint can be rendered
/// on the leading edge of the underlying Match's `text`,
/// on the leading edge of the underlying Span's `text`,
/// or on the trailing edge.
///
/// # Note
/// Multibyte characters are taken into account, so that the Match's `text`
/// Multibyte characters are taken into account, so that the Span's `text`
/// and `hint` are rendered in their proper position.
fn full_render(&self, stdout: &mut dyn io::Write) {
// 1. Trim all lines and render non-empty ones.
@ -376,31 +376,31 @@ impl<'a> ViewController<'a> {
&self.rendering_colors,
);
for (index, mat) in self.model.matches.iter().enumerate() {
for (index, span) in self.model.spans.iter().enumerate() {
let focused = index == self.focus_index;
self.render_match(stdout, mat, focused);
self.render_span(stdout, span, focused);
}
stdout.flush().unwrap();
}
/// Render the previous match with its hint, and render the newly focused
/// match without its hint. This is more efficient than a full render.
/// Render the previous span with its hint, and render the newly focused
/// span without its hint. This is more efficient than a full render.
fn diff_render(
&self,
stdout: &mut dyn io::Write,
old_focus_index: usize,
new_focus_index: usize,
) {
// Render the previously focused match as non-focused
let mat = self.model.matches.get(old_focus_index).unwrap();
// Render the previously focused span as non-focused
let span = self.model.spans.get(old_focus_index).unwrap();
let focused = false;
self.render_match(stdout, mat, focused);
self.render_span(stdout, span, focused);
// Render the previously focused match as non-focused
let mat = self.model.matches.get(new_focus_index).unwrap();
// Render the previously focused span as non-focused
let span = self.model.spans.get(new_focus_index).unwrap();
let focused = true;
self.render_match(stdout, mat, focused);
self.render_span(stdout, span, focused);
stdout.flush().unwrap();
}
@ -409,7 +409,7 @@ impl<'a> ViewController<'a> {
// Listening {{{1
/// Listen to keys entered on stdin, moving focus accordingly, or
/// selecting one match.
/// selecting one span.
///
/// # Panics
///
@ -417,7 +417,7 @@ impl<'a> ViewController<'a> {
fn listen(&mut self, reader: &mut dyn io::Read, writer: &mut dyn io::Write) -> Event {
use termion::input::TermRead; // Trait for `reader.keys().next()`.
if self.model.matches.is_empty() {
if self.model.spans.is_empty() {
return Event::Exit;
}
@ -448,7 +448,7 @@ impl<'a> ViewController<'a> {
break;
}
// Move focus to next/prev match.
// Move focus to next/prev span.
event::Key::Up => {
let (old_index, focused_index) = self.prev_focus_index();
self.diff_render(writer, old_index, focused_index);
@ -484,16 +484,16 @@ impl<'a> ViewController<'a> {
// Yank/copy
event::Key::Char(_ch @ 'y') | event::Key::Char(_ch @ '\n') => {
let text = self.model.matches.get(self.focus_index).unwrap().text;
return Event::Match(Selection {
let text = self.model.spans.get(self.focus_index).unwrap().text;
return Event::Select(Selection {
text: text.to_string(),
uppercased: false,
output_destination,
});
}
event::Key::Char(_ch @ 'Y') => {
let text = self.model.matches.get(self.focus_index).unwrap().text;
return Event::Match(Selection {
let text = self.model.spans.get(self.focus_index).unwrap().text;
return Event::Select(Selection {
text: text.to_string(),
uppercased: true,
output_destination,
@ -511,7 +511,7 @@ impl<'a> ViewController<'a> {
// Use a Trie or another data structure to determine
// if the entered key belongs to a longer hint.
// Attempts at finding a match with a corresponding hint.
// Attempts at finding a span with a corresponding hint.
//
// If any of the typed character is caps, the typed hint is
// deemed as uppercased.
@ -535,12 +535,12 @@ impl<'a> ViewController<'a> {
let node = node.unwrap();
if node.is_leaf() {
// The last key of a hint was entered.
let match_index = node.value().expect(
let span_index = node.value().expect(
"By construction, the Lookup Trie should have a value for each leaf.",
);
let mat = self.model.matches.get(*match_index).expect("By construction, the value in a leaf should correspond to an existing hint.");
let text = mat.text.to_string();
return Event::Match(Selection {
let span = self.model.spans.get(*span_index).expect("By construction, the value in a leaf should correspond to an existing hint.");
let text = span.text.to_string();
return Event::Select(Selection {
text,
uppercased,
output_destination,
@ -585,7 +585,7 @@ impl<'a> ViewController<'a> {
let selection = match self.listen(&mut stdin, &mut stdout) {
Event::Exit => None,
Event::Match(selection) => Some(selection),
Event::Select(selection) => Some(selection),
};
write!(stdout, "{}", cursor::Show).unwrap();
@ -623,10 +623,10 @@ fn get_line_offsets(lines: &[&str], term_width: u16) -> Vec<usize> {
/// Returned value after the `Ui` has finished listening to events.
enum Event {
/// Exit with no selected matches,
/// Exit with no selected spans,
Exit,
/// A vector of matched text and whether it was selected with uppercase.
Match(Selection),
/// The selected span of text and whether it was selected with uppercase.
Select(Selection),
}
#[cfg(test)]
@ -650,8 +650,8 @@ path: /usr/local/bin/cargo";
text_bg: Box::new(color::White),
focused_fg: Box::new(color::Red),
focused_bg: Box::new(color::Blue),
match_fg: Box::new(color::Green),
match_bg: Box::new(color::Magenta),
span_fg: Box::new(color::Green),
span_bg: Box::new(color::Magenta),
hint_fg: Box::new(color::Yellow),
hint_bg: Box::new(color::Cyan),
};
@ -688,8 +688,8 @@ path: /usr/local/bin/cargo";
text_bg: Box::new(color::White),
focused_fg: Box::new(color::Red),
focused_bg: Box::new(color::Blue),
match_fg: Box::new(color::Green),
match_bg: Box::new(color::Magenta),
span_fg: Box::new(color::Green),
span_bg: Box::new(color::Magenta),
hint_fg: Box::new(color::Yellow),
hint_bg: Box::new(color::Cyan),
};
@ -722,8 +722,8 @@ path: /usr/local/bin/cargo";
text_bg: Box::new(color::White),
focused_fg: Box::new(color::Red),
focused_bg: Box::new(color::Blue),
match_fg: Box::new(color::Green),
match_bg: Box::new(color::Magenta),
span_fg: Box::new(color::Green),
span_bg: Box::new(color::Magenta),
hint_fg: Box::new(color::Yellow),
hint_bg: Box::new(color::Cyan),
};
@ -735,8 +735,8 @@ path: /usr/local/bin/cargo";
format!(
"{goto}{bg}{fg}{text}{fg_reset}{bg_reset}",
goto = cursor::Goto(4, 2),
fg = color::Fg(colors.match_fg.as_ref()),
bg = color::Bg(colors.match_bg.as_ref()),
fg = color::Fg(colors.span_fg.as_ref()),
bg = color::Bg(colors.span_bg.as_ref()),
fg_reset = color::Fg(color::Reset),
bg_reset = color::Bg(color::Reset),
text = &text,
@ -755,8 +755,8 @@ path: /usr/local/bin/cargo";
text_bg: Box::new(color::White),
focused_fg: Box::new(color::Red),
focused_bg: Box::new(color::Blue),
match_fg: Box::new(color::Green),
match_bg: Box::new(color::Magenta),
span_fg: Box::new(color::Green),
span_bg: Box::new(color::Magenta),
hint_fg: Box::new(color::Yellow),
hint_bg: Box::new(color::Cyan),
};
@ -797,8 +797,8 @@ path: /usr/local/bin/cargo";
text_bg: Box::new(color::White),
focused_fg: Box::new(color::Red),
focused_bg: Box::new(color::Blue),
match_fg: Box::new(color::Green),
match_bg: Box::new(color::Magenta),
span_fg: Box::new(color::Green),
span_bg: Box::new(color::Magenta),
hint_fg: Box::new(color::Yellow),
hint_bg: Box::new(color::Cyan),
};
@ -841,8 +841,8 @@ path: /usr/local/bin/cargo";
text_bg: Box::new(color::White),
focused_fg: Box::new(color::Red),
focused_bg: Box::new(color::Blue),
match_fg: Box::new(color::Green),
match_bg: Box::new(color::Magenta),
span_fg: Box::new(color::Green),
span_bg: Box::new(color::Magenta),
hint_fg: Box::new(color::Yellow),
hint_bg: Box::new(color::Cyan),
};
@ -876,8 +876,8 @@ path: /usr/local/bin/cargo";
}
#[test]
/// Simulates rendering without any match.
fn test_render_full_without_matches() {
/// Simulates rendering without any span.
fn test_render_full_without_available_spans() {
let buffer = "lorem 127.0.0.1 lorem
Barcelona https://en.wikipedia.org/wiki/Barcelona - ";
@ -905,14 +905,14 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - ";
text_bg: Box::new(color::White),
focused_fg: Box::new(color::Red),
focused_bg: Box::new(color::Blue),
match_fg: Box::new(color::Green),
match_bg: Box::new(color::Magenta),
span_fg: Box::new(color::Green),
span_bg: Box::new(color::Magenta),
hint_fg: Box::new(color::Yellow),
hint_bg: Box::new(color::Cyan),
};
let hint_alignment = HintAlignment::Leading;
// create a Ui without any match
// create a Ui without any span
let ui = ViewController {
model: &mut model,
term_width,
@ -945,15 +945,12 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - ";
// println!("{:?}", writer);
// println!("{:?}", expected.as_bytes());
// println!("matches: {}", ui.matches.len());
// println!("lines: {}", lines.len());
assert_eq!(writer, expected.as_bytes());
}
#[test]
/// Simulates rendering with matches.
fn test_render_full_with_matches() {
/// Simulates rendering with available spans.
fn test_render_full_with_spans() {
let buffer = "lorem 127.0.0.1 lorem
Barcelona https://en.wikipedia.org/wiki/Barcelona - ";
@ -982,8 +979,8 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - ";
text_bg: Box::new(color::White),
focused_fg: Box::new(color::Red),
focused_bg: Box::new(color::Blue),
match_fg: Box::new(color::Green),
match_bg: Box::new(color::Magenta),
span_fg: Box::new(color::Green),
span_bg: Box::new(color::Magenta),
hint_fg: Box::new(color::Yellow),
hint_bg: Box::new(color::Cyan),
};
@ -1021,10 +1018,10 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - ";
let expected_match1_text = {
let goto7_1 = cursor::Goto(7, 1);
format!(
"{goto7_1}{match_bg}{match_fg}127.0.0.1{fg_reset}{bg_reset}",
"{goto7_1}{span_bg}{span_fg}127.0.0.1{fg_reset}{bg_reset}",
goto7_1 = goto7_1,
match_fg = color::Fg(rendering_colors.match_fg.as_ref()),
match_bg = color::Bg(rendering_colors.match_bg.as_ref()),
span_fg = color::Fg(rendering_colors.span_fg.as_ref()),
span_bg = color::Bg(rendering_colors.span_bg.as_ref()),
fg_reset = color::Fg(color::Reset),
bg_reset = color::Bg(color::Reset)
)
@ -1055,7 +1052,7 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - ";
)
};
// Because reverse is true, this second match is focused,
// Because reverse is true, this second span is focused,
// then the hint should not be rendered.
// let expected_match2_hint = {
@ -1090,7 +1087,7 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - ";
// .find(|(_idx, (&l, &r))| l != r);
// println!("{:?}", diff_point);
assert_eq!(2, ui.model.matches.len());
assert_eq!(2, ui.model.spans.len());
assert_eq!(writer, expected.as_bytes());
}