From bc34928ea8a8ac0af2cc3b590b15135f148fa84f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 30 Apr 2020 11:32:14 +0200 Subject: [PATCH 01/10] Follow-up of previous commit (Slic3r::GUI::format issues) Another missing namespace qualification caused infinite recursion --- src/slic3r/GUI/format.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/format.hpp b/src/slic3r/GUI/format.hpp index 5cb3aed4c..bc6fe7b1b 100644 --- a/src/slic3r/GUI/format.hpp +++ b/src/slic3r/GUI/format.hpp @@ -29,7 +29,7 @@ inline wxString format_wxstr(const wxString& fmt, TArgs&&... args) { } template inline std::string format(const wxString& fmt, TArgs&&... args) { - return format(fmt.ToUTF8().data(), std::forward(args)...); + return Slic3r::format(fmt.ToUTF8().data(), std::forward(args)...); } } // namespace GUI From cc2d33f6a0ce3b7981a45b1ed52034c633e24663 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Apr 2020 12:03:49 +0200 Subject: [PATCH 02/10] Search: Fixed returning of a pointer to temporary. --- src/slic3r/GUI/Search.cpp | 19 +++++++------------ src/slic3r/GUI/Search.hpp | 8 ++++---- src/slic3r/GUI/Tab.cpp | 1 + src/slic3r/GUI/Tab.hpp | 1 - 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 1478e725a..90566c370 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -89,15 +89,10 @@ FMFlag Option::fuzzy_match(const std::string& search, int& outScore) const return fuzzy_match(search_pattern, outScore); } -void FoundOption::get_label(const char** out_text) const -{ - *out_text = label.utf8_str(); -} - void FoundOption::get_marked_label_and_tooltip(const char** label_, const char** tooltip_) const { - *label_ = marked_label.utf8_str(); - *tooltip_ = tooltip.utf8_str(); + *label_ = marked_label.c_str(); + *tooltip_ = tooltip.c_str(); } template @@ -254,8 +249,8 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) { const Option &opt = options[i]; if (full_list) { - wxString label = get_label(opt); - found.emplace_back(FoundOption{ label, label, get_tooltip(opt), i, 0 }); + std::string label = into_u8(get_label(opt)); + found.emplace_back(FoundOption{ label, label, into_u8(get_tooltip(opt)), i, 0 }); continue; } @@ -275,7 +270,7 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) mark_string(marked_label, from_u8(search)); clear_marked_string(marked_label); - found.emplace_back(FoundOption{ label, marked_label, get_tooltip(opt), i, score }); + found.emplace_back(FoundOption{ into_u8(label), into_u8(marked_label), into_u8(get_tooltip(opt)), i, score }); } } @@ -357,7 +352,7 @@ bool SearchComboPopup::Create(wxWindow* parent) void SearchComboPopup::SetStringValue(const wxString& s) { int n = wxListBox::FindString(s); - if (n >= 0 && n < wxListBox::GetCount()) + if (n >= 0 && n < int(wxListBox::GetCount())) wxListBox::Select(n); // save a combo control's string @@ -549,7 +544,7 @@ void SearchDialog::update_list() const std::vector& filters = searcher->found_options(); for (const FoundOption& item : filters) - search_list->Append(item.label); + search_list->Append(from_u8(item.label)); } void SearchDialog::OnKeyDown(wxKeyEvent& event) diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index d6e2689de..37767b0c6 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -71,13 +71,13 @@ struct Option { }; struct FoundOption { - wxString label; - wxString marked_label; - wxString tooltip; + std::string label; + std::string marked_label; + std::string tooltip; size_t option_idx {0}; int outScore {0}; - void get_label(const char** out_text) const; + // Returning pointers to contents of std::string members, to be used by ImGUI for rendering. void get_marked_label_and_tooltip(const char** label, const char** tooltip) const; }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9c9c8b2a6..a21947b15 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -11,6 +11,7 @@ #include "BonjourDialog.hpp" #include "WipeTowerDialog.hpp" #include "ButtonsDescription.hpp" +#include "Search.hpp" #include #include diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 7b231daf8..a13d13f2d 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -33,7 +33,6 @@ #include "Event.hpp" #include "wxExtensions.hpp" #include "ConfigManipulation.hpp" -#include "Search.hpp" namespace Slic3r { namespace GUI { From f479b77e01ae94c54df8f54087f1d0005e72b221 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 30 Apr 2020 12:04:49 +0200 Subject: [PATCH 03/10] Fixed compilation warning. --- src/libslic3r/Arrange.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 352e9e1cf..8ff1e37a9 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -67,7 +67,7 @@ struct ArrangeParams { /// The minimum distance which is allowed for any /// pair of items on the print bed in any direction. - coord_t min_obj_distance = 0.; + coord_t min_obj_distance = 0; /// The accuracy of optimization. /// Goes from 0.0 to 1.0 and scales performance as well From e5c5ee66361cb3b91aaa323696d5e3e67ac5fabc Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 30 Apr 2020 14:32:43 +0200 Subject: [PATCH 04/10] deps: disable wxWidgets library internal asserts --- deps/wxWidgets/wxWidgets.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 4f89fe82c..ee8a22c4f 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -19,6 +19,7 @@ prusaslicer_add_cmake_project(wxWidgets -DwxBUILD_PRECOMP=ON ${_wx_toolkit} "-DCMAKE_DEBUG_POSTFIX:STRING=" + -DwxBUILD_DEBUG_LEVEL=0 -DwxUSE_DETECT_SM=OFF -DwxUSE_UNICODE=ON -DwxUSE_OPENGL=ON From 07ab5c31e6035799781fa7152bf36f8c80143927 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 1 May 2020 08:57:24 +0200 Subject: [PATCH 05/10] WIP Fuzzy search rework. 1) fts_fuzzy_match has been extended to support wchar_t for a char type and uint16_t for an index type for the match indices. 2) fts_fuzzy_match has been extended to place a proper stopper character into the match buffer. 3) Slicer integration now uses the fuzzy match indices for highlighting. 4) Slicer integration now correctly highlights the matched word. 5) Slicer search dialog now sorts based on match AND category. Further modifications are planned: 1) Matching in local language vs. English: Just show the English variant if matched in English. Don't mix the two together. 2) Matching the group or category: Continue matching the label. 3) For matches with equal match quality and category sort alphanumerically. --- src/slic3r/GUI/Plater.cpp | 3 +- src/slic3r/GUI/Search.cpp | 182 ++++++++++++++----------------- src/slic3r/GUI/Search.hpp | 28 ++--- src/slic3r/GUI/fts_fuzzy_match.h | 106 ++++++++++-------- 4 files changed, 160 insertions(+), 159 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 02baaa17e..d454a95fe 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -1098,7 +1099,7 @@ void Sidebar::search() void Sidebar::jump_to_option(size_t selected) { const Search::Option& opt = p->searcher.get_option(selected); - wxGetApp().get_tab(opt.type)->activate_option(opt.opt_key, opt.category); + wxGetApp().get_tab(opt.type)->activate_option(boost::nowide::narrow(opt.opt_key), boost::nowide::narrow(opt.category)); // Switch to the Settings NotePad, if plater is shown if (p->plater->IsShown()) diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 90566c370..c6894bc5b 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -2,7 +2,9 @@ #include #include +#include #include +#include #include "libslic3r/PrintConfig.hpp" #include "GUI_App.hpp" @@ -31,64 +33,35 @@ static std::map NameByType = { { Preset::TYPE_PRINTER, L("Printer") } }; -FMFlag Option::fuzzy_match_simple(char const * search_pattern) const -{ - return fts::fuzzy_match_simple(search_pattern, label_local.utf8_str()) ? fmLabelLocal : - fts::fuzzy_match_simple(search_pattern, group_local.utf8_str()) ? fmGroupLocal : - fts::fuzzy_match_simple(search_pattern, category_local.utf8_str()) ? fmCategoryLocal : - fts::fuzzy_match_simple(search_pattern, opt_key.c_str()) ? fmOptKey : - fts::fuzzy_match_simple(search_pattern, label.utf8_str()) ? fmLabel : - fts::fuzzy_match_simple(search_pattern, group.utf8_str()) ? fmGroup : - fts::fuzzy_match_simple(search_pattern, category.utf8_str()) ? fmCategory : fmUndef ; -} - -FMFlag Option::fuzzy_match_simple(const wxString& search) const -{ - char const* search_pattern = search.utf8_str(); - return fuzzy_match_simple(search_pattern); -} - -FMFlag Option::fuzzy_match_simple(const std::string& search) const -{ - char const* search_pattern = search.c_str(); - return fuzzy_match_simple(search_pattern); -} - -FMFlag Option::fuzzy_match(char const* search_pattern, int& outScore) const +FMFlag Option::fuzzy_match(wchar_t const* search_pattern, int& outScore, std::vector &out_matches) const { FMFlag flag = fmUndef; int score; - if (fts::fuzzy_match(search_pattern, label_local.utf8_str(), score) && outScore < score) { - outScore = score; flag = fmLabelLocal ; } - if (fts::fuzzy_match(search_pattern, group_local.utf8_str(), score) && outScore < score) { - outScore = score; flag = fmGroupLocal ; } - if (fts::fuzzy_match(search_pattern, category_local.utf8_str(), score) && outScore < score) { - outScore = score; flag = fmCategoryLocal; } - if (fts::fuzzy_match(search_pattern, opt_key.c_str(), score) && outScore < score) { - outScore = score; flag = fmOptKey ; } - if (fts::fuzzy_match(search_pattern, label.utf8_str(), score) && outScore < score) { - outScore = score; flag = fmLabel ; } - if (fts::fuzzy_match(search_pattern, group.utf8_str(), score) && outScore < score) { - outScore = score; flag = fmGroup ; } - if (fts::fuzzy_match(search_pattern, category.utf8_str(), score) && outScore < score) { - outScore = score; flag = fmCategory ; } + uint16_t matches[fts::max_matches + 1]; // +1 for the stopper + auto save_matches = [&matches, &out_matches]() { + size_t cnt = 0; + for (; matches[cnt] != fts::stopper; ++cnt); + out_matches.assign(matches, matches + cnt); + }; + if (fts::fuzzy_match(search_pattern, label_local.c_str(), score, matches) && outScore < score) { + outScore = score; flag = fmLabelLocal ; save_matches(); } + if (fts::fuzzy_match(search_pattern, group_local.c_str(), score, matches) && outScore < score) { + outScore = score; flag = fmGroupLocal ; save_matches(); } + if (fts::fuzzy_match(search_pattern, category_local.c_str(), score, matches) && outScore < score) { + outScore = score; flag = fmCategoryLocal; save_matches(); } + if (fts::fuzzy_match(search_pattern, opt_key.c_str(), score, matches) && outScore < score) { + outScore = score; flag = fmOptKey ; save_matches(); } + if (fts::fuzzy_match(search_pattern, label.c_str(), score, matches) && outScore < score) { + outScore = score; flag = fmLabel ; save_matches(); } + if (fts::fuzzy_match(search_pattern, group.c_str(), score, matches) && outScore < score) { + outScore = score; flag = fmGroup ; save_matches(); } + if (fts::fuzzy_match(search_pattern, category.c_str(), score, matches) && outScore < score) { + outScore = score; flag = fmCategory ; save_matches(); } return flag; } -FMFlag Option::fuzzy_match(const wxString& search, int& outScore) const -{ - char const* search_pattern = search.utf8_str(); - return fuzzy_match(search_pattern, outScore); -} - -FMFlag Option::fuzzy_match(const std::string& search, int& outScore) const -{ - char const* search_pattern = search.c_str(); - return fuzzy_match(search_pattern, outScore); -} - void FoundOption::get_marked_label_and_tooltip(const char** label_, const char** tooltip_) const { *label_ = marked_label.c_str(); @@ -120,10 +93,10 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty suffix = opt_key.back()=='1' ? L("Stealth") : L("Normal"); if (!label.IsEmpty()) - options.emplace_back(Option{ opt_key, type, - label+ " " + suffix, _(label)+ " " + _(suffix), - gc.group, _(gc.group), - gc.category, _(gc.category) }); + options.emplace_back(Option{ boost::nowide::widen(opt_key), type, + (label+ " " + suffix).ToStdWstring(), (_(label)+ " " + _(suffix)).ToStdWstring(), + gc.group.ToStdWstring(), _(gc.group).ToStdWstring(), + gc.category.ToStdWstring(), _(gc.category).ToStdWstring() }); }; for (std::string opt_key : config->keys()) @@ -173,41 +146,32 @@ static wxString wrap_string(const wxString& str) } // Mark a string using ColorMarkerStart and ColorMarkerEnd symbols -static void mark_string(wxString& str, const wxString& search_str) +static std::wstring mark_string(const std::wstring &str, const std::vector &matches) { - // Try to find whole search string - if (str.Replace(search_str, wrap_string(search_str), false) != 0) - return; - - // Try to find whole capitalized search string - wxString search_str_capitalized = search_str.Capitalize(); - if (str.Replace(search_str_capitalized, wrap_string(search_str_capitalized), false) != 0) - return; - - // if search string is just a one letter now, there is no reason to continue - if (search_str.Len()==1) - return; - - // Split a search string for two strings (string without last letter and last letter) - // and repeat a function with new search strings - mark_string(str, search_str.SubString(0, search_str.Len() - 2)); - mark_string(str, search_str.Last()); -} - -// clear marked string from a redundant use of ColorMarkers -static void clear_marked_string(wxString& str) -{ - // Check if the string has a several ColorMarkerStart in a row and replace them to only one, if any - wxString delete_string = wxString::Format("%c%c", ImGui::ColorMarkerStart, ImGui::ColorMarkerStart); - str.Replace(delete_string, ImGui::ColorMarkerStart, true); - // If there were several ColorMarkerStart in a row, it means there should be a several ColorMarkerStop in a row, - // replace them to only one - delete_string = wxString::Format("%c%c", ImGui::ColorMarkerEnd, ImGui::ColorMarkerEnd); - str.Replace(delete_string, ImGui::ColorMarkerEnd, true); - - // And we should to remove redundant ColorMarkers, if they are in "End, Start" sequence in a row - delete_string = wxString::Format("%c%c", ImGui::ColorMarkerEnd, ImGui::ColorMarkerStart); - str.Replace(delete_string, wxEmptyString, true); + std::wstring out; + if (matches.empty()) + out = str; + else { + out.reserve(str.size() * 2); + if (matches.front() > 0) + out += str.substr(0, matches.front()); + for (size_t i = 0;;) { + // Find the longest string of successive indices. + size_t j = i + 1; + while (j < matches.size() && matches[j] == matches[j - 1] + 1) + ++ j; + out += ImGui::ColorMarkerStart; + out += str.substr(matches[i], matches[j - 1] - matches[i] + 1); + out += ImGui::ColorMarkerEnd; + if (j == matches.size()) { + out += str.substr(matches[j - 1] + 1); + break; + } + out += str.substr(matches[j - 1] + 1, matches[j] - matches[j - 1] - 1); + i = j; + } + } + return out; } bool OptionsSearcher::search() @@ -245,32 +209,50 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) opt.group_local + sep + opt.label_local; }; + std::vector matches; for (size_t i=0; i < options.size(); i++) { const Option &opt = options[i]; if (full_list) { std::string label = into_u8(get_label(opt)); - found.emplace_back(FoundOption{ label, label, into_u8(get_tooltip(opt)), i, 0 }); + found.emplace_back(FoundOption{ label, label, into_u8(get_tooltip(opt)), i, fmUndef, 0 }); continue; } int score = 0; - - FMFlag fuzzy_match_flag = opt.fuzzy_match(search, score); + FMFlag fuzzy_match_flag = opt.fuzzy_match(boost::nowide::widen(search).c_str(), score, matches); if (fuzzy_match_flag != fmUndef) { - wxString label = get_label(opt); + wxString label; - if ( fuzzy_match_flag == fmLabel ) label += "(" + opt.label + ")"; - else if (fuzzy_match_flag == fmGroup ) label += "(" + opt.group + ")"; - else if (fuzzy_match_flag == fmCategory) label += "(" + opt.category + ")"; - else if (fuzzy_match_flag == fmOptKey ) label += "(" + opt.opt_key + ")"; + if (view_params.type) + label += _(NameByType[opt.type]) + sep; + if (fuzzy_match_flag == fmCategoryLocal) + label += mark_string(opt.category_local, matches) + sep; + else if (view_params.category) + label += opt.category_local + sep; + if (fuzzy_match_flag == fmGroupLocal) + label += mark_string(opt.group_local, matches) + sep; + else if (view_params.group) + label += opt.group_local + sep; + label += ((fuzzy_match_flag == fmLabelLocal) ? mark_string(opt.label_local, matches) : opt.label_local) + sep; - wxString marked_label = label; - mark_string(marked_label, from_u8(search)); - clear_marked_string(marked_label); + switch (fuzzy_match_flag) { + case fmLabelLocal: + case fmGroupLocal: + case fmCategoryLocal: + break; + case fmLabel: label = get_label(opt) + "(" + mark_string(opt.label, matches) + ")"; break; + case fmGroup: label = get_label(opt) + "(" + mark_string(opt.group, matches) + ")"; break; + case fmCategory: label = get_label(opt) + "(" + mark_string(opt.category, matches) + ")"; break; + case fmOptKey: label = get_label(opt) + "(" + mark_string(opt.opt_key, matches) + ")"; break; + case fmUndef: assert(false); break; + } - found.emplace_back(FoundOption{ into_u8(label), into_u8(marked_label), into_u8(get_tooltip(opt)), i, score }); + std::string label_plain = into_u8(label); + boost::erase_all(label_plain, std::wstring(1, wchar_t(ImGui::ColorMarkerStart))); + boost::erase_all(label_plain, std::wstring(1, wchar_t(ImGui::ColorMarkerEnd))); + found.emplace_back(FoundOption{ label_plain, into_u8(label), into_u8(get_tooltip(opt)), i, fuzzy_match_flag, score }); } } diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index 37767b0c6..1c24248ac 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -37,6 +37,7 @@ struct GroupAndCategory { }; // fuzzy_match flag +// Sorted by the order of importance. The outputs will be sorted by the importance if the match value given by fuzzy_match is equal. enum FMFlag { fmUndef = 0, // didn't find @@ -53,28 +54,27 @@ struct Option { bool operator<(const Option& other) const { return other.label > this->label; } bool operator>(const Option& other) const { return other.label < this->label; } - std::string opt_key; + // Fuzzy matching works at a character level. Thus matching with wide characters is a safer bet than with short characters, + // though for some languages (Chinese?) it may not work correctly. + std::wstring opt_key; Preset::Type type {Preset::TYPE_INVALID}; - wxString label; - wxString label_local; - wxString group; - wxString group_local; - wxString category; - wxString category_local; + std::wstring label; + std::wstring label_local; + std::wstring group; + std::wstring group_local; + std::wstring category; + std::wstring category_local; - FMFlag fuzzy_match_simple(char const *search_pattern) const; - FMFlag fuzzy_match_simple(const wxString& search) const; - FMFlag fuzzy_match_simple(const std::string &search) const; - FMFlag fuzzy_match(char const *search_pattern, int &outScore) const; - FMFlag fuzzy_match(const wxString &search, int &outScore) const ; - FMFlag fuzzy_match(const std::string &search, int &outScore) const ; + FMFlag fuzzy_match(wchar_t const *search_pattern, int &outScore, std::vector &out_matches) const; }; struct FoundOption { + // UTF8 encoding, to be consumed by ImGUI by reference. std::string label; std::string marked_label; std::string tooltip; size_t option_idx {0}; + FMFlag category {fmUndef}; int outScore {0}; // Returning pointers to contents of std::string members, to be used by ImGUI for rendering. @@ -106,7 +106,7 @@ class OptionsSearcher } void sort_found() { std::sort(found.begin(), found.end(), [](const FoundOption& f1, const FoundOption& f2) { - return f1.outScore > f2.outScore; }); + return f1.outScore > f2.outScore || (f1.outScore == f2.outScore && int(f1.category) < int(f2.category)); }); }; size_t options_size() const { return options.size(); } diff --git a/src/slic3r/GUI/fts_fuzzy_match.h b/src/slic3r/GUI/fts_fuzzy_match.h index da4b5d2a0..4776a3647 100644 --- a/src/slic3r/GUI/fts_fuzzy_match.h +++ b/src/slic3r/GUI/fts_fuzzy_match.h @@ -1,4 +1,4 @@ - // LICENSE +// LICENSE // // This software is dual-licensed to the public domain and under the following // license: you are granted a perpetual, irrevocable license to copy, modify, @@ -23,7 +23,7 @@ // Performs exhaustive search via recursion to find all possible matches and match with highest score. // Scores values have no intrinsic meaning. Possible score range is not normalized and varies with pattern. // Recursion is limited internally (default=10) to prevent degenerate cases (pattern="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") -// Uses uint8_t for match indices. Therefore patterns are limited to 256 characters. +// Uses uint8_t for match indices. Therefore patterns are limited to max_matches characters. // Score system should be tuned for YOUR use case. Words, sentences, file names, or method names all prefer different tuning. @@ -39,54 +39,61 @@ // Public interface namespace fts { - static bool fuzzy_match_simple(char const * pattern, char const * str); - static bool fuzzy_match(char const * pattern, char const * str, int & outScore); - static bool fuzzy_match(char const * pattern, char const * str, int & outScore, uint8_t * matches, int maxMatches); -} + using char_type = wchar_t; + using pos_type = uint16_t; + static constexpr pos_type stopper = pos_type(-1); + static constexpr int max_matches = 255; + static bool fuzzy_match(char_type const * pattern, char_type const * str, int & outScore); + static bool fuzzy_match(char_type const * pattern, char_type const * str, int & outScore, pos_type * matches); +} #ifdef FTS_FUZZY_MATCH_IMPLEMENTATION namespace fts { // Forward declarations for "private" implementation namespace fuzzy_internal { - static bool fuzzy_match_recursive(const char * pattern, const char * str, int & outScore, const char * strBegin, - uint8_t const * srcMatches, uint8_t * newMatches, int maxMatches, int nextMatch, + static bool fuzzy_match_recursive(const char_type * pattern, const char_type * str, int & outScore, const char_type * strBegin, + pos_type const * srcMatches, pos_type * newMatches, int nextMatch, int & recursionCount, int recursionLimit); + static void copy_matches(pos_type * dst, pos_type const* src); } // Public interface - static bool fuzzy_match_simple(char const * pattern, char const * str) { - while (*pattern != '\0' && *str != '\0') { - if (tolower(*pattern) == tolower(*str)) - ++pattern; - ++str; - } - - return *pattern == '\0' ? true : false; - } - - static bool fuzzy_match(char const * pattern, char const * str, int & outScore) { + static bool fuzzy_match(char_type const * pattern, char_type const * str, int & outScore) { - uint8_t matches[256]; - return fuzzy_match(pattern, str, outScore, matches, sizeof(matches)); + pos_type matches[max_matches + 1]; // with the room for the stopper + matches[0] = stopper; + return fuzzy_match(pattern, str, outScore, matches); } - static bool fuzzy_match(char const * pattern, char const * str, int & outScore, uint8_t * matches, int maxMatches) { + static bool fuzzy_match(char_type const * pattern, char_type const * str, int & outScore, pos_type * matches) { int recursionCount = 0; int recursionLimit = 10; - - return fuzzy_internal::fuzzy_match_recursive(pattern, str, outScore, str, nullptr, matches, maxMatches, 0, recursionCount, recursionLimit); + return fuzzy_internal::fuzzy_match_recursive(pattern, str, outScore, str, nullptr, matches, 0, recursionCount, recursionLimit); } // Private implementation - static bool fuzzy_internal::fuzzy_match_recursive(const char * pattern, const char * str, int & outScore, - const char * strBegin, uint8_t const * srcMatches, uint8_t * matches, int maxMatches, - int nextMatch, int & recursionCount, int recursionLimit) + static bool fuzzy_internal::fuzzy_match_recursive( + // Pattern to match over str. + const char_type * pattern, + // Text to match the pattern over. + const char_type * str, + // Score of the pattern matching str. Output variable. + int & outScore, + const char_type * strBegin, + // Matches when entering this function. + pos_type const * srcMatches, + // Output matches. + pos_type * matches, + // Number of matched characters stored in srcMatches when entering this function, also tracking the successive matches. + int nextMatch, + // Recursion count is input / output to track the maximum depth reached. + int & recursionCount, + int recursionLimit) { // Count recursions - ++recursionCount; - if (recursionCount >= recursionLimit) + if (++ recursionCount >= recursionLimit) return false; // Detect end of strings @@ -95,7 +102,7 @@ namespace fts { // Recursion params bool recursiveMatch = false; - uint8_t bestRecursiveMatches[256]; + pos_type bestRecursiveMatches[max_matches + 1]; // with the room for the stopper int bestRecursiveScore = 0; // Loop through pattern and str looking for a match @@ -106,30 +113,32 @@ namespace fts { if (tolower(*pattern) == tolower(*str)) { // Supplied matches buffer was too short - if (nextMatch >= maxMatches) + if (nextMatch >= max_matches) return false; // "Copy-on-Write" srcMatches into matches if (first_match && srcMatches) { - memcpy(matches, srcMatches, nextMatch); + memcpy(matches, srcMatches, sizeof(pos_type) * (nextMatch + 1)); // including the stopper first_match = false; } // Recursive call that "skips" this match - uint8_t recursiveMatches[256]; + pos_type recursiveMatches[max_matches]; int recursiveScore; - if (fuzzy_match_recursive(pattern, str + 1, recursiveScore, strBegin, matches, recursiveMatches, sizeof(recursiveMatches), nextMatch, recursionCount, recursionLimit)) { + if (fuzzy_match_recursive(pattern, str + 1, recursiveScore, strBegin, matches, recursiveMatches, nextMatch, recursionCount, recursionLimit)) { // Pick best recursive score if (!recursiveMatch || recursiveScore > bestRecursiveScore) { - memcpy(bestRecursiveMatches, recursiveMatches, 256); - bestRecursiveScore = recursiveScore; + copy_matches(bestRecursiveMatches, recursiveMatches); + bestRecursiveScore = recursiveScore; } recursiveMatch = true; } // Advance - matches[nextMatch++] = (uint8_t)(str - strBegin); + matches[nextMatch++] = (pos_type)(str - strBegin); + // Write a stopper sign. + matches[nextMatch] = stopper; ++pattern; } ++str; @@ -168,10 +177,10 @@ namespace fts { // Apply ordering bonuses for (int i = 0; i < nextMatch; ++i) { - uint8_t currIdx = matches[i]; + pos_type currIdx = matches[i]; if (i > 0) { - uint8_t prevIdx = matches[i - 1]; + pos_type prevIdx = matches[i - 1]; // Sequential if (currIdx == (prevIdx + 1)) @@ -182,13 +191,13 @@ namespace fts { if (currIdx > 0) { // Camel case // ::islower() expects an unsigned char in range of 0 to 255. - unsigned char uneighbor = ((unsigned char *)strBegin)[currIdx - 1]; - unsigned char ucurr = ((unsigned char*)strBegin)[currIdx]; - if (::islower(uneighbor) && ::isupper(ucurr)) + char_type uneighbor = strBegin[currIdx - 1]; + char_type ucurr = strBegin[currIdx]; + if (std::islower(uneighbor) && std::isupper(ucurr)) outScore += camel_bonus; // Separator - char neighbor = strBegin[currIdx - 1]; + char_type neighbor = strBegin[currIdx - 1]; bool neighborSeparator = neighbor == '_' || neighbor == ' '; if (neighborSeparator) outScore += separator_bonus; @@ -203,7 +212,7 @@ namespace fts { // Return best result if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) { // Recursive score is better than "this" - memcpy(matches, bestRecursiveMatches, maxMatches); + copy_matches(matches, bestRecursiveMatches); outScore = bestRecursiveScore; return true; } @@ -216,6 +225,15 @@ namespace fts { return false; } } + + // Copy matches up to a stopper. + static void fuzzy_internal::copy_matches(pos_type * dst, pos_type const* src) + { + while (*src != stopper) + *dst++ = *src++; + *dst = stopper; + } + } // namespace fts #endif // FTS_FUZZY_MATCH_IMPLEMENTATION From e27c6c79281612989af83a013863742ba844886d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 May 2020 14:14:40 +0200 Subject: [PATCH 06/10] fts_fuzzy_match fixes: Recursive fix https://github.com/forrestthewoods/lib_fts/issues/21 End of string penalty fix https://github.com/forrestthewoods/lib_fts/issues/24 Some refactoring for succintness (based on Vojtech's taste) --- src/slic3r/GUI/fts_fuzzy_match.h | 66 +++++++++++++------------------- 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/src/slic3r/GUI/fts_fuzzy_match.h b/src/slic3r/GUI/fts_fuzzy_match.h index 4776a3647..cf2b88a95 100644 --- a/src/slic3r/GUI/fts_fuzzy_match.h +++ b/src/slic3r/GUI/fts_fuzzy_match.h @@ -53,7 +53,7 @@ namespace fts { // Forward declarations for "private" implementation namespace fuzzy_internal { - static bool fuzzy_match_recursive(const char_type * pattern, const char_type * str, int & outScore, const char_type * strBegin, + static bool fuzzy_match_recursive(const char_type * pattern, const char_type * str, int & outScore, const char_type * const strBegin, pos_type const * srcMatches, pos_type * newMatches, int nextMatch, int & recursionCount, int recursionLimit); static void copy_matches(pos_type * dst, pos_type const* src); @@ -61,7 +61,6 @@ namespace fts { // Public interface static bool fuzzy_match(char_type const * pattern, char_type const * str, int & outScore) { - pos_type matches[max_matches + 1]; // with the room for the stopper matches[0] = stopper; return fuzzy_match(pattern, str, outScore, matches); @@ -69,7 +68,7 @@ namespace fts { static bool fuzzy_match(char_type const * pattern, char_type const * str, int & outScore, pos_type * matches) { int recursionCount = 0; - int recursionLimit = 10; + static constexpr int recursionLimit = 10; return fuzzy_internal::fuzzy_match_recursive(pattern, str, outScore, str, nullptr, matches, 0, recursionCount, recursionLimit); } @@ -81,7 +80,8 @@ namespace fts { const char_type * str, // Score of the pattern matching str. Output variable. int & outScore, - const char_type * strBegin, + // The very start of str, for calculating indices of matches and for calculating matches from the start of the input string. + const char_type * const strBegin, // Matches when entering this function. pos_type const * srcMatches, // Output matches. @@ -89,6 +89,8 @@ namespace fts { // Number of matched characters stored in srcMatches when entering this function, also tracking the successive matches. int nextMatch, // Recursion count is input / output to track the maximum depth reached. + // Was given by reference &recursionCount, see discussion in https://github.com/forrestthewoods/lib_fts/issues/21 +// int & recursionCount, int & recursionCount, int recursionLimit) { @@ -123,7 +125,7 @@ namespace fts { } // Recursive call that "skips" this match - pos_type recursiveMatches[max_matches]; + pos_type recursiveMatches[max_matches + 1]; // with the room for the stopper int recursiveScore; if (fuzzy_match_recursive(pattern, str + 1, recursiveScore, strBegin, matches, recursiveMatches, nextMatch, recursionCount, recursionLimit)) { @@ -145,18 +147,18 @@ namespace fts { } // Determine if full pattern was matched - bool matched = *pattern == '\0' ? true : false; + bool matched = *pattern == '\0'; // Calculate score if (matched) { - const int sequential_bonus = 15; // bonus for adjacent matches - const int separator_bonus = 30; // bonus if match occurs after a separator - const int camel_bonus = 30; // bonus if match is uppercase and prev is lower - const int first_letter_bonus = 15; // bonus if the first letter is matched + static constexpr int sequential_bonus = 15; // bonus for adjacent matches + static constexpr int separator_bonus = 30; // bonus if match occurs after a separator + static constexpr int camel_bonus = 30; // bonus if match is uppercase and prev is lower + static constexpr int first_letter_bonus = 15; // bonus if the first letter is matched - const int leading_letter_penalty = -5; // penalty applied for every letter in str before the first match - const int max_leading_letter_penalty = -15; // maximum penalty for leading letters - const int unmatched_letter_penalty = -1; // penalty for every letter that doesn't matter + static constexpr int leading_letter_penalty = -5; // penalty applied for every letter in str before the first match + static constexpr int max_leading_letter_penalty = -15; // maximum penalty for leading letters + static constexpr int unmatched_letter_penalty = -1; // penalty for every letter that doesn't matter // Iterate str to end while (*str != '\0') @@ -165,47 +167,31 @@ namespace fts { // Initialize score outScore = 100; - // Apply leading letter penalty - int penalty = leading_letter_penalty * matches[0]; - if (penalty < max_leading_letter_penalty) - penalty = max_leading_letter_penalty; - outScore += penalty; + // Apply leading letter penalty or bonus. + outScore += matches[0] == 0 ? + first_letter_bonus : + std::max(matches[0] * leading_letter_penalty, max_leading_letter_penalty); // Apply unmatched penalty - int unmatched = (int)(str - strBegin) - nextMatch; - outScore += unmatched_letter_penalty * unmatched; + outScore += (int(str - strBegin) - matches[nextMatch-1] + 1) * unmatched_letter_penalty; // Apply ordering bonuses for (int i = 0; i < nextMatch; ++i) { pos_type currIdx = matches[i]; - if (i > 0) { - pos_type prevIdx = matches[i - 1]; - - // Sequential - if (currIdx == (prevIdx + 1)) - outScore += sequential_bonus; - } - // Check for bonuses based on neighbor character value if (currIdx > 0) { + if (i > 0 && currIdx == matches[i - 1] + 1) + // Sequential + outScore += sequential_bonus; // Camel case - // ::islower() expects an unsigned char in range of 0 to 255. - char_type uneighbor = strBegin[currIdx - 1]; - char_type ucurr = strBegin[currIdx]; - if (std::islower(uneighbor) && std::isupper(ucurr)) + char_type prev = strBegin[currIdx - 1]; + if (std::islower(prev) && std::isupper(strBegin[currIdx])) outScore += camel_bonus; - // Separator - char_type neighbor = strBegin[currIdx - 1]; - bool neighborSeparator = neighbor == '_' || neighbor == ' '; - if (neighborSeparator) + if (prev == '_' || prev == ' ') outScore += separator_bonus; } - else { - // First letter - outScore += first_letter_bonus; - } } } From 28c0880b24f36e3b3c7c9fb8415725f32f1ae670 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 May 2020 15:45:47 +0200 Subject: [PATCH 07/10] ASCII folding applied over the matched string --- src/slic3r/GUI/fts_fuzzy_match.h | 30 +- src/slic3r/Utils/ASCIIFolding.cpp | 889 +++++++++++++++--------------- src/slic3r/Utils/ASCIIFolding.hpp | 18 +- 3 files changed, 487 insertions(+), 450 deletions(-) diff --git a/src/slic3r/GUI/fts_fuzzy_match.h b/src/slic3r/GUI/fts_fuzzy_match.h index cf2b88a95..c4ef62e3a 100644 --- a/src/slic3r/GUI/fts_fuzzy_match.h +++ b/src/slic3r/GUI/fts_fuzzy_match.h @@ -37,6 +37,8 @@ #include +#include "../Utils/ASCIIFolding.hpp" + // Public interface namespace fts { using char_type = wchar_t; @@ -110,14 +112,27 @@ namespace fts { // Loop through pattern and str looking for a match bool first_match = true; while (*pattern != '\0' && *str != '\0') { + + int num_matched = std::tolower(*pattern) == std::tolower(*str) ? 1 : 0; + bool folded_match = false; + if (! num_matched) { + char tmp[4]; + char *end = Slic3r::fold_to_ascii(*str, tmp); + char *c = tmp; + for (const wchar_t* d = pattern; c != end && *d != 0 && wchar_t(std::tolower(*c)) == std::tolower(*d); ++c, ++d); + if (c == end) { + folded_match = true; + num_matched = end - tmp; + } + } // Found match - if (tolower(*pattern) == tolower(*str)) { + if (num_matched) { // Supplied matches buffer was too short - if (nextMatch >= max_matches) + if (nextMatch + num_matched > max_matches) return false; - + // "Copy-on-Write" srcMatches into matches if (first_match && srcMatches) { memcpy(matches, srcMatches, sizeof(pos_type) * (nextMatch + 1)); // including the stopper @@ -141,8 +156,9 @@ namespace fts { matches[nextMatch++] = (pos_type)(str - strBegin); // Write a stopper sign. matches[nextMatch] = stopper; - ++pattern; - } + // Advance pattern by the number of matched characters (could be more if ASCII folding triggers in). + pattern += num_matched; + } ++str; } @@ -172,8 +188,10 @@ namespace fts { first_letter_bonus : std::max(matches[0] * leading_letter_penalty, max_leading_letter_penalty); + // Apply unmatched letters after the end penalty +// outScore += (int(str - strBegin) - matches[nextMatch-1] + 1) * unmatched_letter_penalty; // Apply unmatched penalty - outScore += (int(str - strBegin) - matches[nextMatch-1] + 1) * unmatched_letter_penalty; + outScore += (int(str - strBegin) - nextMatch) * unmatched_letter_penalty; // Apply ordering bonuses for (int i = 0; i < nextMatch; ++i) { diff --git a/src/slic3r/Utils/ASCIIFolding.cpp b/src/slic3r/Utils/ASCIIFolding.cpp index c61fe2902..fe70ef47f 100644 --- a/src/slic3r/Utils/ASCIIFolding.cpp +++ b/src/slic3r/Utils/ASCIIFolding.cpp @@ -5,12 +5,16 @@ #include #include +namespace Slic3r { + // Based on http://svn.apache.org/repos/asf/lucene/java/tags/lucene_solr_4_5_1/lucene/analysis/common/src/java/org/apache/lucene/analysis/miscellaneous/ASCIIFoldingFilter.java -template -static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) +// Convert the input UNICODE character to a string of maximum 4 output ASCII characters. +// Return the end of the string written to the output. +// The output buffer must be at least 4 characters long. +char* fold_to_ascii(wchar_t c, char *out) { if (c < 0x080) { - *out = c; + *out ++ = c; } else { switch (c) { case L'\u00C0': // [LATIN CAPITAL LETTER A WITH GRAVE] @@ -47,7 +51,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u1EB6': // [LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW] case L'\u24B6': // [CIRCLED LATIN CAPITAL LETTER A] case L'\uFF21': // [FULLWIDTH LATIN CAPITAL LETTER A] - *out = 'A'; + *out ++ = 'A'; break; case L'\u00E0': // [LATIN SMALL LETTER A WITH GRAVE] case L'\u00E1': // [LATIN SMALL LETTER A WITH ACUTE] @@ -90,68 +94,68 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2C65': // [LATIN SMALL LETTER A WITH STROKE] case L'\u2C6F': // [LATIN CAPITAL LETTER TURNED A] case L'\uFF41': // [FULLWIDTH LATIN SMALL LETTER A] - *out = 'a'; + *out ++ = 'a'; break; case L'\uA732': // [LATIN CAPITAL LETTER AA] - *out = 'A'; - *out = 'A'; + *out ++ = 'A'; + *out ++ = 'A'; break; case L'\u00C6': // [LATIN CAPITAL LETTER AE] case L'\u01E2': // [LATIN CAPITAL LETTER AE WITH MACRON] case L'\u01FC': // [LATIN CAPITAL LETTER AE WITH ACUTE] case L'\u1D01': // [LATIN LETTER SMALL CAPITAL AE] - *out = 'A'; - *out = 'E'; + *out ++ = 'A'; + *out ++ = 'E'; break; case L'\uA734': // [LATIN CAPITAL LETTER AO] - *out = 'A'; - *out = 'O'; + *out ++ = 'A'; + *out ++ = 'O'; break; case L'\uA736': // [LATIN CAPITAL LETTER AU] - *out = 'A'; - *out = 'U'; + *out ++ = 'A'; + *out ++ = 'U'; break; case L'\uA738': // [LATIN CAPITAL LETTER AV] case L'\uA73A': // [LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR] - *out = 'A'; - *out = 'V'; + *out ++ = 'A'; + *out ++ = 'V'; break; case L'\uA73C': // [LATIN CAPITAL LETTER AY] - *out = 'A'; - *out = 'Y'; + *out ++ = 'A'; + *out ++ = 'Y'; break; case L'\u249C': // [PARENTHESIZED LATIN SMALL LETTER A] - *out = '('; - *out = 'a'; - *out = ')'; + *out ++ = '('; + *out ++ = 'a'; + *out ++ = ')'; break; case L'\uA733': // [LATIN SMALL LETTER AA] - *out = 'a'; - *out = 'a'; + *out ++ = 'a'; + *out ++ = 'a'; break; case L'\u00E6': // [LATIN SMALL LETTER AE] case L'\u01E3': // [LATIN SMALL LETTER AE WITH MACRON] case L'\u01FD': // [LATIN SMALL LETTER AE WITH ACUTE] case L'\u1D02': // [LATIN SMALL LETTER TURNED AE] - *out = 'a'; - *out = 'e'; + *out ++ = 'a'; + *out ++ = 'e'; break; case L'\uA735': // [LATIN SMALL LETTER AO] - *out = 'a'; - *out = 'o'; + *out ++ = 'a'; + *out ++ = 'o'; break; case L'\uA737': // [LATIN SMALL LETTER AU] - *out = 'a'; - *out = 'u'; + *out ++ = 'a'; + *out ++ = 'u'; break; case L'\uA739': // [LATIN SMALL LETTER AV] case L'\uA73B': // [LATIN SMALL LETTER AV WITH HORIZONTAL BAR] - *out = 'a'; - *out = 'v'; + *out ++ = 'a'; + *out ++ = 'v'; break; case L'\uA73D': // [LATIN SMALL LETTER AY] - *out = 'a'; - *out = 'y'; + *out ++ = 'a'; + *out ++ = 'y'; break; case L'\u0181': // [LATIN CAPITAL LETTER B WITH HOOK] case L'\u0182': // [LATIN CAPITAL LETTER B WITH TOPBAR] @@ -163,7 +167,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u1E06': // [LATIN CAPITAL LETTER B WITH LINE BELOW] case L'\u24B7': // [CIRCLED LATIN CAPITAL LETTER B] case L'\uFF22': // [FULLWIDTH LATIN CAPITAL LETTER B] - *out = 'B'; + *out ++ = 'B'; break; case L'\u0180': // [LATIN SMALL LETTER B WITH STROKE] case L'\u0183': // [LATIN SMALL LETTER B WITH TOPBAR] @@ -175,12 +179,12 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u1E07': // [LATIN SMALL LETTER B WITH LINE BELOW] case L'\u24D1': // [CIRCLED LATIN SMALL LETTER B] case L'\uFF42': // [FULLWIDTH LATIN SMALL LETTER B] - *out = 'b'; + *out ++ = 'b'; break; case L'\u249D': // [PARENTHESIZED LATIN SMALL LETTER B] - *out = '('; - *out = 'b'; - *out = ')'; + *out ++ = '('; + *out ++ = 'b'; + *out ++ = ')'; break; case L'\u00C7': // [LATIN CAPITAL LETTER C WITH CEDILLA] case L'\u0106': // [LATIN CAPITAL LETTER C WITH ACUTE] @@ -194,7 +198,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u1E08': // [LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE] case L'\u24B8': // [CIRCLED LATIN CAPITAL LETTER C] case L'\uFF23': // [FULLWIDTH LATIN CAPITAL LETTER C] - *out = 'C'; + *out ++ = 'C'; break; case L'\u00E7': // [LATIN SMALL LETTER C WITH CEDILLA] case L'\u0107': // [LATIN SMALL LETTER C WITH ACUTE] @@ -210,12 +214,12 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA73E': // [LATIN CAPITAL LETTER REVERSED C WITH DOT] case L'\uA73F': // [LATIN SMALL LETTER REVERSED C WITH DOT] case L'\uFF43': // [FULLWIDTH LATIN SMALL LETTER C] - *out = 'c'; + *out ++ = 'c'; break; case L'\u249E': // [PARENTHESIZED LATIN SMALL LETTER C] - *out = '('; - *out = 'c'; - *out = ')'; + *out ++ = '('; + *out ++ = 'c'; + *out ++ = ')'; break; case L'\u00D0': // [LATIN CAPITAL LETTER ETH] case L'\u010E': // [LATIN CAPITAL LETTER D WITH CARON] @@ -233,7 +237,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24B9': // [CIRCLED LATIN CAPITAL LETTER D] case L'\uA779': // [LATIN CAPITAL LETTER INSULAR D] case L'\uFF24': // [FULLWIDTH LATIN CAPITAL LETTER D] - *out = 'D'; + *out ++ = 'D'; break; case L'\u00F0': // [LATIN SMALL LETTER ETH] case L'\u010F': // [LATIN SMALL LETTER D WITH CARON] @@ -253,33 +257,33 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24D3': // [CIRCLED LATIN SMALL LETTER D] case L'\uA77A': // [LATIN SMALL LETTER INSULAR D] case L'\uFF44': // [FULLWIDTH LATIN SMALL LETTER D] - *out = 'd'; + *out ++ = 'd'; break; case L'\u01C4': // [LATIN CAPITAL LETTER DZ WITH CARON] case L'\u01F1': // [LATIN CAPITAL LETTER DZ] - *out = 'D'; - *out = 'Z'; + *out ++ = 'D'; + *out ++ = 'Z'; break; case L'\u01C5': // [LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON] case L'\u01F2': // [LATIN CAPITAL LETTER D WITH SMALL LETTER Z] - *out = 'D'; - *out = 'z'; + *out ++ = 'D'; + *out ++ = 'z'; break; case L'\u249F': // [PARENTHESIZED LATIN SMALL LETTER D] - *out = '('; - *out = 'd'; - *out = ')'; + *out ++ = '('; + *out ++ = 'd'; + *out ++ = ')'; break; case L'\u0238': // [LATIN SMALL LETTER DB DIGRAPH] - *out = 'd'; - *out = 'b'; + *out ++ = 'd'; + *out ++ = 'b'; break; case L'\u01C6': // [LATIN SMALL LETTER DZ WITH CARON] case L'\u01F3': // [LATIN SMALL LETTER DZ] case L'\u02A3': // [LATIN SMALL LETTER DZ DIGRAPH] case L'\u02A5': // [LATIN SMALL LETTER DZ DIGRAPH WITH CURL] - *out = 'd'; - *out = 'z'; + *out ++ = 'd'; + *out ++ = 'z'; break; case L'\u00C8': // [LATIN CAPITAL LETTER E WITH GRAVE] case L'\u00C9': // [LATIN CAPITAL LETTER E WITH ACUTE] @@ -313,7 +317,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24BA': // [CIRCLED LATIN CAPITAL LETTER E] case L'\u2C7B': // [LATIN LETTER SMALL CAPITAL TURNED E] case L'\uFF25': // [FULLWIDTH LATIN CAPITAL LETTER E] - *out = 'E'; + *out ++ = 'E'; break; case L'\u00E8': // [LATIN SMALL LETTER E WITH GRAVE] case L'\u00E9': // [LATIN SMALL LETTER E WITH ACUTE] @@ -356,12 +360,12 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24D4': // [CIRCLED LATIN SMALL LETTER E] case L'\u2C78': // [LATIN SMALL LETTER E WITH NOTCH] case L'\uFF45': // [FULLWIDTH LATIN SMALL LETTER E] - *out = 'e'; + *out ++ = 'e'; break; case L'\u24A0': // [PARENTHESIZED LATIN SMALL LETTER E] - *out = '('; - *out = 'e'; - *out = ')'; + *out ++ = '('; + *out ++ = 'e'; + *out ++ = ')'; break; case L'\u0191': // [LATIN CAPITAL LETTER F WITH HOOK] case L'\u1E1E': // [LATIN CAPITAL LETTER F WITH DOT ABOVE] @@ -370,7 +374,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA77B': // [LATIN CAPITAL LETTER INSULAR F] case L'\uA7FB': // [LATIN EPIGRAPHIC LETTER REVERSED F] case L'\uFF26': // [FULLWIDTH LATIN CAPITAL LETTER F] - *out = 'F'; + *out ++ = 'F'; break; case L'\u0192': // [LATIN SMALL LETTER F WITH HOOK] case L'\u1D6E': // [LATIN SMALL LETTER F WITH MIDDLE TILDE] @@ -380,34 +384,34 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24D5': // [CIRCLED LATIN SMALL LETTER F] case L'\uA77C': // [LATIN SMALL LETTER INSULAR F] case L'\uFF46': // [FULLWIDTH LATIN SMALL LETTER F] - *out = 'f'; + *out ++ = 'f'; break; case L'\u24A1': // [PARENTHESIZED LATIN SMALL LETTER F] - *out = '('; - *out = 'f'; - *out = ')'; + *out ++ = '('; + *out ++ = 'f'; + *out ++ = ')'; break; case L'\uFB00': // [LATIN SMALL LIGATURE FF] - *out = 'f'; - *out = 'f'; + *out ++ = 'f'; + *out ++ = 'f'; break; case L'\uFB03': // [LATIN SMALL LIGATURE FFI] - *out = 'f'; - *out = 'f'; - *out = 'i'; + *out ++ = 'f'; + *out ++ = 'f'; + *out ++ = 'i'; break; case L'\uFB04': // [LATIN SMALL LIGATURE FFL] - *out = 'f'; - *out = 'f'; - *out = 'l'; + *out ++ = 'f'; + *out ++ = 'f'; + *out ++ = 'l'; break; case L'\uFB01': // [LATIN SMALL LIGATURE FI] - *out = 'f'; - *out = 'i'; + *out ++ = 'f'; + *out ++ = 'i'; break; case L'\uFB02': // [LATIN SMALL LIGATURE FL] - *out = 'f'; - *out = 'l'; + *out ++ = 'f'; + *out ++ = 'l'; break; case L'\u011C': // [LATIN CAPITAL LETTER G WITH CIRCUMFLEX] case L'\u011E': // [LATIN CAPITAL LETTER G WITH BREVE] @@ -426,7 +430,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA77D': // [LATIN CAPITAL LETTER INSULAR G] case L'\uA77E': // [LATIN CAPITAL LETTER TURNED INSULAR G] case L'\uFF27': // [FULLWIDTH LATIN CAPITAL LETTER G] - *out = 'G'; + *out ++ = 'G'; break; case L'\u011D': // [LATIN SMALL LETTER G WITH CIRCUMFLEX] case L'\u011F': // [LATIN SMALL LETTER G WITH BREVE] @@ -442,12 +446,12 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24D6': // [CIRCLED LATIN SMALL LETTER G] case L'\uA77F': // [LATIN SMALL LETTER TURNED INSULAR G] case L'\uFF47': // [FULLWIDTH LATIN SMALL LETTER G] - *out = 'g'; + *out ++ = 'g'; break; case L'\u24A2': // [PARENTHESIZED LATIN SMALL LETTER G] - *out = '('; - *out = 'g'; - *out = ')'; + *out ++ = '('; + *out ++ = 'g'; + *out ++ = ')'; break; case L'\u0124': // [LATIN CAPITAL LETTER H WITH CIRCUMFLEX] case L'\u0126': // [LATIN CAPITAL LETTER H WITH STROKE] @@ -462,7 +466,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2C67': // [LATIN CAPITAL LETTER H WITH DESCENDER] case L'\u2C75': // [LATIN CAPITAL LETTER HALF H] case L'\uFF28': // [FULLWIDTH LATIN CAPITAL LETTER H] - *out = 'H'; + *out ++ = 'H'; break; case L'\u0125': // [LATIN SMALL LETTER H WITH CIRCUMFLEX] case L'\u0127': // [LATIN SMALL LETTER H WITH STROKE] @@ -481,20 +485,20 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2C68': // [LATIN SMALL LETTER H WITH DESCENDER] case L'\u2C76': // [LATIN SMALL LETTER HALF H] case L'\uFF48': // [FULLWIDTH LATIN SMALL LETTER H] - *out = 'h'; + *out ++ = 'h'; break; case L'\u01F6': // [LATIN CAPITAL LETTER HWAIR] - *out = 'H'; - *out = 'V'; + *out ++ = 'H'; + *out ++ = 'V'; break; case L'\u24A3': // [PARENTHESIZED LATIN SMALL LETTER H] - *out = '('; - *out = 'h'; - *out = ')'; + *out ++ = '('; + *out ++ = 'h'; + *out ++ = ')'; break; case L'\u0195': // [LATIN SMALL LETTER HV] - *out = 'h'; - *out = 'v'; + *out ++ = 'h'; + *out ++ = 'v'; break; case L'\u00CC': // [LATIN CAPITAL LETTER I WITH GRAVE] case L'\u00CD': // [LATIN CAPITAL LETTER I WITH ACUTE] @@ -519,7 +523,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24BE': // [CIRCLED LATIN CAPITAL LETTER I] case L'\uA7FE': // [LATIN EPIGRAPHIC LETTER I LONGA] case L'\uFF29': // [FULLWIDTH LATIN CAPITAL LETTER I] - *out = 'I'; + *out ++ = 'I'; break; case L'\u00EC': // [LATIN SMALL LETTER I WITH GRAVE] case L'\u00ED': // [LATIN SMALL LETTER I WITH ACUTE] @@ -545,27 +549,27 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2071': // [SUPERSCRIPT LATIN SMALL LETTER I] case L'\u24D8': // [CIRCLED LATIN SMALL LETTER I] case L'\uFF49': // [FULLWIDTH LATIN SMALL LETTER I] - *out = 'i'; + *out ++ = 'i'; break; case L'\u0132': // [LATIN CAPITAL LIGATURE IJ] - *out = 'I'; - *out = 'J'; + *out ++ = 'I'; + *out ++ = 'J'; break; case L'\u24A4': // [PARENTHESIZED LATIN SMALL LETTER I] - *out = '('; - *out = 'i'; - *out = ')'; + *out ++ = '('; + *out ++ = 'i'; + *out ++ = ')'; break; case L'\u0133': // [LATIN SMALL LIGATURE IJ] - *out = 'i'; - *out = 'j'; + *out ++ = 'i'; + *out ++ = 'j'; break; case L'\u0134': // [LATIN CAPITAL LETTER J WITH CIRCUMFLEX] case L'\u0248': // [LATIN CAPITAL LETTER J WITH STROKE] case L'\u1D0A': // [LATIN LETTER SMALL CAPITAL J] case L'\u24BF': // [CIRCLED LATIN CAPITAL LETTER J] case L'\uFF2A': // [FULLWIDTH LATIN CAPITAL LETTER J] - *out = 'J'; + *out ++ = 'J'; break; case L'\u0135': // [LATIN SMALL LETTER J WITH CIRCUMFLEX] case L'\u01F0': // [LATIN SMALL LETTER J WITH CARON] @@ -577,12 +581,12 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24D9': // [CIRCLED LATIN SMALL LETTER J] case L'\u2C7C': // [LATIN SUBSCRIPT SMALL LETTER J] case L'\uFF4A': // [FULLWIDTH LATIN SMALL LETTER J] - *out = 'j'; + *out ++ = 'j'; break; case L'\u24A5': // [PARENTHESIZED LATIN SMALL LETTER J] - *out = '('; - *out = 'j'; - *out = ')'; + *out ++ = '('; + *out ++ = 'j'; + *out ++ = ')'; break; case L'\u0136': // [LATIN CAPITAL LETTER K WITH CEDILLA] case L'\u0198': // [LATIN CAPITAL LETTER K WITH HOOK] @@ -597,7 +601,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA742': // [LATIN CAPITAL LETTER K WITH DIAGONAL STROKE] case L'\uA744': // [LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE] case L'\uFF2B': // [FULLWIDTH LATIN CAPITAL LETTER K] - *out = 'K'; + *out ++ = 'K'; break; case L'\u0137': // [LATIN SMALL LETTER K WITH CEDILLA] case L'\u0199': // [LATIN SMALL LETTER K WITH HOOK] @@ -613,12 +617,12 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA743': // [LATIN SMALL LETTER K WITH DIAGONAL STROKE] case L'\uA745': // [LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE] case L'\uFF4B': // [FULLWIDTH LATIN SMALL LETTER K] - *out = 'k'; + *out ++ = 'k'; break; case L'\u24A6': // [PARENTHESIZED LATIN SMALL LETTER K] - *out = '('; - *out = 'k'; - *out = ')'; + *out ++ = '('; + *out ++ = 'k'; + *out ++ = ')'; break; case L'\u0139': // [LATIN CAPITAL LETTER L WITH ACUTE] case L'\u013B': // [LATIN CAPITAL LETTER L WITH CEDILLA] @@ -639,7 +643,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA748': // [LATIN CAPITAL LETTER L WITH HIGH STROKE] case L'\uA780': // [LATIN CAPITAL LETTER TURNED L] case L'\uFF2C': // [FULLWIDTH LATIN CAPITAL LETTER L] - *out = 'L'; + *out ++ = 'L'; break; case L'\u013A': // [LATIN SMALL LETTER L WITH ACUTE] case L'\u013C': // [LATIN SMALL LETTER L WITH CEDILLA] @@ -662,40 +666,40 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA749': // [LATIN SMALL LETTER L WITH HIGH STROKE] case L'\uA781': // [LATIN SMALL LETTER TURNED L] case L'\uFF4C': // [FULLWIDTH LATIN SMALL LETTER L] - *out = 'l'; + *out ++ = 'l'; break; case L'\u01C7': // [LATIN CAPITAL LETTER LJ] - *out = 'L'; - *out = 'J'; + *out ++ = 'L'; + *out ++ = 'J'; break; case L'\u1EFA': // [LATIN CAPITAL LETTER MIDDLE-WELSH LL] - *out = 'L'; - *out = 'L'; + *out ++ = 'L'; + *out ++ = 'L'; break; case L'\u01C8': // [LATIN CAPITAL LETTER L WITH SMALL LETTER J] - *out = 'L'; - *out = 'j'; + *out ++ = 'L'; + *out ++ = 'j'; break; case L'\u24A7': // [PARENTHESIZED LATIN SMALL LETTER L] - *out = '('; - *out = 'l'; - *out = ')'; + *out ++ = '('; + *out ++ = 'l'; + *out ++ = ')'; break; case L'\u01C9': // [LATIN SMALL LETTER LJ] - *out = 'l'; - *out = 'j'; + *out ++ = 'l'; + *out ++ = 'j'; break; case L'\u1EFB': // [LATIN SMALL LETTER MIDDLE-WELSH LL] - *out = 'l'; - *out = 'l'; + *out ++ = 'l'; + *out ++ = 'l'; break; case L'\u02AA': // [LATIN SMALL LETTER LS DIGRAPH] - *out = 'l'; - *out = 's'; + *out ++ = 'l'; + *out ++ = 's'; break; case L'\u02AB': // [LATIN SMALL LETTER LZ DIGRAPH] - *out = 'l'; - *out = 'z'; + *out ++ = 'l'; + *out ++ = 'z'; break; case L'\u019C': // [LATIN CAPITAL LETTER TURNED M] case L'\u1D0D': // [LATIN LETTER SMALL CAPITAL M] @@ -707,7 +711,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA7FD': // [LATIN EPIGRAPHIC LETTER INVERTED M] case L'\uA7FF': // [LATIN EPIGRAPHIC LETTER ARCHAIC M] case L'\uFF2D': // [FULLWIDTH LATIN CAPITAL LETTER M] - *out = 'M'; + *out ++ = 'M'; break; case L'\u026F': // [LATIN SMALL LETTER TURNED M] case L'\u0270': // [LATIN SMALL LETTER TURNED M WITH LONG LEG] @@ -719,12 +723,12 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u1E43': // [LATIN SMALL LETTER M WITH DOT BELOW] case L'\u24DC': // [CIRCLED LATIN SMALL LETTER M] case L'\uFF4D': // [FULLWIDTH LATIN SMALL LETTER M] - *out = 'm'; + *out ++ = 'm'; break; case L'\u24A8': // [PARENTHESIZED LATIN SMALL LETTER M] - *out = '('; - *out = 'm'; - *out = ')'; + *out ++ = '('; + *out ++ = 'm'; + *out ++ = ')'; break; case L'\u00D1': // [LATIN CAPITAL LETTER N WITH TILDE] case L'\u0143': // [LATIN CAPITAL LETTER N WITH ACUTE] @@ -742,7 +746,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u1E4A': // [LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW] case L'\u24C3': // [CIRCLED LATIN CAPITAL LETTER N] case L'\uFF2E': // [FULLWIDTH LATIN CAPITAL LETTER N] - *out = 'N'; + *out ++ = 'N'; break; case L'\u00F1': // [LATIN SMALL LETTER N WITH TILDE] case L'\u0144': // [LATIN SMALL LETTER N WITH ACUTE] @@ -764,24 +768,24 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u207F': // [SUPERSCRIPT LATIN SMALL LETTER N] case L'\u24DD': // [CIRCLED LATIN SMALL LETTER N] case L'\uFF4E': // [FULLWIDTH LATIN SMALL LETTER N] - *out = 'n'; + *out ++ = 'n'; break; case L'\u01CA': // [LATIN CAPITAL LETTER NJ] - *out = 'N'; - *out = 'J'; + *out ++ = 'N'; + *out ++ = 'J'; break; case L'\u01CB': // [LATIN CAPITAL LETTER N WITH SMALL LETTER J] - *out = 'N'; - *out = 'j'; + *out ++ = 'N'; + *out ++ = 'j'; break; case L'\u24A9': // [PARENTHESIZED LATIN SMALL LETTER N] - *out = '('; - *out = 'n'; - *out = ')'; + *out ++ = '('; + *out ++ = 'n'; + *out ++ = ')'; break; case L'\u01CC': // [LATIN SMALL LETTER NJ] - *out = 'n'; - *out = 'j'; + *out ++ = 'n'; + *out ++ = 'j'; break; case L'\u00D2': // [LATIN CAPITAL LETTER O WITH GRAVE] case L'\u00D3': // [LATIN CAPITAL LETTER O WITH ACUTE] @@ -827,7 +831,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA74A': // [LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY] case L'\uA74C': // [LATIN CAPITAL LETTER O WITH LOOP] case L'\uFF2F': // [FULLWIDTH LATIN CAPITAL LETTER O] - *out = 'O'; + *out ++ = 'O'; break; case L'\u00F2': // [LATIN SMALL LETTER O WITH GRAVE] case L'\u00F3': // [LATIN SMALL LETTER O WITH ACUTE] @@ -876,39 +880,39 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA74B': // [LATIN SMALL LETTER O WITH LONG STROKE OVERLAY] case L'\uA74D': // [LATIN SMALL LETTER O WITH LOOP] case L'\uFF4F': // [FULLWIDTH LATIN SMALL LETTER O] - *out = 'o'; + *out ++ = 'o'; break; case L'\u0152': // [LATIN CAPITAL LIGATURE OE] case L'\u0276': // [LATIN LETTER SMALL CAPITAL OE] - *out = 'O'; - *out = 'E'; + *out ++ = 'O'; + *out ++ = 'E'; break; case L'\uA74E': // [LATIN CAPITAL LETTER OO] - *out = 'O'; - *out = 'O'; + *out ++ = 'O'; + *out ++ = 'O'; break; case L'\u0222': // [LATIN CAPITAL LETTER OU] case L'\u1D15': // [LATIN LETTER SMALL CAPITAL OU] - *out = 'O'; - *out = 'U'; + *out ++ = 'O'; + *out ++ = 'U'; break; case L'\u24AA': // [PARENTHESIZED LATIN SMALL LETTER O] - *out = '('; - *out = 'o'; - *out = ')'; + *out ++ = '('; + *out ++ = 'o'; + *out ++ = ')'; break; case L'\u0153': // [LATIN SMALL LIGATURE OE] case L'\u1D14': // [LATIN SMALL LETTER TURNED OE] - *out = 'o'; - *out = 'e'; + *out ++ = 'o'; + *out ++ = 'e'; break; case L'\uA74F': // [LATIN SMALL LETTER OO] - *out = 'o'; - *out = 'o'; + *out ++ = 'o'; + *out ++ = 'o'; break; case L'\u0223': // [LATIN SMALL LETTER OU] - *out = 'o'; - *out = 'u'; + *out ++ = 'o'; + *out ++ = 'u'; break; case L'\u01A4': // [LATIN CAPITAL LETTER P WITH HOOK] case L'\u1D18': // [LATIN LETTER SMALL CAPITAL P] @@ -920,7 +924,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA752': // [LATIN CAPITAL LETTER P WITH FLOURISH] case L'\uA754': // [LATIN CAPITAL LETTER P WITH SQUIRREL TAIL] case L'\uFF30': // [FULLWIDTH LATIN CAPITAL LETTER P] - *out = 'P'; + *out ++ = 'P'; break; case L'\u01A5': // [LATIN SMALL LETTER P WITH HOOK] case L'\u1D71': // [LATIN SMALL LETTER P WITH MIDDLE TILDE] @@ -934,19 +938,19 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA755': // [LATIN SMALL LETTER P WITH SQUIRREL TAIL] case L'\uA7FC': // [LATIN EPIGRAPHIC LETTER REVERSED P] case L'\uFF50': // [FULLWIDTH LATIN SMALL LETTER P] - *out = 'p'; + *out ++ = 'p'; break; case L'\u24AB': // [PARENTHESIZED LATIN SMALL LETTER P] - *out = '('; - *out = 'p'; - *out = ')'; + *out ++ = '('; + *out ++ = 'p'; + *out ++ = ')'; break; case L'\u024A': // [LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL] case L'\u24C6': // [CIRCLED LATIN CAPITAL LETTER Q] case L'\uA756': // [LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER] case L'\uA758': // [LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE] case L'\uFF31': // [FULLWIDTH LATIN CAPITAL LETTER Q] - *out = 'Q'; + *out ++ = 'Q'; break; case L'\u0138': // [LATIN SMALL LETTER KRA] case L'\u024B': // [LATIN SMALL LETTER Q WITH HOOK TAIL] @@ -955,16 +959,16 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA757': // [LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER] case L'\uA759': // [LATIN SMALL LETTER Q WITH DIAGONAL STROKE] case L'\uFF51': // [FULLWIDTH LATIN SMALL LETTER Q] - *out = 'q'; + *out ++ = 'q'; break; case L'\u24AC': // [PARENTHESIZED LATIN SMALL LETTER Q] - *out = '('; - *out = 'q'; - *out = ')'; + *out ++ = '('; + *out ++ = 'q'; + *out ++ = ')'; break; case L'\u0239': // [LATIN SMALL LETTER QP DIGRAPH] - *out = 'q'; - *out = 'p'; + *out ++ = 'q'; + *out ++ = 'p'; break; case L'\u0154': // [LATIN CAPITAL LETTER R WITH ACUTE] case L'\u0156': // [LATIN CAPITAL LETTER R WITH CEDILLA] @@ -985,7 +989,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA75A': // [LATIN CAPITAL LETTER R ROTUNDA] case L'\uA782': // [LATIN CAPITAL LETTER INSULAR R] case L'\uFF32': // [FULLWIDTH LATIN CAPITAL LETTER R] - *out = 'R'; + *out ++ = 'R'; break; case L'\u0155': // [LATIN SMALL LETTER R WITH ACUTE] case L'\u0157': // [LATIN SMALL LETTER R WITH CEDILLA] @@ -1009,12 +1013,12 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA75B': // [LATIN SMALL LETTER R ROTUNDA] case L'\uA783': // [LATIN SMALL LETTER INSULAR R] case L'\uFF52': // [FULLWIDTH LATIN SMALL LETTER R] - *out = 'r'; + *out ++ = 'r'; break; case L'\u24AD': // [PARENTHESIZED LATIN SMALL LETTER R] - *out = '('; - *out = 'r'; - *out = ')'; + *out ++ = '('; + *out ++ = 'r'; + *out ++ = ')'; break; case L'\u015A': // [LATIN CAPITAL LETTER S WITH ACUTE] case L'\u015C': // [LATIN CAPITAL LETTER S WITH CIRCUMFLEX] @@ -1030,7 +1034,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA731': // [LATIN LETTER SMALL CAPITAL S] case L'\uA785': // [LATIN SMALL LETTER INSULAR S] case L'\uFF33': // [FULLWIDTH LATIN CAPITAL LETTER S] - *out = 'S'; + *out ++ = 'S'; break; case L'\u015B': // [LATIN SMALL LETTER S WITH ACUTE] case L'\u015D': // [LATIN SMALL LETTER S WITH CIRCUMFLEX] @@ -1052,24 +1056,24 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24E2': // [CIRCLED LATIN SMALL LETTER S] case L'\uA784': // [LATIN CAPITAL LETTER INSULAR S] case L'\uFF53': // [FULLWIDTH LATIN SMALL LETTER S] - *out = 's'; + *out ++ = 's'; break; case L'\u1E9E': // [LATIN CAPITAL LETTER SHARP S] - *out = 'S'; - *out = 'S'; + *out ++ = 'S'; + *out ++ = 'S'; break; case L'\u24AE': // [PARENTHESIZED LATIN SMALL LETTER S] - *out = '('; - *out = 's'; - *out = ')'; + *out ++ = '('; + *out ++ = 's'; + *out ++ = ')'; break; case L'\u00DF': // [LATIN SMALL LETTER SHARP S] - *out = 's'; - *out = 's'; + *out ++ = 's'; + *out ++ = 's'; break; case L'\uFB06': // [LATIN SMALL LIGATURE ST] - *out = 's'; - *out = 't'; + *out ++ = 's'; + *out ++ = 't'; break; case L'\u0162': // [LATIN CAPITAL LETTER T WITH CEDILLA] case L'\u0164': // [LATIN CAPITAL LETTER T WITH CARON] @@ -1086,7 +1090,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24C9': // [CIRCLED LATIN CAPITAL LETTER T] case L'\uA786': // [LATIN CAPITAL LETTER INSULAR T] case L'\uFF34': // [FULLWIDTH LATIN CAPITAL LETTER T] - *out = 'T'; + *out ++ = 'T'; break; case L'\u0163': // [LATIN SMALL LETTER T WITH CEDILLA] case L'\u0165': // [LATIN SMALL LETTER T WITH CARON] @@ -1106,39 +1110,39 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24E3': // [CIRCLED LATIN SMALL LETTER T] case L'\u2C66': // [LATIN SMALL LETTER T WITH DIAGONAL STROKE] case L'\uFF54': // [FULLWIDTH LATIN SMALL LETTER T] - *out = 't'; + *out ++ = 't'; break; case L'\u00DE': // [LATIN CAPITAL LETTER THORN] case L'\uA766': // [LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER] - *out = 'T'; - *out = 'H'; + *out ++ = 'T'; + *out ++ = 'H'; break; case L'\uA728': // [LATIN CAPITAL LETTER TZ] - *out = 'T'; - *out = 'Z'; + *out ++ = 'T'; + *out ++ = 'Z'; break; case L'\u24AF': // [PARENTHESIZED LATIN SMALL LETTER T] - *out = '('; - *out = 't'; - *out = ')'; + *out ++ = '('; + *out ++ = 't'; + *out ++ = ')'; break; case L'\u02A8': // [LATIN SMALL LETTER TC DIGRAPH WITH CURL] - *out = 't'; - *out = 'c'; + *out ++ = 't'; + *out ++ = 'c'; break; case L'\u00FE': // [LATIN SMALL LETTER THORN] case L'\u1D7A': // [LATIN SMALL LETTER TH WITH STRIKETHROUGH] case L'\uA767': // [LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER] - *out = 't'; - *out = 'h'; + *out ++ = 't'; + *out ++ = 'h'; break; case L'\u02A6': // [LATIN SMALL LETTER TS DIGRAPH] - *out = 't'; - *out = 's'; + *out ++ = 't'; + *out ++ = 's'; break; case L'\uA729': // [LATIN SMALL LETTER TZ] - *out = 't'; - *out = 'z'; + *out ++ = 't'; + *out ++ = 'z'; break; case L'\u00D9': // [LATIN CAPITAL LETTER U WITH GRAVE] case L'\u00DA': // [LATIN CAPITAL LETTER U WITH ACUTE] @@ -1175,7 +1179,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u1EF0': // [LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW] case L'\u24CA': // [CIRCLED LATIN CAPITAL LETTER U] case L'\uFF35': // [FULLWIDTH LATIN CAPITAL LETTER U] - *out = 'U'; + *out ++ = 'U'; break; case L'\u00F9': // [LATIN SMALL LETTER U WITH GRAVE] case L'\u00FA': // [LATIN SMALL LETTER U WITH ACUTE] @@ -1212,16 +1216,16 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u1EF1': // [LATIN SMALL LETTER U WITH HORN AND DOT BELOW] case L'\u24E4': // [CIRCLED LATIN SMALL LETTER U] case L'\uFF55': // [FULLWIDTH LATIN SMALL LETTER U] - *out = 'u'; + *out ++ = 'u'; break; case L'\u24B0': // [PARENTHESIZED LATIN SMALL LETTER U] - *out = '('; - *out = 'u'; - *out = ')'; + *out ++ = '('; + *out ++ = 'u'; + *out ++ = ')'; break; case L'\u1D6B': // [LATIN SMALL LETTER UE] - *out = 'u'; - *out = 'e'; + *out ++ = 'u'; + *out ++ = 'e'; break; case L'\u01B2': // [LATIN CAPITAL LETTER V WITH HOOK] case L'\u0245': // [LATIN CAPITAL LETTER TURNED V] @@ -1233,7 +1237,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\uA75E': // [LATIN CAPITAL LETTER V WITH DIAGONAL STROKE] case L'\uA768': // [LATIN CAPITAL LETTER VEND] case L'\uFF36': // [FULLWIDTH LATIN CAPITAL LETTER V] - *out = 'V'; + *out ++ = 'V'; break; case L'\u028B': // [LATIN SMALL LETTER V WITH HOOK] case L'\u028C': // [LATIN SMALL LETTER TURNED V] @@ -1246,20 +1250,20 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2C74': // [LATIN SMALL LETTER V WITH CURL] case L'\uA75F': // [LATIN SMALL LETTER V WITH DIAGONAL STROKE] case L'\uFF56': // [FULLWIDTH LATIN SMALL LETTER V] - *out = 'v'; + *out ++ = 'v'; break; case L'\uA760': // [LATIN CAPITAL LETTER VY] - *out = 'V'; - *out = 'Y'; + *out ++ = 'V'; + *out ++ = 'Y'; break; case L'\u24B1': // [PARENTHESIZED LATIN SMALL LETTER V] - *out = '('; - *out = 'v'; - *out = ')'; + *out ++ = '('; + *out ++ = 'v'; + *out ++ = ')'; break; case L'\uA761': // [LATIN SMALL LETTER VY] - *out = 'v'; - *out = 'y'; + *out ++ = 'v'; + *out ++ = 'y'; break; case L'\u0174': // [LATIN CAPITAL LETTER W WITH CIRCUMFLEX] case L'\u01F7': // [LATIN CAPITAL LETTER WYNN] @@ -1272,7 +1276,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24CC': // [CIRCLED LATIN CAPITAL LETTER W] case L'\u2C72': // [LATIN CAPITAL LETTER W WITH HOOK] case L'\uFF37': // [FULLWIDTH LATIN CAPITAL LETTER W] - *out = 'W'; + *out ++ = 'W'; break; case L'\u0175': // [LATIN SMALL LETTER W WITH CIRCUMFLEX] case L'\u01BF': // [LATIN LETTER WYNN] @@ -1286,18 +1290,18 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u24E6': // [CIRCLED LATIN SMALL LETTER W] case L'\u2C73': // [LATIN SMALL LETTER W WITH HOOK] case L'\uFF57': // [FULLWIDTH LATIN SMALL LETTER W] - *out = 'w'; + *out ++ = 'w'; break; case L'\u24B2': // [PARENTHESIZED LATIN SMALL LETTER W] - *out = '('; - *out = 'w'; - *out = ')'; + *out ++ = '('; + *out ++ = 'w'; + *out ++ = ')'; break; case L'\u1E8A': // [LATIN CAPITAL LETTER X WITH DOT ABOVE] case L'\u1E8C': // [LATIN CAPITAL LETTER X WITH DIAERESIS] case L'\u24CD': // [CIRCLED LATIN CAPITAL LETTER X] case L'\uFF38': // [FULLWIDTH LATIN CAPITAL LETTER X] - *out = 'X'; + *out ++ = 'X'; break; case L'\u1D8D': // [LATIN SMALL LETTER X WITH PALATAL HOOK] case L'\u1E8B': // [LATIN SMALL LETTER X WITH DOT ABOVE] @@ -1305,12 +1309,12 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2093': // [LATIN SUBSCRIPT SMALL LETTER X] case L'\u24E7': // [CIRCLED LATIN SMALL LETTER X] case L'\uFF58': // [FULLWIDTH LATIN SMALL LETTER X] - *out = 'x'; + *out ++ = 'x'; break; case L'\u24B3': // [PARENTHESIZED LATIN SMALL LETTER X] - *out = '('; - *out = 'x'; - *out = ')'; + *out ++ = '('; + *out ++ = 'x'; + *out ++ = ')'; break; case L'\u00DD': // [LATIN CAPITAL LETTER Y WITH ACUTE] case L'\u0176': // [LATIN CAPITAL LETTER Y WITH CIRCUMFLEX] @@ -1327,7 +1331,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u1EFE': // [LATIN CAPITAL LETTER Y WITH LOOP] case L'\u24CE': // [CIRCLED LATIN CAPITAL LETTER Y] case L'\uFF39': // [FULLWIDTH LATIN CAPITAL LETTER Y] - *out = 'Y'; + *out ++ = 'Y'; break; case L'\u00FD': // [LATIN SMALL LETTER Y WITH ACUTE] case L'\u00FF': // [LATIN SMALL LETTER Y WITH DIAERESIS] @@ -1345,12 +1349,12 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u1EFF': // [LATIN SMALL LETTER Y WITH LOOP] case L'\u24E8': // [CIRCLED LATIN SMALL LETTER Y] case L'\uFF59': // [FULLWIDTH LATIN SMALL LETTER Y] - *out = 'y'; + *out ++ = 'y'; break; case L'\u24B4': // [PARENTHESIZED LATIN SMALL LETTER Y] - *out = '('; - *out = 'y'; - *out = ')'; + *out ++ = '('; + *out ++ = 'y'; + *out ++ = ')'; break; case L'\u0179': // [LATIN CAPITAL LETTER Z WITH ACUTE] case L'\u017B': // [LATIN CAPITAL LETTER Z WITH DOT ABOVE] @@ -1366,7 +1370,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2C6B': // [LATIN CAPITAL LETTER Z WITH DESCENDER] case L'\uA762': // [LATIN CAPITAL LETTER VISIGOTHIC Z] case L'\uFF3A': // [FULLWIDTH LATIN CAPITAL LETTER Z] - *out = 'Z'; + *out ++ = 'Z'; break; case L'\u017A': // [LATIN SMALL LETTER Z WITH ACUTE] case L'\u017C': // [LATIN SMALL LETTER Z WITH DOT ABOVE] @@ -1386,19 +1390,19 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2C6C': // [LATIN SMALL LETTER Z WITH DESCENDER] case L'\uA763': // [LATIN SMALL LETTER VISIGOTHIC Z] case L'\uFF5A': // [FULLWIDTH LATIN SMALL LETTER Z] - *out = 'z'; + *out ++ = 'z'; break; case L'\u24B5': // [PARENTHESIZED LATIN SMALL LETTER Z] - *out = '('; - *out = 'z'; - *out = ')'; + *out ++ = '('; + *out ++ = 'z'; + *out ++ = ')'; break; case L'\u2070': // [SUPERSCRIPT ZERO] case L'\u2080': // [SUBSCRIPT ZERO] case L'\u24EA': // [CIRCLED DIGIT ZERO] case L'\u24FF': // [NEGATIVE CIRCLED DIGIT ZERO] case L'\uFF10': // [FULLWIDTH DIGIT ZERO] - *out = '0'; + *out ++ = '0'; break; case L'\u00B9': // [SUPERSCRIPT ONE] case L'\u2081': // [SUBSCRIPT ONE] @@ -1408,16 +1412,16 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2780': // [DINGBAT CIRCLED SANS-SERIF DIGIT ONE] case L'\u278A': // [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE] case L'\uFF11': // [FULLWIDTH DIGIT ONE] - *out = '1'; + *out ++ = '1'; break; case L'\u2488': // [DIGIT ONE FULL STOP] - *out = '1'; - *out = '.'; + *out ++ = '1'; + *out ++ = '.'; break; case L'\u2474': // [PARENTHESIZED DIGIT ONE] - *out = '('; - *out = '1'; - *out = ')'; + *out ++ = '('; + *out ++ = '1'; + *out ++ = ')'; break; case L'\u00B2': // [SUPERSCRIPT TWO] case L'\u2082': // [SUBSCRIPT TWO] @@ -1427,16 +1431,16 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2781': // [DINGBAT CIRCLED SANS-SERIF DIGIT TWO] case L'\u278B': // [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO] case L'\uFF12': // [FULLWIDTH DIGIT TWO] - *out = '2'; + *out ++ = '2'; break; case L'\u2489': // [DIGIT TWO FULL STOP] - *out = '2'; - *out = '.'; + *out ++ = '2'; + *out ++ = '.'; break; case L'\u2475': // [PARENTHESIZED DIGIT TWO] - *out = '('; - *out = '2'; - *out = ')'; + *out ++ = '('; + *out ++ = '2'; + *out ++ = ')'; break; case L'\u00B3': // [SUPERSCRIPT THREE] case L'\u2083': // [SUBSCRIPT THREE] @@ -1446,16 +1450,16 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2782': // [DINGBAT CIRCLED SANS-SERIF DIGIT THREE] case L'\u278C': // [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE] case L'\uFF13': // [FULLWIDTH DIGIT THREE] - *out = '3'; + *out ++ = '3'; break; case L'\u248A': // [DIGIT THREE FULL STOP] - *out = '3'; - *out = '.'; + *out ++ = '3'; + *out ++ = '.'; break; case L'\u2476': // [PARENTHESIZED DIGIT THREE] - *out = '('; - *out = '3'; - *out = ')'; + *out ++ = '('; + *out ++ = '3'; + *out ++ = ')'; break; case L'\u2074': // [SUPERSCRIPT FOUR] case L'\u2084': // [SUBSCRIPT FOUR] @@ -1465,16 +1469,16 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2783': // [DINGBAT CIRCLED SANS-SERIF DIGIT FOUR] case L'\u278D': // [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR] case L'\uFF14': // [FULLWIDTH DIGIT FOUR] - *out = '4'; + *out ++ = '4'; break; case L'\u248B': // [DIGIT FOUR FULL STOP] - *out = '4'; - *out = '.'; + *out ++ = '4'; + *out ++ = '.'; break; case L'\u2477': // [PARENTHESIZED DIGIT FOUR] - *out = '('; - *out = '4'; - *out = ')'; + *out ++ = '('; + *out ++ = '4'; + *out ++ = ')'; break; case L'\u2075': // [SUPERSCRIPT FIVE] case L'\u2085': // [SUBSCRIPT FIVE] @@ -1484,16 +1488,16 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2784': // [DINGBAT CIRCLED SANS-SERIF DIGIT FIVE] case L'\u278E': // [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE] case L'\uFF15': // [FULLWIDTH DIGIT FIVE] - *out = '5'; + *out ++ = '5'; break; case L'\u248C': // [DIGIT FIVE FULL STOP] - *out = '5'; - *out = '.'; + *out ++ = '5'; + *out ++ = '.'; break; case L'\u2478': // [PARENTHESIZED DIGIT FIVE] - *out = '('; - *out = '5'; - *out = ')'; + *out ++ = '('; + *out ++ = '5'; + *out ++ = ')'; break; case L'\u2076': // [SUPERSCRIPT SIX] case L'\u2086': // [SUBSCRIPT SIX] @@ -1503,16 +1507,16 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2785': // [DINGBAT CIRCLED SANS-SERIF DIGIT SIX] case L'\u278F': // [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX] case L'\uFF16': // [FULLWIDTH DIGIT SIX] - *out = '6'; + *out ++ = '6'; break; case L'\u248D': // [DIGIT SIX FULL STOP] - *out = '6'; - *out = '.'; + *out ++ = '6'; + *out ++ = '.'; break; case L'\u2479': // [PARENTHESIZED DIGIT SIX] - *out = '('; - *out = '6'; - *out = ')'; + *out ++ = '('; + *out ++ = '6'; + *out ++ = ')'; break; case L'\u2077': // [SUPERSCRIPT SEVEN] case L'\u2087': // [SUBSCRIPT SEVEN] @@ -1522,16 +1526,16 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2786': // [DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN] case L'\u2790': // [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN] case L'\uFF17': // [FULLWIDTH DIGIT SEVEN] - *out = '7'; + *out ++ = '7'; break; case L'\u248E': // [DIGIT SEVEN FULL STOP] - *out = '7'; - *out = '.'; + *out ++ = '7'; + *out ++ = '.'; break; case L'\u247A': // [PARENTHESIZED DIGIT SEVEN] - *out = '('; - *out = '7'; - *out = ')'; + *out ++ = '('; + *out ++ = '7'; + *out ++ = ')'; break; case L'\u2078': // [SUPERSCRIPT EIGHT] case L'\u2088': // [SUBSCRIPT EIGHT] @@ -1541,16 +1545,16 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2787': // [DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT] case L'\u2791': // [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT] case L'\uFF18': // [FULLWIDTH DIGIT EIGHT] - *out = '8'; + *out ++ = '8'; break; case L'\u248F': // [DIGIT EIGHT FULL STOP] - *out = '8'; - *out = '.'; + *out ++ = '8'; + *out ++ = '.'; break; case L'\u247B': // [PARENTHESIZED DIGIT EIGHT] - *out = '('; - *out = '8'; - *out = ')'; + *out ++ = '('; + *out ++ = '8'; + *out ++ = ')'; break; case L'\u2079': // [SUPERSCRIPT NINE] case L'\u2089': // [SUBSCRIPT NINE] @@ -1560,195 +1564,195 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u2788': // [DINGBAT CIRCLED SANS-SERIF DIGIT NINE] case L'\u2792': // [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE] case L'\uFF19': // [FULLWIDTH DIGIT NINE] - *out = '9'; + *out ++ = '9'; break; case L'\u2490': // [DIGIT NINE FULL STOP] - *out = '9'; - *out = '.'; + *out ++ = '9'; + *out ++ = '.'; break; case L'\u247C': // [PARENTHESIZED DIGIT NINE] - *out = '('; - *out = '9'; - *out = ')'; + *out ++ = '('; + *out ++ = '9'; + *out ++ = ')'; break; case L'\u2469': // [CIRCLED NUMBER TEN] case L'\u24FE': // [DOUBLE CIRCLED NUMBER TEN] case L'\u277F': // [DINGBAT NEGATIVE CIRCLED NUMBER TEN] case L'\u2789': // [DINGBAT CIRCLED SANS-SERIF NUMBER TEN] case L'\u2793': // [DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN] - *out = '1'; - *out = '0'; + *out ++ = '1'; + *out ++ = '0'; break; case L'\u2491': // [NUMBER TEN FULL STOP] - *out = '1'; - *out = '0'; - *out = '.'; + *out ++ = '1'; + *out ++ = '0'; + *out ++ = '.'; break; case L'\u247D': // [PARENTHESIZED NUMBER TEN] - *out = '('; - *out = '1'; - *out = '0'; - *out = ')'; + *out ++ = '('; + *out ++ = '1'; + *out ++ = '0'; + *out ++ = ')'; break; case L'\u246A': // [CIRCLED NUMBER ELEVEN] case L'\u24EB': // [NEGATIVE CIRCLED NUMBER ELEVEN] - *out = '1'; - *out = '1'; + *out ++ = '1'; + *out ++ = '1'; break; case L'\u2492': // [NUMBER ELEVEN FULL STOP] - *out = '1'; - *out = '1'; - *out = '.'; + *out ++ = '1'; + *out ++ = '1'; + *out ++ = '.'; break; case L'\u247E': // [PARENTHESIZED NUMBER ELEVEN] - *out = '('; - *out = '1'; - *out = '1'; - *out = ')'; + *out ++ = '('; + *out ++ = '1'; + *out ++ = '1'; + *out ++ = ')'; break; case L'\u246B': // [CIRCLED NUMBER TWELVE] case L'\u24EC': // [NEGATIVE CIRCLED NUMBER TWELVE] - *out = '1'; - *out = '2'; + *out ++ = '1'; + *out ++ = '2'; break; case L'\u2493': // [NUMBER TWELVE FULL STOP] - *out = '1'; - *out = '2'; - *out = '.'; + *out ++ = '1'; + *out ++ = '2'; + *out ++ = '.'; break; case L'\u247F': // [PARENTHESIZED NUMBER TWELVE] - *out = '('; - *out = '1'; - *out = '2'; - *out = ')'; + *out ++ = '('; + *out ++ = '1'; + *out ++ = '2'; + *out ++ = ')'; break; case L'\u246C': // [CIRCLED NUMBER THIRTEEN] case L'\u24ED': // [NEGATIVE CIRCLED NUMBER THIRTEEN] - *out = '1'; - *out = '3'; + *out ++ = '1'; + *out ++ = '3'; break; case L'\u2494': // [NUMBER THIRTEEN FULL STOP] - *out = '1'; - *out = '3'; - *out = '.'; + *out ++ = '1'; + *out ++ = '3'; + *out ++ = '.'; break; case L'\u2480': // [PARENTHESIZED NUMBER THIRTEEN] - *out = '('; - *out = '1'; - *out = '3'; - *out = ')'; + *out ++ = '('; + *out ++ = '1'; + *out ++ = '3'; + *out ++ = ')'; break; case L'\u246D': // [CIRCLED NUMBER FOURTEEN] case L'\u24EE': // [NEGATIVE CIRCLED NUMBER FOURTEEN] - *out = '1'; - *out = '4'; + *out ++ = '1'; + *out ++ = '4'; break; case L'\u2495': // [NUMBER FOURTEEN FULL STOP] - *out = '1'; - *out = '4'; - *out = '.'; + *out ++ = '1'; + *out ++ = '4'; + *out ++ = '.'; break; case L'\u2481': // [PARENTHESIZED NUMBER FOURTEEN] - *out = '('; - *out = '1'; - *out = '4'; - *out = ')'; + *out ++ = '('; + *out ++ = '1'; + *out ++ = '4'; + *out ++ = ')'; break; case L'\u246E': // [CIRCLED NUMBER FIFTEEN] case L'\u24EF': // [NEGATIVE CIRCLED NUMBER FIFTEEN] - *out = '1'; - *out = '5'; + *out ++ = '1'; + *out ++ = '5'; break; case L'\u2496': // [NUMBER FIFTEEN FULL STOP] - *out = '1'; - *out = '5'; - *out = '.'; + *out ++ = '1'; + *out ++ = '5'; + *out ++ = '.'; break; case L'\u2482': // [PARENTHESIZED NUMBER FIFTEEN] - *out = '('; - *out = '1'; - *out = '5'; - *out = ')'; + *out ++ = '('; + *out ++ = '1'; + *out ++ = '5'; + *out ++ = ')'; break; case L'\u246F': // [CIRCLED NUMBER SIXTEEN] case L'\u24F0': // [NEGATIVE CIRCLED NUMBER SIXTEEN] - *out = '1'; - *out = '6'; + *out ++ = '1'; + *out ++ = '6'; break; case L'\u2497': // [NUMBER SIXTEEN FULL STOP] - *out = '1'; - *out = '6'; - *out = '.'; + *out ++ = '1'; + *out ++ = '6'; + *out ++ = '.'; break; case L'\u2483': // [PARENTHESIZED NUMBER SIXTEEN] - *out = '('; - *out = '1'; - *out = '6'; - *out = ')'; + *out ++ = '('; + *out ++ = '1'; + *out ++ = '6'; + *out ++ = ')'; break; case L'\u2470': // [CIRCLED NUMBER SEVENTEEN] case L'\u24F1': // [NEGATIVE CIRCLED NUMBER SEVENTEEN] - *out = '1'; - *out = '7'; + *out ++ = '1'; + *out ++ = '7'; break; case L'\u2498': // [NUMBER SEVENTEEN FULL STOP] - *out = '1'; - *out = '7'; - *out = '.'; + *out ++ = '1'; + *out ++ = '7'; + *out ++ = '.'; break; case L'\u2484': // [PARENTHESIZED NUMBER SEVENTEEN] - *out = '('; - *out = '1'; - *out = '7'; - *out = ')'; + *out ++ = '('; + *out ++ = '1'; + *out ++ = '7'; + *out ++ = ')'; break; case L'\u2471': // [CIRCLED NUMBER EIGHTEEN] case L'\u24F2': // [NEGATIVE CIRCLED NUMBER EIGHTEEN] - *out = '1'; - *out = '8'; + *out ++ = '1'; + *out ++ = '8'; break; case L'\u2499': // [NUMBER EIGHTEEN FULL STOP] - *out = '1'; - *out = '8'; - *out = '.'; + *out ++ = '1'; + *out ++ = '8'; + *out ++ = '.'; break; case L'\u2485': // [PARENTHESIZED NUMBER EIGHTEEN] - *out = '('; - *out = '1'; - *out = '8'; - *out = ')'; + *out ++ = '('; + *out ++ = '1'; + *out ++ = '8'; + *out ++ = ')'; break; case L'\u2472': // [CIRCLED NUMBER NINETEEN] case L'\u24F3': // [NEGATIVE CIRCLED NUMBER NINETEEN] - *out = '1'; - *out = '9'; + *out ++ = '1'; + *out ++ = '9'; break; case L'\u249A': // [NUMBER NINETEEN FULL STOP] - *out = '1'; - *out = '9'; - *out = '.'; + *out ++ = '1'; + *out ++ = '9'; + *out ++ = '.'; break; case L'\u2486': // [PARENTHESIZED NUMBER NINETEEN] - *out = '('; - *out = '1'; - *out = '9'; - *out = ')'; + *out ++ = '('; + *out ++ = '1'; + *out ++ = '9'; + *out ++ = ')'; break; case L'\u2473': // [CIRCLED NUMBER TWENTY] case L'\u24F4': // [NEGATIVE CIRCLED NUMBER TWENTY] - *out = '2'; - *out = '0'; + *out ++ = '2'; + *out ++ = '0'; break; case L'\u249B': // [NUMBER TWENTY FULL STOP] - *out = '2'; - *out = '0'; - *out = '.'; + *out ++ = '2'; + *out ++ = '0'; + *out ++ = '.'; break; case L'\u2487': // [PARENTHESIZED NUMBER TWENTY] - *out = '('; - *out = '2'; - *out = '0'; - *out = ')'; + *out ++ = '('; + *out ++ = '2'; + *out ++ = '0'; + *out ++ = ')'; break; case L'\u00AB': // [LEFT-POINTING DOUBLE ANGLE QUOTATION MARK] case L'\u00BB': // [RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK] @@ -1762,7 +1766,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u276E': // [HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT] case L'\u276F': // [HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT] case L'\uFF02': // [FULLWIDTH QUOTATION MARK] - *out = '"'; + *out ++ = '"'; break; case L'\u2018': // [LEFT SINGLE QUOTATION MARK] case L'\u2019': // [RIGHT SINGLE QUOTATION MARK] @@ -1775,7 +1779,7 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u275B': // [HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT] case L'\u275C': // [HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT] case L'\uFF07': // [FULLWIDTH APOSTROPHE] - *out = '\''; + *out ++ = '\''; break; case L'\u2010': // [HYPHEN] case L'\u2011': // [NON-BREAKING HYPHEN] @@ -1785,149 +1789,150 @@ static void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) case L'\u207B': // [SUPERSCRIPT MINUS] case L'\u208B': // [SUBSCRIPT MINUS] case L'\uFF0D': // [FULLWIDTH HYPHEN-MINUS] - *out = '-'; + *out ++ = '-'; break; case L'\u2045': // [LEFT SQUARE BRACKET WITH QUILL] case L'\u2772': // [LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT] case L'\uFF3B': // [FULLWIDTH LEFT SQUARE BRACKET] - *out = '['; + *out ++ = '['; break; case L'\u2046': // [RIGHT SQUARE BRACKET WITH QUILL] case L'\u2773': // [LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT] case L'\uFF3D': // [FULLWIDTH RIGHT SQUARE BRACKET] - *out = ']'; + *out ++ = ']'; break; case L'\u207D': // [SUPERSCRIPT LEFT PARENTHESIS] case L'\u208D': // [SUBSCRIPT LEFT PARENTHESIS] case L'\u2768': // [MEDIUM LEFT PARENTHESIS ORNAMENT] case L'\u276A': // [MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT] case L'\uFF08': // [FULLWIDTH LEFT PARENTHESIS] - *out = '('; + *out ++ = '('; break; case L'\u2E28': // [LEFT DOUBLE PARENTHESIS] - *out = '('; - *out = '('; + *out ++ = '('; + *out ++ = '('; break; case L'\u207E': // [SUPERSCRIPT RIGHT PARENTHESIS] case L'\u208E': // [SUBSCRIPT RIGHT PARENTHESIS] case L'\u2769': // [MEDIUM RIGHT PARENTHESIS ORNAMENT] case L'\u276B': // [MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT] case L'\uFF09': // [FULLWIDTH RIGHT PARENTHESIS] - *out = ')'; + *out ++ = ')'; break; case L'\u2E29': // [RIGHT DOUBLE PARENTHESIS] - *out = ')'; - *out = ')'; + *out ++ = ')'; + *out ++ = ')'; break; case L'\u276C': // [MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT] case L'\u2770': // [HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT] case L'\uFF1C': // [FULLWIDTH LESS-THAN SIGN] - *out = '<'; + *out ++ = '<'; break; case L'\u276D': // [MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT] case L'\u2771': // [HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT] case L'\uFF1E': // [FULLWIDTH GREATER-THAN SIGN] - *out = '>'; + *out ++ = '>'; break; case L'\u2774': // [MEDIUM LEFT CURLY BRACKET ORNAMENT] case L'\uFF5B': // [FULLWIDTH LEFT CURLY BRACKET] - *out = '{'; + *out ++ = '{'; break; case L'\u2775': // [MEDIUM RIGHT CURLY BRACKET ORNAMENT] case L'\uFF5D': // [FULLWIDTH RIGHT CURLY BRACKET] - *out = '}'; + *out ++ = '}'; break; case L'\u207A': // [SUPERSCRIPT PLUS SIGN] case L'\u208A': // [SUBSCRIPT PLUS SIGN] case L'\uFF0B': // [FULLWIDTH PLUS SIGN] - *out = '+'; + *out ++ = '+'; break; case L'\u207C': // [SUPERSCRIPT EQUALS SIGN] case L'\u208C': // [SUBSCRIPT EQUALS SIGN] case L'\uFF1D': // [FULLWIDTH EQUALS SIGN] - *out = '='; + *out ++ = '='; break; case L'\uFF01': // [FULLWIDTH EXCLAMATION MARK] - *out = '!'; + *out ++ = '!'; break; case L'\u203C': // [DOUBLE EXCLAMATION MARK] - *out = '!'; - *out = '!'; + *out ++ = '!'; + *out ++ = '!'; break; case L'\u2049': // [EXCLAMATION QUESTION MARK] - *out = '!'; - *out = '?'; + *out ++ = '!'; + *out ++ = '?'; break; case L'\uFF03': // [FULLWIDTH NUMBER SIGN] - *out = '#'; + *out ++ = '#'; break; case L'\uFF04': // [FULLWIDTH DOLLAR SIGN] - *out = '$'; + *out ++ = '$'; break; case L'\u2052': // [COMMERCIAL MINUS SIGN] case L'\uFF05': // [FULLWIDTH PERCENT SIGN] - *out = '%'; + *out ++ = '%'; break; case L'\uFF06': // [FULLWIDTH AMPERSAND] - *out = '&'; + *out ++ = '&'; break; case L'\u204E': // [LOW ASTERISK] case L'\uFF0A': // [FULLWIDTH ASTERISK] - *out = '*'; + *out ++ = '*'; break; case L'\uFF0C': // [FULLWIDTH COMMA] - *out = ','; + *out ++ = ','; break; case L'\uFF0E': // [FULLWIDTH FULL STOP] - *out = '.'; + *out ++ = '.'; break; case L'\u2044': // [FRACTION SLASH] case L'\uFF0F': // [FULLWIDTH SOLIDUS] - *out = '/'; + *out ++ = '/'; break; case L'\uFF1A': // [FULLWIDTH COLON] - *out = ':'; + *out ++ = ':'; break; case L'\u204F': // [REVERSED SEMICOLON] case L'\uFF1B': // [FULLWIDTH SEMICOLON] - *out = ';'; + *out ++ = ';'; break; case L'\uFF1F': // [FULLWIDTH QUESTION MARK] - *out = '?'; + *out ++ = '?'; break; case L'\u2047': // [DOUBLE QUESTION MARK] - *out = '?'; - *out = '?'; + *out ++ = '?'; + *out ++ = '?'; break; case L'\u2048': // [QUESTION EXCLAMATION MARK] - *out = '?'; - *out = '!'; + *out ++ = '?'; + *out ++ = '!'; break; case L'\uFF20': // [FULLWIDTH COMMERCIAL AT] - *out = '@'; + *out ++ = '@'; break; case L'\uFF3C': // [FULLWIDTH REVERSE SOLIDUS] - *out = '\\'; + *out ++ = '\\'; break; case L'\u2038': // [CARET] case L'\uFF3E': // [FULLWIDTH CIRCUMFLEX ACCENT] - *out = '^'; + *out ++ = '^'; break; case L'\uFF3F': // [FULLWIDTH LOW LINE] - *out = '_'; + *out ++ = '_'; break; case L'\u2053': // [SWUNG DASH] case L'\uFF5E': // [FULLWIDTH TILDE] - *out = '~'; + *out ++ = '~'; break; default: - *out = c; + *out ++ = c; break; } } -} -namespace Slic3r { + // Return end of the output string. + return out; +} std::string fold_utf8_to_ascii(const std::string &src) { diff --git a/src/slic3r/Utils/ASCIIFolding.hpp b/src/slic3r/Utils/ASCIIFolding.hpp index 55f56482d..d6ad491ab 100644 --- a/src/slic3r/Utils/ASCIIFolding.hpp +++ b/src/slic3r/Utils/ASCIIFolding.hpp @@ -7,8 +7,22 @@ namespace Slic3r { // If possible, remove accents from accented latin characters. // This function is useful for generating file names to be processed by legacy firmwares. -extern std::string fold_utf8_to_ascii(const char *src); -extern std::string fold_utf8_to_ascii(const std::string &src); +extern std::string fold_utf8_to_ascii(const char *src); +extern std::string fold_utf8_to_ascii(const std::string &src); + +// Convert the input UNICODE character to a string of maximum 4 output ASCII characters. +// Return the end of the string written to the output. +// The output buffer must be at least 4 characters long. +extern char* fold_to_ascii(wchar_t c, char *out); + +template +void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out) +{ + char tmp[4]; + char *end = fold_to_ascii(c, tmp); + for (char *it = tmp; it != end; ++ it) + *out = *it; +} }; // namespace Slic3r From 568448fa7d0ed0ef4e6c13b4bba4939c9a49e200 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 4 May 2020 15:00:17 +0200 Subject: [PATCH 08/10] Fix font size on windows with wx3.1.3 --- src/slic3r/GUI/GUI_Utils.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index dc64141ba..5b23aeee0 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -134,11 +134,8 @@ wxFont get_default_font_for_dpi(int dpi) NONCLIENTMETRICS nm; memset(&nm, 0, sizeof(NONCLIENTMETRICS)); nm.cbSize = sizeof(NONCLIENTMETRICS); - if (SystemParametersInfoForDpi_fn(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &nm, 0, dpi)) { - wxNativeFontInfo info; - info.lf = nm.lfMessageFont; - return wxFont(info); - } + if (SystemParametersInfoForDpi_fn(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &nm, 0, dpi)) + return wxFont(wxNativeFontInfo(nm.lfMessageFont)); } // Then try to guesstimate the font DPI scaling on Windows 8. // Let's hope that the font returned by the SystemParametersInfo(), which is used by wxWidgets internally, makes sense. From 95cf8c2d30ca46b8aa34fdb95c42f7241adcce93 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 4 May 2020 16:18:20 +0200 Subject: [PATCH 09/10] Fuzzy matching of parameters in the search box: Fix of https://github.com/forrestthewoods/lib_fts/issues/21 finally applied, disabled CamelCase matching as we are not using CamelCase in Slicer parameters. --- src/slic3r/GUI/fts_fuzzy_match.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/fts_fuzzy_match.h b/src/slic3r/GUI/fts_fuzzy_match.h index c4ef62e3a..6d26965a1 100644 --- a/src/slic3r/GUI/fts_fuzzy_match.h +++ b/src/slic3r/GUI/fts_fuzzy_match.h @@ -93,7 +93,7 @@ namespace fts { // Recursion count is input / output to track the maximum depth reached. // Was given by reference &recursionCount, see discussion in https://github.com/forrestthewoods/lib_fts/issues/21 // int & recursionCount, - int & recursionCount, + int recursionCount, int recursionLimit) { // Count recursions @@ -202,10 +202,12 @@ namespace fts { if (i > 0 && currIdx == matches[i - 1] + 1) // Sequential outScore += sequential_bonus; +/* // Camel case char_type prev = strBegin[currIdx - 1]; if (std::islower(prev) && std::isupper(strBegin[currIdx])) outScore += camel_bonus; +*/ // Separator if (prev == '_' || prev == ' ') outScore += separator_bonus; From 80b684b4b7bb71406e4f9a95115ecb0c76863511 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 4 May 2020 21:22:56 +0200 Subject: [PATCH 10/10] Fuzzy matching: 1) Handle localized and English searches as equivalent. 2) Search the whole section : group : label string, still give precedence to just the label. --- src/slic3r/GUI/Preset.hpp | 1 + src/slic3r/GUI/Search.cpp | 186 ++++++++++++++++--------------- src/slic3r/GUI/Search.hpp | 19 +--- src/slic3r/GUI/fts_fuzzy_match.h | 33 ++++-- 4 files changed, 124 insertions(+), 115 deletions(-) diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index 9c2cebd50..dc0078091 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -125,6 +125,7 @@ public: TYPE_FILAMENT, TYPE_SLA_MATERIAL, TYPE_PRINTER, + TYPE_COUNT, }; Preset(Type type, const std::string &name, bool is_default = false) : type(type), is_default(is_default), name(name) {} diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index c6894bc5b..844d2244c 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -25,41 +25,18 @@ using GUI::into_u8; namespace Search { -static std::map NameByType = { - { Preset::TYPE_PRINT, L("Print") }, - { Preset::TYPE_FILAMENT, L("Filament") }, - { Preset::TYPE_SLA_MATERIAL, L("Material") }, - { Preset::TYPE_SLA_PRINT, L("Print") }, - { Preset::TYPE_PRINTER, L("Printer") } -}; - -FMFlag Option::fuzzy_match(wchar_t const* search_pattern, int& outScore, std::vector &out_matches) const +static const std::vector& NameByType() { - FMFlag flag = fmUndef; - int score; - - uint16_t matches[fts::max_matches + 1]; // +1 for the stopper - auto save_matches = [&matches, &out_matches]() { - size_t cnt = 0; - for (; matches[cnt] != fts::stopper; ++cnt); - out_matches.assign(matches, matches + cnt); - }; - if (fts::fuzzy_match(search_pattern, label_local.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmLabelLocal ; save_matches(); } - if (fts::fuzzy_match(search_pattern, group_local.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmGroupLocal ; save_matches(); } - if (fts::fuzzy_match(search_pattern, category_local.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmCategoryLocal; save_matches(); } - if (fts::fuzzy_match(search_pattern, opt_key.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmOptKey ; save_matches(); } - if (fts::fuzzy_match(search_pattern, label.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmLabel ; save_matches(); } - if (fts::fuzzy_match(search_pattern, group.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmGroup ; save_matches(); } - if (fts::fuzzy_match(search_pattern, category.c_str(), score, matches) && outScore < score) { - outScore = score; flag = fmCategory ; save_matches(); } - - return flag; + static std::vector data; + if (data.empty()) { + data.assign(Preset::TYPE_COUNT, std::wstring()); + data[Preset::TYPE_PRINT ] = _L("Print" ).ToStdWstring(); + data[Preset::TYPE_FILAMENT ] = _L("Filament" ).ToStdWstring(); + data[Preset::TYPE_SLA_MATERIAL ] = _L("Material" ).ToStdWstring(); + data[Preset::TYPE_SLA_PRINT ] = _L("Print" ).ToStdWstring(); + data[Preset::TYPE_PRINTER ] = _L("Printer" ).ToStdWstring(); + }; + return data; } void FoundOption::get_marked_label_and_tooltip(const char** label_, const char** tooltip_) const @@ -89,12 +66,16 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty return; wxString suffix; - if (gc.category == "Machine limits") + wxString suffix_local; + if (gc.category == "Machine limits") { suffix = opt_key.back()=='1' ? L("Stealth") : L("Normal"); + suffix_local = " " + _(suffix); + suffix = " " + suffix; + } if (!label.IsEmpty()) options.emplace_back(Option{ boost::nowide::widen(opt_key), type, - (label+ " " + suffix).ToStdWstring(), (_(label)+ " " + _(suffix)).ToStdWstring(), + (label + suffix).ToStdWstring(), (_(label) + suffix_local).ToStdWstring(), gc.group.ToStdWstring(), _(gc.group).ToStdWstring(), gc.category.ToStdWstring(), _(gc.category).ToStdWstring() }); }; @@ -125,7 +106,7 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty emplace(opt_key, label); else for (int i = 0; i < cnt; ++i) - emplace(opt_key + "#" + std::to_string(i), label); + emplace(opt_key + "[" + std::to_string(i) + "]", label); /*const GroupAndCategory& gc = groups_and_categories[opt_key]; if (gc.group.IsEmpty() || gc.category.IsEmpty()) @@ -179,6 +160,20 @@ bool OptionsSearcher::search() return search(search_line, true); } +static bool fuzzy_match(const std::wstring &search_pattern, const std::wstring &label, int& out_score, std::vector &out_matches) +{ + uint16_t matches[fts::max_matches + 1]; // +1 for the stopper + int score; + if (fts::fuzzy_match(search_pattern.c_str(), label.c_str(), score, matches)) { + size_t cnt = 0; + for (; matches[cnt] != fts::stopper; ++cnt); + out_matches.assign(matches, matches + cnt); + out_score = score; + return true; + } else + return false; +} + bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) { if (search_line == search && !force) @@ -187,78 +182,95 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) found.clear(); bool full_list = search.empty(); - wxString sep = " : "; + std::wstring sep = L" : "; + const std::vector& name_by_type = NameByType(); - auto get_label = [this, sep](const Option& opt) + auto get_label = [this, &name_by_type, &sep](const Option& opt) { - wxString label; - if (view_params.type) - label += _(NameByType[opt.type]) + sep; - if (view_params.category) - label += opt.category_local + sep; - if (view_params.group) - label += opt.group_local + sep; - label += opt.label_local; - return label; + std::wstring out; + const std::wstring *prev = nullptr; + for (const std::wstring * const s : { + view_params.type ? &(name_by_type[opt.type]) : nullptr, + view_params.category ? &opt.category_local : nullptr, + view_params.group ? &opt.group_local : nullptr, + &opt.label_local }) + if (s != nullptr && (prev == nullptr || *prev != *s)) { + if (! out.empty()) + out += sep; + out += *s; + prev = s; + } + return out; }; - auto get_tooltip = [this, sep](const Option& opt) + auto get_label_english = [this, &name_by_type, &sep](const Option& opt) { - return _(NameByType[opt.type]) + sep + + std::wstring out; + const std::wstring*prev = nullptr; + for (const std::wstring * const s : { + view_params.type ? &name_by_type[opt.type] : nullptr, + view_params.category ? &opt.category : nullptr, + view_params.group ? &opt.group : nullptr, + &opt.label }) + if (s != nullptr && (prev == nullptr || *prev != *s)) { + if (! out.empty()) + out += sep; + out += *s; + prev = s; + } + return out; + }; + + auto get_tooltip = [this, &name_by_type, &sep](const Option& opt) + { + return name_by_type[opt.type] + sep + opt.category_local + sep + opt.group_local + sep + opt.label_local; }; - std::vector matches; + std::vector matches, matches2; for (size_t i=0; i < options.size(); i++) { const Option &opt = options[i]; if (full_list) { std::string label = into_u8(get_label(opt)); - found.emplace_back(FoundOption{ label, label, into_u8(get_tooltip(opt)), i, fmUndef, 0 }); + found.emplace_back(FoundOption{ label, label, boost::nowide::narrow(get_tooltip(opt)), i, 0 }); continue; } - int score = 0; - FMFlag fuzzy_match_flag = opt.fuzzy_match(boost::nowide::widen(search).c_str(), score, matches); - if (fuzzy_match_flag != fmUndef) - { - wxString label; - - if (view_params.type) - label += _(NameByType[opt.type]) + sep; - if (fuzzy_match_flag == fmCategoryLocal) - label += mark_string(opt.category_local, matches) + sep; - else if (view_params.category) - label += opt.category_local + sep; - if (fuzzy_match_flag == fmGroupLocal) - label += mark_string(opt.group_local, matches) + sep; - else if (view_params.group) - label += opt.group_local + sep; - label += ((fuzzy_match_flag == fmLabelLocal) ? mark_string(opt.label_local, matches) : opt.label_local) + sep; - - switch (fuzzy_match_flag) { - case fmLabelLocal: - case fmGroupLocal: - case fmCategoryLocal: - break; - case fmLabel: label = get_label(opt) + "(" + mark_string(opt.label, matches) + ")"; break; - case fmGroup: label = get_label(opt) + "(" + mark_string(opt.group, matches) + ")"; break; - case fmCategory: label = get_label(opt) + "(" + mark_string(opt.category, matches) + ")"; break; - case fmOptKey: label = get_label(opt) + "(" + mark_string(opt.opt_key, matches) + ")"; break; - case fmUndef: assert(false); break; - } - - std::string label_plain = into_u8(label); - boost::erase_all(label_plain, std::wstring(1, wchar_t(ImGui::ColorMarkerStart))); - boost::erase_all(label_plain, std::wstring(1, wchar_t(ImGui::ColorMarkerEnd))); - found.emplace_back(FoundOption{ label_plain, into_u8(label), into_u8(get_tooltip(opt)), i, fuzzy_match_flag, score }); + std::wstring wsearch = boost::nowide::widen(search); + boost::trim_left(wsearch); + std::wstring label = get_label(opt); + std::wstring label_english = get_label_english(opt); + int score = std::numeric_limits::min(); + int score2; + matches.clear(); + fuzzy_match(wsearch, label, score, matches); + if (fuzzy_match(wsearch, opt.opt_key, score2, matches2) && score2 > score) { + for (fts::pos_type &pos : matches2) + pos += label.size() + 1; + label += L"(" + opt.opt_key + L")"; + append(matches, matches2); + score = score2; + } + if (fuzzy_match(wsearch, label_english, score2, matches2) && score2 > score) { + label = std::move(label_english); + matches = std::move(matches2); + score = score2; + } + if (score > std::numeric_limits::min()) { + label = mark_string(label, matches); + std::string label_u8 = into_u8(label); + std::string label_plain = label_u8; + boost::erase_all(label_plain, std::string(1, char(ImGui::ColorMarkerStart))); + boost::erase_all(label_plain, std::string(1, char(ImGui::ColorMarkerEnd))); + found.emplace_back(FoundOption{ label_plain, label_u8, boost::nowide::narrow(get_tooltip(opt)), i, score }); } } if (!full_list) sort_found(); - + if (search_line != search) search_line = search; diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index 1c24248ac..5fcb58f1e 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -36,20 +36,6 @@ struct GroupAndCategory { wxString category; }; -// fuzzy_match flag -// Sorted by the order of importance. The outputs will be sorted by the importance if the match value given by fuzzy_match is equal. -enum FMFlag -{ - fmUndef = 0, // didn't find - fmOptKey, - fmLabel, - fmLabelLocal, - fmGroup, - fmGroupLocal, - fmCategory, - fmCategoryLocal -}; - struct Option { bool operator<(const Option& other) const { return other.label > this->label; } bool operator>(const Option& other) const { return other.label < this->label; } @@ -64,8 +50,6 @@ struct Option { std::wstring group_local; std::wstring category; std::wstring category_local; - - FMFlag fuzzy_match(wchar_t const *search_pattern, int &outScore, std::vector &out_matches) const; }; struct FoundOption { @@ -74,7 +58,6 @@ struct FoundOption { std::string marked_label; std::string tooltip; size_t option_idx {0}; - FMFlag category {fmUndef}; int outScore {0}; // Returning pointers to contents of std::string members, to be used by ImGUI for rendering. @@ -106,7 +89,7 @@ class OptionsSearcher } void sort_found() { std::sort(found.begin(), found.end(), [](const FoundOption& f1, const FoundOption& f2) { - return f1.outScore > f2.outScore || (f1.outScore == f2.outScore && int(f1.category) < int(f2.category)); }); + return f1.outScore > f2.outScore || (f1.outScore == f2.outScore && f1.label < f2.label); }); }; size_t options_size() const { return options.size(); } diff --git a/src/slic3r/GUI/fts_fuzzy_match.h b/src/slic3r/GUI/fts_fuzzy_match.h index 6d26965a1..e692232cc 100644 --- a/src/slic3r/GUI/fts_fuzzy_match.h +++ b/src/slic3r/GUI/fts_fuzzy_match.h @@ -57,7 +57,7 @@ namespace fts { namespace fuzzy_internal { static bool fuzzy_match_recursive(const char_type * pattern, const char_type * str, int & outScore, const char_type * const strBegin, pos_type const * srcMatches, pos_type * newMatches, int nextMatch, - int & recursionCount, int recursionLimit); + int recursionCount, const int recursionLimit); static void copy_matches(pos_type * dst, pos_type const* src); } @@ -93,8 +93,8 @@ namespace fts { // Recursion count is input / output to track the maximum depth reached. // Was given by reference &recursionCount, see discussion in https://github.com/forrestthewoods/lib_fts/issues/21 // int & recursionCount, - int recursionCount, - int recursionLimit) + int recursionCount, + const int recursionLimit) { // Count recursions if (++ recursionCount >= recursionLimit) @@ -183,28 +183,41 @@ namespace fts { // Initialize score outScore = 100; + // Start of the first group that contains matches[0]. + const char_type *group_start = strBegin + matches[0]; + for (const char_type *c = group_start; c >= strBegin && *c != ':'; -- c) + if (*c != ' ' && *c != '\t') + group_start = c; + // Apply leading letter penalty or bonus. - outScore += matches[0] == 0 ? + outScore += matches[0] == int(group_start - strBegin) ? first_letter_bonus : - std::max(matches[0] * leading_letter_penalty, max_leading_letter_penalty); + std::max((matches[0] - int(group_start - strBegin)) * leading_letter_penalty, max_leading_letter_penalty); // Apply unmatched letters after the end penalty -// outScore += (int(str - strBegin) - matches[nextMatch-1] + 1) * unmatched_letter_penalty; +// outScore += (int(str - group_start) - matches[nextMatch-1] + 1) * unmatched_letter_penalty; // Apply unmatched penalty - outScore += (int(str - strBegin) - nextMatch) * unmatched_letter_penalty; + outScore += (int(str - group_start) - nextMatch) * unmatched_letter_penalty; // Apply ordering bonuses + int sequential_state = sequential_bonus; for (int i = 0; i < nextMatch; ++i) { pos_type currIdx = matches[i]; // Check for bonuses based on neighbor character value if (currIdx > 0) { - if (i > 0 && currIdx == matches[i - 1] + 1) + if (i > 0 && currIdx == matches[i - 1] + 1) { // Sequential - outScore += sequential_bonus; + outScore += sequential_state; + // Exponential grow of the sequential bonus. + sequential_state = std::min(5 * sequential_bonus, sequential_state + sequential_state / 3); + } else { + // Reset the sequential bonus exponential grow. + sequential_state = sequential_bonus; + } + char_type prev = strBegin[currIdx - 1]; /* // Camel case - char_type prev = strBegin[currIdx - 1]; if (std::islower(prev) && std::isupper(strBegin[currIdx])) outScore += camel_bonus; */