mirror of
https://github.com/qingjian-team/qingjian.git
synced 2026-11-06 08:13:51 +00:00
之前九宫格在拼音列选定的音节与没定的看起来一样;退格把选定的音节一个字母一个字母地拆,退回成数字后还会换成默认读法(ni 变成 mi)。之前也没有一次删掉已上屏文字的办法,上屏联系人名字后按删除会连名字一起删,按重输不起作用。 - 九宫格:拼音行上选定的音节画下划线(渲染器新增 `PreeditStyle::Pinned`) - 九宫格退格:先撤回已选的字,末尾是选定的音节就取消选定、整个退回成按键,可以重选,之后再逐个删键;删完它后面的键时自动接的分隔符一起去掉 - 退回成按键后,候选与拼音行仍按用户选过的写法排,`64` 还读 ni;打了新键就不再偏向 - 在拼音列选音节不再清掉前面选好还没上屏的词 - 删除键上划(「设置 → 键盘 → 手势 → 上划删除键清空」,缺省开)删掉光标前的全部文字,最多 5000 字;候选条给「撤销」,点了原样放回,包括按下那一下删掉的字;打拼音时上划只清拼音 - 候选条上放着联系人的电话邮箱或「撤销」时,删除键、重输键的第一下只收起它们,不删已上屏的字 测试:Core、会话层单元测试;真机(Android 16)九宫格 64426 选 ni、hao 后逐次退格;上划清空后撤销;上屏联系人后按重输、删除只收起号码。
263 lines
8.4 KiB
Rust
263 lines
8.4 KiB
Rust
//! 会话层端到端:样例词库上走一遍 敲键 → 选词 → 上屏。
|
|
use std::path::Path;
|
|
|
|
use qingjian_android::{Frame, InputOptions, Session};
|
|
use qingjian_core::{Engine, Language};
|
|
use qingjian_dictionary::Dictionary;
|
|
use qingjian_translate::Glossary;
|
|
|
|
fn sample(name: &str) -> std::path::PathBuf {
|
|
Path::new(env!("CARGO_MANIFEST_DIR"))
|
|
.join("../../../assets/sample")
|
|
.join(name)
|
|
}
|
|
|
|
fn session() -> Session {
|
|
let glossary = Glossary::from_path(Language::English, sample("glossary-en.tsv")).unwrap();
|
|
let engine = Engine::new(Dictionary::from_path(sample("dict.tsv")).unwrap())
|
|
.with_translator(Box::new(glossary));
|
|
let code_table =
|
|
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../assets/schemes/wubi86/table.tsv");
|
|
let user_dir = std::env::temp_dir().join(format!("qingjian-session-{}", std::process::id()));
|
|
Session::new(engine, code_table, sample(""), user_dir)
|
|
}
|
|
|
|
fn type_keys(session: &mut Session, keys: &str) {
|
|
for c in keys.chars() {
|
|
session.push(c);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn selecting_first_candidate_commits_and_clears() {
|
|
let mut session = session();
|
|
type_keys(&mut session, "niha");
|
|
let frame = session.push('o');
|
|
assert_eq!(frame.preedit, "ni'hao");
|
|
assert_eq!(frame.candidates[0].text, "你好");
|
|
|
|
let frame = session.select(0);
|
|
assert_eq!(frame.commit, "你好");
|
|
assert!(frame.preedit.is_empty());
|
|
assert!(frame.candidates.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn commit_raw_returns_typed_letters() {
|
|
let mut session = session();
|
|
type_keys(&mut session, "niha");
|
|
let frame = session.commit_raw();
|
|
assert_eq!(frame.commit, "niha");
|
|
assert!(frame.preedit.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn backspace_to_empty_gives_empty_frame() {
|
|
let mut session = session();
|
|
session.push('n');
|
|
let frame = session.backspace();
|
|
assert!(frame.preedit.is_empty());
|
|
assert!(frame.candidates.is_empty());
|
|
assert!(frame.commit.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn frame_json_shape() {
|
|
let mut session = session();
|
|
type_keys(&mut session, "niha");
|
|
let json = session.push('o').to_json();
|
|
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(value["preedit"], "ni'hao");
|
|
assert_eq!(value["candidates"][0]["text"], "你好");
|
|
}
|
|
|
|
#[test]
|
|
fn switching_to_english_commits_typed_letters() {
|
|
let mut session = session();
|
|
type_keys(&mut session, "niha");
|
|
let frame = session.set_english_mode(true);
|
|
assert_eq!(frame.commit, "niha");
|
|
assert!(frame.preedit.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn selecting_translation_commits_the_gloss() {
|
|
let mut session = session();
|
|
type_keys(&mut session, "kaif");
|
|
let frame = session.push('a');
|
|
let index = frame
|
|
.candidates
|
|
.iter()
|
|
.position(|candidate| candidate.text == "开发")
|
|
.unwrap();
|
|
let gloss = frame.candidates[index].senses[0].text.clone();
|
|
|
|
let frame = session.select_translation(index, 0);
|
|
assert_eq!(frame.commit, gloss);
|
|
assert!(frame.preedit.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn missing_sense_leaves_composition_alone() {
|
|
let mut session = session();
|
|
type_keys(&mut session, "kaif");
|
|
session.push('a');
|
|
let frame = session.select_translation(0, 9);
|
|
assert!(frame.commit.is_empty());
|
|
assert_eq!(frame.preedit, "kai'fa");
|
|
}
|
|
|
|
#[test]
|
|
fn forgetting_reports_a_notice() {
|
|
let mut session = session();
|
|
type_keys(&mut session, "kaif");
|
|
session.push('a');
|
|
let frame = session.forget(0);
|
|
assert!(frame.notice.contains("开发"));
|
|
assert_eq!(frame.preedit, "kai'fa");
|
|
}
|
|
|
|
#[test]
|
|
fn nine_key_digits_compose_and_pin() {
|
|
let mut session = session();
|
|
session.set_nine_key(true);
|
|
type_keys(&mut session, "5243");
|
|
let frame = session.push('2');
|
|
assert_eq!(frame.preedit, "kai'fa");
|
|
assert_eq!(frame.candidates[0].text, "开发");
|
|
assert_eq!(frame.pinyin_choices[0], "kai");
|
|
|
|
let frame = session.pin("kai");
|
|
assert_eq!(frame.candidates[0].text, "开发");
|
|
let frame = session.select(0);
|
|
assert_eq!(frame.commit, "开发");
|
|
assert!(frame.preedit.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn shuangpin_keys_decode_with_the_chosen_scheme() {
|
|
let mut session = session();
|
|
session.set_shuangpin("xiaohe", false);
|
|
type_keys(&mut session, "nihc");
|
|
let frame = session.select(0);
|
|
assert_eq!(frame.commit, "你好");
|
|
session.set_shuangpin("", false);
|
|
type_keys(&mut session, "nihao");
|
|
assert_eq!(session.select(0).commit, "你好");
|
|
}
|
|
|
|
#[test]
|
|
fn wubi_codes_look_up_the_code_table() {
|
|
let mut session = session();
|
|
session.set_wubi(true, false);
|
|
type_keys(&mut session, "wqvb");
|
|
assert_eq!(session.select(0).commit, "你好");
|
|
session.set_wubi(true, true);
|
|
type_keys(&mut session, "nihao");
|
|
assert_eq!(session.select(0).commit, "你好");
|
|
session.set_wubi(false, false);
|
|
type_keys(&mut session, "nihao");
|
|
assert_eq!(session.select(0).commit, "你好");
|
|
}
|
|
|
|
#[test]
|
|
fn learning_language_off_drops_translations() {
|
|
let mut session = session();
|
|
let options = |language: &str| -> InputOptions {
|
|
serde_json::from_str(&format!(
|
|
r#"{{"full_width_punctuation":true,"english_in_chinese":true,"chinese_first":false,"learning_language":"{language}"}}"#
|
|
))
|
|
.unwrap()
|
|
};
|
|
session.set_options(&options("off"));
|
|
type_keys(&mut session, "kaifa");
|
|
let frame = session.set_options(&options("off"));
|
|
assert!(frame.candidates.iter().all(|c| c.senses.is_empty()));
|
|
|
|
let frame = session.set_options(&options("en"));
|
|
let kaifa = frame.candidates.iter().find(|c| c.text == "开发").unwrap();
|
|
assert!(!kaifa.senses.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn translation_engine_loads_without_learning_data_and_needs_cloud() {
|
|
let user_dir =
|
|
std::env::temp_dir().join(format!("qingjian-translation-{}", std::process::id()));
|
|
let engine =
|
|
qingjian_android::assemble_translation(&sample(""), &user_dir, Some(Language::English))
|
|
.unwrap();
|
|
assert_eq!(engine.learning_language(), Language::English);
|
|
let mut session = Session::new(engine, sample("wubi86.tsv"), sample(""), user_dir);
|
|
// 没接云端时不发
|
|
assert!(!session.translate("hello"));
|
|
}
|
|
|
|
fn select_text(session: &mut Session, frame: &Frame, text: &str) -> Frame {
|
|
let index = frame
|
|
.candidates
|
|
.iter()
|
|
.position(|c| c.text == text)
|
|
.unwrap();
|
|
session.select(index)
|
|
}
|
|
|
|
#[test]
|
|
fn partial_selection_stays_in_the_composition() {
|
|
let mut session = session();
|
|
type_keys(&mut session, "nihaokaif");
|
|
let frame = session.push('a');
|
|
let frame = select_text(&mut session, &frame, "你好");
|
|
assert_eq!(frame.commit, "");
|
|
assert!(frame.preedit.starts_with("你好"));
|
|
// 空格接着选剩下的,整段选完一起上屏
|
|
let frame = session.space();
|
|
assert_eq!(frame.commit, "你好开发");
|
|
assert!(frame.preedit.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn backspace_undoes_and_enter_keeps_held_words() {
|
|
let mut session = session();
|
|
type_keys(&mut session, "nihaokaif");
|
|
let frame = session.push('a');
|
|
let frame = select_text(&mut session, &frame, "你好");
|
|
assert!(frame.preedit.starts_with("你好"));
|
|
let frame = session.backspace();
|
|
assert!(!frame.preedit.contains("你好"));
|
|
assert!(frame.candidates.iter().any(|c| c.text == "你好"));
|
|
|
|
let frame = select_text(&mut session, &frame, "你好");
|
|
assert_eq!(frame.commit, "");
|
|
assert_eq!(session.commit_raw().commit, "你好kaifa");
|
|
}
|
|
|
|
#[test]
|
|
fn holding_selections_can_be_turned_off() {
|
|
let mut session = session();
|
|
let options: InputOptions = serde_json::from_str(
|
|
r#"{"full_width_punctuation":true,"english_in_chinese":true,"chinese_first":false,"hold_selections":false}"#,
|
|
)
|
|
.unwrap();
|
|
session.set_options(&options);
|
|
type_keys(&mut session, "nihaokaif");
|
|
let frame = session.push('a');
|
|
let frame = select_text(&mut session, &frame, "你好");
|
|
assert_eq!(frame.commit, "你好");
|
|
}
|
|
|
|
#[test]
|
|
fn clearing_offers_an_undo_until_text_comes_back() {
|
|
let mut session = session();
|
|
let frame = session.cleared("你好,世界");
|
|
assert_eq!(frame.candidates.len(), 1);
|
|
assert_eq!(frame.candidates[0].text, "撤销");
|
|
// 删完光标前是空的,壳问补全时留着
|
|
assert_eq!(session.complete("").candidates.len(), 1);
|
|
let frame = session.select(0);
|
|
assert_eq!(frame.commit, "你好,世界");
|
|
assert!(frame.candidates.is_empty());
|
|
|
|
session.cleared("abc");
|
|
assert!(session.complete("x").candidates.is_empty());
|
|
}
|