| 13 | const MAX_CANDIDATE_ITEMS: usize = 750; |
| 14 | |
| 15 | pub fn calculate_suggestions<'a>( |
| 16 | dir_iter: impl ExactSizeIterator<Item = &'a PyObjectRef>, |
| 17 | name: &PyObject, |
| 18 | ) -> Option<PyStrRef> { |
| 19 | if dir_iter.len() >= MAX_CANDIDATE_ITEMS { |
| 20 | return None; |
| 21 | } |
| 22 | |
| 23 | let mut suggestion: Option<&Py<PyStr>> = None; |
| 24 | let mut suggestion_distance = usize::MAX; |
| 25 | let name = name.downcast_ref::<PyStr>()?; |
| 26 | |
| 27 | for item in dir_iter { |
| 28 | let item_name = item.downcast_ref::<PyStr>()?; |
| 29 | if name.as_bytes() == item_name.as_bytes() { |
| 30 | continue; |
| 31 | } |
| 32 | // No more than 1/3 of the characters should need changed |
| 33 | let max_distance = usize::min( |
| 34 | (name.len() + item_name.len() + 3) * MOVE_COST / 6, |
| 35 | suggestion_distance - 1, |
| 36 | ); |
| 37 | let current_distance = |
| 38 | levenshtein_distance(name.as_bytes(), item_name.as_bytes(), max_distance); |
| 39 | if current_distance > max_distance { |
| 40 | continue; |
| 41 | } |
| 42 | if suggestion.is_none() || current_distance < suggestion_distance { |
| 43 | suggestion = Some(item_name); |
| 44 | suggestion_distance = current_distance; |
| 45 | } |
| 46 | } |
| 47 | suggestion.map(|r| r.to_owned()) |
| 48 | } |
| 49 | |
| 50 | pub fn offer_suggestions(exc: &Py<PyBaseException>, vm: &VirtualMachine) -> Option<PyStrRef> { |
| 51 | if exc |