gtk で設定画面をつくった

This commit is contained in:
Tokuhiro Matsuno
2023-01-24 19:00:18 +09:00
parent 11cd3326a0
commit 9635a6d2e9
14 changed files with 309 additions and 132 deletions

7
Cargo.lock generated
View File

@ -26,10 +26,13 @@ dependencies = [
name = "akaza-conf" name = "akaza-conf"
version = "0.1.7" version = "0.1.7"
dependencies = [ dependencies = [
"anyhow",
"env_logger", "env_logger",
"gtk4", "gtk4",
"libakaza", "libakaza",
"log", "log",
"serde",
"serde_yaml",
"xdg", "xdg",
] ]
@ -1533,9 +1536,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_yaml" name = "serde_yaml"
version = "0.9.16" version = "0.9.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92b5b431e8907b50339b51223b97d102db8d987ced36f6e4d03621db9316c834" checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"itoa", "itoa",

View File

@ -1,3 +1,7 @@
# 2023-01-24(Tue)
* 設定画面を gtk4-rs で実装した。
# 2022-12-23(Fri) # 2022-12-23(Fri)
* Started development, again. * Started development, again.

View File

@ -56,36 +56,6 @@ ibus-akaza をインストールしてください。
## 設定方法 ## 設定方法
### config.yml
XDG の設定ファイルディレクトリ以下、通常であれば `$HOME/.config/akaza/config.yml` に設定ファイルを書くことができます。
設定可能な項目は以下のもの。
* ユーザー辞書の設定
サンプルの設定は以下のような感じになります。
akaza が提供しているシステム辞書は偏りがすごくあるので、SKK-JISYO.L を読み込むことをおすすめします。たとえば以下のように設定すると良いでしょう。
---
dicts:
- path: /usr/share/skk/SKK-JISYO.L
encoding: EucJp
dict_type: SKK
- path: /usr/share/skk/SKK-JISYO.jinmei
encoding: EucJp
dict_type: SKK
single_term:
- path: /usr/share/akaza/SKK-JISYO.dynamic
encoding: Utf8
dict_type: SKK
akaza に付属する SKK-JISYO.dynamic を利用すると、「きょう」を変換すると、今日の日付がでるという機能が利用可能です。
↓ かな入力したい場合は以下のように設定してください。
romkan: kana
### Keymap の設定 ### Keymap の設定
Akaza は典型的には以下の順番で探します。 Akaza は典型的には以下の順番で探します。

View File

@ -10,3 +10,6 @@ xdg = "2.4.1"
log = "0.4.17" log = "0.4.17"
env_logger = "0.10.0" env_logger = "0.10.0"
libakaza = { path = "../libakaza" } libakaza = { path = "../libakaza" }
anyhow = "1.0.68"
serde = "1.0.152"
serde_yaml = "0.9.17"

View File

@ -1,65 +1,109 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command;
use std::sync::{Arc, Mutex};
use anyhow::Result;
use gtk::glib::signal::Inhibit; use gtk::glib::signal::Inhibit;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button, Label, Notebook}; use gtk::{Application, ApplicationWindow, Button, Label, Notebook};
use gtk4 as gtk; use gtk4 as gtk;
use gtk4::builders::ComboBoxTextBuilder;
use gtk4::gio::ApplicationFlags; use gtk4::gio::ApplicationFlags;
use gtk4::{ComboBoxText, Grid}; use gtk4::{ComboBoxText, Grid};
use libakaza::config::Config; use log::{error, info};
use log::info;
pub fn open_configuration_window() { use libakaza::config::{Config, DictUsage};
pub fn open_configuration_window() -> Result<()> {
let config = Arc::new(Mutex::new(Config::load()?));
let app = Application::new(Some("com.github.akaza.config"), ApplicationFlags::empty()); let app = Application::new(Some("com.github.akaza.config"), ApplicationFlags::empty());
app.connect_activate(|app| { app.connect_activate(move |app| {
let window = ApplicationWindow::builder() connect_activate(app, config.clone()).unwrap();
.application(app)
.default_width(320)
.default_height(200)
.title("Akaza の設定")
.build();
let notebook = Notebook::builder().build();
notebook.append_page(&build_core_pane(), Some(&Label::new(Some("基本設定"))));
notebook.append_page(&build_dict_pane(), Some(&Label::new(Some("辞書"))));
notebook.append_page(&build_about_pane(), Some(&Label::new(Some("アバウト"))));
let grid = Grid::builder().build();
grid.attach(&notebook, 0, 0, 6, 1);
let ok_button = Button::with_label("OK");
ok_button.connect_clicked(|_| {
eprintln!("Save the configuration...");
// TODO: 保存処理
});
let cancel_button = Button::with_label("Cancel");
{
let window_clone = window.clone();
cancel_button.connect_clicked(move |_| {
eprintln!("Close the configuration window!");
window_clone.close();
});
}
grid.attach(&ok_button, 4, 1, 1, 1);
grid.attach(&cancel_button, 5, 1, 1, 1);
window.set_child(Some(&grid));
window.connect_close_request(move |window| {
if let Some(application) = window.application() {
application.remove_window(window);
}
Inhibit(false)
});
window.show();
}); });
let v: Vec<String> = Vec::new(); let v: Vec<String> = Vec::new();
app.run_with_args(v.as_slice()); app.run_with_args(v.as_slice());
Ok(())
}
fn connect_activate(app: &Application, config: Arc<Mutex<Config>>) -> Result<()> {
let window = ApplicationWindow::builder()
.application(app)
.default_width(320)
.default_height(200)
.title("Akaza の設定")
.build();
let notebook = Notebook::builder().build();
notebook.append_page(
&build_core_pane(config.clone())?,
Some(&Label::new(Some("基本設定"))),
);
notebook.append_page(
&build_dict_pane(config.clone()),
Some(&Label::new(Some("辞書"))),
);
notebook.append_page(&build_about_pane(), Some(&Label::new(Some("アバウト"))));
let grid = Grid::builder().build();
grid.attach(&notebook, 0, 0, 6, 1);
let ok_button = Button::with_label("OK");
let config = config;
ok_button.connect_clicked(move |_| {
eprintln!("Save the configuration...");
// TODO: 保存処理
let config = config.lock().unwrap();
let config = Config {
keymap: config.keymap.to_string(),
romkan: config.romkan.to_string(),
model: config.model.to_string(),
dicts: config.dicts.clone(),
};
info!("Saving config: {}", serde_yaml::to_string(&config).unwrap());
config.save().unwrap();
// 最後に ibus restart をしちゃおう。設定の再読み込みとか実装するのは大変。
let output = Command::new("ibus").arg("restart").output().unwrap();
if !output.status.success() {
error!(
"Cannot run `ibus restart`: out={}, err={}",
String::from_utf8(output.stdout).unwrap(),
String::from_utf8(output.stderr).unwrap()
);
} else {
info!(
"Ran `ibus restart`: out={}, err={}",
String::from_utf8(output.stdout).unwrap(),
String::from_utf8(output.stderr).unwrap()
);
}
});
let cancel_button = Button::with_label("Cancel");
{
let window_clone = window.clone();
cancel_button.connect_clicked(move |_| {
eprintln!("Close the configuration window!");
window_clone.close();
});
}
grid.attach(&ok_button, 4, 1, 1, 1);
grid.attach(&cancel_button, 5, 1, 1, 1);
window.set_child(Some(&grid));
window.connect_close_request(move |window| {
if let Some(application) = window.application() {
application.remove_window(window);
}
Inhibit(false)
});
window.show();
Ok(())
} }
#[derive(Debug)] #[derive(Debug)]
@ -68,7 +112,7 @@ struct PathConfItem {
path: String, path: String,
} }
fn get_keymap_list<P>(path: &str, filter: P) -> Vec<PathConfItem> fn get_list<P>(path: &str, filter: P) -> Vec<PathConfItem>
where where
P: FnMut(&&PathBuf) -> bool, P: FnMut(&&PathBuf) -> bool,
{ {
@ -85,13 +129,14 @@ where
.unwrap() .unwrap()
.to_str() .to_str()
.unwrap() .unwrap()
.to_string(), .to_string()
.replace(".yml", ""),
path: f.to_string_lossy().to_string(), path: f.to_string_lossy().to_string(),
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
fn build_core_pane() -> Grid { fn build_core_pane(config: Arc<Mutex<Config>>) -> anyhow::Result<Grid> {
// キーマップとローマ字テーブル、モデルの設定ができるようにする。 // キーマップとローマ字テーブル、モデルの設定ができるようにする。
let grid = Grid::new(); let grid = Grid::new();
// xalign: 0 は左寄という意味。 // xalign: 0 は左寄という意味。
@ -104,13 +149,20 @@ fn build_core_pane() -> Grid {
); );
grid.attach( grid.attach(
&{ &{
//TODO: 設定ファイルに書いてあるものを default value として選ぶ
let cbt = ComboBoxText::new(); let cbt = ComboBoxText::new();
let romkan = get_keymap_list("keymap", { |f| f.to_string_lossy().ends_with(".yml") }); let keymap = get_list("keymap", { |f| f.to_string_lossy().ends_with(".yml") });
info!("keymap: {:?}", romkan); for item in keymap {
for item in romkan {
cbt.append(Some(&item.path), &item.name); cbt.append(Some(&item.path), &item.name);
} }
cbt.set_active_id(Some(&config.lock().unwrap().keymap));
{
let config = config.clone();
cbt.connect_changed(move |f| {
if let Some(id) = f.active_id() {
config.lock().unwrap().keymap = id.to_string();
}
});
}
cbt cbt
}, },
1, 1,
@ -131,11 +183,20 @@ fn build_core_pane() -> Grid {
grid.attach( grid.attach(
&{ &{
let cbt = ComboBoxText::new(); let cbt = ComboBoxText::new();
let romkan = get_keymap_list("romkan", { |f| f.to_string_lossy().ends_with(".yml") }); let romkan = get_list("romkan", { |f| f.to_string_lossy().ends_with(".yml") });
info!("romkan: {:?}", romkan); info!("romkan: {:?}", romkan);
for item in romkan { for item in romkan {
cbt.append(Some(&item.path), &item.name); cbt.append(Some(&item.path), &item.name);
} }
cbt.set_active_id(Some(&config.lock().unwrap().romkan));
let config = config.clone();
cbt.connect_changed(move |f| {
if let Some(id) = f.active_id() {
config.lock().unwrap().romkan = id.to_string();
}
});
cbt cbt
}, },
1, 1,
@ -153,13 +214,21 @@ fn build_core_pane() -> Grid {
grid.attach( grid.attach(
&{ &{
let cbt = ComboBoxText::new(); let cbt = ComboBoxText::new();
let romkan = get_keymap_list("model", { let model = get_list("model", {
|f| !f.file_name().unwrap().to_string_lossy().starts_with('.') |f| !f.file_name().unwrap().to_string_lossy().starts_with('.')
}); });
info!("model: {:?}", romkan); info!("model: {:?}", model);
for item in romkan { for item in model {
cbt.append(Some(&item.path), &item.name); cbt.append(Some(&item.path), &item.name);
} }
cbt.set_active_id(Some(&config.lock().unwrap().model));
cbt.connect_changed(move |f| {
if let Some(id) = f.active_id() {
config.lock().unwrap().model = id.to_string();
}
});
cbt cbt
}, },
1, 1,
@ -167,11 +236,59 @@ fn build_core_pane() -> Grid {
1, 1,
1, 1,
); );
grid Ok(grid)
} }
fn build_dict_pane() -> Label { fn build_dict_pane(config: Arc<Mutex<Config>>) -> Grid {
Label::new(Some("(工事中)")) let grid = Grid::builder().column_spacing(10).build();
// TODO /usr/share/skk/ 以下のものを拾ってきて入れる
for (i, dict_config) in config.lock().unwrap().dicts.iter().enumerate() {
grid.attach(
&Label::builder()
.xalign(0_f32)
.label(dict_config.path.as_str())
.build(),
0,
i as i32,
1,
1,
);
let cbt = ComboBoxText::builder().build();
for usage in vec![
DictUsage::Normal,
DictUsage::SingleTerm,
DictUsage::Disabled,
] {
cbt.append(Some(usage.as_str()), usage.text_jp());
}
cbt.set_active_id(Some(dict_config.usage.as_str()));
{
let config = config.clone();
let path = dict_config.path.clone();
cbt.connect_changed(move |f| {
if let Some(id) = f.active_id() {
let mut config = config.lock().unwrap();
for mut dict in &mut config.dicts {
if dict.path == path {
dict.usage = DictUsage::from(&id).unwrap();
break;
}
}
}
});
}
grid.attach(&cbt, 1, i as i32, 1, 1);
grid.attach(
&Label::new(Some(dict_config.dict_type.as_str())),
2,
i as i32,
1,
1,
);
}
grid
} }
fn build_about_pane() -> Label { fn build_about_pane() -> Label {

View File

@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex};
use log::info; use log::info;
use libakaza::config::{Config, DictConfig, DictEncoding, DictType}; use libakaza::config::{Config, DictConfig, DictEncoding, DictType, DictUsage};
use libakaza::engine::bigram_word_viterbi_engine::BigramWordViterbiEngineBuilder; use libakaza::engine::bigram_word_viterbi_engine::BigramWordViterbiEngineBuilder;
use libakaza::user_side_data::user_data::UserData; use libakaza::user_side_data::user_data::UserData;
@ -16,14 +16,15 @@ pub fn check(yomi: &str, expected: Option<String>, user_data: bool) -> anyhow::R
dict_type: DictType::SKK, dict_type: DictType::SKK,
encoding: DictEncoding::EucJp, encoding: DictEncoding::EucJp,
path: "/usr/share/skk/SKK-JISYO.L".to_string(), path: "/usr/share/skk/SKK-JISYO.L".to_string(),
usage: DictUsage::Normal,
}, },
DictConfig { DictConfig {
dict_type: DictType::SKK, dict_type: DictType::SKK,
encoding: DictEncoding::Utf8, encoding: DictEncoding::Utf8,
path: "data/SKK-JISYO.akaza".to_string(), path: "data/SKK-JISYO.akaza".to_string(),
usage: DictUsage::Normal,
}, },
], ],
single_term: Default::default(),
romkan: Default::default(), romkan: Default::default(),
keymap: Default::default(), keymap: Default::default(),
model: Default::default(), model: Default::default(),

View File

@ -5,7 +5,7 @@ use std::time::SystemTime;
use anyhow::Context; use anyhow::Context;
use log::info; use log::info;
use libakaza::config::{Config, DictConfig, DictEncoding, DictType}; use libakaza::config::{Config, DictConfig, DictEncoding, DictType, DictUsage};
use libakaza::engine::base::HenkanEngine; use libakaza::engine::base::HenkanEngine;
use libakaza::engine::bigram_word_viterbi_engine::BigramWordViterbiEngineBuilder; use libakaza::engine::bigram_word_viterbi_engine::BigramWordViterbiEngineBuilder;
@ -58,6 +58,7 @@ pub fn evaluate(
dict_type: DictType::SKK, dict_type: DictType::SKK,
encoding: DictEncoding::EucJp, encoding: DictEncoding::EucJp,
path: path.clone(), path: path.clone(),
usage: DictUsage::Normal,
}) })
} }
@ -66,12 +67,12 @@ pub fn evaluate(
dict_type: DictType::SKK, dict_type: DictType::SKK,
encoding: DictEncoding::Utf8, encoding: DictEncoding::Utf8,
path: path.clone(), path: path.clone(),
usage: DictUsage::Normal,
}) })
} }
let mut builder = BigramWordViterbiEngineBuilder::new(Config { let mut builder = BigramWordViterbiEngineBuilder::new(Config {
dicts, dicts,
single_term: Default::default(),
romkan: Default::default(), romkan: Default::default(),
keymap: Default::default(), keymap: Default::default(),
model: Default::default(), model: Default::default(),

View File

@ -102,7 +102,10 @@ impl AkazaContext {
) { ) {
debug!("do_property_activate: {}, {}", prop_name, prop_state); debug!("do_property_activate: {}, {}", prop_name, prop_state);
if prop_name == "PrefPane" { if prop_name == "PrefPane" {
open_configuration_window(); match open_configuration_window() {
Ok(_) => {}
Err(e) => info!("Err: {}", e),
}
} else if prop_state == IBusPropState_PROP_STATE_CHECKED } else if prop_state == IBusPropState_PROP_STATE_CHECKED
&& prop_name.starts_with("InputMode.") && prop_name.starts_with("InputMode.")
{ {

View File

@ -38,8 +38,8 @@ impl KeyMap {
unsafe { ibus_keyval_from_name(cs.as_ptr()) } unsafe { ibus_keyval_from_name(cs.as_ptr()) }
} }
pub(crate) fn new(keymap_name: String) -> anyhow::Result<Self> { pub(crate) fn new(keymap_path: String) -> anyhow::Result<Self> {
let keymap = Keymap::load(keymap_name.as_str())?; let keymap = Keymap::load(keymap_path.as_str())?;
let mut mapping: HashMap<KeyPattern, String> = HashMap::new(); let mut mapping: HashMap<KeyPattern, String> = HashMap::new();
for (key_pattern, command) in keymap { for (key_pattern, command) in keymap {

View File

@ -8,17 +8,21 @@ dicts:
use std::fmt::Display; use std::fmt::Display;
use std::fmt::Formatter; use std::fmt::Formatter;
use std::fs::File; use std::fs::File;
use std::io::BufReader; use std::io::{BufReader, Write};
use std::path::PathBuf;
use anyhow::Result; use anyhow::{bail, Result};
use log::{info, warn}; use log::{info, warn};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use DictEncoding::Utf8; use DictEncoding::Utf8;
use crate::config::DictUsage::{Normal, SingleTerm};
use crate::resource::detect_resource_path;
#[derive(Debug, PartialEq, Serialize, Deserialize, Default)] #[derive(Debug, PartialEq, Serialize, Deserialize, Default)]
pub struct Config { pub struct Config {
pub dicts: Vec<DictConfig>, pub dicts: Vec<DictConfig>,
pub single_term: Vec<DictConfig>,
/// ローマ字かな変換テーブルの指定 /// ローマ字かな変換テーブルの指定
/// "default", "kana", etc. /// "default", "kana", etc.
@ -37,15 +41,15 @@ pub struct Config {
} }
fn default_romkan() -> String { fn default_romkan() -> String {
"default".to_string() detect_resource_path("romkan", "default").unwrap()
} }
fn default_keymap() -> String { fn default_keymap() -> String {
"default".to_string() detect_resource_path("keymap", "default").unwrap()
} }
fn default_model() -> String { fn default_model() -> String {
"default".to_string() detect_resource_path("model", "default").unwrap()
} }
impl Config { impl Config {
@ -56,9 +60,22 @@ impl Config {
Ok(config) Ok(config)
} }
pub fn load() -> Result<Self> { pub fn file_name() -> Result<PathBuf> {
let basedir = xdg::BaseDirectories::with_prefix("akaza")?; let basedir = xdg::BaseDirectories::with_prefix("akaza")?;
let configfile = basedir.get_config_file("config.yml"); Ok(basedir.get_config_file("config.yml"))
}
pub fn save(&self) -> Result<()> {
let file_name = Self::file_name()?;
let yml = serde_yaml::to_string(self)?;
info!("Write to file: {}", file_name.to_str().unwrap());
let mut fp = File::create(file_name)?;
fp.write_all(yml.as_bytes())?;
Ok(())
}
pub fn load() -> Result<Self> {
let configfile = Self::file_name()?;
let config = match Config::load_from_file(configfile.to_str().unwrap()) { let config = match Config::load_from_file(configfile.to_str().unwrap()) {
Ok(config) => config, Ok(config) => config,
Err(err) => { Err(err) => {
@ -85,11 +102,11 @@ pub struct DictConfig {
/// Encoding of the dictionary /// Encoding of the dictionary
/// Default: UTF-8 /// Default: UTF-8
// #[serde(default = "default_encoding")]
pub encoding: DictEncoding, pub encoding: DictEncoding,
// #[serde(default = "default_dict_type")]
pub dict_type: DictType, pub dict_type: DictType,
pub usage: DictUsage,
} }
fn default_encoding() -> DictEncoding { fn default_encoding() -> DictEncoding {
@ -121,7 +138,7 @@ impl Display for DictEncoding {
impl DictEncoding { impl DictEncoding {
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
match self { match self {
DictEncoding::Utf8 => "UTF-8", Utf8 => "UTF-8",
DictEncoding::EucJp => "EUC-JP", DictEncoding::EucJp => "EUC-JP",
} }
} }
@ -152,6 +169,46 @@ impl DictType {
} }
} }
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub enum DictUsage {
Normal,
SingleTerm,
Disabled,
}
impl Default for DictUsage {
fn default() -> Self {
Normal
}
}
impl DictUsage {
pub fn from(s: &str) -> Result<DictUsage> {
match s {
"Normal" => Ok(Normal),
"SingleTerm" => Ok(SingleTerm),
"Disabled" => Ok(DictUsage::Disabled),
_ => bail!("Unknown name: {:?}", s),
}
}
pub fn as_str(&self) -> &'static str {
match self {
Normal => "Normal",
SingleTerm => "SingleTerm",
DictUsage::Disabled => "Disabled",
}
}
pub fn text_jp(&self) -> &'static str {
match self {
Normal => "通常辞書",
SingleTerm => "単項",
DictUsage::Disabled => "無効",
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -166,6 +223,7 @@ mod tests {
path: "/usr/share/skk/SKK-JISYO.L".to_string(), path: "/usr/share/skk/SKK-JISYO.L".to_string(),
encoding: DictEncoding::EucJp, encoding: DictEncoding::EucJp,
dict_type: DictType::SKK, dict_type: DictType::SKK,
usage: DictUsage::Normal,
} }
); );
Ok(()) Ok(())

View File

@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex};
use anyhow::Result; use anyhow::Result;
use crate::config::{Config, DictConfig, DictEncoding, DictType}; use crate::config::{Config, DictConfig, DictEncoding, DictType, DictUsage};
use crate::dict::loader::load_dicts_ex; use crate::dict::loader::load_dicts_ex;
use crate::engine::base::HenkanEngine; use crate::engine::base::HenkanEngine;
use crate::graph::candidate::Candidate; use crate::graph::candidate::Candidate;
@ -124,21 +124,22 @@ impl BigramWordViterbiEngineBuilder {
let system_unigram_lm = match &self.model_dir { let system_unigram_lm = match &self.model_dir {
Some(path) => MarisaSystemUnigramLM::load(&format!("{}/unigram.model", path)), Some(path) => MarisaSystemUnigramLM::load(&format!("{}/unigram.model", path)),
None => MarisaSystemUnigramLM::load( None => {
Self::try_load(&format!("{}/unigram.model", model_name))?.as_str(), MarisaSystemUnigramLM::load(Self::try_load(&model_name, "unigram.model")?.as_str())
), }
}?; }?;
let system_bigram_lm = match &self.model_dir { let system_bigram_lm = match &self.model_dir {
Some(path) => MarisaSystemBigramLM::load(&format!("{}/bigram.model", path.clone())), Some(path) => MarisaSystemBigramLM::load(&format!("{}/bigram.model", path.clone())),
None => MarisaSystemBigramLM::load( None => {
Self::try_load(&format!("{}/bigram.model", model_name))?.as_str(), MarisaSystemBigramLM::load(Self::try_load(&model_name, "bigram.model")?.as_str())
), }
}?; }?;
// TODO Merge self.model_dir and config.model
let system_dict = match &self.model_dir { let system_dict = match &self.model_dir {
Some(path) => { Some(path) => {
format!("{}/SKK-JISYO.akaza", path) format!("{}/SKK-JISYO.akaza", path)
} }
None => Self::try_load(&format!("{}/SKK-JISYO.akaza", model_name))?, None => Self::try_load(&model_name, "SKK-JISYO.akaza")?,
}; };
let user_data = if let Some(d) = &self.user_data { let user_data = if let Some(d) = &self.user_data {
@ -148,17 +149,33 @@ impl BigramWordViterbiEngineBuilder {
}; };
let dict = { let dict = {
let mut dicts = self.config.dicts.to_vec(); let mut dicts = self
.config
.dicts
.iter()
.filter(|it| it.usage == DictUsage::Normal)
.cloned()
.collect::<Vec<_>>();
dicts.push(DictConfig { dicts.push(DictConfig {
path: system_dict, path: system_dict,
dict_type: DictType::SKK, dict_type: DictType::SKK,
encoding: DictEncoding::Utf8, encoding: DictEncoding::Utf8,
usage: DictUsage::Normal,
}); });
load_dicts_ex(&dicts, "kana_kanji_cache.marisa")? load_dicts_ex(&dicts, "kana_kanji_cache.marisa")?
}; };
let single_term = load_dicts_ex(&self.config.single_term, "single_term_cache.marisa")?; let single_term = load_dicts_ex(
&self
.config
.dicts
.iter()
.filter(|it| it.usage == DictUsage::SingleTerm)
.cloned()
.collect::<Vec<_>>(),
"single_term_cache.marisa",
)?;
// 辞書を元に、トライを作成していく。 // 辞書を元に、トライを作成していく。
let mut kana_trie = CedarwoodKanaTrie::default(); let mut kana_trie = CedarwoodKanaTrie::default();
@ -203,7 +220,7 @@ impl BigramWordViterbiEngineBuilder {
}) })
} }
fn try_load(name: &str) -> Result<String> { fn try_load(model_dir: &str, name: &str) -> Result<String> {
detect_resource_path("model", name) Ok(model_dir.to_string() + "/" + name)
} }
} }

View File

@ -92,11 +92,10 @@ pub enum KeyState {
} }
impl Keymap { impl Keymap {
pub fn load(name: &str) -> Result<HashMap<KeyPattern, String>> { pub fn load(keymap_path: &str) -> Result<HashMap<KeyPattern, String>> {
let pathstr = detect_resource_path("keymap", &format!("{}.yml", name))?; info!("Load {}", keymap_path);
info!("Load {}", pathstr);
let got: Keymap = serde_yaml::from_reader(BufReader::new( let got: Keymap = serde_yaml::from_reader(BufReader::new(
File::open(&pathstr).with_context(|| pathstr)?, File::open(keymap_path).with_context(|| keymap_path.to_string())?,
))?; ))?;
if let Some(parent) = &got.extends { if let Some(parent) = &got.extends {

View File

@ -11,10 +11,12 @@ pub fn detect_resource_path(base: &str, name: &str) -> anyhow::Result<String> {
.with_context(|| "Opening xdg directory with 'akaza' prefix")?; .with_context(|| "Opening xdg directory with 'akaza' prefix")?;
let pathbuf = basedirs.find_data_file(&target_path); let pathbuf = basedirs.find_data_file(&target_path);
let Some(pathbuf) = pathbuf else { let Some(pathbuf) = pathbuf else {
bail!("Cannot find {:?} in XDG_DATA_HOME or XDG_DATA_DIRS(XDG_DATA_HOME={:?}, XDG_DATA_DIRS={:?})", bail!("Cannot find {:?} in XDG_DATA_HOME or XDG_DATA_DIRS(XDG_DATA_HOME={:?}, XDG_DATA_DIRS={:?}, base={:?}, name={:?})",
target_path, target_path,
basedirs.get_data_home().to_string_lossy().to_string(), basedirs.get_data_home().to_string_lossy().to_string(),
basedirs.get_data_dirs().iter().map(|x| x.to_string_lossy().to_string()).collect::<Vec<_>>(), basedirs.get_data_dirs().iter().map(|x| x.to_string_lossy().to_string()).collect::<Vec<_>>(),
base,
name
) )
}; };
pathbuf.to_string_lossy().to_string() pathbuf.to_string_lossy().to_string()

View File

@ -14,11 +14,10 @@ pub struct RomKanConfig {
extends: Option<String>, extends: Option<String>,
} }
fn load_romkan_map(name: &str) -> anyhow::Result<HashMap<String, String>> { fn load_romkan_map(file_path: &str) -> anyhow::Result<HashMap<String, String>> {
let pathstr = resource::detect_resource_path("romkan", &format!("{}.yml", name))?; info!("Load {}", file_path);
info!("Load {}", pathstr);
let got: RomKanConfig = serde_yaml::from_reader(BufReader::new( let got: RomKanConfig = serde_yaml::from_reader(BufReader::new(
File::open(&pathstr).with_context(|| pathstr)?, File::open(file_path).with_context(|| file_path.to_string())?,
))?; ))?;
if let Some(parent) = got.extends { if let Some(parent) = got.extends {