Add Knuth–Morris–Pratt to pager
parent
c9469f26ee
commit
1245eae0be
|
@ -1127,57 +1127,8 @@ easy to take MORE than nothing.'"#;
|
||||||
println!("{}", l);
|
println!("{}", l);
|
||||||
}
|
}
|
||||||
println!("");
|
println!("");
|
||||||
let text = r#"CHAPTER I. Down the Rabbit-Hole
|
use super::_ALICE_CHAPTER_1;
|
||||||
|
for l in split_lines_reflow(_ALICE_CHAPTER_1, Reflow::FormatFlowed, Some(72)) {
|
||||||
Alice was beginning to get very tired of sitting by her sister on the
|
|
||||||
bank, and of having nothing to do: once or twice she had peeped into the
|
|
||||||
book her sister was reading, but it had no pictures or conversations in
|
|
||||||
it, ‘and what is the use of a book,’ thought Alice ‘without pictures or
|
|
||||||
conversations?’
|
|
||||||
|
|
||||||
So she was considering in her own mind (as well as she could, for the
|
|
||||||
hot day made her feel very sleepy and stupid), whether the pleasure
|
|
||||||
of making a daisy-chain would be worth the trouble of getting up and
|
|
||||||
picking the daisies, when suddenly a White Rabbit with pink eyes ran
|
|
||||||
close by her.
|
|
||||||
|
|
||||||
>>There was nothing so VERY remarkable in that; nor did Alice think it so
|
|
||||||
>>VERY much out of the way to hear the Rabbit say to itself, ‘Oh dear!
|
|
||||||
>> Oh dear! I shall be late!’ (when she thought it over afterwards, it
|
|
||||||
>>occurred to her that she ought to have wondered at this, but at the time
|
|
||||||
>>it all seemed quite natural); but when the Rabbit actually TOOK A WATCH
|
|
||||||
OUT OF ITS WAISTCOAT-POCKET, and looked at it, and then hurried on,
|
|
||||||
>>Alice started to her feet, for it flashed across her mind that she had
|
|
||||||
>>never before seen a rabbit with either a waistcoat-pocket, or a watch
|
|
||||||
>>to take out of it, and burning with curiosity, she ran across the field
|
|
||||||
after it, and fortunately was just in time to see it pop down a large
|
|
||||||
rabbit-hole under the hedge.
|
|
||||||
|
|
||||||
In another moment down went Alice after it, never once considering how
|
|
||||||
in the world she was to get out again.
|
|
||||||
|
|
||||||
The rabbit-hole went straight on like a tunnel for some way, and then
|
|
||||||
dipped suddenly down, so suddenly that Alice had not a moment to think
|
|
||||||
about stopping herself before she found herself falling down a very deep
|
|
||||||
well.
|
|
||||||
|
|
||||||
Either the well was very deep, or she fell very slowly, for she had
|
|
||||||
plenty of time as she went down to look about her and to wonder what was
|
|
||||||
going to happen next. First, she tried to look down and make out what
|
|
||||||
she was coming to, but it was too dark to see anything; then she
|
|
||||||
looked at the sides of the well, and noticed that they were filled with
|
|
||||||
cupboards and book-shelves; here and there she saw maps and pictures
|
|
||||||
hung upon pegs. She took down a jar from one of the shelves as
|
|
||||||
she passed; it was labelled ‘ORANGE MARMALADE’, but to her great
|
|
||||||
disappointment it was empty: she did not like to drop the jar for fear
|
|
||||||
of killing somebody, so managed to put it into one of the cupboards as
|
|
||||||
she fell past it.
|
|
||||||
|
|
||||||
‘Well!’ thought Alice to herself, ‘after such a fall as this, I shall
|
|
||||||
think nothing of tumbling down stairs! How brave they’ll all think me at
|
|
||||||
home! Why, I wouldn’t say anything about it, even if I fell off the top
|
|
||||||
of the house!’ (Which was very likely true.)"#;
|
|
||||||
for l in split_lines_reflow(text, Reflow::FormatFlowed, Some(72)) {
|
|
||||||
println!("{}", l);
|
println!("{}", l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
pub mod grapheme_clusters;
|
pub mod grapheme_clusters;
|
||||||
pub mod line_break;
|
pub mod line_break;
|
||||||
|
pub mod search;
|
||||||
mod tables;
|
mod tables;
|
||||||
mod types;
|
mod types;
|
||||||
pub use types::Reflow;
|
pub use types::Reflow;
|
||||||
|
@ -137,3 +138,54 @@ fn test_globmatch() {
|
||||||
|
|
||||||
assert!(!"INBOX/Lists/".matches_glob("INBOX/Lists/*"));
|
assert!(!"INBOX/Lists/".matches_glob("INBOX/Lists/*"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _ALICE_CHAPTER_1: &'static str = r#"CHAPTER I. Down the Rabbit-Hole
|
||||||
|
|
||||||
|
Alice was beginning to get very tired of sitting by her sister on the
|
||||||
|
bank, and of having nothing to do: once or twice she had peeped into the
|
||||||
|
book her sister was reading, but it had no pictures or conversations in
|
||||||
|
it, ‘and what is the use of a book,’ thought Alice ‘without pictures or
|
||||||
|
conversations?’
|
||||||
|
|
||||||
|
So she was considering in her own mind (as well as she could, for the
|
||||||
|
hot day made her feel very sleepy and stupid), whether the pleasure
|
||||||
|
of making a daisy-chain would be worth the trouble of getting up and
|
||||||
|
picking the daisies, when suddenly a White Rabbit with pink eyes ran
|
||||||
|
close by her.
|
||||||
|
|
||||||
|
>>There was nothing so VERY remarkable in that; nor did Alice think it so
|
||||||
|
>>VERY much out of the way to hear the Rabbit say to itself, ‘Oh dear!
|
||||||
|
>> Oh dear! I shall be late!’ (when she thought it over afterwards, it
|
||||||
|
>>occurred to her that she ought to have wondered at this, but at the time
|
||||||
|
>>it all seemed quite natural); but when the Rabbit actually TOOK A WATCH
|
||||||
|
OUT OF ITS WAISTCOAT-POCKET, and looked at it, and then hurried on,
|
||||||
|
>>Alice started to her feet, for it flashed across her mind that she had
|
||||||
|
>>never before seen a rabbit with either a waistcoat-pocket, or a watch
|
||||||
|
>>to take out of it, and burning with curiosity, she ran across the field
|
||||||
|
after it, and fortunately was just in time to see it pop down a large
|
||||||
|
rabbit-hole under the hedge.
|
||||||
|
|
||||||
|
In another moment down went Alice after it, never once considering how
|
||||||
|
in the world she was to get out again.
|
||||||
|
|
||||||
|
The rabbit-hole went straight on like a tunnel for some way, and then
|
||||||
|
dipped suddenly down, so suddenly that Alice had not a moment to think
|
||||||
|
about stopping herself before she found herself falling down a very deep
|
||||||
|
well.
|
||||||
|
|
||||||
|
Either the well was very deep, or she fell very slowly, for she had
|
||||||
|
plenty of time as she went down to look about her and to wonder what was
|
||||||
|
going to happen next. First, she tried to look down and make out what
|
||||||
|
she was coming to, but it was too dark to see anything; then she
|
||||||
|
looked at the sides of the well, and noticed that they were filled with
|
||||||
|
cupboards and book-shelves; here and there she saw maps and pictures
|
||||||
|
hung upon pegs. She took down a jar from one of the shelves as
|
||||||
|
she passed; it was labelled ‘ORANGE MARMALADE’, but to her great
|
||||||
|
disappointment it was empty: she did not like to drop the jar for fear
|
||||||
|
of killing somebody, so managed to put it into one of the cupboards as
|
||||||
|
she fell past it.
|
||||||
|
|
||||||
|
‘Well!’ thought Alice to herself, ‘after such a fall as this, I shall
|
||||||
|
think nothing of tumbling down stairs! How brave they’ll all think me at
|
||||||
|
home! Why, I wouldn’t say anything about it, even if I fell off the top
|
||||||
|
of the house!’ (Which was very likely true.)"#;
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* meli - text_processing mod.
|
||||||
|
*
|
||||||
|
* Copyright 2020 Manos Pitsidianakis
|
||||||
|
*
|
||||||
|
* This file is part of meli.
|
||||||
|
*
|
||||||
|
* meli is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* meli is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use super::TextProcessing;
|
||||||
|
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
pub trait KMP: TextProcessing {
|
||||||
|
fn kmp_search(&self, pattern: &str) -> SmallVec<[usize; 256]> {
|
||||||
|
let w = pattern.split_graphemes();
|
||||||
|
let t = kmp_table(&w);
|
||||||
|
let mut j = 0; // (the position of the current character in text)
|
||||||
|
let mut k = 0; // (the position of the current character in pattern)
|
||||||
|
let mut ret = SmallVec::new();
|
||||||
|
|
||||||
|
let s = self.graphemes_indices();
|
||||||
|
while j < s.len() && k < w.len() as i32 {
|
||||||
|
if w[k as usize] == s[j].1 {
|
||||||
|
j += 1;
|
||||||
|
k += 1;
|
||||||
|
if k as usize == w.len() {
|
||||||
|
ret.push(s[j - (k as usize)].0);
|
||||||
|
k = t[k as usize];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
k = t[k as usize];
|
||||||
|
if k < 0 {
|
||||||
|
j += 1;
|
||||||
|
k += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KMP for str {}
|
||||||
|
|
||||||
|
fn kmp_table(graphemes: &[&str]) -> SmallVec<[i32; 256]> {
|
||||||
|
let mut ret: SmallVec<_> = SmallVec::with_capacity(graphemes.len() + 1);
|
||||||
|
if graphemes.is_empty() {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret.push(-1);
|
||||||
|
for _ in 0..graphemes.len() {
|
||||||
|
ret.push(0);
|
||||||
|
}
|
||||||
|
let mut pos: usize = 1;
|
||||||
|
let mut cnd: i32 = 0;
|
||||||
|
while pos < graphemes.len() {
|
||||||
|
if graphemes[pos] == graphemes[cnd as usize] {
|
||||||
|
ret[pos] = ret[cnd as usize];
|
||||||
|
} else {
|
||||||
|
ret[pos] = cnd;
|
||||||
|
cnd = ret[cnd as usize];
|
||||||
|
while cnd >= 0 && graphemes[pos] != graphemes[cnd as usize] {
|
||||||
|
cnd = ret[cnd as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos += 1;
|
||||||
|
cnd += 1;
|
||||||
|
}
|
||||||
|
ret[pos] = cnd;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_search() {
|
||||||
|
use super::_ALICE_CHAPTER_1;
|
||||||
|
for ind in _ALICE_CHAPTER_1.kmp_search("Alice") {
|
||||||
|
println!(
|
||||||
|
"{:#?}",
|
||||||
|
&_ALICE_CHAPTER_1
|
||||||
|
[ind.saturating_sub(0)..std::cmp::min(_ALICE_CHAPTER_1.len(), ind + 25)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,6 +90,7 @@ pub struct MailView {
|
||||||
pager: Pager,
|
pager: Pager,
|
||||||
subview: Option<Box<dyn Component>>,
|
subview: Option<Box<dyn Component>>,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
|
initialised: bool,
|
||||||
mode: ViewMode,
|
mode: ViewMode,
|
||||||
expand_headers: bool,
|
expand_headers: bool,
|
||||||
headers_no: usize,
|
headers_no: usize,
|
||||||
|
@ -133,6 +134,7 @@ impl MailView {
|
||||||
pager: pager.unwrap_or_default(),
|
pager: pager.unwrap_or_default(),
|
||||||
subview,
|
subview,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
|
initialised: false,
|
||||||
mode: ViewMode::Normal,
|
mode: ViewMode::Normal,
|
||||||
expand_headers: false,
|
expand_headers: false,
|
||||||
|
|
||||||
|
@ -343,6 +345,7 @@ impl MailView {
|
||||||
pub fn update(&mut self, new_coordinates: (usize, FolderHash, EnvelopeHash)) {
|
pub fn update(&mut self, new_coordinates: (usize, FolderHash, EnvelopeHash)) {
|
||||||
self.coordinates = new_coordinates;
|
self.coordinates = new_coordinates;
|
||||||
self.mode = ViewMode::Normal;
|
self.mode = ViewMode::Normal;
|
||||||
|
self.initialised = false;
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -587,7 +590,8 @@ impl Component for MailView {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.dirty {
|
if !self.initialised {
|
||||||
|
self.initialised = true;
|
||||||
let body = {
|
let body = {
|
||||||
let account = &mut context.accounts[self.coordinates.0];
|
let account = &mut context.accounts[self.coordinates.0];
|
||||||
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
|
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
|
||||||
|
@ -595,7 +599,6 @@ impl Component for MailView {
|
||||||
match envelope.body(op) {
|
match envelope.body(op) {
|
||||||
Ok(body) => body,
|
Ok(body) => body,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.dirty = false;
|
|
||||||
clear_area(
|
clear_area(
|
||||||
grid,
|
grid,
|
||||||
(set_y(upper_left, y), bottom_right),
|
(set_y(upper_left, y), bottom_right),
|
||||||
|
@ -626,10 +629,12 @@ impl Component for MailView {
|
||||||
let attachment = &body.attachments()[aidx];
|
let attachment = &body.attachments()[aidx];
|
||||||
self.subview = Some(Box::new(HtmlView::new(&attachment, context)));
|
self.subview = Some(Box::new(HtmlView::new(&attachment, context)));
|
||||||
self.mode = ViewMode::Subview;
|
self.mode = ViewMode::Subview;
|
||||||
|
self.initialised = false;
|
||||||
}
|
}
|
||||||
ViewMode::Normal if body.is_html() => {
|
ViewMode::Normal if body.is_html() => {
|
||||||
self.subview = Some(Box::new(HtmlView::new(&body, context)));
|
self.subview = Some(Box::new(HtmlView::new(&body, context)));
|
||||||
self.mode = ViewMode::Subview;
|
self.mode = ViewMode::Subview;
|
||||||
|
self.initialised = false;
|
||||||
}
|
}
|
||||||
ViewMode::Normal
|
ViewMode::Normal
|
||||||
if context
|
if context
|
||||||
|
@ -659,6 +664,7 @@ impl Component for MailView {
|
||||||
context,
|
context,
|
||||||
)));
|
)));
|
||||||
self.mode = ViewMode::Subview;
|
self.mode = ViewMode::Subview;
|
||||||
|
self.initialised = false;
|
||||||
}
|
}
|
||||||
ViewMode::Subview | ViewMode::ContactSelector(_) => {}
|
ViewMode::Subview | ViewMode::ContactSelector(_) => {}
|
||||||
ViewMode::Source(source) => {
|
ViewMode::Source(source) => {
|
||||||
|
@ -787,6 +793,7 @@ impl Component for MailView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.mode = ViewMode::Normal;
|
self.mode = ViewMode::Normal;
|
||||||
|
self.initialised = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
(ViewMode::ContactSelector(ref mut s), _) => {
|
(ViewMode::ContactSelector(ref mut s), _) => {
|
||||||
|
@ -879,6 +886,7 @@ impl Component for MailView {
|
||||||
context,
|
context,
|
||||||
));
|
));
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
self.initialised = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Esc) | UIEvent::Input(Key::Alt(''))
|
UIEvent::Input(Key::Esc) | UIEvent::Input(Key::Alt(''))
|
||||||
|
@ -886,6 +894,7 @@ impl Component for MailView {
|
||||||
{
|
{
|
||||||
self.mode = ViewMode::Normal;
|
self.mode = ViewMode::Normal;
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
|
self.initialised = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Esc) | UIEvent::Input(Key::Alt('')) if !self.cmd_buf.is_empty() => {
|
UIEvent::Input(Key::Esc) | UIEvent::Input(Key::Alt('')) if !self.cmd_buf.is_empty() => {
|
||||||
|
@ -916,6 +925,7 @@ impl Component for MailView {
|
||||||
_ => ViewMode::Source(Source::Decoded),
|
_ => ViewMode::Source(Source::Decoded),
|
||||||
};
|
};
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
|
self.initialised = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(ref key)
|
UIEvent::Input(ref key)
|
||||||
|
@ -931,6 +941,7 @@ impl Component for MailView {
|
||||||
{
|
{
|
||||||
self.mode = ViewMode::Normal;
|
self.mode = ViewMode::Normal;
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
|
self.initialised = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(ref key)
|
UIEvent::Input(ref key)
|
||||||
|
@ -1052,6 +1063,7 @@ impl Component for MailView {
|
||||||
|
|
||||||
ContentType::Text { .. } | ContentType::PGPSignature => {
|
ContentType::Text { .. } | ContentType::PGPSignature => {
|
||||||
self.mode = ViewMode::Attachment(lidx);
|
self.mode = ViewMode::Attachment(lidx);
|
||||||
|
self.initialised = false;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
ContentType::Multipart { .. } => {
|
ContentType::Multipart { .. } => {
|
||||||
|
@ -1091,6 +1103,7 @@ impl Component for MailView {
|
||||||
{
|
{
|
||||||
let raw_buf = RawBuffer::new(buf, name_opt);
|
let raw_buf = RawBuffer::new(buf, name_opt);
|
||||||
self.mode = ViewMode::Ansi(raw_buf);
|
self.mode = ViewMode::Ansi(raw_buf);
|
||||||
|
self.initialised = false;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1227,6 +1240,7 @@ impl Component for MailView {
|
||||||
ViewMode::Url => self.mode = ViewMode::Normal,
|
ViewMode::Url => self.mode = ViewMode::Normal,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
self.initialised = false;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -276,6 +276,14 @@ pub enum PageMovement {
|
||||||
End,
|
End,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct SearchPattern {
|
||||||
|
pattern: String,
|
||||||
|
positions: Vec<(usize, usize)>,
|
||||||
|
cursor: usize,
|
||||||
|
movement: Option<PageMovement>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A pager for text.
|
/// A pager for text.
|
||||||
/// `Pager` holds its own content in its own `CellBuffer` and when `draw` is called, it draws the
|
/// `Pager` holds its own content in its own `CellBuffer` and when `draw` is called, it draws the
|
||||||
/// current view of the text. It is responsible for scrolling etc.
|
/// current view of the text. It is responsible for scrolling etc.
|
||||||
|
@ -287,6 +295,7 @@ pub struct Pager {
|
||||||
height: usize,
|
height: usize,
|
||||||
width: usize,
|
width: usize,
|
||||||
minimum_width: usize,
|
minimum_width: usize,
|
||||||
|
search: Option<SearchPattern>,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
|
|
||||||
colors: ThemeAttribute,
|
colors: ThemeAttribute,
|
||||||
|
@ -530,7 +539,40 @@ impl Component for Pager {
|
||||||
empty_cell.set_bg(self.colors.bg);
|
empty_cell.set_bg(self.colors.bg);
|
||||||
let mut content = CellBuffer::new(width, height, empty_cell);
|
let mut content = CellBuffer::new(width, height, empty_cell);
|
||||||
content.set_ascii_drawing(self.content.ascii_drawing);
|
content.set_ascii_drawing(self.content.ascii_drawing);
|
||||||
|
if let Some(ref mut search) = self.search {
|
||||||
|
use melib::text_processing::search::KMP;
|
||||||
|
search.positions.clear();
|
||||||
|
for (y, l) in lines.iter().enumerate() {
|
||||||
|
search.positions.extend(
|
||||||
|
l.kmp_search(&search.pattern)
|
||||||
|
.into_iter()
|
||||||
|
.map(|offset| (y, offset)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Pager::print_string(&mut content, lines, self.colors);
|
Pager::print_string(&mut content, lines, self.colors);
|
||||||
|
if let Some(ref mut search) = self.search {
|
||||||
|
let results_attr = crate::conf::value(context, "pager.highlight_search");
|
||||||
|
let results_current_attr =
|
||||||
|
crate::conf::value(context, "pager.highlight_search_current");
|
||||||
|
search.cursor =
|
||||||
|
std::cmp::min(search.positions.len().saturating_sub(1), search.cursor);
|
||||||
|
for (i, (y, x)) in search.positions.iter().enumerate() {
|
||||||
|
for c in content.row_iter(*x..*x + search.pattern.grapheme_len(), *y) {
|
||||||
|
if i == search.cursor {
|
||||||
|
content[c]
|
||||||
|
.set_fg(results_current_attr.fg)
|
||||||
|
.set_bg(results_current_attr.bg)
|
||||||
|
.set_attrs(results_current_attr.attrs);
|
||||||
|
} else {
|
||||||
|
content[c]
|
||||||
|
.set_fg(results_attr.fg)
|
||||||
|
.set_bg(results_attr.bg)
|
||||||
|
.set_attrs(results_attr.attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
self.content = content;
|
self.content = content;
|
||||||
self.height = height;
|
self.height = height;
|
||||||
self.width = width;
|
self.width = width;
|
||||||
|
@ -580,6 +622,25 @@ impl Component for Pager {
|
||||||
if self.height == 0 || self.width == 0 {
|
if self.height == 0 || self.width == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if let Some(ref mut search) = self.search {
|
||||||
|
if !search.positions.is_empty() {
|
||||||
|
if let Some(mvm) = search.movement.take() {
|
||||||
|
match mvm {
|
||||||
|
PageMovement::Up(_) => {
|
||||||
|
if self.cursor.1 > search.positions[search.cursor].0 {
|
||||||
|
self.cursor.1 = search.positions[search.cursor].0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PageMovement::Down(_) => {
|
||||||
|
if self.cursor.1 + height < search.positions[search.cursor].0 {
|
||||||
|
self.cursor.1 = search.positions[search.cursor].0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clear_area(grid, area, crate::conf::value(context, "theme_default"));
|
clear_area(grid, area, crate::conf::value(context, "theme_default"));
|
||||||
let (width, height) = self.content.size();
|
let (width, height) = self.content.size();
|
||||||
|
@ -694,6 +755,49 @@ impl Component for Pager {
|
||||||
))));
|
))));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
UIEvent::Action(Action::Listing(ListingAction::Filter(pattern))) => {
|
||||||
|
self.search = Some(SearchPattern {
|
||||||
|
pattern: pattern.to_string(),
|
||||||
|
positions: vec![],
|
||||||
|
cursor: 0,
|
||||||
|
movement: None,
|
||||||
|
});
|
||||||
|
self.initialised = false;
|
||||||
|
self.dirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::Input(Key::Char('n')) if self.search.is_some() => {
|
||||||
|
if let Some(ref mut search) = self.search {
|
||||||
|
search.movement = Some(PageMovement::Down(1));
|
||||||
|
search.cursor += 1;
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
std::hint::unreachable_unchecked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.initialised = false;
|
||||||
|
self.dirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::Input(Key::Char('N')) if self.search.is_some() => {
|
||||||
|
if let Some(ref mut search) = self.search {
|
||||||
|
search.movement = Some(PageMovement::Up(1));
|
||||||
|
search.cursor = search.cursor.saturating_sub(1);
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
std::hint::unreachable_unchecked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.initialised = false;
|
||||||
|
self.dirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::Input(Key::Esc) if self.search.is_some() => {
|
||||||
|
self.search = None;
|
||||||
|
self.initialised = false;
|
||||||
|
self.dirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
UIEvent::Resize => {
|
UIEvent::Resize => {
|
||||||
self.initialised = false;
|
self.initialised = false;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
|
@ -195,6 +195,8 @@ const DEFAULT_KEYS: &'static [&'static str] = &[
|
||||||
"mail.view.body",
|
"mail.view.body",
|
||||||
"mail.listing.attachment_flag",
|
"mail.listing.attachment_flag",
|
||||||
"mail.listing.thread_snooze_flag",
|
"mail.listing.thread_snooze_flag",
|
||||||
|
"pager.highlight_search",
|
||||||
|
"pager.highlight_search_current",
|
||||||
];
|
];
|
||||||
|
|
||||||
/// `ThemeAttributeInner` but with the links resolved.
|
/// `ThemeAttributeInner` but with the links resolved.
|
||||||
|
@ -717,6 +719,8 @@ impl Default for Theme {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
add!("pager.highlight_search", light = { fg: Color::White, bg: Color::Byte(6) /* Teal */, attrs: Attr::Bold }, dark = { fg: Color::White, bg: Color::Byte(6) /* Teal */, attrs: Attr::Bold });
|
||||||
|
add!("pager.highlight_search_current", light = { fg: Color::White, bg: Color::Byte(17) /* NavyBlue */, attrs: Attr::Bold }, dark = { fg: Color::White, bg: Color::Byte(17) /* NavyBlue */, attrs: Attr::Bold });
|
||||||
Theme {
|
Theme {
|
||||||
light,
|
light,
|
||||||
dark,
|
dark,
|
||||||
|
|
Loading…
Reference in New Issue