Reduce allocations when deduping completions in place

This is still suboptimal because we are allocating a vector of indices to be
removed (but allocation-free in the normal case of no duplicates) but
significantly better than the previous version of the code that duplicated the
strings (which are larger and spread out all over the heap).

The ideal code (similar to what we had in the C++ version, iirc) would look like
this, but it's not allowed because the borrow checker hates you:

```
fn unique_in_place_illegal(comps: &mut Vec<Completion>) {
    let mut seen = HashSet::with_capacity(comps.len());
    let mut idx = 0;
    while idx < comps.len() {
        if !seen.insert(&comps[idx].completion) {
            comps.remove(idx);
            continue;
        }
        idx += 1;
    }
}
```
This commit is contained in:
Mahmoud Al-Qudsi 2024-11-22 14:11:01 -06:00
parent 36c5ee045c
commit b570c7f6a6

View File

@ -512,14 +512,17 @@ fn compare_completions_by_tilde(a: &Completion, b: &Completion) -> Ordering {
/// Unique the list of completions, without perturbing their order.
fn unique_completions_retaining_order(comps: &mut Vec<Completion>) {
let mut seen = HashSet::with_capacity(comps.len());
let removals = comps.iter().enumerate().fold(vec![], |mut v, (i, c)| {
if !seen.insert(&c.completion) {
v.push(i);
}
v
});
let pred = |c: &Completion| {
// Keep (return true) if insertion succeeds.
// todo!("don't clone here");
seen.insert(c.completion.to_owned())
};
comps.retain(pred);
// Remove in reverse order so the indexes remain valid
for idx in removals.iter().rev() {
comps.remove(*idx);
}
}
/// Sorts and removes any duplicate completions in the completion list, then puts them in priority