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
+58 -2
View File
@@ -111,8 +111,8 @@ int main(int argc, char **argv)
require(diagnostics.record(L"later-failure", true, 30500).find(L"expired-context") == std::wstring::npos,
"context expires after thirty seconds");
diagnostics.record(L"oldest-context", false, 30600);
for (int i = 0; i < 40; ++i)
diagnostics.record(L"bounded-context", false, 30600 + i);
for (int i = 0; i < 128; ++i)
diagnostics.record(L"bounded-context", false, 30600);
require(diagnostics.record(L"bounded-failure", true, 30700).find(L"oldest-context") == std::wstring::npos,
"context buffer has a fixed size");
diagnostics.record(L"disabled-context", false, 30800);
@@ -217,6 +217,13 @@ int main(int argc, char **argv)
require(!autoSettings.autoCorrect, "auto-correction defaults off");
autoSettings.cycle = {L"en", L"uk"};
corrector.configure(L"dictionaries", autoSettings.autoLanguages);
{
auto options = autoSettings;
options.autoTypos = true;
auto typo = corrector.suggest(L"првиіт ", 2, layouts, options);
require(typo && typo->text == L"привіт " && typo->target == 2,
"bundled Ukrainian dictionary corrects a common transposition");
}
auto suggested = corrector.suggest(L"ghbdsn ", 0, layouts, autoSettings);
require(suggested && suggested->text == L"привіт " && suggested->target == 2,
"dictionary corrects Ukrainian and retains trailing space");
@@ -497,6 +504,45 @@ int main(int argc, char **argv)
auto correction = edited.suggest(L"руддщ ", 2, layouts, options);
require(correction && correction->text == L"hello ",
"unsorted dictionaries and duplicates load using lowest duplicate rank");
require(!edited.suggest(L"hlelo ", 0, layouts, options), "spelling correction defaults off");
options.autoTypos = true;
for (auto typo : {L"hlelo ", L"helo ", L"helllo ", L"hezlo "})
{
correction = edited.suggest(typo, 0, layouts, options);
require(correction && correction->text == L"hello " && correction->target == 0,
"single transposition, deletion, insertion and substitution retain the source layout");
}
correction = edited.suggest(L"(Hlelo), ", 0, layouts, options);
require(correction && correction->text == L"(Hello), ", "typos retain case and punctuation");
for (auto text : {L"hello ", L"HLELO ", L"hLelo ", L"h3llo ", L"h.llo ", L"hlo ", L"hello@example.com "})
require(!edited.suggest(text, 0, layouts, options), "known or unsafe typo input is unchanged");
options.autoWords[L"en"] = {L"hella"};
require(!edited.suggest(L"hell ", 0, layouts, options), "personal dictionary alternatives create ambiguity");
options.autoWords.clear();
options.autoReplacements = {{L"hlelo", L"greeting"}, {L"hello", L"greetings"}, {L"првиіт", L"привіт"}};
options.autoReplace = true;
correction = edited.suggest(L"Hlelo! ", 0, layouts, options);
require(correction && correction->text == L"Greeting! ", "explicit replacements override spelling guesses");
correction = edited.suggest(L"hello ", 0, layouts, options);
require(correction && correction->text == L"greetings ", "explicit replacements can override known words");
correction = edited.suggest(L"Првиіт ", 2, layouts, options);
require(correction && correction->text == L"Привіт " && correction->target == 2,
"Ukrainian replacements work without dictionary membership");
options.autoNever = {L"hlelo"};
require(!edited.suggest(L"hlelo ", 0, layouts, options), "never-correct overrides explicit pairs");
options.autoNever.clear();
edited.reject(L"(Hlelo)! ", options);
require(!edited.suggest(L"hlelo ", 0, layouts, options), "undo rejection suppresses custom replacements");
options.autoReplace = false;
options.autoWords[L"uk"] = {L"привіт"};
correction = edited.suggest(L"првиіт ", 2, layouts, options);
require(correction && correction->text == L"привіт ", "Ukrainian personal words are typo targets");
options.cycle = {L"en"};
require(!edited.suggest(L"првиіт ", 2, layouts, options), "disabled source layouts do not correct typos");
put(L"en.txt", L"hello\t1\nhella\t99999\n");
ks::AutoCorrector ambiguous;
ambiguous.configure(folder, {L"en", L"uk"});
require(!ambiguous.suggest(L"hell ", 0, layouts, options), "frequency filter does not conceal ambiguous typo targets");
}
ks::Pixels pixels{2, 1, {10, 20, 30, 255, 40, 50, 60, 255}};
ks::Clip firstImage{{ks::imageDib(pixels), {0, L"Ole Private Data", {1, 2, 3}}}};
@@ -542,6 +588,8 @@ int main(int argc, char **argv)
settings.writeLog = false;
settings.elevatedAutostart = true;
settings.autoCorrect = true;
settings.autoReplace = settings.autoTypos = true;
settings.autoReplacements = {{L"првиіт", L"привіт"}, {L"helo", L"hello"}};
settings.autoMaxRank = 10000;
settings.autoMinLength = 5;
settings.autoPrefixChars = L"(#";
@@ -554,6 +602,14 @@ int main(int argc, char **argv)
ks::saveSettings(temp / L"settings.json", settings);
auto restored = ks::loadSettings(temp / L"settings.json", {});
require(restored.autoWords == settings.autoWords, "personal dictionaries survive settings roundtrip");
require(restored.autoReplace && restored.autoTypos && restored.autoReplacements == settings.autoReplacements,
"typo options and Unicode pairs survive settings roundtrip");
for (const auto &pair : {std::pair{L"hello", L"привіт"}, {L"hello", L"hello"}, {L"hi!", L"hello"}})
{
auto invalid = settings;
invalid.autoReplacements = {{pair.first, pair.second}};
rejects([&] { ks::validateSettings(invalid); }, "invalid replacement pairs rejected");
}
auto invalidWords = settings;
invalidWords.autoWords[L"uk"] = {L"hello"};
rejects([&] { ks::validateSettings(invalidWords); }, "personal dictionary rejects wrong language letters");