feat: commit dictated text through TSF edit sessions

This commit is contained in:
millionart committed 2026-05-01 20:34:15 +08:00
1 parent 1ab6f7b7ab
commit 0f28bb1a3e
4 files changed
+151 -2

No files matched your search

@@ -76,11 +76,13 @@
<ItemGroup>
<ClCompile Include="src\class_factory.cpp" />
<ClCompile Include="src\dllmain.cpp" />
<ClCompile Include="src\edit_session.cpp" />
<ClCompile Include="src\registry.cpp" />
<ClCompile Include="src\text_service.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\class_factory.h" />
<ClInclude Include="src\edit_session.h" />
<ClInclude Include="src\guids.h" />
<ClInclude Include="src\registry.h" />
<ClInclude Include="src\text_service.h" />
@@ -0,0 +1,81 @@
#include "edit_session.h"
#include <utility>
OpenLessEditSession::OpenLessEditSession(ITfContext* context,
std::wstring text)
: context_(context), text_(std::move(text)) {
if (context_ != nullptr) {
context_->AddRef();
}
}
OpenLessEditSession::~OpenLessEditSession() {
if (context_ != nullptr) {
context_->Release();
context_ = nullptr;
}
}
STDMETHODIMP OpenLessEditSession::QueryInterface(REFIID iid, void** object) {
if (object == nullptr) {
return E_POINTER;
}
*object = nullptr;
if (iid == IID_IUnknown || iid == IID_ITfEditSession) {
*object = static_cast<ITfEditSession*>(this);
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) OpenLessEditSession::AddRef() {
return static_cast<ULONG>(InterlockedIncrement(&ref_count_));
}
STDMETHODIMP_(ULONG) OpenLessEditSession::Release() {
const ULONG count = static_cast<ULONG>(InterlockedDecrement(&ref_count_));
if (count == 0) {
delete this;
}
return count;
}
STDMETHODIMP OpenLessEditSession::DoEditSession(TfEditCookie edit_cookie) {
if (context_ == nullptr) {
return E_UNEXPECTED;
}
ITfInsertAtSelection* insert_at_selection = nullptr;
HRESULT hr = context_->QueryInterface(IID_ITfInsertAtSelection,
reinterpret_cast<void**>(
&insert_at_selection));
if (FAILED(hr)) {
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)) {
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) {
committed_range->Release();
}
}
insert_at_selection->Release();
return hr;
}
@@ -0,0 +1,23 @@
#pragma once
#include <msctf.h>
#include <string>
#include <windows.h>
class OpenLessEditSession final : public ITfEditSession {
public:
OpenLessEditSession(ITfContext* context, std::wstring text);
OpenLessEditSession(const OpenLessEditSession&) = delete;
OpenLessEditSession& operator=(const OpenLessEditSession&) = delete;
~OpenLessEditSession() override;
STDMETHODIMP QueryInterface(REFIID iid, void** object) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
STDMETHODIMP DoEditSession(TfEditCookie edit_cookie) override;
private:
LONG ref_count_ = 1;
ITfContext* context_ = nullptr;
std::wstring text_;
};
@@ -1,5 +1,9 @@
#include "text_service.h"
#include <new>
#include "edit_session.h"
extern LONG g_object_count;
OpenLessTextService::OpenLessTextService() {
@@ -84,8 +88,47 @@ HRESULT OpenLessTextService::SubmitTextFromPipe(
const std::wstring& session_id,
const std::wstring& text) {
UNREFERENCED_PARAMETER(session_id);
UNREFERENCED_PARAMETER(text);
return E_NOTIMPL;
if (thread_mgr_ == nullptr || client_id_ == TF_CLIENTID_NULL) {
return E_UNEXPECTED;
}
ITfDocumentMgr* document_mgr = nullptr;
HRESULT hr = thread_mgr_->GetFocus(&document_mgr);
if (FAILED(hr)) {
return hr;
}
if (document_mgr == nullptr) {
return E_FAIL;
}
ITfContext* context = nullptr;
hr = document_mgr->GetTop(&context);
document_mgr->Release();
document_mgr = nullptr;
if (FAILED(hr)) {
return hr;
}
if (context == nullptr) {
return E_FAIL;
}
auto* session = new (std::nothrow) OpenLessEditSession(context, text);
if (session == nullptr) {
context->Release();
return E_OUTOFMEMORY;
}
HRESULT edit_result = S_OK;
hr = context->RequestEditSession(client_id_, session,
TF_ES_SYNC | TF_ES_READWRITE, &edit_result);
session->Release();
context->Release();
if (FAILED(hr)) {
return hr;
}
return edit_result;
}
HRESULT OpenLessTextService::StartIpcServer() {