From 9fbfff70f30db0d0a810fd9d2269cf87ce457bb5 Mon Sep 17 00:00:00 2001 From: graelo Date: Mon, 22 Mar 2021 23:56:35 +0100 Subject: [PATCH 1/9] refactor: rename Match -> Span --- README.md | 16 +-- copyrat.tmux | 2 +- src/bin/copyrat.rs | 2 +- src/config/basic.rs | 6 +- src/config/extended.rs | 8 +- src/lib.rs | 2 +- src/textbuf/alphabet.rs | 14 +- src/textbuf/matches.rs | 10 -- src/textbuf/mod.rs | 271 +++++++++++++++++++-------------------- src/textbuf/model.rs | 90 +++++++------ src/textbuf/raw_match.rs | 8 -- src/textbuf/raw_span.rs | 8 ++ src/textbuf/span.rs | 10 ++ src/ui/colors.rs | 20 +-- src/ui/mod.rs | 6 +- src/ui/vc.rs | 173 ++++++++++++------------- 16 files changed, 316 insertions(+), 330 deletions(-) delete mode 100644 src/textbuf/matches.rs delete mode 100644 src/textbuf/raw_match.rs create mode 100644 src/textbuf/raw_span.rs create mode 100644 src/textbuf/span.rs diff --git a/README.md b/README.md index 2407a25..5dc2e31 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ set -g @thumbs-reverse `default: disabled` -Choose if you want to assign the same hint for the same matched strings. +Choose if you want to assign the same hint for the same text spans. For example: @@ -146,7 +146,7 @@ set -g @thumbs-unique `default: left` -Choose where do you want to show the hint in the matched string. Options (left, right). +Choose where do you want to show the hint in the text spans. Options (left, right). For example: @@ -193,7 +193,7 @@ set -g @thumbs-upcase-command 'echo -n {} | pbcopy' `default: black` -Sets the background color for matches +Sets the background color for spans For example: @@ -205,7 +205,7 @@ set -g @thumbs-bg-color blue `default: green` -Sets the foreground color for matches +Sets the foreground color for spans For example: @@ -314,7 +314,7 @@ This is the list of available alphabets: ## Extra features -- **Arrow navigation:** You can use the arrows to move around between all matched items. +- **Arrow navigation:** You can use the arrows to move around between all spans. - **Auto paste:** If your last typed hint character is uppercase, you are going to pick and paste the desired hint. - **Multi selection:** If you run thumb with multi selection mode you will be able to choose multiple hints pressing the desired letter and `Space` to finalize the selection. @@ -361,13 +361,13 @@ FLAGS: -h, --help Prints help information -m, --multi Enable multi-selection -r, --reverse Reverse the order for assigned hints - -u, --unique Don't show duplicated hints for the same match + -u, --unique Don't show duplicated hints for the same span -V, --version Prints version information OPTIONS: -a, --alphabet Sets the alphabet [default: qwerty] - --bg-color Sets the background color for matches [default: black] - --fg-color Sets the foregroud color for matches [default: green] + --bg-color Sets the background color for spans [default: black] + --fg-color Sets the foregroud color for spans [default: green] -f, --format Specifies the out format for the picked hint. (%U: Upcase, %H: Hint) [default: %H] diff --git a/copyrat.tmux b/copyrat.tmux index 3097848..52b5aa1 100755 --- a/copyrat.tmux +++ b/copyrat.tmux @@ -7,7 +7,7 @@ # # set -g @copyrat-keytable "foobar" # set -g @copyrat-keyswitch "z" -# set -g @copyrat-match-bg "magenta" +# set -g @copyrat-span-bg "magenta" # # and bindings like # diff --git a/src/bin/copyrat.rs b/src/bin/copyrat.rs index b665441..84c9c7f 100644 --- a/src/bin/copyrat.rs +++ b/src/bin/copyrat.rs @@ -15,7 +15,7 @@ fn main() { let lines = buffer.split('\n').collect::>(); // 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 = run(&lines, &opt); // Early exit, signaling no selections were found. diff --git a/src/config/basic.rs b/src/config/basic.rs index 3859d6e..993936e 100644 --- a/src/config/basic.rs +++ b/src/config/basic.rs @@ -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, diff --git a/src/config/extended.rs b/src/config/extended.rs index 52f077a..9c545f6 100644 --- a/src/config/extended.rs +++ b/src/config/extended.rs @@ -80,12 +80,8 @@ impl ConfigExt { wrapped.unique_hint = value.parse::()?; } - "@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)? } diff --git a/src/lib.rs b/src/lib.rs index f61e584..5bfd7eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ pub fn run(lines: &[&str], opt: &config::basic::Config) -> Option opt.unique_hint, ); - if model.matches.is_empty() { + if model.spans.is_empty() { return None; } diff --git a/src/textbuf/alphabet.rs b/src/textbuf/alphabet.rs index 091583d..d2dacab 100644 --- a/src/textbuf/alphabet.rs +++ b/src/textbuf/alphabet.rs @@ -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 diff --git a/src/textbuf/matches.rs b/src/textbuf/matches.rs deleted file mode 100644 index 1695605..0000000 --- a/src/textbuf/matches.rs +++ /dev/null @@ -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, -} diff --git a/src/textbuf/mod.rs b/src/textbuf/mod.rs index af6abe6..45642ed 100644 --- a/src/textbuf/mod.rs +++ b/src/textbuf/mod.rs @@ -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" ); } diff --git a/src/textbuf/model.rs b/src/textbuf/model.rs index 9208e35..742b42d 100644 --- a/src/textbuf/model.rs +++ b/src/textbuf/model.rs @@ -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>, + pub spans: Vec>, pub lookup_trie: SequenceTrie, } 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::>(); - - 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> { +) -> Vec> { 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> { - let hints = alphabet.make_hints(raw_matches.len()); +) -> Vec> { + let hints = alphabet.make_hints(raw_spans.len()); let mut hints_iter = hints.iter(); - let mut result: Vec> = vec![]; + let mut result: Vec> = 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 { +/// directly returns a reference to the corresponding `Span` if any. +fn build_lookup_trie<'a>(spans: &'a [Span<'a>]) -> SequenceTrie { let mut trie = SequenceTrie::new(); - for (index, mat) in matches.iter().enumerate() { - let hint_chars = mat.hint.chars().collect::>(); + for (index, span) in spans.iter().enumerate() { + let hint_chars = span.hint.chars().collect::>(); // no need to insert twice the same hint if trie.get(&hint_chars).is_none() { diff --git a/src/textbuf/raw_match.rs b/src/textbuf/raw_match.rs deleted file mode 100644 index 7da45ce..0000000 --- a/src/textbuf/raw_match.rs +++ /dev/null @@ -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, -} diff --git a/src/textbuf/raw_span.rs b/src/textbuf/raw_span.rs new file mode 100644 index 0000000..4b181df --- /dev/null +++ b/src/textbuf/raw_span.rs @@ -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, +} diff --git a/src/textbuf/span.rs b/src/textbuf/span.rs new file mode 100644 index 0000000..a4d44d8 --- /dev/null +++ b/src/textbuf/span.rs @@ -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, +} diff --git a/src/ui/colors.rs b/src/ui/colors.rs index daa8674..af2d5aa 100644 --- a/src/ui/colors.rs +++ b/src/ui/colors.rs @@ -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, - /// Foreground color for matches. + /// Foreground color for spans. #[clap(long, default_value = "yellow", parse(try_from_str = parse_color))] - pub match_fg: Box, + pub span_fg: Box, - /// Background color for matches. + /// Background color for spans. #[clap(long, default_value = "bright-white", parse(try_from_str = parse_color))] - pub match_bg: Box, + pub span_bg: Box, - /// 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, - /// 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, diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 2db4d8d..d047e6d 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -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) //! diff --git a/src/ui/vc.rs b/src/ui/vc.rs index 6dcfc24..22a2ec0 100644 --- a/src/ui/vc.rs +++ b/src/ui/vc.rs @@ -33,7 +33,7 @@ impl<'a> ViewController<'a> { hint_style: Option, ) -> 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 { /// 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()); } From 3f4e8547005e42422aa047ef37ef38dd5e5ec850 Mon Sep 17 00:00:00 2001 From: graelo Date: Tue, 23 Mar 2021 00:08:47 +0100 Subject: [PATCH 2/9] refactor: fix old names --- src/ui/vc.rs | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/ui/vc.rs b/src/ui/vc.rs index 22a2ec0..e49f753 100644 --- a/src/ui/vc.rs +++ b/src/ui/vc.rs @@ -188,7 +188,7 @@ impl<'a> ViewController<'a> { /// # Note /// /// This writes directly on the writer, avoiding extra allocation. - fn render_matched_text( + fn render_span_text( stdout: &mut dyn io::Write, text: &str, focused: bool, @@ -220,13 +220,14 @@ impl<'a> ViewController<'a> { /// /// This renders the hint according to some provided style: /// - just colors - /// - underlined with colors + /// - styled (bold, italic, underlined) with colors /// - surrounding the hint's text with some delimiters, see /// `HintStyle::Delimited`. /// /// # Note + /// /// This writes directly on the writer, avoiding extra allocation. - fn render_matched_hint( + fn render_span_hint( stdout: &mut dyn io::Write, hint_text: &str, offset: (usize, usize), @@ -326,7 +327,7 @@ impl<'a> ViewController<'a> { 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( + ViewController::render_span_text( stdout, text, focused, @@ -343,7 +344,7 @@ impl<'a> ViewController<'a> { HintAlignment::Trailing => text.len() - span.hint.len(), }; - ViewController::render_matched_hint( + ViewController::render_span_hint( stdout, &span.hint, (offset_x + extra_offset, offset_y), @@ -678,7 +679,7 @@ path: /usr/local/bin/cargo"; } #[test] - fn test_render_focused_matched_text() { + fn test_render_focused_span_text() { let mut writer = vec![]; let text = "https://en.wikipedia.org/wiki/Barcelona"; let focused = true; @@ -694,7 +695,7 @@ path: /usr/local/bin/cargo"; hint_bg: Box::new(color::Cyan), }; - ViewController::render_matched_text(&mut writer, text, focused, offset, &colors); + ViewController::render_span_text(&mut writer, text, focused, offset, &colors); assert_eq!( writer, @@ -712,7 +713,7 @@ path: /usr/local/bin/cargo"; } #[test] - fn test_render_matched_text() { + fn test_render_span_text() { let mut writer = vec![]; let text = "https://en.wikipedia.org/wiki/Barcelona"; let focused = false; @@ -728,7 +729,7 @@ path: /usr/local/bin/cargo"; hint_bg: Box::new(color::Cyan), }; - ViewController::render_matched_text(&mut writer, text, focused, offset, &colors); + ViewController::render_span_text(&mut writer, text, focused, offset, &colors); assert_eq!( writer, @@ -746,7 +747,7 @@ path: /usr/local/bin/cargo"; } #[test] - fn test_render_unstyled_matched_hint() { + fn test_render_unstyled_span_hint() { let mut writer = vec![]; let hint_text = "eo"; let offset: (usize, usize) = (3, 1); @@ -764,7 +765,7 @@ path: /usr/local/bin/cargo"; let extra_offset = 0; let hint_style = None; - ViewController::render_matched_hint( + ViewController::render_span_hint( &mut writer, hint_text, (offset.0 + extra_offset, offset.1), @@ -788,7 +789,7 @@ path: /usr/local/bin/cargo"; } #[test] - fn test_render_underlined_matched_hint() { + fn test_render_underlined_span_hint() { let mut writer = vec![]; let hint_text = "eo"; let offset: (usize, usize) = (3, 1); @@ -806,7 +807,7 @@ path: /usr/local/bin/cargo"; let extra_offset = 0; let hint_style = Some(HintStyle::Underline); - ViewController::render_matched_hint( + ViewController::render_span_hint( &mut writer, hint_text, (offset.0 + extra_offset, offset.1), @@ -832,7 +833,7 @@ path: /usr/local/bin/cargo"; } #[test] - fn test_render_bracketed_matched_hint() { + fn test_render_bracketed_span_hint() { let mut writer = vec![]; let hint_text = "eo"; let offset: (usize, usize) = (3, 1); @@ -850,7 +851,7 @@ path: /usr/local/bin/cargo"; let extra_offset = 0; let hint_style = Some(HintStyle::Surround('{', '}')); - ViewController::render_matched_hint( + ViewController::render_span_hint( &mut writer, hint_text, (offset.0 + extra_offset, offset.1), @@ -1015,7 +1016,7 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - "; ) }; - let expected_match1_text = { + let expected_span1_text = { let goto7_1 = cursor::Goto(7, 1); format!( "{goto7_1}{span_bg}{span_fg}127.0.0.1{fg_reset}{bg_reset}", @@ -1027,7 +1028,7 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - "; ) }; - let expected_match1_hint = { + let expected_span1_hint = { let goto7_1 = cursor::Goto(7, 1); format!( @@ -1040,7 +1041,7 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - "; ) }; - let expected_match2_text = { + let expected_span2_text = { let goto11_3 = cursor::Goto(11, 3); format!( "{goto11_3}{focus_bg}{focus_fg}https://en.wikipedia.org/wiki/Barcelona{fg_reset}{bg_reset}", @@ -1055,7 +1056,7 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - "; // Because reverse is true, this second span is focused, // then the hint should not be rendered. - // let expected_match2_hint = { + // let expected_span2_hint = { // let goto11_3 = cursor::Goto(11, 3); // format!( @@ -1070,10 +1071,10 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - "; let expected = [ expected_content, - expected_match1_text, - expected_match1_hint, - expected_match2_text, - // expected_match2_hint, + expected_span1_text, + expected_span1_hint, + expected_span2_text, + // expected_span2_hint, ] .concat(); From e0cb0e95649a6bdf41c6eaf1f21cef668e81e64c Mon Sep 17 00:00:00 2001 From: graelo Date: Tue, 23 Mar 2021 00:26:44 +0100 Subject: [PATCH 3/9] fix: default colors --- src/ui/colors.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ui/colors.rs b/src/ui/colors.rs index af2d5aa..064ea14 100644 --- a/src/ui/colors.rs +++ b/src/ui/colors.rs @@ -56,11 +56,11 @@ mod tests { #[clap(about)] // Needed to avoid this doc comment to be used as overall `about`. pub struct UiColors { /// Foreground color for base text. - #[clap(long, default_value = "bright-cyan", parse(try_from_str = parse_color))] + #[clap(long, default_value = "bright-blue", parse(try_from_str = parse_color))] pub text_fg: Box, /// Background color for base text. - #[clap(long, default_value = "bright-white", parse(try_from_str = parse_color))] + #[clap(long, default_value = "white", parse(try_from_str = parse_color))] pub text_bg: Box, /// Foreground color for spans. @@ -69,7 +69,7 @@ pub struct UiColors { pub span_fg: Box, /// Background color for spans. - #[clap(long, default_value = "bright-white", + #[clap(long, default_value = "white", parse(try_from_str = parse_color))] pub span_bg: Box, @@ -79,7 +79,7 @@ pub struct UiColors { pub focused_fg: Box, /// Background color for the focused span. - #[clap(long, default_value = "bright-white", + #[clap(long, default_value = "white", parse(try_from_str = parse_color))] pub focused_bg: Box, From 8cc42dbe44c695a54789b0ef0fcc1949b12d5507 Mon Sep 17 00:00:00 2001 From: graelo Date: Tue, 23 Mar 2021 07:25:02 +0100 Subject: [PATCH 4/9] fix: update tmux shortcuts --- copyrat.tmux | 4 ++-- src/textbuf/mod.rs | 8 ++++---- src/textbuf/regexes.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/copyrat.tmux b/copyrat.tmux index 52b5aa1..134ee92 100755 --- a/copyrat.tmux +++ b/copyrat.tmux @@ -94,9 +94,9 @@ setup_pattern_binding "U" "--pattern-name uuid" # prefix + t + v searches for version numbers setup_pattern_binding "v" "--pattern-name version" # prefix + t + d searches for any string of 4+ digits -setup_pattern_binding "d" "--pattern-name digits" +setup_pattern_binding "G" "--pattern-name digits" # prefix + t + m searches for hex numbers: 0xbedead -setup_pattern_binding "m" "--pattern-name mem-address" +setup_pattern_binding "P" "--pattern-name pointer-address" # prefix + t + 4 searches for IPV4 setup_pattern_binding "4" "--pattern-name ipv4" # prefix + t + 6 searches for IPV6 diff --git a/src/textbuf/mod.rs b/src/textbuf/mod.rs index 45642ed..60c9031 100644 --- a/src/textbuf/mod.rs +++ b/src/textbuf/mod.rs @@ -386,7 +386,7 @@ mod tests { } #[test] - fn match_addresses() { + fn match_pointer_addresses() { let buffer = "Lorem 0xfd70b5695 0x5246ddf lorem\n Lorem 0x973113tlorem"; let lines = buffer.split('\n').collect::>(); let use_all_patterns = true; @@ -407,11 +407,11 @@ mod tests { .spans; assert_eq!(spans.len(), 3); - assert_eq!(spans.get(0).unwrap().pattern, "mem-address"); + assert_eq!(spans.get(0).unwrap().pattern, "pointer-address"); assert_eq!(spans.get(0).unwrap().text, "0xfd70b5695"); - assert_eq!(spans.get(1).unwrap().pattern, "mem-address"); + assert_eq!(spans.get(1).unwrap().pattern, "pointer-address"); assert_eq!(spans.get(1).unwrap().text, "0x5246ddf"); - assert_eq!(spans.get(2).unwrap().pattern, "mem-address"); + assert_eq!(spans.get(2).unwrap().pattern, "pointer-address"); assert_eq!(spans.get(2).unwrap().text, "0x973113"); } diff --git a/src/textbuf/regexes.rs b/src/textbuf/regexes.rs index 6b823b5..5f57914 100644 --- a/src/textbuf/regexes.rs +++ b/src/textbuf/regexes.rs @@ -31,7 +31,7 @@ pub(super) const PATTERNS: [(&str, &str); 16] = [ ("sha", r"[0-9a-f]{7,40}"), ("ipv4", r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"), ("ipv6", r"[A-f0-9:]+:+[A-f0-9:]+[%\w\d]+"), - ("mem-address", r"0x[0-9a-fA-F]+"), + ("pointer-address", r"0x[0-9a-fA-F]+"), ("digits", r"[0-9]{4,}"), ]; From 7ce0e95c8c70ec1af1679e52bc7057e31e74cebb Mon Sep 17 00:00:00 2001 From: graelo Date: Tue, 23 Mar 2021 07:56:49 +0100 Subject: [PATCH 5/9] feat: match ISO datetimes --- copyrat.tmux | 6 ++++-- src/textbuf/mod.rs | 26 ++++++++++++++++++++++++++ src/textbuf/regexes.rs | 6 +++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/copyrat.tmux b/copyrat.tmux index 134ee92..b07d1be 100755 --- a/copyrat.tmux +++ b/copyrat.tmux @@ -81,8 +81,10 @@ setup_pattern_binding "p" "--pattern-name path" setup_pattern_binding "u" "--pattern-name url" # prefix + t + m searches for Markdown URLs [...](matched.url) setup_pattern_binding "m" "--pattern-name markdown-url" -# prefix + t + h searches for SHA1/2 (hashes) +# prefix + t + h searches for SHA1/2 short or long hashes setup_pattern_binding "h" "--pattern-name sha" +# prefix + t + d searches for dates or datetimes +setup_pattern_binding "d" "--pattern-name datetime" # prefix + t + e searches for email addresses (see https://www.regular-expressions.info/email.html) setup_pattern_binding "e" "--pattern-name email" # prefix + t + D searches for docker shas @@ -93,7 +95,7 @@ setup_pattern_binding "c" "--pattern-name hexcolor" setup_pattern_binding "U" "--pattern-name uuid" # prefix + t + v searches for version numbers setup_pattern_binding "v" "--pattern-name version" -# prefix + t + d searches for any string of 4+ digits +# prefix + t + G searches for any string of 4+ digits setup_pattern_binding "G" "--pattern-name digits" # prefix + t + m searches for hex numbers: 0xbedead setup_pattern_binding "P" "--pattern-name pointer-address" diff --git a/src/textbuf/mod.rs b/src/textbuf/mod.rs index 60c9031..25a0295 100644 --- a/src/textbuf/mod.rs +++ b/src/textbuf/mod.rs @@ -547,6 +547,32 @@ mod tests { assert_eq!(spans.get(0).unwrap().text, "src/main.rs"); } + #[test] + fn match_datetime() { + let buffer = "12 days ago = 2021-03-04T12:23:34 text"; + let lines = buffer.split('\n').collect::>(); + let use_all_patterns = true; + let named_pat = vec![]; + let custom = vec![]; + let alphabet = Alphabet("abcd".to_string()); + let reverse = false; + let unique_hint = false; + let spans = Model::new( + &lines, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + unique_hint, + ) + .spans; + + assert_eq!(spans.len(), 1); + assert_eq!(spans.get(0).unwrap().pattern, "datetime"); + assert_eq!(spans.get(0).unwrap().text, "2021-03-04T12:23:34"); + } + #[test] fn priority_between_regexes() { let buffer = "Lorem [link](http://foo.bar) ipsum CUSTOM-52463 lorem ISSUE-123 lorem\nLorem /var/fd70b569/9999.log 52463 lorem\n Lorem 973113 lorem 123e4567-e89b-12d3-a456-426655440000 lorem 8888 lorem\n https://crates.io/23456/fd70b569 lorem"; diff --git a/src/textbuf/regexes.rs b/src/textbuf/regexes.rs index 5f57914..31603ea 100644 --- a/src/textbuf/regexes.rs +++ b/src/textbuf/regexes.rs @@ -7,7 +7,7 @@ pub(super) const EXCLUDE_PATTERNS: [(&str, &str); 1] = /// /// The email address was obtained at https://www.regular-expressions.info/email.html. /// Others were obtained from Ferran Basora. -pub(super) const PATTERNS: [(&str, &str); 16] = [ +pub(super) const PATTERNS: [(&str, &str); 17] = [ ("markdown-url", r"\[[^]]*\]\(([^)]+)\)"), ( "url", @@ -32,6 +32,10 @@ pub(super) const PATTERNS: [(&str, &str); 16] = [ ("ipv4", r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"), ("ipv6", r"[A-f0-9:]+:+[A-f0-9:]+[%\w\d]+"), ("pointer-address", r"0x[0-9a-fA-F]+"), + ( + "datetime", + r"(\d{4}-?\d{2}-?\d{2}([ T]\d{2}:\d{2}:\d{2}(\.\d{3,9})?)?)", + ), ("digits", r"[0-9]{4,}"), ]; From ae19f2b4e4cbc4f217c42bac7841665a95b0e119 Mon Sep 17 00:00:00 2001 From: graelo Date: Tue, 23 Mar 2021 08:38:12 +0100 Subject: [PATCH 6/9] fix: tmux options read correctly --- src/config/basic.rs | 8 +++++--- src/config/extended.rs | 18 ++++-------------- src/tmux.rs | 4 ++-- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/config/basic.rs b/src/config/basic.rs index 993936e..785a0fa 100644 --- a/src/config/basic.rs +++ b/src/config/basic.rs @@ -83,10 +83,12 @@ impl FromStr for HintStyleArg { fn from_str(s: &str) -> Result { match s { - "leading" => Ok(HintStyleArg::Underline), - "trailing" => Ok(HintStyleArg::Surround), + "bold" => Ok(HintStyleArg::Bold), + "italic" => Ok(HintStyleArg::Italic), + "underline" => Ok(HintStyleArg::Underline), + "surrond" => Ok(HintStyleArg::Surround), _ => Err(error::ParseError::ExpectedString(String::from( - "underline or surround", + "bold, italic, underline or surround", ))), } } diff --git a/src/config/extended.rs b/src/config/extended.rs index 9c545f6..1b3686d 100644 --- a/src/config/extended.rs +++ b/src/config/extended.rs @@ -4,11 +4,7 @@ use std::fmt; use std::str::FromStr; use super::basic; -use crate::{ - error, - textbuf::{alphabet, regexes}, - tmux, ui, -}; +use crate::{error, textbuf::alphabet, tmux, ui}; /// Extended configuration for handling Tmux-specific configuration (options /// and outputs). This is only used by `tmux-copyrat` and parsed from command @@ -61,18 +57,12 @@ impl ConfigExt { for (name, value) in &tmux_options { match name.as_ref() { - "@copyrat-capture" => { + "@copyrat-capture-region" => { config_ext.capture_region = CaptureRegion::from_str(&value)? } "@copyrat-alphabet" => { wrapped.alphabet = alphabet::parse_alphabet(value)?; } - "@copyrat-pattern-name" => { - wrapped.named_patterns = vec![regexes::parse_pattern_name(value)?] - } - "@copyrat-custom-pattern" => { - wrapped.custom_patterns = vec![String::from(value)] - } "@copyrat-reverse" => { wrapped.reverse = value.parse::()?; } @@ -127,8 +117,8 @@ impl FromStr for CaptureRegion { fn from_str(s: &str) -> Result { match s { - "leading" => Ok(CaptureRegion::EntireHistory), - "trailing" => Ok(CaptureRegion::VisibleArea), + "entire-history" => Ok(CaptureRegion::EntireHistory), + "visible-area" => Ok(CaptureRegion::VisibleArea), _ => Err(error::ParseError::ExpectedString(String::from( "entire-history or visible-area", ))), diff --git a/src/tmux.rs b/src/tmux.rs index b87375a..fc15719 100644 --- a/src/tmux.rs +++ b/src/tmux.rs @@ -141,10 +141,10 @@ pub fn list_panes() -> Result, ParseError> { /// # Example /// ```get_options("@copyrat-")``` pub fn get_options(prefix: &str) -> Result, ParseError> { - let output = duct::cmd!("tmux", "show", "-g").read()?; + let output = duct::cmd!("tmux", "show-options", "-g").read()?; let lines: Vec<&str> = output.split('\n').collect(); - let pattern = format!(r#"{prefix}([\w\-0-9]+) "?(\w+)"?"#, prefix = prefix); + let pattern = format!(r#"({prefix}[\w\-0-9]+) "?(\w+)"?"#, prefix = prefix); let re = Regex::new(&pattern).unwrap(); let args: HashMap = lines From 7550895602c9f1f7e63b0959085717932cee8c3e Mon Sep 17 00:00:00 2001 From: graelo Date: Tue, 23 Mar 2021 22:20:21 +0100 Subject: [PATCH 7/9] fix: default colors --- src/ui/colors.rs | 32 ++++++++++++++++---------------- src/ui/vc.rs | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ui/colors.rs b/src/ui/colors.rs index 064ea14..a6f3be2 100644 --- a/src/ui/colors.rs +++ b/src/ui/colors.rs @@ -12,15 +12,15 @@ pub fn parse_color(src: &str) -> Result, error::ParseError "magenta" => Ok(Box::new(color::Magenta)), "cyan" => Ok(Box::new(color::Cyan)), "white" => Ok(Box::new(color::White)), - "bright-black" => Ok(Box::new(color::LightBlack)), - "bright-red" => Ok(Box::new(color::LightRed)), - "bright-green" => Ok(Box::new(color::LightGreen)), - "bright-yellow" => Ok(Box::new(color::LightYellow)), - "bright-blue" => Ok(Box::new(color::LightBlue)), - "bright-magenta" => Ok(Box::new(color::LightMagenta)), - "bright-cyan" => Ok(Box::new(color::LightCyan)), - "bright-white" => Ok(Box::new(color::LightWhite)), - // "default" => Ok(Box::new(color::Reset)), + "bright-black" | "brightblack" => Ok(Box::new(color::LightBlack)), + "bright-red" | "brightred" => Ok(Box::new(color::LightRed)), + "bright-green" | "brightgreen" => Ok(Box::new(color::LightGreen)), + "bright-yellow" | "brightyellow" => Ok(Box::new(color::LightYellow)), + "bright-blue" | "brightblue" => Ok(Box::new(color::LightBlue)), + "bright-magenta" | "brightmagenta" => Ok(Box::new(color::LightMagenta)), + "bright-cyan" | "brightcyan" => Ok(Box::new(color::LightCyan)), + "bright-white" | "brightwhite" => Ok(Box::new(color::LightWhite)), + "none" => Ok(Box::new(color::Reset)), _ => Err(error::ParseError::UnknownColor), } } @@ -56,20 +56,20 @@ mod tests { #[clap(about)] // Needed to avoid this doc comment to be used as overall `about`. pub struct UiColors { /// Foreground color for base text. - #[clap(long, default_value = "bright-blue", parse(try_from_str = parse_color))] + #[clap(long, default_value = "bright-cyan", parse(try_from_str = parse_color))] pub text_fg: Box, /// Background color for base text. - #[clap(long, default_value = "white", parse(try_from_str = parse_color))] + #[clap(long, default_value = "none", parse(try_from_str = parse_color))] pub text_bg: Box, /// Foreground color for spans. - #[clap(long, default_value = "yellow", + #[clap(long, default_value = "blue", parse(try_from_str = parse_color))] pub span_fg: Box, /// Background color for spans. - #[clap(long, default_value = "white", + #[clap(long, default_value = "none", parse(try_from_str = parse_color))] pub span_bg: Box, @@ -79,17 +79,17 @@ pub struct UiColors { pub focused_fg: Box, /// Background color for the focused span. - #[clap(long, default_value = "white", + #[clap(long, default_value = "none", parse(try_from_str = parse_color))] pub focused_bg: Box, /// Foreground color for hints. - #[clap(long, default_value = "white", + #[clap(long, default_value = "yellow", parse(try_from_str = parse_color))] pub hint_fg: Box, /// Background color for hints. - #[clap(long, default_value = "magenta", + #[clap(long, default_value = "none", parse(try_from_str = parse_color))] pub hint_bg: Box, } diff --git a/src/ui/vc.rs b/src/ui/vc.rs index e49f753..ec85a27 100644 --- a/src/ui/vc.rs +++ b/src/ui/vc.rs @@ -265,7 +265,7 @@ impl<'a> ViewController<'a> { fg_reset = fg_reset, bg_reset = bg_reset, sty = style::Bold, - sty_reset = style::NoBold, + sty_reset = style::Reset, // NoBold is not sufficient hint = hint_text, ) .unwrap(); From b049fba6429dc9de6554d74b62d92187a0e7c647 Mon Sep 17 00:00:00 2001 From: graelo Date: Wed, 24 Mar 2021 11:36:58 +0100 Subject: [PATCH 8/9] feat: capture in copy mode --- src/bin/tmux_copyrat.rs | 4 +++ src/tmux.rs | 62 ++++++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/bin/tmux_copyrat.rs b/src/bin/tmux_copyrat.rs index bba6f79..df8b672 100644 --- a/src/bin/tmux_copyrat.rs +++ b/src/bin/tmux_copyrat.rs @@ -40,6 +40,10 @@ fn main() -> Result<(), error::ParseError> { output_destination, }) => { if uppercased { + if active_pane.is_copy_mode { + // break out of copy mode + duct::cmd!("tmux", "copy-mode", "-t", active_pane.id.as_str(), "-q").run()?; + } duct::cmd!("tmux", "send-keys", "-t", active_pane.id.as_str(), &text).run()?; } diff --git a/src/tmux.rs b/src/tmux.rs index fc15719..4e5c459 100644 --- a/src/tmux.rs +++ b/src/tmux.rs @@ -15,16 +15,16 @@ use crate::error::ParseError; pub struct Pane { /// Pane identifier, e.g. `%37`. pub id: PaneId, - /// Describes if the pane is in some mode. - pub in_mode: bool, + /// Describes if the pane is in copy mode. + pub is_copy_mode: bool, /// Number of lines in the pane. - pub height: u32, + pub height: i32, /// Optional offset from the bottom if the pane is in some mode. /// /// When a pane is in copy mode, scrolling up changes the /// `scroll_position`. If the pane is in normal mode, or unscrolled, /// then `0` is returned. - pub scroll_position: u32, + pub scroll_position: i32, /// Describes if the pane is currently active (focused). pub is_active: bool, } @@ -59,9 +59,9 @@ impl FromStr for Pane { // let id = id_str[1..].parse::()?; // let id = format!("%{}", id); - let in_mode = iter.next().unwrap().parse::()?; + let is_copy_mode = iter.next().unwrap().parse::()?; - let height = iter.next().unwrap().parse::()?; + let height = iter.next().unwrap().parse::()?; let scroll_position = iter.next().unwrap(); let scroll_position = if scroll_position.is_empty() { @@ -69,13 +69,13 @@ impl FromStr for Pane { } else { scroll_position }; - let scroll_position = scroll_position.parse::()?; + let scroll_position = scroll_position.parse::()?; let is_active = iter.next().unwrap().parse::()?; Ok(Pane { id, - in_mode, + is_copy_mode, height, scroll_position, is_active, @@ -167,35 +167,41 @@ pub fn get_options(prefix: &str) -> Result, ParseError> /// The provided `region` specifies if the visible area is captured, or the /// entire history. /// -/// # TODO -/// -/// Capture with `capture-pane -J` joins wrapped lines. -/// /// # Note /// -/// If the pane is in normal mode, capturing the visible area can be done -/// without extra arguments (default behavior of `capture-pane`), but if the -/// pane is in copy mode, we need to take into account the current scroll -/// position. To support both cases, the implementation always provides those -/// parameters to tmux. +/// In Tmux, the start line is the line at the top of the pane. The end line +/// is the last line at the bottom of the pane. +/// +/// - In normal mode, the index of the start line is always 0. The index of +/// the end line is always the pane's height minus one. These do not need to +/// be specified when capturing the pane's content. +/// +/// - If navigating history in copy mode, the index of the start line is the +/// opposite of the pane's scroll position. For instance a pane of 40 lines, +/// scrolled up by 3 lines. It is necessarily in copy mode. Its start line +/// index is `-3`. The index of the last line is `(40-1) - 3 = 36`. +/// pub fn capture_pane(pane: &Pane, region: &CaptureRegion) -> Result { - let mut args = format!("capture-pane -t {pane_id} -J -p", pane_id = pane.id); + let mut args_str = format!("capture-pane -t {pane_id} -J -p", pane_id = pane.id); let region_str = match region { CaptureRegion::VisibleArea => { - // Providing start/end helps support both copy and normal modes. - format!( - " -S {start} -E {end}", - start = pane.scroll_position, - end = pane.height - pane.scroll_position - 1 - ) + if pane.is_copy_mode && pane.scroll_position > 0 { + format!( + " -S {start} -E {end}", + start = -pane.scroll_position, + end = pane.height - pane.scroll_position - 1 + ) + } else { + String::new() + } } CaptureRegion::EntireHistory => String::from(" -S - -E -"), }; - args.push_str(®ion_str); + args_str.push_str(®ion_str); - let args: Vec<&str> = args.split(' ').collect(); + let args: Vec<&str> = args_str.split(' ').collect(); let output = duct::cmd("tmux", &args).read()?; Ok(output) @@ -226,7 +232,7 @@ mod tests { let expected = vec![ Pane { id: PaneId::from_str("%52").unwrap(), - in_mode: false, + is_copy_mode: false, height: 62, scroll_position: 3, is_active: false, @@ -234,7 +240,7 @@ mod tests { Pane { // id: PaneId::from_str("%53").unwrap(), id: PaneId(String::from("%53")), - in_mode: false, + is_copy_mode: false, height: 23, scroll_position: 0, is_active: true, From f2fe3ec5902282e4a06d915b8cbc297cb887ce8d Mon Sep 17 00:00:00 2001 From: graelo Date: Thu, 25 Mar 2021 09:59:21 +0100 Subject: [PATCH 9/9] refactor: ui/vc better names, offset/pos, ScreenLine --- src/ui/vc.rs | 183 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 110 insertions(+), 73 deletions(-) diff --git a/src/ui/vc.rs b/src/ui/vc.rs index ec85a27..c8f7378 100644 --- a/src/ui/vc.rs +++ b/src/ui/vc.rs @@ -9,10 +9,34 @@ use super::Selection; use super::{HintAlignment, HintStyle}; use crate::{config::extended::OutputDestination, textbuf}; +/// Describes where a line from the buffer is displayed on the screen and how +/// much vertical lines it takes. +/// +/// The `pos_y` field is the actual vertical position due to wrapped lines +/// before this line. The `size` field is the number of screen lines occupied +/// by this line. +/// +/// For example, given a buffer in which +/// +/// - the first line is smaller than the screen width, +/// - the second line is slightly larger, +/// - and the third line is smaller than the screen width, +/// +/// The corresponding `WrappedLine`s are +/// +/// - the first `WrappedLine` has `pos_y: 0` and `size: 1` +/// - the second `WrappedLine` has `pos_y: 1` and `size: 2` (larger than screen +/// width) +/// - the third `WrappedLine` has `pos_y: 3` and `size: 1` +/// +struct WrappedLine { + pos_y: usize, +} + pub struct ViewController<'a> { model: &'a textbuf::Model<'a>, term_width: u16, - line_offsets: Vec, + wrapped_lines: Vec, focus_index: usize, focus_wrap_around: bool, default_output_destination: OutputDestination, @@ -39,12 +63,12 @@ impl<'a> ViewController<'a> { }; let (term_width, _) = termion::terminal_size().unwrap_or((80u16, 30u16)); // .expect("Cannot read the terminal size."); - let line_offsets = get_line_offsets(&model.lines, term_width); + let wrapped_lines = compute_wrapped_lines(&model.lines, term_width); ViewController { model, term_width, - line_offsets, + wrapped_lines, focus_index, focus_wrap_around, default_output_destination, @@ -57,41 +81,44 @@ impl<'a> ViewController<'a> { // }}} // Coordinates {{{1 - /// Convert the `Span` text into the coordinates of the wrapped lines. + /// Returns the adjusted position of a given `Span` within the buffer + /// line. /// - /// Compute the new x offset of the text as the remainder of the line width - /// (e.g. the Span could start at offset 120 in a 80-width terminal, the new - /// offset being 40). + /// This adjustment is necessary if multibyte characters occur before the + /// span (in the "prefix"). If this is the case then their compouding + /// takes less space on screen when printed: for instance ´ + e = é. + /// Consequently the span position has to be adjusted to the left. /// - /// Compute the new y offset of the text as the initial y offset plus any - /// additional offset due to previous split lines. This is obtained thanks to - /// the `offset_per_line` member. - fn map_coords_to_wrapped_space(&self, offset_x: usize, offset_y: usize) -> (usize, usize) { - let line_width = self.term_width as usize; - - let new_offset_x = offset_x % line_width; - let new_offset_y = - self.line_offsets.get(offset_y as usize).unwrap() + offset_x / line_width; - - (new_offset_x, new_offset_y) - } - - /// 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 span_offsets(&self, span: &textbuf::Span<'a>) -> (usize, usize) { - let offset_x = { + /// This computation must happen before mapping the span position to the + /// wrapped screen space. + fn adjusted_span_position(&self, span: &textbuf::Span<'a>) -> (usize, usize) { + let pos_x = { let line = &self.model.lines[span.y as usize]; let prefix = &line[0..span.x as usize]; let adjust = prefix.len() - prefix.chars().count(); - (span.x as usize) - (adjust) + (span.x as usize) - adjust }; - let offset_y = span.y as usize; + let pos_y = span.y as usize; - (offset_x, offset_y) + (pos_x, pos_y) + } + + /// Convert the `Span` text into the coordinates of the wrapped lines. + /// + /// Compute the new x position of the text as the remainder of the line width + /// (e.g. the Span could start at position 120 in a 80-width terminal, the new + /// position being 40). + /// + /// Compute the new y position of the text as the initial y position plus any + /// additional offset due to previous split lines. This is obtained thanks to + /// the `wrapped_lines` field. + fn map_coords_to_wrapped_space(&self, pos_x: usize, pos_y: usize) -> (usize, usize) { + let line_width = self.term_width as usize; + + let new_pos_x = pos_x % line_width; + let new_pos_y = self.wrapped_lines[pos_y as usize].pos_y + pos_x / line_width; + + (new_pos_x, new_pos_y) } // }}} @@ -144,7 +171,7 @@ impl<'a> ViewController<'a> { fn render_base_text( stdout: &mut dyn io::Write, lines: &[&str], - line_offsets: &[usize], + wrapped_lines: &[WrappedLine], colors: &UiColors, ) { write!( @@ -159,13 +186,12 @@ impl<'a> ViewController<'a> { let trimmed_line = line.trim_end(); if !trimmed_line.is_empty() { - let offset_y: usize = - *(line_offsets.get(line_index)).expect("Cannot get offset_per_line."); + let pos_y: usize = wrapped_lines[line_index].pos_y; write!( stdout, "{goto}{text}", - goto = cursor::Goto(1, offset_y as u16 + 1), + goto = cursor::Goto(1, pos_y as u16 + 1), text = &trimmed_line, ) .unwrap(); @@ -192,7 +218,7 @@ impl<'a> ViewController<'a> { stdout: &mut dyn io::Write, text: &str, focused: bool, - offset: (usize, usize), + pos: (usize, usize), colors: &UiColors, ) { // To help identify it, the span thas has focus is rendered with a dedicated color. @@ -206,7 +232,7 @@ impl<'a> ViewController<'a> { write!( stdout, "{goto}{bg_color}{fg_color}{text}{fg_reset}{bg_reset}", - goto = cursor::Goto(offset.0 as u16 + 1, offset.1 as u16 + 1), + goto = cursor::Goto(pos.0 as u16 + 1, pos.1 as u16 + 1), fg_color = color::Fg(fg_color.as_ref()), bg_color = color::Bg(bg_color.as_ref()), fg_reset = color::Fg(color::Reset), @@ -230,7 +256,7 @@ impl<'a> ViewController<'a> { fn render_span_hint( stdout: &mut dyn io::Write, hint_text: &str, - offset: (usize, usize), + pos: (usize, usize), colors: &UiColors, hint_style: &Option, ) { @@ -238,7 +264,7 @@ impl<'a> ViewController<'a> { let bg_color = color::Bg(colors.hint_bg.as_ref()); let fg_reset = color::Fg(color::Reset); let bg_reset = color::Bg(color::Reset); - let goto = cursor::Goto(offset.0 as u16 + 1, offset.1 as u16 + 1); + let goto = cursor::Goto(pos.0 as u16 + 1, pos.1 as u16 + 1); match hint_style { None => { @@ -324,14 +350,14 @@ impl<'a> ViewController<'a> { 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.span_offsets(span); - let (offset_x, offset_y) = self.map_coords_to_wrapped_space(offset_x, offset_y); + let (pos_x, pos_y) = self.adjusted_span_position(span); + let (pos_x, pos_y) = self.map_coords_to_wrapped_space(pos_x, pos_y); ViewController::render_span_text( stdout, text, focused, - (offset_x, offset_y), + (pos_x, pos_y), &self.rendering_colors, ); @@ -339,7 +365,7 @@ impl<'a> ViewController<'a> { // If not focused, render the hint (e.g. "eo") as an overlay on // top of the rendered text span, aligned at its leading or the // trailing edge. - let extra_offset = match self.hint_alignment { + let offset = match self.hint_alignment { HintAlignment::Leading => 0, HintAlignment::Trailing => text.len() - span.hint.len(), }; @@ -347,7 +373,7 @@ impl<'a> ViewController<'a> { ViewController::render_span_hint( stdout, &span.hint, - (offset_x + extra_offset, offset_y), + (pos_x + offset, pos_y), &self.rendering_colors, &self.hint_style, ); @@ -361,11 +387,12 @@ impl<'a> ViewController<'a> { /// - 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 Span's `text`, - /// or on the trailing edge. + /// Depending on the value of `self.hint_alignment`, the hint can be + /// rendered 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 Span's `text` /// and `hint` are rendered in their proper position. fn full_render(&self, stdout: &mut dyn io::Write) { @@ -373,7 +400,7 @@ impl<'a> ViewController<'a> { ViewController::render_base_text( stdout, &self.model.lines, - &self.line_offsets, + &self.wrapped_lines, &self.rendering_colors, ); @@ -597,27 +624,30 @@ impl<'a> ViewController<'a> { // }}} } -/// Compute each line's actual y offset if displayed in a terminal of width +/// Compute each line's actual y position and size if displayed in a terminal of width /// `term_width`. -fn get_line_offsets(lines: &[&str], term_width: u16) -> Vec { +fn compute_wrapped_lines(lines: &[&str], term_width: u16) -> Vec { lines .iter() - .scan(0, |offset, &line| { + .scan(0, |position, &line| { // Save the value to return (yield is in unstable). - let value = *offset; + let value = *position; let line_width = line.trim_end().chars().count() as isize; // Amount of extra y space taken by this line. // If the line has n chars, on a term of width n, this does not // produce an extra line; it needs to exceed the width by 1 char. - // In case the width is 0, we need to clamp line_width - 1 first. + // In case the width is 0, we need to first clamp line_width - 1. let extra = cmp::max(0, line_width - 1) as usize / term_width as usize; - // Update the offset of the next line. - *offset = *offset + 1 + extra; + // Update the position of the next line. + *position += 1 + extra; - Some(value) + Some(WrappedLine { + pos_y: value, + // size: 1 + extra, + }) }) .collect() } @@ -644,7 +674,14 @@ path: /usr/local/bin/git path: /usr/local/bin/cargo"; let lines: Vec<&str> = content.split('\n').collect(); - let line_offsets: Vec = (0..lines.len()).collect(); + let wrapped_lines: Vec = vec![ + WrappedLine { pos_y: 0 }, + WrappedLine { pos_y: 1 }, + WrappedLine { pos_y: 2 }, + WrappedLine { pos_y: 3 }, + WrappedLine { pos_y: 4 }, + WrappedLine { pos_y: 5 }, + ]; let colors = UiColors { text_fg: Box::new(color::Black), @@ -658,7 +695,7 @@ path: /usr/local/bin/cargo"; }; let mut writer = vec![]; - ViewController::render_base_text(&mut writer, &lines, &line_offsets, &colors); + ViewController::render_base_text(&mut writer, &lines, &wrapped_lines, &colors); let goto1 = cursor::Goto(1, 1); let goto2 = cursor::Goto(1, 2); @@ -683,7 +720,7 @@ path: /usr/local/bin/cargo"; let mut writer = vec![]; let text = "https://en.wikipedia.org/wiki/Barcelona"; let focused = true; - let offset: (usize, usize) = (3, 1); + let position: (usize, usize) = (3, 1); let colors = UiColors { text_fg: Box::new(color::Black), text_bg: Box::new(color::White), @@ -695,7 +732,7 @@ path: /usr/local/bin/cargo"; hint_bg: Box::new(color::Cyan), }; - ViewController::render_span_text(&mut writer, text, focused, offset, &colors); + ViewController::render_span_text(&mut writer, text, focused, position, &colors); assert_eq!( writer, @@ -717,7 +754,7 @@ path: /usr/local/bin/cargo"; let mut writer = vec![]; let text = "https://en.wikipedia.org/wiki/Barcelona"; let focused = false; - let offset: (usize, usize) = (3, 1); + let position: (usize, usize) = (3, 1); let colors = UiColors { text_fg: Box::new(color::Black), text_bg: Box::new(color::White), @@ -729,7 +766,7 @@ path: /usr/local/bin/cargo"; hint_bg: Box::new(color::Cyan), }; - ViewController::render_span_text(&mut writer, text, focused, offset, &colors); + ViewController::render_span_text(&mut writer, text, focused, position, &colors); assert_eq!( writer, @@ -750,7 +787,7 @@ path: /usr/local/bin/cargo"; fn test_render_unstyled_span_hint() { let mut writer = vec![]; let hint_text = "eo"; - let offset: (usize, usize) = (3, 1); + let position: (usize, usize) = (3, 1); let colors = UiColors { text_fg: Box::new(color::Black), text_bg: Box::new(color::White), @@ -762,13 +799,13 @@ path: /usr/local/bin/cargo"; hint_bg: Box::new(color::Cyan), }; - let extra_offset = 0; + let offset = 0; let hint_style = None; ViewController::render_span_hint( &mut writer, hint_text, - (offset.0 + extra_offset, offset.1), + (position.0 + offset, position.1), &colors, &hint_style, ); @@ -792,7 +829,7 @@ path: /usr/local/bin/cargo"; fn test_render_underlined_span_hint() { let mut writer = vec![]; let hint_text = "eo"; - let offset: (usize, usize) = (3, 1); + let position: (usize, usize) = (3, 1); let colors = UiColors { text_fg: Box::new(color::Black), text_bg: Box::new(color::White), @@ -804,13 +841,13 @@ path: /usr/local/bin/cargo"; hint_bg: Box::new(color::Cyan), }; - let extra_offset = 0; + let offset = 0; let hint_style = Some(HintStyle::Underline); ViewController::render_span_hint( &mut writer, hint_text, - (offset.0 + extra_offset, offset.1), + (position.0 + offset, position.1), &colors, &hint_style, ); @@ -836,7 +873,7 @@ path: /usr/local/bin/cargo"; fn test_render_bracketed_span_hint() { let mut writer = vec![]; let hint_text = "eo"; - let offset: (usize, usize) = (3, 1); + let position: (usize, usize) = (3, 1); let colors = UiColors { text_fg: Box::new(color::Black), text_bg: Box::new(color::White), @@ -848,13 +885,13 @@ path: /usr/local/bin/cargo"; hint_bg: Box::new(color::Cyan), }; - let extra_offset = 0; + let offset = 0; let hint_style = Some(HintStyle::Surround('{', '}')); ViewController::render_span_hint( &mut writer, hint_text, - (offset.0 + extra_offset, offset.1), + (position.0 + offset, position.1), &colors, &hint_style, ); @@ -900,7 +937,7 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - "; unique_hint, ); let term_width: u16 = 80; - let line_offsets = get_line_offsets(&model.lines, term_width); + let wrapped_lines = compute_wrapped_lines(&model.lines, term_width); let rendering_colors = UiColors { text_fg: Box::new(color::Black), text_bg: Box::new(color::White), @@ -917,7 +954,7 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - "; let ui = ViewController { model: &mut model, term_width, - line_offsets, + wrapped_lines, focus_index: 0, focus_wrap_around: false, default_output_destination: OutputDestination::Tmux,