Skip to content

Commit

Permalink
implement MatchedRanges::insert
Browse files Browse the repository at this point in the history
  • Loading branch information
noib3 committed Nov 11, 2023
1 parent 735da2f commit 00c90fc
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 31 deletions.
22 changes: 7 additions & 15 deletions src/algos/fzf/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,9 @@ pub(super) fn calculate_score(
};

if with_matched_ranges {
if consecutive == 0 {
let start = range_start + offset;
let end = start + candidate_ch.len_utf8();
matched_ranges.push(start..end);
} else if let Some(last_range) = matched_ranges.last_mut() {
last_range.end += candidate_ch.len_utf8();
} else {
unreachable!(
"if consecutive is > 0 we've already pushed a range"
);
}
let start = range_start + offset;
let end = start + candidate_ch.len_utf8();
matched_ranges.insert(start..end);
}

is_in_gap = false;
Expand Down Expand Up @@ -201,7 +193,7 @@ pub(super) fn exact_match(
);

if with_matched_ranges {
matched_ranges.push(matched_range);
matched_ranges.insert(matched_range);
}

Some(score)
Expand Down Expand Up @@ -248,7 +240,7 @@ pub(super) fn prefix_match(
);

if with_matched_ranges {
matched_ranges.push(matched_range);
matched_ranges.insert(matched_range);
}

Some(score)
Expand Down Expand Up @@ -297,7 +289,7 @@ pub(super) fn suffix_match(
);

if with_matched_ranges {
matched_ranges.push(matched_range);
matched_ranges.insert(matched_range);
}

Some(score)
Expand Down Expand Up @@ -355,7 +347,7 @@ pub(super) fn equal_match(
);

if with_matched_ranges {
matched_ranges.push(matched_range);
matched_ranges.insert(matched_range);
}

Some(score)
Expand Down
11 changes: 1 addition & 10 deletions src/algos/fzf/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,16 +646,7 @@ fn matched_ranges(

offset += start_offset;

let char_len_utf8 = ch.len_utf8();

match ranges.last_mut() {
Some(last) if last.start == offset + char_len_utf8 => {
last.start = offset;
},
_ => {
ranges.push(offset..offset + char_len_utf8);
},
}
ranges.insert(offset..offset + ch.len_utf8());

if let Some(up_left) = cell_up_left {
cell = up_left;
Expand Down
193 changes: 187 additions & 6 deletions src/matched_ranges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ pub(crate) struct MatchedRanges {
ranges: Vec<Range<usize>>,
}

impl core::fmt::Debug for MatchedRanges {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list().entries(self.ranges.iter()).finish()
}
}

impl MatchedRanges {
/// TODO: docs
#[inline(always)]
Expand All @@ -15,13 +21,188 @@ impl MatchedRanges {

/// TODO: docs
#[inline(always)]
pub(crate) fn last_mut(&mut self) -> Option<&mut Range<usize>> {
self.ranges.last_mut()
pub(crate) fn insert(&mut self, new_range: Range<usize>) {
let insert_idx = match self
.ranges
.binary_search_by(|range| range.start.cmp(&new_range.start))
{
Err(idx) => idx,

// The range at `idx` and the new range have the same start.
Ok(idx) => {
let (range, next_range) = {
let (left, right) = self.ranges.split_at_mut(idx + 1);
(&mut left[idx], right.get_mut(0))
};

if range.end >= new_range.end {
// The new range is completely contained within this
// existing range.
return;
}

if let Some(next_range) = next_range {
if new_range.end >= next_range.start {
// The new range fills the gap between this range and
// the next one.
range.end = next_range.end;
self.ranges.remove(idx + 1);
return;
}
}

range.end = new_range.end;

return;
},
};

if insert_idx == 0 {
let Some(first_range) = self.ranges.get_mut(0) else {
// This is the first range.
self.ranges.push(new_range);
return;
};

if new_range.end >= first_range.start {
first_range.start = new_range.start;
} else {
self.ranges.insert(0, new_range);
}

return;
}

if insert_idx == self.ranges.len() {
let last_range = &mut self.ranges[insert_idx - 1];

if new_range.start <= last_range.end {
last_range.end = new_range.end;
} else {
self.ranges.push(new_range);
}

return;
}

let (prev_range, next_range) = {
let (left, right) = self.ranges.split_at_mut(insert_idx);
(&mut left[insert_idx - 1], &mut right[0])
};

match (
new_range.start <= prev_range.end,
new_range.end >= next_range.start,
) {
// The new range fills the gap between two existing ranges, so
// we merge them.
//
// ------ ------ => ---------------
// xxxxxxx
(true, true) => {
prev_range.end = next_range.end;
self.ranges.remove(insert_idx);
},

// The new range starts within an existing range but ends before
// the next one starts, so we extend the end of the existing range.
//
// ------ ------ => -------- ------
// xxxx
(true, false) => {
prev_range.end = new_range.end;
},

// The new range ends within an existing range but starts after
// the previous one ends, so we extend the start of the existing
// range.
//
// ------ ------ => ------ --------
// xxxx
(false, true) => {
next_range.start = new_range.start;
},

// The new range is strictly within an existing gap, so we just
// insert it.
// ------ ------ => ------ ----- ------
// xxxxx
(false, false) => {
self.ranges.insert(insert_idx, new_range);
},
}
}
}

/// TODO: docs
#[inline(always)]
pub(crate) fn push(&mut self, range: Range<usize>) {
self.ranges.push(range);
#[cfg(test)]
mod tests {
#![allow(clippy::single_range_in_vec_init)]

use super::*;

#[test]
fn matched_ranges_insert_same_start_increasing_end() {
let mut ranges = MatchedRanges::default();
ranges.insert(0..1);
ranges.insert(0..2);
ranges.insert(0..3);
assert_eq!(ranges.as_slice(), [0..3]);
ranges.insert(0..2);
assert_eq!(ranges.as_slice(), [0..3]);
}

#[test]
fn matched_ranges_insert_consecutive_1() {
let mut ranges = MatchedRanges::default();
ranges.insert(0..1);
ranges.insert(1..2);
ranges.insert(2..3);
assert_eq!(ranges.as_slice(), [0..3]);
}

#[test]
fn matched_ranges_insert_consecutive_2() {
let mut ranges = MatchedRanges::default();
ranges.insert(2..3);
ranges.insert(1..2);
ranges.insert(0..1);
assert_eq!(ranges.as_slice(), [0..3]);
}

#[test]
fn matched_ranges_insert_fill_gap() {
let mut ranges = MatchedRanges::default();
ranges.insert(0..1);
ranges.insert(2..3);
assert_eq!(ranges.as_slice(), [0..1, 2..3]);
ranges.insert(1..2);
assert_eq!(ranges.as_slice(), [0..3]);
}

#[test]
fn matched_ranges_insert_extend_end() {
let mut ranges = MatchedRanges::default();
ranges.insert(0..2);
ranges.insert(4..6);
ranges.insert(1..3);
assert_eq!(ranges.as_slice(), [0..3, 4..6]);
}

#[test]
fn matched_ranges_insert_extend_start() {
let mut ranges = MatchedRanges::default();
ranges.insert(0..2);
ranges.insert(4..6);
ranges.insert(3..5);
assert_eq!(ranges.as_slice(), [0..2, 3..6]);
}

#[test]
fn matched_ranges_insert_insert() {
let mut ranges = MatchedRanges::default();
ranges.insert(0..4);
ranges.insert(6..8);
ranges.insert(10..14);
assert_eq!(ranges.as_slice(), [0..4, 6..8, 10..14]);
}
}

0 comments on commit 00c90fc

Please sign in to comment.