From 8cb289ea8c460bd0bbd490e9507572ee2db61d97 Mon Sep 17 00:00:00 2001 From: millionart Date: Sun, 3 May 2026 23:53:29 +0800 Subject: [PATCH] fix: move Word caret after IME commit --- openless-all/app/scripts/windows-ime-register.ps1 | 3 ++- .../app/scripts/windows-package-msvc.test.mjs | 5 +++++ openless-all/app/windows-ime/src/edit_session.cpp | 11 +++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/openless-all/app/scripts/windows-ime-register.ps1 b/openless-all/app/scripts/windows-ime-register.ps1 index 47edd880..f252ccb0 100644 --- a/openless-all/app/scripts/windows-ime-register.ps1 +++ b/openless-all/app/scripts/windows-ime-register.ps1 @@ -34,7 +34,8 @@ function Test-IsAdministrator { } $appRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path -$stagingRoot = Join-Path $appRoot "src-tauri\target\windows-ime-register" +$stagingStamp = "{0:yyyyMMddHHmmss}-{1}" -f (Get-Date), $PID +$stagingRoot = Join-Path $appRoot "src-tauri\target\windows-ime-register\$stagingStamp" function Get-DllPath { param( diff --git a/openless-all/app/scripts/windows-package-msvc.test.mjs b/openless-all/app/scripts/windows-package-msvc.test.mjs index 7c578601..e5b4ef3d 100644 --- a/openless-all/app/scripts/windows-package-msvc.test.mjs +++ b/openless-all/app/scripts/windows-package-msvc.test.mjs @@ -67,6 +67,8 @@ assert.match(imeBuild, /\/p:IntDir=/, "IME build should pass IntDir to MSBuild") assert.match(imeRegister, /windows-ime-build\.ps1/, "IME register should build before registering"); assert.doesNotMatch(imeRegister, /if \(-not \(Test-Path \$dll\)\)/, "IME register must rebuild stale DLLs, not only missing DLLs"); assert.match(imeRegister, /windows-ime-register/, "IME register should use a side-by-side staging output to avoid locked registered DLLs"); +assert.match(imeRegister, /Get-Date/, "IME register should create a fresh staging output for each registration run"); +assert.match(imeRegister, /\$PID/, "IME register should include the process id in the staging output to avoid path reuse"); assert.match(imeRegister, /-OutputDirectory/, "IME register should pass a staging output directory to the build script"); assert.match(imeRegister, /-IntermediateDirectory/, "IME register should pass a staging intermediate directory to the build script"); @@ -82,6 +84,9 @@ assert.match(imeTextService, /TF_E_SYNCHRONOUS/, "IME should detect hosts like W assert.match(imeTextService, /TF_ES_ASYNC \| TF_ES_READWRITE/, "IME should retry Word-hosted commits with an async edit session"); 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(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"); diff --git a/openless-all/app/windows-ime/src/edit_session.cpp b/openless-all/app/windows-ime/src/edit_session.cpp index 79261886..6e1a32f1 100644 --- a/openless-all/app/windows-ime/src/edit_session.cpp +++ b/openless-all/app/windows-ime/src/edit_session.cpp @@ -105,6 +105,17 @@ HRESULT OpenLessEditSession::InsertText(TfEditCookie edit_cookie) { edit_cookie, 0, text_.c_str(), static_cast(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(); } }