21#define UI_MENU_ARROW_SEP BLI_STR_UTF8_BLACK_RIGHT_POINTING_SMALL_TRIANGLE
22#define UI_MENU_ARROW_SEP_UNICODE 0x25b8
33 constexpr int deletion_cost = 1;
34 constexpr int insertion_cost = 1;
35 constexpr int substitution_cost = 1;
36 constexpr int transposition_cost = 1;
45 const int row_length = size_b + 1;
55 v1[
i] =
i * insertion_cost;
58 uint32_t prev_unicode_a;
61 v2[0] = (
i + 1) * deletion_cost;
65 uint32_t prev_unicode_b;
72 int new_cost = std::min({v1[j + 1] + deletion_cost,
73 v2[j] + insertion_cost,
74 v1[j] + (unicode_a != unicode_b) * substitution_cost});
76 if (unicode_a == prev_unicode_b && prev_unicode_a == unicode_b) {
77 new_cost = std::min(new_cost, v0[j - 1] + transposition_cost);
82 prev_unicode_b = unicode_b;
88 prev_unicode_a = unicode_a;
105 if (query_size == 1) {
111 const int max_errors = query_size <= 1 ? 0 : query_size / 8 + 1;
114 if (query_size - full_size > max_errors) {
122 const char *full_begin = full.
begin();
123 const char *full_end = full.
end();
125 const char *window_begin = full_begin;
126 const char *window_end = window_begin;
127 const int window_size = std::min(query_size + max_errors, full_size);
128 const int extra_chars = std::max(window_size - query_size, 0);
129 const int max_acceptable_distance = max_errors + extra_chars;
131 for (
int i = 0;
i < window_size;
i++) {
136 StringRef window{window_begin, window_end};
141 if (
ELEM(window_begin_unicode, query_first_unicode, query_second_unicode)) {
143 if (
distance <= max_acceptable_distance) {
147 if (window_end == full_end) {
153 const int window_offset = std::max(1,
distance / 2);
154 for (
int i = 0;
i < window_offset && window_end < full_end;
i++) {
169 for (
const int i : this->matched_word_indices) {
196 if (start >= words.
size()) {
202 size_t query_index = 0;
203 int word_index = start;
204 size_t char_index = 0;
206 int first_found_word_index = -1;
208 while (query_index < query.
size()) {
210 query.
data(), query.
size(), &query_index);
213 if (word_index >= words.
size()) {
214 if (first_found_word_index >= 0) {
231 if (
int(char_index) < word.
size()) {
233 word.
data(), word.
size(), &char_index);
234 if (query_unicode == char_unicode) {
236 if (first_found_word_index == -1) {
237 first_found_word_index = word_index;
250 query, item, word_match_map, first_found_word_index + 1))
252 if (sub_match->better_than(match, item)) {
279 bool use_shortest_match =
false;
280 for (
const StringRef other_word : remaining_query_words) {
281 if (other_word.startswith(query)) {
282 use_shortest_match =
true;
288 int best_word_index = -1;
289 bool best_word_in_main_group =
false;
297 bool found_new_best =
false;
298 if (use_shortest_match) {
299 if (word.
size() < best_word_size) {
300 found_new_best =
true;
304 if (!best_word_in_main_group) {
305 found_new_best =
true;
308 if (found_new_best) {
310 best_word_size = word.
size();
311 best_word_in_main_group = word_in_main_group;
315 return best_word_index;
329 if (error_count >= 0) {
330 *r_error_count = error_count;
351 for (
const int query_word_index : query_words.
index_range()) {
352 const StringRef query_word = query_words[query_word_index];
356 query_word, item, word_match_map, query_words.
drop_front(query_word_index + 1));
357 if (word_index >= 0) {
360 total_match_score += is_main_group ? 10 : 9;
361 word_match_map[word_index] = query_word_index;
368 query_word, item, word_match_map))
371 bool all_main_group_matches = match->count_main_group_matches(item) ==
372 match->matched_word_indices.size();
373 total_match_score += all_main_group_matches ? 4 : 3;
374 for (
const int i : match->matched_word_indices) {
375 word_match_map[
i] = query_word_index;
385 if (word_index >= 0) {
386 total_match_score += 3 - error_count;
387 word_match_map[word_index] = query_word_index;
399 for (
const int index : word_match_map) {
401 match_indices.
append(index);
406 if (match_indices[
i] > match_indices[
i + 1]) {
407 total_match_score -= 1;
413 return total_match_score;
421 const uint32_t unicode_space = uint32_t(
' ');
422 const uint32_t unicode_dash = uint32_t(
'-');
423 const uint32_t unicode_underscore = uint32_t(
'_');
424 const uint32_t unicode_slash = uint32_t(
'/');
433 auto is_separator = [&](uint32_t unicode) {
439 unicode_right_triangle);
446 char *mutable_copy =
const_cast<char *
>(str_copy.
data());
447 const size_t str_size_in_bytes = size_t(
str.size());
452 bool is_in_word =
false;
453 size_t word_start = 0;
455 while (offset < str_size_in_bytes) {
456 size_t size = offset;
459 if (is_separator(unicode)) {
461 const StringRef word = str_copy.
substr(
int(word_start),
int(offset - word_start));
463 r_word_group_ids.
append(group_id);
473 if (unicode == unicode_right_triangle) {
482 r_word_group_ids.
append(group_id);
495 int main_group_id = 0;
503 main_group_id = word_group_ids.
last();
508 word_group_ids.
fill(0);
514 int main_group_length = 0;
516 if (word_group_ids[
i] == main_group_id) {
517 main_group_length += int(words[
i].
size());
546 for (const int i : range) {
547 const SearchItem &item = items_[i];
548 const std::optional<float> score = string_search::score_query_against_words(query_words,
550 all_scores[i] = score;
554 for (
const int i : items_.index_range()) {
555 const std::optional<float> score = all_scores[
i];
556 if (score.has_value()) {
557 result_indices_by_score.
add(*score,
i);
562 for (
const float score : result_indices_by_score.
keys()) {
563 found_scores.
append(score);
565 std::sort(found_scores.
begin(), found_scores.
end(), std::greater<>());
569 Vector<int> sorted_result_indices;
570 for (
const float score : found_scores) {
571 MutableSpan<int>
indices = result_indices_by_score.
lookup(score);
572 if (score == found_scores[0]) {
573 if (!query.is_empty()) {
578 const SearchItem &item_a = items_[a];
579 const SearchItem &item_b = items_[b];
581 return item_a.main_group_length < item_b.main_group_length ||
582 (item_a.main_group_length == item_b.main_group_length &&
583 item_a.total_length < item_b.total_length);
588 return items_[a].weight > items_[b].weight;
593 if (query.size() <= 1) {
596 return items_[a].recent_time > items_[b].recent_time;
603 Vector<void *> sorted_data(sorted_result_indices.
size());
604 for (
const int i : sorted_result_indices.
index_range()) {
605 const int result_index = sorted_result_indices[
i];
606 const SearchItem &item = items_[result_index];
void BLI_str_tolower_ascii(char *str, size_t len) ATTR_NONNULL(1)
ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t str_len) ATTR_NONNULL(1)
size_t size_t BLI_strnlen_utf8(const char *strc, size_t strc_maxlen) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
int BLI_str_utf8_size_safe(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
unsigned int BLI_str_utf8_as_unicode_step_safe(const char *__restrict p, size_t p_len, size_t *__restrict index) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
unsigned int BLI_str_utf8_as_unicode_safe(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
void append(const T &value)
IndexRange index_range() const
void extend(Span< T > array)
StringRefNull copy_string(StringRef str)
MapType::KeyIterator keys() const
Span< Value > lookup(const Key &key) const
void add(const Key &key, const Value &value)
constexpr T & last(const int64_t n=0) const
constexpr Span drop_front(int64_t n) const
constexpr int64_t size() const
constexpr IndexRange index_range() const
static constexpr int64_t not_found
constexpr int64_t find(char c, int64_t pos=0) const
constexpr const char * begin() const
constexpr const char * end() const
constexpr StringRef substr(int64_t start, int64_t size) const
constexpr bool startswith(StringRef prefix) const
constexpr int64_t size() const
constexpr const char * data() const
constexpr StringRef drop_prefix(int64_t n) const
void append(const T &value)
const T & last(const int64_t n=0) const
IndexRange index_range() const
void fill(const T &value) const
Span< T > as_span() const
LinearAllocator allocator_
const RecentCache * recent_cache_
void add_impl(StringRef str, void *user_data, float weight)
Vector< SearchItem > items_
MainWordsHeuristic main_words_heuristic_
Vector< void * > query_impl(StringRef query) const
float distance(VecOp< float, D >, VecOp< float, D >) RET
static int64_t count_utf8_code_points(StringRef str)
static constexpr int unused_word
int get_fuzzy_match_errors(StringRef query, StringRef full)
static std::optional< InitialsMatch > match_word_initials(StringRef query, const SearchItem &item, const Span< int > word_match_map, int start=0)
static int get_best_word_index_that_startswith(StringRef query, const SearchItem &item, Span< int > word_match_map, Span< StringRef > remaining_query_words)
int damerau_levenshtein_distance(StringRef a, StringRef b)
static std::optional< float > score_query_against_words(Span< StringRef > query_words, const SearchItem &item)
static int get_word_index_that_fuzzy_matches(StringRef query, Span< StringRef > words, Span< int > word_match_map, int *r_error_count)
void extract_normalized_words(StringRef str, LinearAllocator<> &allocator, Vector< StringRef, 64 > &r_words, Vector< int, 64 > &r_word_group_ids)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
#define UI_MENU_ARROW_SEP
#define UI_MENU_ARROW_SEP_UNICODE
int count_main_group_matches(const SearchItem &item) const
bool better_than(const InitialsMatch &other, const SearchItem &item) const
Vector< int > matched_word_indices
Span< StringRef > normalized_words
Span< int > word_group_ids