perf: optimize entry ranges (#110)

This commit is contained in:
Bertrand Chardon 2024-12-09 23:39:06 +01:00 committed by GitHub
parent 758bfc290a
commit a4d15af694
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 255 additions and 131 deletions

View File

@ -3,6 +3,7 @@ use devicons::FileIcon;
use ratatui::layout::Alignment;
use ratatui::prelude::{Line, Style};
use ratatui::widgets::{Block, BorderType, Borders, ListDirection, Padding};
use television_channels::entry::merge_ranges;
use television_channels::entry::{Entry, PreviewType};
use television_screen::colors::BORDER_COLOR;
use television_screen::results::build_results_list;
@ -15,7 +16,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/LICENSE".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{f016}',
@ -27,7 +33,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/README.md".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{f48a}',
@ -39,7 +50,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/re.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -51,7 +67,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/io.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -63,7 +84,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/gc.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -75,7 +101,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/uu.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -87,7 +118,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/nt.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -99,7 +135,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/dis.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -111,7 +152,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/imp.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -123,7 +169,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/bdb.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -135,7 +186,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/abc.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -147,7 +203,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/cgi.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -159,7 +220,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/bz2.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -171,7 +237,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/grp.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -183,7 +254,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/ast.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -195,7 +271,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/csv.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -207,7 +288,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/pdb.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -219,7 +305,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/pwd.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -231,7 +322,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/ssl.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -243,7 +339,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/tty.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -255,7 +356,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/nis.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -267,7 +373,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/pty.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -279,7 +390,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/cmd.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -291,7 +407,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/tests/utils.py".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -303,7 +424,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/pyproject.toml".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e6b2}',
@ -315,7 +441,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/MAINTAINERS.md".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{f48a}',
@ -327,7 +458,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/enum.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -339,7 +475,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/hmac.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -351,7 +492,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/uuid.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -363,7 +509,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/glob.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -375,7 +526,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/_ast.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -387,7 +543,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/_csv.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -399,7 +560,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/code.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -411,7 +577,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/spwd.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -423,7 +594,12 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/_msi.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
@ -435,14 +611,20 @@ pub fn results_list_benchmark(c: &mut Criterion) {
Entry {
name: "typeshed/stdlib/time.pyi".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
value_match_ranges: None,
icon: Some(FileIcon {
icon: '\u{e606}',
color: "#ffbc03",
}),
line_number: None,
preview_type: PreviewType::Files,
name_match_ranges: Some(merge_ranges(&vec![
(0, 1),
(1, 2),
(2, 3),
(3, 4),
])),
value_match_ranges: None,
},
];

View File

