mirror of
https://github.com/mii443/vrclipboard-ime-gui.git
synced 2025-08-22 16:15:32 +00:00
add tracing
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vrclipboard-ime-gui",
|
"name": "vrclipboard-ime-gui",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.7.0",
|
"version": "1.8.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
10
release.json
10
release.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"url": "https://r2-vrime.mii.dev/releases/vrclipboard-ime-gui_1.6.0_x64_ja-JP.msi.zip",
|
"url": "https://r2-vrime.mii.dev/releases/vrclipboard-ime-gui_1.7.0_x64_ja-JP.msi.zip",
|
||||||
"version": "1.6.0",
|
"version": "1.7.0",
|
||||||
"notes": "ベータ機能 Text Services Framework を使用した再変換を追加",
|
"notes": "Text Services Framework 再変換のバグ修正および仕様変更",
|
||||||
"pub_date": "2024-09-23T07:59:37+00:00",
|
"pub_date": "2024-09-25T12:10:54+00:00",
|
||||||
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVTTStkVlpUR0NpcVdnb0phVVBYd2dYWjlHbXdCRGloZzJaZ21aejR2UjgyYXlkWnpvTUI1aERjellPc2lYVHhqaWUrWmdQUXZTMkR4MkVPMWdROHM3RlN2cUFDV3ZBNFE0PQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzI3MDc4MzExCWZpbGU6dnJjbGlwYm9hcmQtaW1lLWd1aV8xLjYuMF94NjRfamEtSlAubXNpLnppcAo2WXhDUWlUNkNtamJmdkloaXRBUEEzdmhvTnJubDQ3Q3dNaXk0OGZISnU4WmM0eVp1NG9KMS9RUUgyY25pd01uWGlCVnpYUDlVc0tIYVlVRU10NzhBdz09Cg=="
|
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVTTStkVlpUR0NpcVlQZFdBNEJzYk1QbVl0WUx3YU03SG1KTlJHT05jbVJwSFJ5UFJOaWNxRUd2bnlTOG1uYlo4VGhPc0xieXJGcUk2cDNDdzU4dEVZeVZhRU5QL2tKS3dNPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzI3MjY2NDI3CWZpbGU6dnJjbGlwYm9hcmQtaW1lLWd1aV8xLjcuMF94NjRfamEtSlAubXNpLnppcApVSyt0VDVyRWl4K1IySTlmNEZWRDEvMVVpRzc2TEdhdyt0WmpvN3FCQm1wN1hpeXZDaUo5WmltM1dsV25raHRkRXNmbW9xazdRb25na29sL1d6MVhCdz09Cg=="
|
||||||
}
|
}
|
||||||
|
42
src-tauri/Cargo.lock
generated
42
src-tauri/Cargo.lock
generated
@ -1987,6 +1987,15 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_threads"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc"
|
name = "objc"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
@ -3535,7 +3544,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa 1.0.11",
|
"itoa 1.0.11",
|
||||||
|
"libc",
|
||||||
"num-conv",
|
"num-conv",
|
||||||
|
"num_threads",
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
"serde",
|
"serde",
|
||||||
"time-core",
|
"time-core",
|
||||||
@ -3706,6 +3717,18 @@ dependencies = [
|
|||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-appender"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"thiserror",
|
||||||
|
"time",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-attributes"
|
name = "tracing-attributes"
|
||||||
version = "0.1.27"
|
version = "0.1.27"
|
||||||
@ -3738,6 +3761,16 @@ dependencies = [
|
|||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-serde"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-subscriber"
|
name = "tracing-subscriber"
|
||||||
version = "0.3.18"
|
version = "0.3.18"
|
||||||
@ -3748,12 +3781,16 @@ dependencies = [
|
|||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"regex",
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"sharded-slab",
|
"sharded-slab",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
|
"time",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log",
|
"tracing-log",
|
||||||
|
"tracing-serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3866,7 +3903,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vrclipboard-ime-gui"
|
name = "vrclipboard-ime-gui"
|
||||||
version = "1.6.0"
|
version = "1.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"calc",
|
"calc",
|
||||||
@ -3884,6 +3921,9 @@ dependencies = [
|
|||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
|
"tracing",
|
||||||
|
"tracing-appender",
|
||||||
|
"tracing-subscriber",
|
||||||
"windows 0.58.0",
|
"windows 0.58.0",
|
||||||
"windows-core 0.58.0",
|
"windows-core 0.58.0",
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "vrclipboard-ime-gui"
|
name = "vrclipboard-ime-gui"
|
||||||
version = "1.6.0"
|
version = "1.8.0"
|
||||||
description = "VRClipboard IME"
|
description = "VRClipboard IME"
|
||||||
authors = ["mii"]
|
authors = ["mii"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@ -27,6 +27,12 @@ once_cell = "1.19.0"
|
|||||||
rosc = "~0.10"
|
rosc = "~0.10"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
windows-core = "0.58.0"
|
windows-core = "0.58.0"
|
||||||
|
tracing = "0.1"
|
||||||
|
tracing-appender = "0.2"
|
||||||
|
|
||||||
|
[dependencies.tracing-subscriber]
|
||||||
|
version = "0.3.16"
|
||||||
|
features = ["env-filter", "fmt", "json", "local-time", "time"]
|
||||||
|
|
||||||
[dependencies.windows]
|
[dependencies.windows]
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use tracing::debug;
|
||||||
use windows::Win32::System::Com::{CoInitialize, CoUninitialize};
|
use windows::Win32::System::Com::{CoInitialize, CoUninitialize};
|
||||||
|
|
||||||
pub struct Com;
|
pub struct Com;
|
||||||
|
|
||||||
impl Drop for Com {
|
impl Drop for Com {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { CoUninitialize() };
|
debug!("Dropping Com instance");
|
||||||
|
unsafe {
|
||||||
|
CoUninitialize();
|
||||||
|
debug!("CoUninitialize called");
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ use serde::Serialize;
|
|||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tauri::State;
|
use tauri::State;
|
||||||
|
use tracing::{info, error, debug, trace};
|
||||||
|
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
|
|
||||||
@ -64,56 +65,71 @@ impl Default for OnCopyMode {
|
|||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn load() -> Result<Config> {
|
pub fn load() -> Result<Config> {
|
||||||
|
debug!("Loading config");
|
||||||
std::fs::create_dir_all(Self::get_path()).unwrap();
|
std::fs::create_dir_all(Self::get_path()).unwrap();
|
||||||
|
|
||||||
if !Path::new(&Self::get_path().join("config.yaml")).exists() {
|
let config_path = Self::get_path().join("config.yaml");
|
||||||
|
if !Path::new(&config_path).exists() {
|
||||||
|
info!("Config file not found, generating default");
|
||||||
Self::generate_default_config()?;
|
Self::generate_default_config()?;
|
||||||
}
|
}
|
||||||
let mut file = File::open(&Self::get_path().join("config.yaml"))?;
|
let mut file = File::open(&config_path)?;
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
file.read_to_string(&mut contents)?;
|
file.read_to_string(&mut contents)?;
|
||||||
|
trace!("Raw config contents: {}", contents);
|
||||||
let config: Config = serde_yaml::from_str(&contents)?;
|
let config: Config = serde_yaml::from_str(&contents)?;
|
||||||
|
debug!("Config loaded successfully");
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&self, state: State<AppState>) -> Result<(), String> {
|
pub fn save(&self, state: State<AppState>) -> Result<(), String> {
|
||||||
|
debug!("Saving config");
|
||||||
std::fs::create_dir_all(Self::get_path()).unwrap();
|
std::fs::create_dir_all(Self::get_path()).unwrap();
|
||||||
|
|
||||||
let mut file = match File::create(&Self::get_path().join("config.yaml")) {
|
let config_path = Self::get_path().join("config.yaml");
|
||||||
|
let mut file = match File::create(&config_path) {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Err: {:?}", e);
|
error!("Failed to create config file: {}", e);
|
||||||
return Err(format!("Failed to create config file: {}", e))
|
return Err(format!("Failed to create config file: {}", e))
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
match serde_yaml::to_string(&self) {
|
match serde_yaml::to_string(&self) {
|
||||||
Ok(yaml) => {
|
Ok(yaml) => {
|
||||||
|
trace!("Config to be saved: {}", yaml);
|
||||||
if let Err(e) = file.write_all(yaml.as_bytes()) {
|
if let Err(e) = file.write_all(yaml.as_bytes()) {
|
||||||
println!("Err: {:?}", e);
|
error!("Failed to write config: {}", e);
|
||||||
return Err(format!("Failed to write config: {}", e));
|
return Err(format!("Failed to write config: {}", e));
|
||||||
}
|
}
|
||||||
let mut app_config = state.config.lock().unwrap();
|
let mut app_config = state.config.lock().unwrap();
|
||||||
*app_config = self.clone();
|
*app_config = self.clone();
|
||||||
|
info!("Config saved successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Err: {:?}", e);
|
error!("Failed to serialize config: {}", e);
|
||||||
Err(format!("Failed to serialize config: {}", e))
|
Err(format!("Failed to serialize config: {}", e))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_default_config() -> Result<()> {
|
pub fn generate_default_config() -> Result<()> {
|
||||||
let mut file = File::create(&Self::get_path().join("config.yaml"))?;
|
debug!("Generating default config");
|
||||||
file.write_all(serde_yaml::to_string(&Config::default()).unwrap().as_bytes())?;
|
let config_path = Self::get_path().join("config.yaml");
|
||||||
|
let mut file = File::create(&config_path)?;
|
||||||
|
let default_config = Config::default();
|
||||||
|
let yaml = serde_yaml::to_string(&default_config).unwrap();
|
||||||
|
file.write_all(yaml.as_bytes())?;
|
||||||
file.flush()?;
|
file.flush()?;
|
||||||
|
info!("Default config generated successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_path() -> PathBuf {
|
pub fn get_path() -> PathBuf {
|
||||||
let app_dirs = AppDirs::new(Some("vrclipboard-ime"), false).unwrap();
|
let app_dirs = AppDirs::new(Some("vrclipboard-ime"), false).unwrap();
|
||||||
let app_data = app_dirs.config_dir;
|
let app_data = app_dirs.config_dir;
|
||||||
|
trace!("Config path: {:?}", app_data);
|
||||||
app_data
|
app_data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::{config::Config, converter::converter::{get_custom_converter, Converter}, STATE};
|
use crate::{config::Config, converter::converter::{get_custom_converter, Converter}, STATE};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use tracing::{info, debug, trace, warn};
|
||||||
|
|
||||||
pub struct ConversionBlock {
|
pub struct ConversionBlock {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
@ -10,69 +11,96 @@ pub struct Conversion;
|
|||||||
|
|
||||||
impl Conversion {
|
impl Conversion {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
info!("Creating new Conversion instance");
|
||||||
Self {}
|
Self {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_text(&self, text: &str) -> Result<String> {
|
pub fn convert_text(&self, text: &str) -> Result<String> {
|
||||||
println!("Processing text: {}", text);
|
info!("Converting text: {}", text);
|
||||||
|
trace!("Text length: {}", text.len());
|
||||||
let blocks = self.split_text(text)?;
|
let blocks = self.split_text(text)?;
|
||||||
|
trace!("Number of blocks after splitting: {}", blocks.len());
|
||||||
self.convert_blocks(blocks)
|
self.convert_blocks(blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_blocks(&self, blocks: Vec<ConversionBlock>) -> Result<String> {
|
pub fn convert_blocks(&self, blocks: Vec<ConversionBlock>) -> Result<String> {
|
||||||
|
debug!("Converting blocks");
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
for block in blocks {
|
for (index, block) in blocks.iter().enumerate() {
|
||||||
|
trace!("Processing block {}/{}", index + 1, blocks.len());
|
||||||
let converted = self.convert_block(&block)?;
|
let converted = self.convert_block(&block)?;
|
||||||
println!(" {}: {} -> {}", block.converter.name(), block.text, converted);
|
debug!("Converted block - {}: {} -> {}", block.converter.name(), block.text, converted);
|
||||||
result.push_str(&converted);
|
result.push_str(&converted);
|
||||||
|
trace!("Current result length: {}", result.len());
|
||||||
}
|
}
|
||||||
println!(" {}", result);
|
trace!("Final conversion result: {}", result);
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_block(&self, block: &ConversionBlock) -> Result<String> {
|
pub fn convert_block(&self, block: &ConversionBlock) -> Result<String> {
|
||||||
if block.text == "" {
|
trace!("Converting block: {}", block.text);
|
||||||
|
trace!("Using converter: {}", block.converter.name());
|
||||||
|
if block.text.is_empty() {
|
||||||
|
trace!("Empty block, returning default string");
|
||||||
return Ok(String::default());
|
return Ok(String::default());
|
||||||
}
|
}
|
||||||
block.converter.convert(&block.text)
|
let result = block.converter.convert(&block.text);
|
||||||
|
trace!("Conversion result: {:?}", result);
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn split_text(&self, text: &str) -> Result<Vec<ConversionBlock>> {
|
pub fn split_text(&self, text: &str) -> Result<Vec<ConversionBlock>> {
|
||||||
|
debug!("Splitting text: {}", text);
|
||||||
let mut text = text.to_string();
|
let mut text = text.to_string();
|
||||||
let mut blocks = Vec::new();
|
let mut blocks = Vec::new();
|
||||||
let mut current_converter = 'r';
|
let mut current_converter = 'r';
|
||||||
|
|
||||||
let config = self.get_config();
|
let config = self.get_config();
|
||||||
|
trace!("Config command: {}, split: {}", config.command, config.split);
|
||||||
|
|
||||||
if text.starts_with(&config.command) {
|
if text.starts_with(&config.command) {
|
||||||
|
trace!("Text starts with command");
|
||||||
text = text.split_off(1);
|
text = text.split_off(1);
|
||||||
if text.len() != 0 {
|
if !text.is_empty() {
|
||||||
current_converter = text.chars().next().unwrap_or('n');
|
current_converter = text.chars().next().unwrap_or('n');
|
||||||
text = text.split_off(1);
|
text = text.split_off(1);
|
||||||
|
trace!("Initial converter set to: {}", current_converter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, command_splitted) in text.split(&config.command).enumerate() {
|
for (i, command_splitted) in text.split(&config.command).enumerate() {
|
||||||
|
trace!("Processing split {}", i);
|
||||||
let mut command_splitted = command_splitted.to_string();
|
let mut command_splitted = command_splitted.to_string();
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
if command_splitted.len() != 0 {
|
if !command_splitted.is_empty() {
|
||||||
current_converter = command_splitted.chars().next().unwrap_or('n');
|
current_converter = command_splitted.chars().next().unwrap_or('n');
|
||||||
command_splitted = command_splitted.split_off(1);
|
command_splitted = command_splitted.split_off(1);
|
||||||
|
trace!("Converter changed to: {}", current_converter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for splitted in command_splitted.split(&config.split) {
|
for splitted in command_splitted.split(&config.split) {
|
||||||
|
trace!("Creating ConversionBlock - text: {}, converter: {}", splitted, current_converter);
|
||||||
|
let converter = get_custom_converter(current_converter).unwrap_or_else(|| {
|
||||||
|
warn!("Failed to get custom converter for '{}', using default", current_converter);
|
||||||
|
get_custom_converter('n').unwrap()
|
||||||
|
});
|
||||||
blocks.push(ConversionBlock {
|
blocks.push(ConversionBlock {
|
||||||
text: splitted.to_string(),
|
text: splitted.to_string(),
|
||||||
converter: get_custom_converter(current_converter).unwrap_or(get_custom_converter('n').unwrap())
|
converter
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("Split text into {} blocks", blocks.len());
|
||||||
|
trace!("Blocks: {:?}", blocks.iter().map(|b| &b.text).collect::<Vec<_>>());
|
||||||
Ok(blocks)
|
Ok(blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_config(&self) -> Config {
|
pub fn get_config(&self) -> Config {
|
||||||
STATE.lock().unwrap().clone()
|
trace!("Getting config");
|
||||||
|
let config = STATE.lock().unwrap().clone();
|
||||||
|
trace!("Config retrieved: {:?}", config);
|
||||||
|
config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use calc::Context;
|
use calc::Context;
|
||||||
|
use tracing::{debug, info, trace};
|
||||||
|
|
||||||
use super::converter::Converter;
|
use super::converter::Converter;
|
||||||
|
|
||||||
@ -6,16 +7,25 @@ pub struct CalculatorConverter;
|
|||||||
|
|
||||||
impl Converter for CalculatorConverter {
|
impl Converter for CalculatorConverter {
|
||||||
fn convert(&self, text: &str) -> anyhow::Result<String> {
|
fn convert(&self, text: &str) -> anyhow::Result<String> {
|
||||||
|
debug!("Evaluating expression: {}", text);
|
||||||
let mut ctx = Context::<f64>::default();
|
let mut ctx = Context::<f64>::default();
|
||||||
let result = match ctx.evaluate(text) {
|
let result = match ctx.evaluate(text) {
|
||||||
Ok(result) => format!("{} = {}", text, result.to_string()),
|
Ok(result) => {
|
||||||
Err(e) => e.to_string(),
|
let formatted = format!("{} = {}", text, result.to_string());
|
||||||
|
info!("Evaluation successful: {}", formatted);
|
||||||
|
formatted
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Evaluation failed: {}", e);
|
||||||
|
e.to_string()
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
|
trace!("Getting converter name");
|
||||||
"calculator".to_string()
|
"calculator".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use tracing::{debug, info, trace};
|
||||||
|
|
||||||
use super::{calculator::CalculatorConverter, hiragana::HiraganaConverter, katakana::KatakanaConverter, none_converter::NoneConverter, roman_to_kanji::RomanToKanjiConverter};
|
use super::{calculator::CalculatorConverter, hiragana::HiraganaConverter, katakana::KatakanaConverter, none_converter::NoneConverter, roman_to_kanji::RomanToKanjiConverter};
|
||||||
|
|
||||||
@ -8,12 +9,18 @@ pub trait Converter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_custom_converter(prefix: char) -> Option<Box<dyn Converter>> {
|
pub fn get_custom_converter(prefix: char) -> Option<Box<dyn Converter>> {
|
||||||
match prefix {
|
debug!("Getting custom converter for prefix: {}", prefix);
|
||||||
|
let converter = match prefix {
|
||||||
'r' => Some(Box::new(RomanToKanjiConverter) as Box<dyn Converter>),
|
'r' => Some(Box::new(RomanToKanjiConverter) as Box<dyn Converter>),
|
||||||
'h' => Some(Box::new(HiraganaConverter) as Box<dyn Converter>),
|
'h' => Some(Box::new(HiraganaConverter) as Box<dyn Converter>),
|
||||||
'c' => Some(Box::new(CalculatorConverter) as Box<dyn Converter>),
|
'c' => Some(Box::new(CalculatorConverter) as Box<dyn Converter>),
|
||||||
'n' => Some(Box::new(NoneConverter) as Box<dyn Converter>),
|
'n' => Some(Box::new(NoneConverter) as Box<dyn Converter>),
|
||||||
'k' => Some(Box::new(KatakanaConverter) as Box<dyn Converter>),
|
'k' => Some(Box::new(KatakanaConverter) as Box<dyn Converter>),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
};
|
||||||
|
match &converter {
|
||||||
|
Some(c) => debug!("Custom converter found: {}", c.name()),
|
||||||
|
None => trace!("No custom converter found for prefix: {}", prefix),
|
||||||
}
|
}
|
||||||
|
converter
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use windows::Win32::UI::Input::Ime::{FELANG_CMODE_HIRAGANAOUT, FELANG_CMODE_NOINVISIBLECHAR, FELANG_CMODE_PRECONV, FELANG_REQ_REV};
|
use windows::Win32::UI::Input::Ime::{FELANG_CMODE_HIRAGANAOUT, FELANG_CMODE_NOINVISIBLECHAR, FELANG_CMODE_PRECONV, FELANG_REQ_REV};
|
||||||
|
use tracing::{debug, info, trace};
|
||||||
|
|
||||||
use crate::felanguage::FElanguage;
|
use crate::felanguage::FElanguage;
|
||||||
|
|
||||||
@ -9,11 +10,22 @@ pub struct HiraganaConverter;
|
|||||||
|
|
||||||
impl Converter for HiraganaConverter {
|
impl Converter for HiraganaConverter {
|
||||||
fn convert(&self, text: &str) -> anyhow::Result<String> {
|
fn convert(&self, text: &str) -> anyhow::Result<String> {
|
||||||
|
debug!("Converting to hiragana: {}", text);
|
||||||
let felanguage = FElanguage::new()?;
|
let felanguage = FElanguage::new()?;
|
||||||
felanguage.j_morph_result(text, FELANG_REQ_REV, FELANG_CMODE_HIRAGANAOUT | FELANG_CMODE_PRECONV | FELANG_CMODE_NOINVISIBLECHAR)
|
trace!("FElanguage instance created");
|
||||||
|
|
||||||
|
let result = felanguage.j_morph_result(text, FELANG_REQ_REV, FELANG_CMODE_HIRAGANAOUT | FELANG_CMODE_PRECONV | FELANG_CMODE_NOINVISIBLECHAR);
|
||||||
|
|
||||||
|
match &result {
|
||||||
|
Ok(converted) => info!("Conversion successful: {} -> {}", text, converted),
|
||||||
|
Err(e) => debug!("Conversion failed: {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
|
trace!("Getting converter name");
|
||||||
"hiragana".to_string()
|
"hiragana".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use windows::Win32::UI::Input::Ime::{FELANG_CMODE_KATAKANAOUT, FELANG_CMODE_NOINVISIBLECHAR, FELANG_CMODE_PRECONV, FELANG_REQ_REV};
|
use windows::Win32::UI::Input::Ime::{FELANG_CMODE_KATAKANAOUT, FELANG_CMODE_NOINVISIBLECHAR, FELANG_CMODE_PRECONV, FELANG_REQ_REV};
|
||||||
|
use tracing::{debug, info, trace};
|
||||||
|
|
||||||
use crate::felanguage::FElanguage;
|
use crate::felanguage::FElanguage;
|
||||||
|
|
||||||
@ -9,11 +10,22 @@ pub struct KatakanaConverter;
|
|||||||
|
|
||||||
impl Converter for KatakanaConverter {
|
impl Converter for KatakanaConverter {
|
||||||
fn convert(&self, text: &str) -> anyhow::Result<String> {
|
fn convert(&self, text: &str) -> anyhow::Result<String> {
|
||||||
|
debug!("Converting to katakana: {}", text);
|
||||||
let felanguage = FElanguage::new()?;
|
let felanguage = FElanguage::new()?;
|
||||||
felanguage.j_morph_result(text, FELANG_REQ_REV, FELANG_CMODE_KATAKANAOUT | FELANG_CMODE_PRECONV | FELANG_CMODE_NOINVISIBLECHAR)
|
trace!("FElanguage instance created");
|
||||||
|
|
||||||
|
let result = felanguage.j_morph_result(text, FELANG_REQ_REV, FELANG_CMODE_KATAKANAOUT | FELANG_CMODE_PRECONV | FELANG_CMODE_NOINVISIBLECHAR);
|
||||||
|
|
||||||
|
match &result {
|
||||||
|
Ok(converted) => info!("Conversion successful: {} -> {}", text, converted),
|
||||||
|
Err(e) => debug!("Conversion failed: {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
|
trace!("Getting converter name");
|
||||||
"katakana".to_string()
|
"katakana".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
|
use tracing::{debug, trace};
|
||||||
use super::converter::Converter;
|
use super::converter::Converter;
|
||||||
|
|
||||||
pub struct NoneConverter;
|
pub struct NoneConverter;
|
||||||
|
|
||||||
impl Converter for NoneConverter {
|
impl Converter for NoneConverter {
|
||||||
fn convert(&self, text: &str) -> anyhow::Result<String> {
|
fn convert(&self, text: &str) -> anyhow::Result<String> {
|
||||||
|
debug!("Converting with NoneConverter: {}", text);
|
||||||
Ok(text.to_string())
|
Ok(text.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
|
trace!("Getting converter name");
|
||||||
"none".to_string()
|
"none".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use windows::Win32::UI::Input::Ime::{FELANG_CMODE_HIRAGANAOUT, FELANG_CMODE_NOINVISIBLECHAR, FELANG_CMODE_PRECONV, FELANG_CMODE_ROMAN, FELANG_REQ_CONV};
|
use windows::Win32::UI::Input::Ime::{FELANG_CMODE_HIRAGANAOUT, FELANG_CMODE_NOINVISIBLECHAR, FELANG_CMODE_PRECONV, FELANG_CMODE_ROMAN, FELANG_REQ_CONV};
|
||||||
|
use tracing::{debug, info, trace};
|
||||||
|
|
||||||
use crate::felanguage::FElanguage;
|
use crate::felanguage::FElanguage;
|
||||||
|
|
||||||
@ -8,14 +9,25 @@ pub struct RomanToKanjiConverter;
|
|||||||
|
|
||||||
impl Converter for RomanToKanjiConverter {
|
impl Converter for RomanToKanjiConverter {
|
||||||
fn convert(&self, text: &str) -> anyhow::Result<String> {
|
fn convert(&self, text: &str) -> anyhow::Result<String> {
|
||||||
|
debug!("Converting roman to kanji: {}", text);
|
||||||
let felanguage = FElanguage::new()?;
|
let felanguage = FElanguage::new()?;
|
||||||
felanguage.j_morph_result(text, FELANG_REQ_CONV, FELANG_CMODE_HIRAGANAOUT
|
trace!("FElanguage instance created");
|
||||||
|
|
||||||
|
let result = felanguage.j_morph_result(text, FELANG_REQ_CONV, FELANG_CMODE_HIRAGANAOUT
|
||||||
| FELANG_CMODE_ROMAN
|
| FELANG_CMODE_ROMAN
|
||||||
| FELANG_CMODE_NOINVISIBLECHAR
|
| FELANG_CMODE_NOINVISIBLECHAR
|
||||||
| FELANG_CMODE_PRECONV)
|
| FELANG_CMODE_PRECONV);
|
||||||
|
|
||||||
|
match &result {
|
||||||
|
Ok(converted) => info!("Conversion successful: {} -> {}", text, converted),
|
||||||
|
Err(e) => debug!("Conversion failed: {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
|
trace!("Getting converter name");
|
||||||
"roman_to_kanji".to_string()
|
"roman_to_kanji".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use tracing::{debug, error, info, trace};
|
||||||
use windows::{
|
use windows::{
|
||||||
core::{w, PCWSTR},
|
core::{w, PCWSTR},
|
||||||
Win32::{
|
Win32::{
|
||||||
@ -15,25 +16,41 @@ pub struct FElanguage {
|
|||||||
|
|
||||||
impl Drop for FElanguage {
|
impl Drop for FElanguage {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { self.ife.Close().ok() };
|
debug!("Dropping FElanguage instance");
|
||||||
|
if let Err(e) = unsafe { self.ife.Close() } {
|
||||||
|
error!("Error closing IFELanguage: {:?}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FElanguage {
|
impl FElanguage {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let clsid = unsafe { CLSIDFromProgID(w!("MSIME.Japan"))? };
|
info!("Creating new FElanguage instance");
|
||||||
let ife: IFELanguage = unsafe { CoCreateInstance(&clsid, None, CLSCTX_ALL)? };
|
let clsid = unsafe {
|
||||||
unsafe { ife.Open()? };
|
trace!("Getting CLSID for MSIME.Japan");
|
||||||
|
CLSIDFromProgID(w!("MSIME.Japan"))?
|
||||||
|
};
|
||||||
|
let ife: IFELanguage = unsafe {
|
||||||
|
trace!("Creating IFELanguage instance");
|
||||||
|
CoCreateInstance(&clsid, None, CLSCTX_ALL)?
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
trace!("Opening IFELanguage");
|
||||||
|
ife.Open()?
|
||||||
|
};
|
||||||
|
debug!("FElanguage instance created successfully");
|
||||||
Ok(FElanguage { ife })
|
Ok(FElanguage { ife })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn j_morph_result(&self, input: &str, request: u32, mode: u32) -> Result<String> {
|
pub fn j_morph_result(&self, input: &str, request: u32, mode: u32) -> Result<String> {
|
||||||
|
debug!("Calling j_morph_result with input: {}, request: {}, mode: {}", input, request, mode);
|
||||||
let input_utf16: Vec<u16> = input.encode_utf16().chain(Some(0)).collect();
|
let input_utf16: Vec<u16> = input.encode_utf16().chain(Some(0)).collect();
|
||||||
let input_len = input_utf16.len();
|
let input_len = input_utf16.len();
|
||||||
let input_pcwstr = PCWSTR::from_raw(input_utf16.as_ptr());
|
let input_pcwstr = PCWSTR::from_raw(input_utf16.as_ptr());
|
||||||
|
|
||||||
let mut result_ptr = ptr::null_mut();
|
let mut result_ptr = ptr::null_mut();
|
||||||
unsafe {
|
unsafe {
|
||||||
|
trace!("Calling GetJMorphResult");
|
||||||
self.ife.GetJMorphResult(
|
self.ife.GetJMorphResult(
|
||||||
request,
|
request,
|
||||||
mode,
|
mode,
|
||||||
@ -45,6 +62,7 @@ impl FElanguage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if result_ptr.is_null() {
|
if result_ptr.is_null() {
|
||||||
|
error!("GetJMorphResult returned null pointer");
|
||||||
return Err(anyhow::anyhow!("GetJMorphResult returned null pointer"));
|
return Err(anyhow::anyhow!("GetJMorphResult returned null pointer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,12 +71,14 @@ impl FElanguage {
|
|||||||
let output_len = result_struct.cchOutput as usize;
|
let output_len = result_struct.cchOutput as usize;
|
||||||
|
|
||||||
if output_bstr_ptr.is_null() {
|
if output_bstr_ptr.is_null() {
|
||||||
|
error!("Output BSTR pointer is null");
|
||||||
return Err(anyhow::anyhow!("Output BSTR pointer is null"));
|
return Err(anyhow::anyhow!("Output BSTR pointer is null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let output_slice = unsafe { std::slice::from_raw_parts(output_bstr_ptr.as_ptr(), output_len) };
|
let output_slice = unsafe { std::slice::from_raw_parts(output_bstr_ptr.as_ptr(), output_len) };
|
||||||
let output_string = String::from_utf16_lossy(output_slice);
|
let output_string = String::from_utf16_lossy(output_slice);
|
||||||
|
|
||||||
|
trace!("j_morph_result output: {}", output_string);
|
||||||
Ok(output_string)
|
Ok(output_string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ use rosc::{encoder, OscMessage, OscPacket, OscType};
|
|||||||
use tauri::{AppHandle, Manager};
|
use tauri::{AppHandle, Manager};
|
||||||
use crate::{config::{Config, OnCopyMode}, conversion::Conversion, tsf_conversion::TsfConversion, Log, STATE};
|
use crate::{config::{Config, OnCopyMode}, conversion::Conversion, tsf_conversion::TsfConversion, Log, STATE};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use tracing::{info, warn, error};
|
||||||
|
|
||||||
pub struct ConversionHandler {
|
pub struct ConversionHandler {
|
||||||
app_handle: AppHandle,
|
app_handle: AppHandle,
|
||||||
@ -23,6 +24,7 @@ impl ConversionHandler {
|
|||||||
let tsf_conversion = None;
|
let tsf_conversion = None;
|
||||||
let clipboard_ctx = ClipboardProvider::new().unwrap();
|
let clipboard_ctx = ClipboardProvider::new().unwrap();
|
||||||
|
|
||||||
|
info!("ConversionHandler created");
|
||||||
Ok(Self { app_handle, conversion, tsf_conversion, clipboard_ctx, last_text: String::new() })
|
Ok(Self { app_handle, conversion, tsf_conversion, clipboard_ctx, last_text: String::new() })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,23 +36,24 @@ impl ConversionHandler {
|
|||||||
impl ConversionHandler {
|
impl ConversionHandler {
|
||||||
fn tsf_conversion(&mut self, contents: &str, config: &Config) -> Result<()> {
|
fn tsf_conversion(&mut self, contents: &str, config: &Config) -> Result<()> {
|
||||||
if contents.chars().count() > 140 {
|
if contents.chars().count() > 140 {
|
||||||
|
info!("Content exceeds 140 characters, skipping TSF conversion");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if config.skip_url && Regex::new(r"(http://|https://){1}[\w\.\-/:\#\?=\&;%\~\+]+").unwrap().is_match(&contents) {
|
if config.skip_url && Regex::new(r"(http://|https://){1}[\w\.\-/:\#\?=\&;%\~\+]+").unwrap().is_match(&contents) {
|
||||||
|
info!("URL detected, skipping TSF conversion");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.tsf_conversion.is_none() {
|
if self.tsf_conversion.is_none() {
|
||||||
self.tsf_conversion = Some(TsfConversion::new());
|
self.tsf_conversion = Some(TsfConversion::new());
|
||||||
|
info!("TSF conversion created");
|
||||||
println!("TSF conversion created.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let tsf_conversion = self.tsf_conversion.as_mut().unwrap();
|
let tsf_conversion = self.tsf_conversion.as_mut().unwrap();
|
||||||
|
|
||||||
let converted = tsf_conversion.convert(contents)?;
|
let converted = tsf_conversion.convert(contents)?;
|
||||||
|
|
||||||
println!("TSF conversion: {} -> {}", contents, converted);
|
info!("TSF conversion: {} -> {}", contents, converted);
|
||||||
|
|
||||||
self.last_text = contents.to_string().clone();
|
self.last_text = contents.to_string().clone();
|
||||||
|
|
||||||
@ -65,10 +68,12 @@ impl ConversionHandler {
|
|||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
while self.clipboard_ctx.set_contents(converted.clone()).is_err() {
|
while self.clipboard_ctx.set_contents(converted.clone()).is_err() {
|
||||||
if count > 4 {
|
if count > 4 {
|
||||||
|
warn!("Failed to set clipboard contents after 5 attempts");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
|
info!("Conversion returned to clipboard");
|
||||||
},
|
},
|
||||||
OnCopyMode::ReturnToChatbox => {
|
OnCopyMode::ReturnToChatbox => {
|
||||||
let sock = UdpSocket::bind("127.0.0.1:0").unwrap();
|
let sock = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||||
@ -81,7 +86,11 @@ impl ConversionHandler {
|
|||||||
]
|
]
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
|
|
||||||
sock.send_to(&msg_buf, "127.0.0.1:9000").unwrap();
|
if let Err(e) = sock.send_to(&msg_buf, "127.0.0.1:9000") {
|
||||||
|
error!("Failed to send UDP packet: {}", e);
|
||||||
|
} else {
|
||||||
|
info!("Conversion returned to chatbox");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
OnCopyMode::SendDirectly => {
|
OnCopyMode::SendDirectly => {
|
||||||
let sock = UdpSocket::bind("127.0.0.1:0").unwrap();
|
let sock = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||||
@ -94,7 +103,11 @@ impl ConversionHandler {
|
|||||||
]
|
]
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
|
|
||||||
sock.send_to(&msg_buf, "127.0.0.1:9000").unwrap();
|
if let Err(e) = sock.send_to(&msg_buf, "127.0.0.1:9000") {
|
||||||
|
error!("Failed to send UDP packet: {}", e);
|
||||||
|
} else {
|
||||||
|
info!("Conversion sent directly");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +118,7 @@ impl ConversionHandler {
|
|||||||
original: parsed_contents,
|
original: parsed_contents,
|
||||||
converted
|
converted
|
||||||
}).is_err() {
|
}).is_err() {
|
||||||
println!("App handle add log failed.");
|
error!("App handle add log failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,14 +128,16 @@ impl ClipboardHandler for ConversionHandler {
|
|||||||
let config = self.get_config();
|
let config = self.get_config();
|
||||||
if let Ok(mut contents) = self.clipboard_ctx.get_contents() {
|
if let Ok(mut contents) = self.clipboard_ctx.get_contents() {
|
||||||
if config.use_tsf_reconvert {
|
if config.use_tsf_reconvert {
|
||||||
self.tsf_conversion(&contents, &config).expect("TSF conversion failed.");
|
if let Err(e) = self.tsf_conversion(&contents, &config) {
|
||||||
|
error!("TSF conversion failed: {}", e);
|
||||||
|
}
|
||||||
return CallbackResult::Next;
|
return CallbackResult::Next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if contents != self.last_text {
|
if contents != self.last_text {
|
||||||
if contents.starts_with(&config.prefix) || config.ignore_prefix {
|
if contents.starts_with(&config.prefix) || config.ignore_prefix {
|
||||||
|
|
||||||
if config.skip_url && Regex::new(r"(http://|https://){1}[\w\.\-/:\#\?=\&;%\~\+]+").unwrap().is_match(&contents) {
|
if config.skip_url && Regex::new(r"(http://|https://){1}[\w\.\-/:\#\?=\&;%\~\+]+").unwrap().is_match(&contents) {
|
||||||
|
info!("URL detected, skipping conversion");
|
||||||
return CallbackResult::Next;
|
return CallbackResult::Next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +145,7 @@ impl ClipboardHandler for ConversionHandler {
|
|||||||
let converted = match self.conversion.convert_text(&parsed_contents) {
|
let converted = match self.conversion.convert_text(&parsed_contents) {
|
||||||
Ok(converted) => converted,
|
Ok(converted) => converted,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("Error: {:?}", err);
|
error!("Conversion error: {:?}", err);
|
||||||
format!("Error: {:?}", err)
|
format!("Error: {:?}", err)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,7 @@ use clipboard_master::Master;
|
|||||||
use com::Com;
|
use com::Com;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use handler::ConversionHandler;
|
use handler::ConversionHandler;
|
||||||
|
use tracing::Level;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
struct Log {
|
struct Log {
|
||||||
@ -55,6 +56,7 @@ fn save_settings(config: Config, state: State<AppState>) -> Result<(), String> {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("VRClipboard-IME Logs\nバグがあった場合はこのログを送ってください。");
|
println!("VRClipboard-IME Logs\nバグがあった場合はこのログを送ってください。");
|
||||||
|
tracing_subscriber::fmt().with_max_level(Level::TRACE).init();
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.manage(AppState {
|
.manage(AppState {
|
||||||
config: Mutex::new(Config::load().unwrap_or_else(|_| {
|
config: Mutex::new(Config::load().unwrap_or_else(|_| {
|
||||||
@ -64,6 +66,7 @@ fn main() {
|
|||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![load_settings, save_settings])
|
.invoke_handler(tauri::generate_handler![load_settings, save_settings])
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
|
let _span = tracing::span!(tracing::Level::INFO, "main");
|
||||||
app.manage(STATE.lock().unwrap().clone());
|
app.manage(STATE.lock().unwrap().clone());
|
||||||
let app_handle = app.app_handle();
|
let app_handle = app.app_handle();
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use tracing::{debug, info, error};
|
||||||
use windows::{
|
use windows::{
|
||||||
core::Interface,
|
core::Interface,
|
||||||
Win32::UI::TextServices::{ITfFnSearchCandidateProvider, ITfFunctionProvider},
|
Win32::UI::TextServices::{ITfFnSearchCandidateProvider, ITfFunctionProvider},
|
||||||
@ -12,12 +13,31 @@ pub struct FunctionProvider {
|
|||||||
|
|
||||||
impl FunctionProvider {
|
impl FunctionProvider {
|
||||||
pub fn new(function_provider: ITfFunctionProvider) -> Self {
|
pub fn new(function_provider: ITfFunctionProvider) -> Self {
|
||||||
|
debug!("Creating new FunctionProvider");
|
||||||
Self { function_provider }
|
Self { function_provider }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_search_candidate_provider(&self) -> Result<SearchCandidateProvider> {
|
pub fn get_search_candidate_provider(&self) -> Result<SearchCandidateProvider> {
|
||||||
|
debug!("Getting search candidate provider");
|
||||||
let zeroed_guid = windows_core::GUID::zeroed();
|
let zeroed_guid = windows_core::GUID::zeroed();
|
||||||
let search_candidate_provider = unsafe { self.function_provider.GetFunction(&zeroed_guid, &ITfFnSearchCandidateProvider::IID)? };
|
match unsafe { self.function_provider.GetFunction(&zeroed_guid, &ITfFnSearchCandidateProvider::IID) } {
|
||||||
Ok(SearchCandidateProvider::new(search_candidate_provider.cast()?))
|
Ok(search_candidate_provider) => {
|
||||||
|
info!("Search candidate provider obtained successfully");
|
||||||
|
match search_candidate_provider.cast() {
|
||||||
|
Ok(provider) => {
|
||||||
|
debug!("Successfully cast search candidate provider");
|
||||||
|
Ok(SearchCandidateProvider::new(provider))
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to cast search candidate provider: {:?}", e);
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to get search candidate provider: {:?}", e);
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use tracing::{debug, info, error};
|
||||||
use windows::Win32::{
|
use windows::Win32::{
|
||||||
System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER},
|
System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER},
|
||||||
UI::{Input::KeyboardAndMouse::HKL, TextServices::{CLSID_TF_InputProcessorProfiles, ITfInputProcessorProfileMgr, GUID_TFCAT_TIP_KEYBOARD, TF_INPUTPROCESSORPROFILE, TF_IPPMF_DONTCARECURRENTINPUTLANGUAGE, TF_PROFILETYPE_INPUTPROCESSOR}},
|
UI::{Input::KeyboardAndMouse::HKL, TextServices::{CLSID_TF_InputProcessorProfiles, ITfInputProcessorProfileMgr, GUID_TFCAT_TIP_KEYBOARD, TF_INPUTPROCESSORPROFILE, TF_IPPMF_DONTCARECURRENTINPUTLANGUAGE, TF_PROFILETYPE_INPUTPROCESSOR}},
|
||||||
@ -10,21 +11,40 @@ pub struct InputProcessorProfileMgr {
|
|||||||
|
|
||||||
impl InputProcessorProfileMgr {
|
impl InputProcessorProfileMgr {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
|
debug!("Creating new InputProcessorProfileMgr");
|
||||||
let input_processor_profile_mgr = unsafe { CoCreateInstance(&CLSID_TF_InputProcessorProfiles, None, CLSCTX_INPROC_SERVER)? };
|
let input_processor_profile_mgr = unsafe { CoCreateInstance(&CLSID_TF_InputProcessorProfiles, None, CLSCTX_INPROC_SERVER)? };
|
||||||
|
info!("InputProcessorProfileMgr created successfully");
|
||||||
Ok(InputProcessorProfileMgr { input_processor_profile_mgr })
|
Ok(InputProcessorProfileMgr { input_processor_profile_mgr })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_active_profile(&self) -> Result<TF_INPUTPROCESSORPROFILE> {
|
pub fn get_active_profile(&self) -> Result<TF_INPUTPROCESSORPROFILE> {
|
||||||
|
debug!("Getting active profile");
|
||||||
let keyboard_guid = GUID_TFCAT_TIP_KEYBOARD;
|
let keyboard_guid = GUID_TFCAT_TIP_KEYBOARD;
|
||||||
let mut profile = TF_INPUTPROCESSORPROFILE::default();
|
let mut profile = TF_INPUTPROCESSORPROFILE::default();
|
||||||
|
|
||||||
unsafe { self.input_processor_profile_mgr.GetActiveProfile(&keyboard_guid, &mut profile)? };
|
match unsafe { self.input_processor_profile_mgr.GetActiveProfile(&keyboard_guid, &mut profile) } {
|
||||||
|
Ok(_) => {
|
||||||
Ok(profile)
|
info!("Active profile retrieved successfully");
|
||||||
|
Ok(profile)
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to get active profile: {:?}", e);
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate_profile(&self, profile: &TF_INPUTPROCESSORPROFILE) -> Result<()> {
|
pub fn activate_profile(&self, profile: &TF_INPUTPROCESSORPROFILE) -> Result<()> {
|
||||||
unsafe { self.input_processor_profile_mgr.ActivateProfile(TF_PROFILETYPE_INPUTPROCESSOR, profile.langid, &profile.clsid, &profile.guidProfile, HKL::default(), TF_IPPMF_DONTCARECURRENTINPUTLANGUAGE)? };
|
debug!("Activating profile: {:?}", profile);
|
||||||
Ok(())
|
match unsafe { self.input_processor_profile_mgr.ActivateProfile(TF_PROFILETYPE_INPUTPROCESSOR, profile.langid, &profile.clsid, &profile.guidProfile, HKL::default(), TF_IPPMF_DONTCARECURRENTINPUTLANGUAGE) } {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Profile activated successfully");
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to activate profile: {:?}", e);
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use tracing::{debug, error};
|
||||||
use windows::Win32::UI::WindowsAndMessaging::{SystemParametersInfoW, SPI_SETTHREADLOCALINPUTSETTINGS, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS};
|
use windows::Win32::UI::WindowsAndMessaging::{SystemParametersInfoW, SPI_SETTHREADLOCALINPUTSETTINGS, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS};
|
||||||
|
|
||||||
pub mod input_processor_profile_mgr;
|
pub mod input_processor_profile_mgr;
|
||||||
@ -7,8 +8,16 @@ pub mod search_candidate_provider;
|
|||||||
pub mod thread_mgr;
|
pub mod thread_mgr;
|
||||||
|
|
||||||
pub fn set_thread_local_input_settings(thread_local_input_settings: bool) -> Result<()> {
|
pub fn set_thread_local_input_settings(thread_local_input_settings: bool) -> Result<()> {
|
||||||
|
debug!("Setting thread local input settings to: {}", thread_local_input_settings);
|
||||||
let mut result = thread_local_input_settings;
|
let mut result = thread_local_input_settings;
|
||||||
unsafe { SystemParametersInfoW(SPI_SETTHREADLOCALINPUTSETTINGS, 0, Some(&mut result as *mut _ as *const _ as *mut _), SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS(0))? };
|
match unsafe { SystemParametersInfoW(SPI_SETTHREADLOCALINPUTSETTINGS, 0, Some(&mut result as *mut _ as *const _ as *mut _), SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS(0)) } {
|
||||||
|
Ok(_) => {
|
||||||
Ok(())
|
debug!("Successfully set thread local input settings");
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to set thread local input settings: {:?}", e);
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use tracing::{debug, info, error, trace};
|
||||||
use windows::Win32::UI::TextServices::{ITfFnSearchCandidateProvider, TF_TMAE_NOACTIVATEKEYBOARDLAYOUT};
|
use windows::Win32::UI::TextServices::{ITfFnSearchCandidateProvider, TF_TMAE_NOACTIVATEKEYBOARDLAYOUT};
|
||||||
|
|
||||||
use super::{function_provider::FunctionProvider, input_processor_profile_mgr::InputProcessorProfileMgr, thread_mgr::ThreadMgr};
|
use super::{function_provider::FunctionProvider, input_processor_profile_mgr::InputProcessorProfileMgr, thread_mgr::ThreadMgr};
|
||||||
@ -9,41 +10,60 @@ pub struct SearchCandidateProvider {
|
|||||||
|
|
||||||
impl SearchCandidateProvider {
|
impl SearchCandidateProvider {
|
||||||
pub fn new(search_candidate_provider: ITfFnSearchCandidateProvider) -> Self {
|
pub fn new(search_candidate_provider: ITfFnSearchCandidateProvider) -> Self {
|
||||||
|
debug!("Creating new SearchCandidateProvider");
|
||||||
Self { search_candidate_provider }
|
Self { search_candidate_provider }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create() -> Result<Self> {
|
pub fn create() -> Result<Self> {
|
||||||
|
info!("Creating SearchCandidateProvider");
|
||||||
let profile_mgr = InputProcessorProfileMgr::new()?;
|
let profile_mgr = InputProcessorProfileMgr::new()?;
|
||||||
let profile = profile_mgr.get_active_profile()?;
|
let profile = profile_mgr.get_active_profile()?;
|
||||||
|
debug!("Activating profile");
|
||||||
profile_mgr.activate_profile(&profile)?;
|
profile_mgr.activate_profile(&profile)?;
|
||||||
|
|
||||||
|
debug!("Creating ThreadMgr");
|
||||||
let thread_mgr = ThreadMgr::new()?;
|
let thread_mgr = ThreadMgr::new()?;
|
||||||
let _client_id = thread_mgr.activate_ex(TF_TMAE_NOACTIVATEKEYBOARDLAYOUT)?;
|
let _client_id = thread_mgr.activate_ex(TF_TMAE_NOACTIVATEKEYBOARDLAYOUT)?;
|
||||||
|
|
||||||
|
debug!("Getting function provider");
|
||||||
let function_provider = thread_mgr.get_function_provider(&profile.clsid)?;
|
let function_provider = thread_mgr.get_function_provider(&profile.clsid)?;
|
||||||
|
|
||||||
|
debug!("Getting search candidate provider");
|
||||||
let search_candidate_provider = FunctionProvider::new(function_provider).get_search_candidate_provider()?;
|
let search_candidate_provider = FunctionProvider::new(function_provider).get_search_candidate_provider()?;
|
||||||
|
|
||||||
|
info!("SearchCandidateProvider created successfully");
|
||||||
Ok(search_candidate_provider)
|
Ok(search_candidate_provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_candidates(&self, input: &str, max: usize) -> Result<Vec<String>> {
|
pub fn get_candidates(&self, input: &str, max: usize) -> Result<Vec<String>> {
|
||||||
|
debug!("Getting candidates for input: {}, max: {}", input, max);
|
||||||
let input_utf16: Vec<u16> = input.encode_utf16().chain(Some(0)).collect();
|
let input_utf16: Vec<u16> = input.encode_utf16().chain(Some(0)).collect();
|
||||||
let input_bstr = windows_core::BSTR::from_wide(&input_utf16)?;
|
let input_bstr = windows_core::BSTR::from_wide(&input_utf16)?;
|
||||||
|
|
||||||
let input_utf16: Vec<u16> = "".encode_utf16().chain(Some(0)).collect();
|
let input_utf16: Vec<u16> = "".encode_utf16().chain(Some(0)).collect();
|
||||||
let input_bstr_empty = windows_core::BSTR::from_wide(&input_utf16)?;
|
let input_bstr_empty = windows_core::BSTR::from_wide(&input_utf16)?;
|
||||||
|
|
||||||
|
trace!("Calling GetSearchCandidates");
|
||||||
let candidates = unsafe { self.search_candidate_provider.GetSearchCandidates(&input_bstr, &input_bstr_empty)? };
|
let candidates = unsafe { self.search_candidate_provider.GetSearchCandidates(&input_bstr, &input_bstr_empty)? };
|
||||||
let candidates_enum = unsafe { candidates.EnumCandidates()? };
|
let candidates_enum = unsafe { candidates.EnumCandidates()? };
|
||||||
|
|
||||||
let mut candidates = vec![None; max];
|
let mut candidates = vec![None; max];
|
||||||
let mut candidates_count = 0;
|
let mut candidates_count = 0;
|
||||||
|
trace!("Enumerating candidates");
|
||||||
unsafe { candidates_enum.Next(&mut candidates, &mut candidates_count)? };
|
unsafe { candidates_enum.Next(&mut candidates, &mut candidates_count)? };
|
||||||
|
|
||||||
candidates.resize(candidates_count as usize, None);
|
candidates.resize(candidates_count as usize, None);
|
||||||
|
|
||||||
let candidates: Vec<String> = candidates.iter().map(|candidate| unsafe { candidate.as_ref().unwrap().GetString().unwrap().to_string() }).collect();
|
let candidates: Vec<String> = candidates.iter().map(|candidate| unsafe {
|
||||||
|
match candidate.as_ref().unwrap().GetString() {
|
||||||
|
Ok(s) => s.to_string(),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to get candidate string: {:?}", e);
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
info!("Retrieved {} candidates", candidates.len());
|
||||||
Ok(candidates)
|
Ok(candidates)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use tracing::{debug, error, info};
|
||||||
use windows::Win32::{
|
use windows::Win32::{
|
||||||
System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER},
|
System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER},
|
||||||
UI::TextServices::{CLSID_TF_ThreadMgr, ITfFunctionProvider, ITfThreadMgr2},
|
UI::TextServices::{CLSID_TF_ThreadMgr, ITfFunctionProvider, ITfThreadMgr2},
|
||||||
@ -10,17 +11,31 @@ pub struct ThreadMgr {
|
|||||||
|
|
||||||
impl ThreadMgr {
|
impl ThreadMgr {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
|
debug!("Creating new ThreadMgr");
|
||||||
let thread_mgr = unsafe { CoCreateInstance(&CLSID_TF_ThreadMgr, None, CLSCTX_INPROC_SERVER)? };
|
let thread_mgr = unsafe { CoCreateInstance(&CLSID_TF_ThreadMgr, None, CLSCTX_INPROC_SERVER)? };
|
||||||
|
info!("ThreadMgr created successfully");
|
||||||
Ok(ThreadMgr { thread_mgr })
|
Ok(ThreadMgr { thread_mgr })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate_ex(&self, flags: u32) -> Result<u32> {
|
pub fn activate_ex(&self, flags: u32) -> Result<u32> {
|
||||||
|
debug!("Activating ThreadMgr with flags: {}", flags);
|
||||||
let mut client_id = 0;
|
let mut client_id = 0;
|
||||||
unsafe { self.thread_mgr.ActivateEx(&mut client_id as *mut _ as *const _ as *mut _, flags)? };
|
unsafe { self.thread_mgr.ActivateEx(&mut client_id as *mut _ as *const _ as *mut _, flags)? };
|
||||||
|
info!("ThreadMgr activated with client_id: {}", client_id);
|
||||||
Ok(client_id)
|
Ok(client_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_function_provider(&self, clsid: &windows_core::GUID) -> Result<ITfFunctionProvider> {
|
pub fn get_function_provider(&self, clsid: &windows_core::GUID) -> Result<ITfFunctionProvider> {
|
||||||
Ok(unsafe { self.thread_mgr.GetFunctionProvider(clsid)? })
|
debug!("Getting function provider for CLSID: {:?}", clsid);
|
||||||
|
match unsafe { self.thread_mgr.GetFunctionProvider(clsid) } {
|
||||||
|
Ok(provider) => {
|
||||||
|
info!("Function provider obtained successfully");
|
||||||
|
Ok(provider)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to get function provider: {:?}", e);
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use tracing::{info, debug, error, trace};
|
||||||
use crate::{converter::{converter::Converter, hiragana::HiraganaConverter, roman_to_kanji::RomanToKanjiConverter}, tsf::{search_candidate_provider::SearchCandidateProvider, set_thread_local_input_settings}};
|
use crate::{converter::{converter::Converter, hiragana::HiraganaConverter, roman_to_kanji::RomanToKanjiConverter}, tsf::{search_candidate_provider::SearchCandidateProvider, set_thread_local_input_settings}};
|
||||||
|
|
||||||
pub struct TsfConversion {
|
pub struct TsfConversion {
|
||||||
@ -14,9 +15,10 @@ pub struct TsfConversion {
|
|||||||
|
|
||||||
impl TsfConversion {
|
impl TsfConversion {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
info!("Creating new TsfConversion instance");
|
||||||
set_thread_local_input_settings(true).unwrap();
|
set_thread_local_input_settings(true).unwrap();
|
||||||
|
|
||||||
Self {
|
let instance = Self {
|
||||||
conversion_history: Vec::new(),
|
conversion_history: Vec::new(),
|
||||||
clipboard_history: Vec::new(),
|
clipboard_history: Vec::new(),
|
||||||
now_reconvertion: false,
|
now_reconvertion: false,
|
||||||
@ -25,55 +27,73 @@ impl TsfConversion {
|
|||||||
reconversion_candidates: None,
|
reconversion_candidates: None,
|
||||||
reconversion_index: None,
|
reconversion_index: None,
|
||||||
reconversion_prefix: None,
|
reconversion_prefix: None,
|
||||||
}
|
};
|
||||||
|
instance
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_conversion_state(&mut self) {
|
fn reset_conversion_state(&mut self) {
|
||||||
|
debug!("Resetting conversion state");
|
||||||
|
trace!("Before reset - now_reconvertion: {}, reconversion_prefix: {:?}, reconversion_index: {:?}, reconversion_candidates: {:?}",
|
||||||
|
self.now_reconvertion, self.reconversion_prefix, self.reconversion_index, self.reconversion_candidates);
|
||||||
self.now_reconvertion = false;
|
self.now_reconvertion = false;
|
||||||
self.reconversion_prefix = None;
|
self.reconversion_prefix = None;
|
||||||
self.reconversion_index = None;
|
self.reconversion_index = None;
|
||||||
self.reconversion_candidates = None;
|
self.reconversion_candidates = None;
|
||||||
|
trace!("After reset - now_reconvertion: {}, reconversion_prefix: {:?}, reconversion_index: {:?}, reconversion_candidates: {:?}",
|
||||||
|
self.now_reconvertion, self.reconversion_prefix, self.reconversion_index, self.reconversion_candidates);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_roman_to_kanji(&mut self, text: &str) -> Result<String> {
|
fn convert_roman_to_kanji(&mut self, text: &str) -> Result<String> {
|
||||||
|
debug!("Converting roman to kanji: {}", text);
|
||||||
let o_minus_1 = self.conversion_history.get(if self.conversion_history.len() > 0 { self.conversion_history.len() - 1 } else { 0 }).unwrap_or(&("".to_string())).clone();
|
let o_minus_1 = self.conversion_history.get(if self.conversion_history.len() > 0 { self.conversion_history.len() - 1 } else { 0 }).unwrap_or(&("".to_string())).clone();
|
||||||
|
trace!("Previous conversion (o_minus_1): {}", o_minus_1);
|
||||||
let mut first_diff_position = o_minus_1.chars().zip(text.chars()).position(|(a, b)| a != b);
|
let mut first_diff_position = o_minus_1.chars().zip(text.chars()).position(|(a, b)| a != b);
|
||||||
|
|
||||||
if o_minus_1 != text && first_diff_position.is_none() {
|
if o_minus_1 != text && first_diff_position.is_none() {
|
||||||
first_diff_position = Some(o_minus_1.chars().count());
|
first_diff_position = Some(o_minus_1.chars().count());
|
||||||
}
|
}
|
||||||
|
trace!("First difference position: {:?}", first_diff_position);
|
||||||
let diff = text.chars().skip(first_diff_position.unwrap_or(0)).collect::<String>();
|
let diff = text.chars().skip(first_diff_position.unwrap_or(0)).collect::<String>();
|
||||||
|
debug!("Difference to convert: {}", diff);
|
||||||
|
|
||||||
let roman_to_kanji_converter = RomanToKanjiConverter;
|
let roman_to_kanji_converter = RomanToKanjiConverter;
|
||||||
let converted = roman_to_kanji_converter.convert(&diff)?;
|
let converted = roman_to_kanji_converter.convert(&diff)?;
|
||||||
|
trace!("Converted difference: {}", converted);
|
||||||
self.conversion_history.push(o_minus_1.chars().zip(text.chars()).take_while(|(a, b)| a == b).map(|(a, _)| a).collect::<String>() + &converted);
|
self.conversion_history.push(o_minus_1.chars().zip(text.chars()).take_while(|(a, b)| a == b).map(|(a, _)| a).collect::<String>() + &converted);
|
||||||
self.clipboard_history.push(text.to_string());
|
self.clipboard_history.push(text.to_string());
|
||||||
return Ok(self.conversion_history.last().unwrap().clone());
|
info!("Roman to kanji conversion result: {}", self.conversion_history.last().unwrap());
|
||||||
|
trace!("Updated conversion history: {:?}", self.conversion_history);
|
||||||
|
trace!("Updated clipboard history: {:?}", self.clipboard_history);
|
||||||
|
Ok(self.conversion_history.last().unwrap().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_tsf(&mut self, text: &str) -> Result<String> {
|
fn convert_tsf(&mut self, text: &str) -> Result<String> {
|
||||||
|
debug!("Converting using TSF: {}", text);
|
||||||
self.now_reconvertion = true;
|
self.now_reconvertion = true;
|
||||||
let mut diff_hiragana = String::new();
|
let mut diff_hiragana = String::new();
|
||||||
let mut diff = String::new();
|
let mut diff = String::new();
|
||||||
if self.reconversion_prefix.is_none() {
|
if self.reconversion_prefix.is_none() {
|
||||||
let o_minus_2 = self.conversion_history.get(if self.conversion_history.len() > 1 { self.conversion_history.len() - 2 } else { 0 }).unwrap_or(&("".to_string())).clone();
|
let o_minus_2 = self.conversion_history.get(if self.conversion_history.len() > 1 { self.conversion_history.len() - 2 } else { 0 }).unwrap_or(&("".to_string())).clone();
|
||||||
let i_minus_1 = self.clipboard_history.get(if self.clipboard_history.len() > 0 { self.clipboard_history.len() - 1 } else { 0 }).unwrap_or(&("".to_string())).clone();
|
let i_minus_1 = self.clipboard_history.get(if self.clipboard_history.len() > 0 { self.clipboard_history.len() - 1 } else { 0 }).unwrap_or(&("".to_string())).clone();
|
||||||
println!("o,i: {}, {}", o_minus_2, i_minus_1);
|
trace!("o_minus_2: {}, i_minus_1: {}", o_minus_2, i_minus_1);
|
||||||
let mut first_diff_position = i_minus_1.chars().zip(o_minus_2.chars()).position(|(a, b)| a != b);
|
let mut first_diff_position = i_minus_1.chars().zip(o_minus_2.chars()).position(|(a, b)| a != b);
|
||||||
println!("diff_pos: {:?}", first_diff_position);
|
trace!("First difference position: {:?}", first_diff_position);
|
||||||
if o_minus_2 != i_minus_1 && first_diff_position.is_none() {
|
if o_minus_2 != i_minus_1 && first_diff_position.is_none() {
|
||||||
first_diff_position = Some(o_minus_2.chars().count());
|
first_diff_position = Some(o_minus_2.chars().count());
|
||||||
}
|
}
|
||||||
diff = i_minus_1.chars().skip(first_diff_position.unwrap_or(0)).collect::<String>();
|
diff = i_minus_1.chars().skip(first_diff_position.unwrap_or(0)).collect::<String>();
|
||||||
println!("diff: {}", diff);
|
debug!("Difference to convert: {}", diff);
|
||||||
diff_hiragana = HiraganaConverter.convert(&diff)?;
|
diff_hiragana = HiraganaConverter.convert(&diff)?;
|
||||||
|
trace!("Hiragana conversion: {}", diff_hiragana);
|
||||||
let prefix = i_minus_1.chars().zip(o_minus_2.chars()).take_while(|(a, b)| a == b).map(|(a, _)| a).collect::<String>();
|
let prefix = i_minus_1.chars().zip(o_minus_2.chars()).take_while(|(a, b)| a == b).map(|(a, _)| a).collect::<String>();
|
||||||
self.reconversion_prefix = Some(prefix.clone());
|
self.reconversion_prefix = Some(prefix.clone());
|
||||||
|
trace!("Set reconversion prefix: {:?}", self.reconversion_prefix);
|
||||||
}
|
}
|
||||||
println!("diff_hiragana: {}", diff_hiragana);
|
|
||||||
|
|
||||||
let candidates = self.reconversion_candidates.get_or_insert_with(|| {
|
let candidates = self.reconversion_candidates.get_or_insert_with(|| {
|
||||||
|
debug!("Generating new candidates");
|
||||||
let mut candidates = self.search_candidate_provider.get_candidates(&diff_hiragana, 10).unwrap_or_default();
|
let mut candidates = self.search_candidate_provider.get_candidates(&diff_hiragana, 10).unwrap_or_default();
|
||||||
|
trace!("Initial candidates: {:?}", candidates);
|
||||||
if candidates.is_empty() {
|
if candidates.is_empty() {
|
||||||
candidates.push(diff_hiragana.clone());
|
candidates.push(diff_hiragana.clone());
|
||||||
let roman_to_kanji_converter = RomanToKanjiConverter;
|
let roman_to_kanji_converter = RomanToKanjiConverter;
|
||||||
@ -81,23 +101,24 @@ impl TsfConversion {
|
|||||||
candidates.push(roman_to_kanji);
|
candidates.push(roman_to_kanji);
|
||||||
}
|
}
|
||||||
candidates.insert(0, diff.to_string());
|
candidates.insert(0, diff.to_string());
|
||||||
|
trace!("Final candidates: {:?}", candidates);
|
||||||
candidates
|
candidates
|
||||||
});
|
});
|
||||||
|
|
||||||
let index = self.reconversion_index.get_or_insert(-1);
|
let index = self.reconversion_index.get_or_insert(-1);
|
||||||
|
trace!("Current reconversion index: {}", index);
|
||||||
|
|
||||||
if *index + 1 < candidates.len() as i32 {
|
if *index + 1 < candidates.len() as i32 {
|
||||||
*index += 1;
|
*index += 1;
|
||||||
} else {
|
} else {
|
||||||
*index = 0;
|
*index = 0;
|
||||||
}
|
}
|
||||||
|
debug!("Updated reconversion index: {}", index);
|
||||||
if self.reconversion_candidates.is_some() {
|
|
||||||
println!("Candidates: {:?}", self.reconversion_candidates.as_ref().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.conversion_history.push(self.reconversion_prefix.clone().unwrap() + &self.reconversion_candidates.as_ref().unwrap()[self.reconversion_index.unwrap() as usize].clone());
|
self.conversion_history.push(self.reconversion_prefix.clone().unwrap() + &self.reconversion_candidates.as_ref().unwrap()[self.reconversion_index.unwrap() as usize].clone());
|
||||||
self.clipboard_history.push(text.to_string());
|
self.clipboard_history.push(text.to_string());
|
||||||
|
trace!("Updated conversion history: {:?}", self.conversion_history);
|
||||||
|
trace!("Updated clipboard history: {:?}", self.clipboard_history);
|
||||||
|
|
||||||
while self.conversion_history.len() > 3 {
|
while self.conversion_history.len() > 3 {
|
||||||
self.conversion_history.remove(0);
|
self.conversion_history.remove(0);
|
||||||
@ -105,32 +126,39 @@ impl TsfConversion {
|
|||||||
while self.clipboard_history.len() > 3 {
|
while self.clipboard_history.len() > 3 {
|
||||||
self.clipboard_history.remove(0);
|
self.clipboard_history.remove(0);
|
||||||
}
|
}
|
||||||
|
trace!("Trimmed conversion history: {:?}", self.conversion_history);
|
||||||
|
trace!("Trimmed clipboard history: {:?}", self.clipboard_history);
|
||||||
|
|
||||||
return Ok(self.conversion_history.last().unwrap().clone());
|
info!("TSF conversion result: {}", self.conversion_history.last().unwrap());
|
||||||
|
Ok(self.conversion_history.last().unwrap().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert(&mut self, text: &str) -> Result<String> {
|
pub fn convert(&mut self, text: &str) -> Result<String> {
|
||||||
println!();
|
debug!("Starting conversion for: {}", text);
|
||||||
println!("History: {:?}, {:?}", self.conversion_history, self.clipboard_history);
|
trace!("Current conversion history: {:?}", self.conversion_history);
|
||||||
println!("{} == {}", text, self.conversion_history.last().unwrap_or(&("".to_string())).clone());
|
trace!("Current clipboard history: {:?}", self.clipboard_history);
|
||||||
let same_as_last_conversion = text.to_string() == self.conversion_history.last().unwrap_or(&("".to_string())).clone();
|
let same_as_last_conversion = text.to_string() == self.conversion_history.last().unwrap_or(&("".to_string())).clone();
|
||||||
|
trace!("Same as last conversion: {}", same_as_last_conversion);
|
||||||
|
|
||||||
self.target_text = text.to_string();
|
self.target_text = text.to_string();
|
||||||
|
trace!("Set target text: {}", self.target_text);
|
||||||
|
|
||||||
if !same_as_last_conversion && self.now_reconvertion {
|
if !same_as_last_conversion && self.now_reconvertion {
|
||||||
|
debug!("Resetting conversion state due to new input");
|
||||||
self.reset_conversion_state();
|
self.reset_conversion_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.now_reconvertion && !same_as_last_conversion {
|
if !self.now_reconvertion && !same_as_last_conversion {
|
||||||
println!("Convert using roman_to_kanji");
|
info!("Converting using roman_to_kanji");
|
||||||
return self.convert_roman_to_kanji(text);
|
return self.convert_roman_to_kanji(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if same_as_last_conversion || self.now_reconvertion {
|
if same_as_last_conversion || self.now_reconvertion {
|
||||||
println!("Convert using TSF");
|
info!("Converting using TSF");
|
||||||
return self.convert_tsf(text);
|
return self.convert_tsf(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error!("Failed to convert: {}", text);
|
||||||
Err(anyhow::anyhow!("Failed to convert"))
|
Err(anyhow::anyhow!("Failed to convert"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "vrclipboard-ime-gui",
|
"productName": "vrclipboard-ime-gui",
|
||||||
"version": "1.7.0"
|
"version": "1.8.0"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"updater": {
|
"updater": {
|
||||||
|
@ -192,7 +192,7 @@ const SettingsComponent = () => {
|
|||||||
/>
|
/>
|
||||||
<label htmlFor="use_tsf_reconvert" className="text-sm font-medium text-gray-700">
|
<label htmlFor="use_tsf_reconvert" className="text-sm font-medium text-gray-700">
|
||||||
ベータ機能: Text Services Framework 再変換を使用(区切り、モード変更、開始文字が無効化されます)<br />
|
ベータ機能: Text Services Framework 再変換を使用(区切り、モード変更、開始文字が無効化されます)<br />
|
||||||
Windows11を使用している場合は、「以前のバージョンの Microsoft IME を使う」を有効化する必要があります。
|
Windows10または11を使用している場合は、「以前のバージョンの Microsoft IME を使う」を有効化する必要があります。
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
@ -10,7 +10,7 @@ const TitleBar = () => {
|
|||||||
<div className="flex justify-between items-center bg-gray-800 text-white h-8 px-2" data-tauri-drag-region>
|
<div className="flex justify-between items-center bg-gray-800 text-white h-8 px-2" data-tauri-drag-region>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="text-sm font-semibold">VRClipboard-IME</span>
|
<span className="text-sm font-semibold">VRClipboard-IME</span>
|
||||||
<span className="text-xs font-semibold ml-2">v1.6.0</span>
|
<span className="text-xs font-semibold ml-2">v1.7.0</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<button onClick={handleMinimize} className="p-1 hover:bg-gray-700 focus:outline-none">
|
<button onClick={handleMinimize} className="p-1 hover:bg-gray-700 focus:outline-none">
|
||||||
|
Reference in New Issue
Block a user