Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions openless-all/app/scripts/windows-package-msvc.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const launcherPath = join(scriptsDir, "windows-package-msvc.cmd");
const ciWorkflowPath = join(repoRoot, ".github", "workflows", "release-tauri.yml");
const imeBuildPath = join(scriptsDir, "windows-ime-build.ps1");
const imeInstallSmokePath = join(scriptsDir, "windows-ime-install-smoke.ps1");
const realAsrInsertionSmokePath = join(scriptsDir, "windows-real-asr-insertion-smoke.ps1");
const imeRegisterPath = join(scriptsDir, "windows-ime-register.ps1");
const imeUnregisterPath = join(scriptsDir, "windows-ime-unregister.ps1");
const imeSolutionPath = join(appRoot, "windows-ime", "OpenLessIme.sln");
Expand All @@ -26,6 +27,7 @@ const launcher = readFileSync(launcherPath, "utf8");
const ciWorkflow = readFileSync(ciWorkflowPath, "utf8");
const imeBuild = readFileSync(imeBuildPath, "utf8");
const imeInstallSmoke = readFileSync(imeInstallSmokePath, "utf8");
const realAsrInsertionSmoke = readFileSync(realAsrInsertionSmokePath, "utf8");
const imeRegister = readFileSync(imeRegisterPath, "utf8");
const imeUnregister = readFileSync(imeUnregisterPath, "utf8");
const imeSolution = readFileSync(imeSolutionPath, "utf8");
Expand Down Expand Up @@ -105,8 +107,11 @@ assert.match(imeTextService, /TF_ES_ASYNC \| TF_ES_READWRITE/, "IME should retry
assert.match(imeTextService, /WaitForSingleObject/, "IME pipe submit should wait for async edit-session completion");
assert.match(imeEditSession, /SetEvent/, "IME edit session should signal async completion back to the pipe submitter");
assert.match(imeEditSession, /Collapse\(edit_cookie, TF_ANCHOR_END\)/, "IME should collapse the committed range to its end after insertion");
assert.match(imeEditSession, /SetSelection\(edit_cookie, 1, &selection\)/, "IME should move the caret to the end of inserted text");
assert.match(imeEditSession, /TF_AE_END/, "IME should make the end of the committed text the active selection end");
assert.match(imeEditSession, /SetSelection\(edit_cookie, 1, &selection\)/, "IME should move the caret to the end of inserted text");
assert.match(imeEditSession, /TF_AE_END/, "IME should make the end of the committed text the active selection end");
const insertAtSelectionCalls = imeEditSession.match(/\bInsertTextAtSelection\s*\(/g) ?? [];
assert.equal(insertAtSelectionCalls.length, 1, "IME should submit the dictated text to the host exactly once");
assert.doesNotMatch(imeEditSession, /TF_IAS_QUERYONLY/, "IME should not preflight the full text through host text stores before committing it");

assert.match(wixFragment, /DirectoryRef Id="INSTALLDIR"/, "WiX fragment should install into the app directory");
assert.match(wixFragment, /Component Id="OpenLessImeDllX64Component"/, "WiX fragment should define the x64 TSF DLL component");
Expand Down Expand Up @@ -145,6 +150,7 @@ assert.match(imeInstallSmoke, /Join-ProcessArguments/, "install smoke should quo
assert.match(imeInstallSmoke, /\$commandLine = Join-ProcessArguments \$ArgumentList/, "install smoke should build a single quoted command line");
assert.match(imeInstallSmoke, /Start-Process -FilePath \$FilePath -ArgumentList \$commandLine/, "install smoke should pass a single quoted command line to Start-Process");
assert.match(imeInstallSmoke, /OpenLessImeSubmit/, "install smoke should preserve TSF backend context");
assert.match(realAsrInsertionSmoke, /\$targetTextForComparison -cne \$finalTextForComparison/, "real insertion smoke should reject duplicate or partial text in initially empty targets");
assert.match(imeInstallSmoke, /Software\\Classes\\CLSID\\\{6B9F3F4F-5EE7-42D6-9C61-9F80B03A5D7D\}\\InprocServer32/, "install smoke should check x64 COM registration");
assert.match(imeInstallSmoke, /Software\\WOW6432Node\\Classes\\CLSID\\\{6B9F3F4F-5EE7-42D6-9C61-9F80B03A5D7D\}\\InprocServer32/, "install smoke should check x86 COM registration");
assert.match(imeInstallSmoke, /LanguageProfile\\0x00000804\\\{9B5F5E04-23F6-47DA-9A26-D221F6C3F02E\}/, "install smoke should check the TSF language profile");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,13 @@ try {
if ([string]::IsNullOrWhiteSpace($targetText)) {
throw "$Target readback is empty."
}
if (-not $targetText.Contains($latest.finalText)) {
$targetTextForComparison = $targetText.Replace("`r`n", "`n")
$finalTextForComparison = ([string]$latest.finalText).Replace("`r`n", "`n")
$targetStartsEmpty = $Target -in @("notepad", "browser", "win32edit")
if ($targetStartsEmpty -and $targetTextForComparison -cne $finalTextForComparison) {
throw "$Target readback does not exactly match latest finalText in an initially empty target; duplicate or partial insertion detected (expected length=$($finalTextForComparison.Length), actual length=$($targetTextForComparison.Length))."
}
if (-not $targetStartsEmpty -and -not $targetText.Contains($latest.finalText)) {
if ($targetText.Contains($clipboardSentinel)) {
throw "$Target readback contains the pre-dictation clipboard sentinel instead of latest finalText."
}
Expand Down
48 changes: 20 additions & 28 deletions openless-all/app/windows-ime/src/edit_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,38 +101,30 @@ HRESULT OpenLessEditSession::InsertText(TfEditCookie edit_cookie) {
return hr;
}

ITfRange* query_range = nullptr;
hr = insert_at_selection->InsertTextAtSelection(
edit_cookie, TF_IAS_QUERYONLY, text_.c_str(),
static_cast<LONG>(text_.size()), &query_range);
if (query_range != nullptr) {
query_range->Release();
query_range = nullptr;
}

if (SUCCEEDED(hr) && cancellation_ && cancellation_->load()) {
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
if (cancellation_ && cancellation_->load()) {
insert_at_selection->Release();
return HRESULT_FROM_WIN32(ERROR_CANCELLED);
}

if (SUCCEEDED(hr)) {
ITfRange* committed_range = nullptr;
hr = insert_at_selection->InsertTextAtSelection(
edit_cookie, 0, text_.c_str(), static_cast<LONG>(text_.size()),
&committed_range);
if (committed_range != nullptr) {
if (SUCCEEDED(hr)) {
const HRESULT collapse_hr =
committed_range->Collapse(edit_cookie, TF_ANCHOR_END);
if (SUCCEEDED(collapse_hr)) {
TF_SELECTION selection = {};
selection.range = committed_range;
selection.style.ase = TF_AE_END;
selection.style.fInterimChar = FALSE;
(void)context_->SetSelection(edit_cookie, 1, &selection);
}
// Commit in a single call. Some Chromium-backed text stores surface a
// full-text QUERYONLY preflight as an edit and then duplicate the real commit.
ITfRange* committed_range = nullptr;
hr = insert_at_selection->InsertTextAtSelection(
edit_cookie, 0, text_.c_str(), static_cast<LONG>(text_.size()),
&committed_range);
if (committed_range != nullptr) {
if (SUCCEEDED(hr)) {
const HRESULT collapse_hr =
committed_range->Collapse(edit_cookie, TF_ANCHOR_END);
if (SUCCEEDED(collapse_hr)) {
TF_SELECTION selection = {};
selection.range = committed_range;
selection.style.ase = TF_AE_END;
selection.style.fInterimChar = FALSE;
(void)context_->SetSelection(edit_cookie, 1, &selection);
}
committed_range->Release();
}
committed_range->Release();
}

insert_at_selection->Release();
Expand Down