Release v0.6.28: typo correction, partial sync and selection diagnostics

This commit is contained in:
2026-11-02 11:19:13 +02:00
parent 738c81dce2
commit 55993c0f90
23 changed files with 676 additions and 76 deletions
+66 -1
View File
@@ -164,6 +164,24 @@ std::optional<AutoSuggestion> AutoCorrector::suggest(const std::wstring &typed,
auto sourceLanguage = layoutLanguage(layouts[source]);
if (!dictionaries.contains(sourceLanguage))
return {};
const std::wstring alphabet = sourceLanguage == L"en" ? L"abcdefghijklmnopqrstuvwxyz" :
sourceLanguage == L"uk" ? L"абвгґдеєжзиіїйклмнопрстуфхцчшщьюя" : L"абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
auto small = lower(word);
bool sameLanguage = safeAutoWord(word, settings.autoMinLength) &&
small.find_first_not_of(alphabet) == small.npos &&
std::find(settings.cycle.begin(), settings.cycle.end(), layouts[source].id) != settings.cycle.end();
auto sameLayout = [&](std::wstring text)
{
if (word != small) text[0] = upper(text.substr(0, 1))[0];
return AutoSuggestion{source, source, prefix + text + suffix};
};
if (settings.autoReplace && sameLanguage)
{
auto replacement = settings.autoReplacements.find(small);
if (replacement != settings.autoReplacements.end() &&
replacement->second.find_first_not_of(alphabet) == small.npos)
return sameLayout(replacement->second);
}
auto personal = [&](const std::wstring &language, const std::wstring &candidate)
{
auto found = settings.autoWords.find(language);
@@ -203,7 +221,54 @@ std::optional<AutoSuggestion> AutoCorrector::suggest(const std::wstring &typed,
(!candidate || priority(i) < priority(candidate->target)))
candidate = AutoSuggestion{source, i, prefix + converted + suffix};
}
return candidate;
if (candidate || matchedText || !settings.autoTypos || !sameLanguage || word.size() < 4)
return candidate;
// Generate only single edits. Stop at a second distinct dictionary hit,
// including rare hits: frequency limits must never hide ambiguity.
std::wstring found;
int foundRank = 0;
std::set<std::wstring> personalTargets;
if (auto it = settings.autoWords.find(sourceLanguage); it != settings.autoWords.end())
for (const auto &text : it->second) personalTargets.insert(lower(text));
auto consider = [&](const std::wstring &text)
{
if (text == small || text == found || text.size() < 4 || text.size() > 32) return true;
int rank = personalTargets.contains(text) ? 1 : dictionaries.at(sourceLanguage).rank(text);
if (!rank) return true;
if (!found.empty()) return false;
found = text;
foundRank = rank;
return true;
};
for (size_t i = 0; i < small.size(); ++i)
{
auto text = small;
text.erase(i, 1);
if (!consider(text)) return {};
if (i + 1 < small.size())
{
text = small;
std::swap(text[i], text[i + 1]);
if (!consider(text)) return {};
}
for (wchar_t c : alphabet)
{
text = small;
text[i] = c;
if (!consider(text)) return {};
}
}
if (small.size() < 32)
for (size_t i = 0; i <= small.size(); ++i)
for (wchar_t c : alphabet)
{
auto text = small;
text.insert(i, 1, c);
if (!consider(text)) return {};
}
if (!found.empty() && (settings.autoMaxRank == 0 || foundRank <= settings.autoMaxRank))
return sameLayout(found);
return {};
}
int matchAutoInput(const Run &run, const std::wstring &text, const std::vector<Layout> &layouts,
const Layout &physical, const Settings &settings)