@ -26,6 +26,25 @@ pub struct Entry {
pub preview_type: PreviewType,
}
#[allow(clippy::needless_return)]
pub fn merge_ranges(ranges: &[(u32, u32)]) -> Vec<(u32, u32)> {
ranges.iter().fold(
Vec::new(),
|mut acc: Vec<(u32, u32)>, x: &(u32, u32)| {
if let Some(last) = acc.last_mut() {
if last.1 == x.0 {
last.1 = x.1;
} else {
acc.push(*x);
}
} else {
acc.push(*x);
}
return acc;
},
)
}
impl Entry {
/// Create a new entry with the given name and preview type.
///
@ -65,36 +84,12 @@ impl Entry {
self.value = Some(value);
self
}
#[allow(clippy::needless_return)]
pub fn minimal_name_match_ranges(&self) -> Option<Vec<(u32, u32)>> {
// This method takes the existing `name_match_ranges`
// and merges contiguous ranges into the minimal equivalent
// set of ranges. If no ranges exist, it returns `None`.
if let Some(name_match_ranges) = &self.name_match_ranges {
let minimal_name_match_ranges: Vec<(u32, u32)> =
name_match_ranges.iter().fold(Vec::new(), |mut acc, x| {
if let Some(last) = acc.last_mut() {
if last.1 == x.0 {
last.1 = x.1;
} else {
acc.push(*x);
}
} else {
acc.push(*x);
}
return acc;
});
Some(minimal_name_match_ranges)
} else {
None
}
}
pub fn with_name_match_ranges(
mut self,
name_match_ranges: Vec<(u32, u32)>,
) -> Self {
self.name_match_ranges = Some(name_match_ranges);
self.name_match_ranges = Some(merge_ranges(&name_match_ranges));
self
}
@ -102,7 +97,7 @@ impl Entry {
mut self,
value_match_ranges: Vec<(u32, u32)>,
) -> Self {
self.value_match_ranges = Some(value_match_ranges);
self.value_match_ranges = Some(merge_ranges(&value_match_ranges));
self
}
@ -176,79 +171,26 @@ mod tests {
use super::*;
#[test]
fn test_minimal_name_match_ranges_none() {
let entry = Entry {
name: "test".to_string(),
value: None,
name_match_ranges: None,
value_match_ranges: None,
icon: None,
line_number: None,
preview_type: PreviewType::Files,
};
assert_eq!(entry.minimal_name_match_ranges(), None);
}
#[test]
fn test_minimal_name_match_ranges_empty() {
let entry = Entry {
name: "test".to_string(),
value: None,
name_match_ranges: Some(vec![]),
value_match_ranges: None,
icon: None,
line_number: None,
preview_type: PreviewType::Files,
};
assert_eq!(entry.minimal_name_match_ranges(), Some(vec![]));
}
#[test]
fn test_minimal_name_match_ranges_non_contiguous() {
let entry = Entry {
name: "test".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (2, 4), (6, 9)]),
value_match_ranges: None,
icon: None,
line_number: None,
preview_type: PreviewType::Files,
};
assert_eq!(
entry.minimal_name_match_ranges(),
Some(vec![(0, 1), (2, 4), (6, 9)])
);
fn test_empty_input() {
let ranges: Vec<(u32, u32)> = vec![];
assert_eq!(merge_ranges(&ranges), Vec::<(u32, u32)>::new());
}
#[test]
fn test_minimal_name_match_ranges_contiguous() {
let entry = Entry {
name: "test".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (1, 2), (2, 3), (3, 4)]),
value_match_ranges: None,
icon: None,
line_number: None,
preview_type: PreviewType::Files,
};
assert_eq!(entry.minimal_name_match_ranges(), Some(vec![(0, 4)]));
fn test_single_range() {
let ranges = vec![(1, 3)];
assert_eq!(merge_ranges(&ranges), vec![(1, 3)]);
}
#[test]
fn test_minimal_name_match_ranges_both_contiguous_and_non_contiguous() {
let entry = Entry {
name: "test".to_string(),
value: None,
name_match_ranges: Some(vec![(0, 1), (2, 3), (3, 4)]),
value_match_ranges: None,
icon: None,
line_number: None,
preview_type: PreviewType::Files,
};
fn test_contiguous_ranges() {
let ranges = vec![(1, 2), (2, 3), (3, 4), (4, 5)];
assert_eq!(merge_ranges(&ranges), vec![(1, 5)]);
}
assert_eq!(
entry.minimal_name_match_ranges(),
Some(vec![(0, 1), (2, 4)])
);
#[test]
fn test_non_contiguous_ranges() {
let ranges = vec![(1, 2), (3, 4), (5, 6)];
assert_eq!(merge_ranges(&ranges), vec![(1, 2), (3, 4), (5, 6)]);
}
}

View File

@ -56,7 +56,7 @@ where
// entry name
let (entry_name, name_match_ranges) = make_matched_string_printable(
&entry.name,
entry.minimal_name_match_ranges().as_deref(),
entry.name_match_ranges.as_deref(),
);
let mut last_match_end = 0;
for (start, end) in name_match_ranges