From 57554cd2da85e88851d27f8f6ef42e8e00c01030 Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Sun, 7 Jan 2024 22:13:19 +0900 Subject: [PATCH 1/9] docs(capi): document setup functions --- Cargo.toml | 4 + src/capi/{ => internal}/key2pho.rs | 0 src/capi/internal/mod.rs | 9 ++ src/capi/{ => internal}/path.rs | 0 src/capi/{ => internal}/utf8.rs | 0 src/capi/io.rs | 9 +- src/capi/mod.rs | 151 +++++++++++++++++++++++++++-- src/capi/public.rs | 65 ++++++++++++- src/capi/types.rs | 42 -------- src/lib.rs | 4 +- 10 files changed, 226 insertions(+), 58 deletions(-) rename src/capi/{ => internal}/key2pho.rs (100%) create mode 100644 src/capi/internal/mod.rs rename src/capi/{ => internal}/path.rs (100%) rename src/capi/{ => internal}/utf8.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index b6390a8a8..6fa93f979 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,3 +47,7 @@ resolver = "2" lto = true debug = true panic = "abort" + +[package.metadata.docs.rs] +features = ["capi", "sqlite"] +rustdoc-args = ["-Zunstable-options", "--sort-modules-by-appearance"] diff --git a/src/capi/key2pho.rs b/src/capi/internal/key2pho.rs similarity index 100% rename from src/capi/key2pho.rs rename to src/capi/internal/key2pho.rs diff --git a/src/capi/internal/mod.rs b/src/capi/internal/mod.rs new file mode 100644 index 000000000..a63f798c1 --- /dev/null +++ b/src/capi/internal/mod.rs @@ -0,0 +1,9 @@ +//! Internal functions visible for tests only + +mod key2pho; +mod path; +mod utf8; + +pub use key2pho::*; +pub use path::*; +pub use utf8::*; diff --git a/src/capi/path.rs b/src/capi/internal/path.rs similarity index 100% rename from src/capi/path.rs rename to src/capi/internal/path.rs diff --git a/src/capi/utf8.rs b/src/capi/internal/utf8.rs similarity index 100% rename from src/capi/utf8.rs rename to src/capi/internal/utf8.rs diff --git a/src/capi/io.rs b/src/capi/io.rs index 9e56256e6..695bf11ab 100644 --- a/src/capi/io.rs +++ b/src/capi/io.rs @@ -12,12 +12,9 @@ use std::{ use tracing::{debug, warn}; use crate::{ - capi::{ - public::{ - ChewingConfigData, IntervalType, CHINESE_MODE, FULLSHAPE_MODE, HALFSHAPE_MODE, - MAX_SELKEY, SYMBOL_MODE, - }, - types::{ChewingContext, SelKeys}, + capi::public::{ + ChewingConfigData, ChewingContext, IntervalType, SelKeys, CHINESE_MODE, FULLSHAPE_MODE, + HALFSHAPE_MODE, MAX_SELKEY, SYMBOL_MODE, }, conversion::{ChewingEngine, Interval, Symbol}, dictionary::{LayeredDictionary, SystemDictionaryLoader, UserDictionaryLoader}, diff --git a/src/capi/mod.rs b/src/capi/mod.rs index b9dd08d2c..8ca61ff72 100644 --- a/src/capi/mod.rs +++ b/src/capi/mod.rs @@ -1,10 +1,147 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(unsafe_code)] -pub mod ffi; -pub mod io; -pub mod key2pho; -pub mod path; -pub mod public; -pub mod types; -pub mod utf8; +mod ffi; +mod io; +mod public; + +#[doc(hidden)] +pub mod internal; + +/// Initializes chewing context and environment settings +/// +/// Most of the Chewing IM APIs require a [ChewingContext]. To create a +/// ChewingContext you must use the [chewing_new] function. +/// +/// # Examples +/// +/// Create a chewing context and deletes it after use. +/// +/// ```c +/// #include +/// int main(int argc, char *argv[]) +/// { +/// ChewingContext *ctx = chewing_new(); +/// +/// /* do something */ +/// +/// chewing_delete( ctx ); +/// +/// return 0; +/// } +/// ``` +/// +/// # Environment variables +/// +/// * `CHEWING_PATH` +/// * The CHEWING_PATH environment variable is used to set the search path +/// of static data used by the Chewing IM. The format of CHEWING_PATH is +/// the same as PATH, which is multiple paths separated by ‘:’ on POSIX +/// and Unix-like platforms, or separated by ‘;’ on Windows platform. The +/// directories in CHEWING_PATH could be read-only. +/// * `CHEWING_USER_PATH` +/// * The CHEWING_USER_PATH environment variable is used to specifies the +/// path where user-defined hash data stores. This path should be writable +/// by the user, or the Chewing IM will lose the ability to remember the +/// learned phrases. +pub mod setup { + /// This function exists only for backword compatibility. + /// + /// The `chewing_Init` function is no-op now. The return value is always 0. + pub use super::io::chewing_Init; + + /// Creates a new instance of the Chewing IM. + /// + /// The return value is a pointer to the new Chewing IM instance. + /// + /// See also the [chewing_new2], and [chewing_delete] functions. + pub use super::io::chewing_new; + + /// Creates a new instance of the Chewing IM. + /// + /// The `syspath` is the directory path to system dictionary. The `userpath` + /// is file path to user dictionary. User shall have enough permission to + /// update this file. The logger and loggerdata is logger function and its + /// data. + /// + /// All parameters will be default if set to NULL. + /// + /// The return value is a pointer to the new Chewing IM instance. See also + /// the [chewing_new], [chewing_delete] function. + pub use super::io::chewing_new2; + + /// Releases the resources used by the given Chewing IM instance. + pub use super::io::chewing_delete; + + /// Sets the selectAreaLen, maxChiSymbolLen and selKey parameter from pcd + /// + /// The pcd argument is a pointer to a Chewing configuration data structure. + /// See also the ChewingConfigData data type. + /// + /// The return value is 0 on success and -1 on failure. + /// + /// **Deprecated**, use the chewing_set_* function series to set parameters + /// instead. + pub use super::io::chewing_Configure; + + /// Resets all settings in the given Chewing IM instance. + /// + /// The return value is 0 on success and -1 on failure. + pub use super::io::chewing_Reset; + + /// Releases the memory allocated by the Chewing IM and returned to the + /// caller. + /// + /// There are functions returning pointers of strings or other data + /// structures that are allocated on the heap. These memory must be freed to + /// avoid memory leak. To avoid memory allocator mismatch between the + /// Chewing IM and the caller, use this function to free the resource. + /// + /// Do nothing if ptr is NULL. + pub use super::io::chewing_free; + + /// Sets the logger function logger. + /// + /// The logger function is used to provide log inside Chewing IM for + /// debugging. The data in chewing_set_logger is passed directly to data in + /// logger when logging. + /// + /// # Examples + /// + /// The following example shows how to use data: + /// + /// ```c + /// void logger( void *data, int level, const char *fmt, ... ) + /// { + /// FILE *fd = (FILE *) data; + /// ... + /// } + /// + /// int main() + /// { + /// ChewingContext *ctx; + /// FILE *fd; + /// ... + /// chewing_set_logger(ctx, logger, fd); + /// ... + /// } + /// ``` + /// + /// The level is log level. + pub use super::io::chewing_set_logger; + + pub use super::public::ChewingContext; + + pub use super::public::CHEWING_LOG_VERBOSE; + + pub use super::public::CHEWING_LOG_DEBUG; + + pub use super::public::CHEWING_LOG_INFO; + + pub use super::public::CHEWING_LOG_WARN; + + pub use super::public::CHEWING_LOG_ERROR; +} + +pub use io::*; +pub use public::*; diff --git a/src/capi/public.rs b/src/capi/public.rs index c7cb06b8e..0391295da 100644 --- a/src/capi/public.rs +++ b/src/capi/public.rs @@ -1,19 +1,38 @@ -use std::ffi::c_int; +use std::{ffi::c_int, iter::Peekable}; +use crate::{ + conversion::{ChewingEngine, Interval}, + dictionary::DictEntries, + editor::{keyboard::AnyKeyboardLayout, syllable::KeyboardLayoutCompat, Editor}, +}; + +/// Indicates chewing will translate keystrokes to Chinese characters pub const CHINESE_MODE: c_int = 1; +/// Indicates the input mode is translating keystrokes to symbols pub const SYMBOL_MODE: c_int = 0; +/// Indicates chewing will translate latin and puctuation characters to double-with characters pub const FULLSHAPE_MODE: c_int = 1; +/// Indicates chewing will not translate latin and puctuation characters pub const HALFSHAPE_MODE: c_int = 0; +/// Indicates automatic user phrase learning is disabled pub const AUTOLEARN_DISABLED: usize = 1; +/// Indicates automatic user phrase learning is enabled pub const AUTOLEARN_ENABLED: usize = 0; +/// The number of minimum candidates that are selectable via shortcut keys pub const MIN_SELKEY: usize = 1; +/// The number of maximum candidates that are selectable via shortcut keys pub const MAX_SELKEY: usize = 10; +/// Log level pub const CHEWING_LOG_VERBOSE: usize = 1; +/// Log level pub const CHEWING_LOG_DEBUG: usize = 2; +/// Log level pub const CHEWING_LOG_INFO: usize = 3; +/// Log level pub const CHEWING_LOG_WARN: usize = 4; +/// Log level pub const CHEWING_LOG_ERROR: usize = 5; /// Use "asdfjkl789" as selection key @@ -21,9 +40,13 @@ pub const HSU_SELKEY_TYPE1: usize = 1; /// Use "asdfzxcv89" as selection key pub const HSU_SELKEY_TYPE2: usize = 2; +/// Configuration for chewing runtime features +/// /// Deprecated, use chewing_set_ series of functions to set parameters instead. +/// /// cbindgen:rename-all=CamelCase #[repr(C)] +#[deprecated] pub struct ChewingConfigData { pub cand_per_page: c_int, pub max_chi_symbol_len: c_int, @@ -37,6 +60,7 @@ pub struct ChewingConfigData { pub hsu_sel_key_type: c_int, } +/// Specifies the interval of a phrase segment in the pre-editng area #[repr(C)] pub struct IntervalType { /// Starting position of certain interval @@ -44,3 +68,42 @@ pub struct IntervalType { /// Ending position of certain interval (exclusive) pub to: c_int, } + +/// Keyboard layout index +/// +/// cbindgen:prefix-with-name +/// cbindgen:enum-trailing-values=[TypeNum] +#[derive(Clone, Copy, Debug, PartialEq)] +#[repr(C)] +pub enum KB { + Default, + Hsu, + Ibm, + GinYieh, + Et, + Et26, + Dvorak, + DvorakHsu, + DachenCp26, + HanyuPinyin, + ThlPinyin, + Mps2Pinyin, + Carpalx, +} + +/// Opaque context handle used for chewing APIs +/// +/// cbindgen:rename-all=None +pub struct ChewingContext { + pub(crate) kb_compat: KeyboardLayoutCompat, + pub(crate) keyboard: AnyKeyboardLayout, + pub(crate) editor: Editor, + pub(crate) kbcompat_iter: Option>>>, + pub(crate) cand_iter: Option>>>, + pub(crate) interval_iter: Option>>>, + pub(crate) userphrase_iter: Option>, + pub(crate) sel_keys: SelKeys, +} + +#[repr(C)] +pub(crate) struct SelKeys(pub(crate) [c_int; MAX_SELKEY]); diff --git a/src/capi/types.rs b/src/capi/types.rs index 1008fbcfd..8b1378917 100644 --- a/src/capi/types.rs +++ b/src/capi/types.rs @@ -1,43 +1 @@ -use std::{ffi::c_int, iter::Peekable}; -use crate::{ - capi::public::MAX_SELKEY, - conversion::{ChewingEngine, Interval}, - dictionary::DictEntries, - editor::{keyboard::AnyKeyboardLayout, syllable::KeyboardLayoutCompat, Editor}, -}; - -/// cbindgen:prefix-with-name -/// cbindgen:enum-trailing-values=[TypeNum] -#[derive(Clone, Copy, Debug, PartialEq)] -#[repr(C)] -pub enum KB { - Default, - Hsu, - Ibm, - GinYieh, - Et, - Et26, - Dvorak, - DvorakHsu, - DachenCp26, - HanyuPinyin, - ThlPinyin, - Mps2Pinyin, - Carpalx, -} - -/// cbindgen:rename-all=None -pub struct ChewingContext { - pub(crate) kb_compat: KeyboardLayoutCompat, - pub(crate) keyboard: AnyKeyboardLayout, - pub(crate) editor: Editor, - pub(crate) kbcompat_iter: Option>>>, - pub(crate) cand_iter: Option>>>, - pub(crate) interval_iter: Option>>>, - pub(crate) userphrase_iter: Option>, - pub(crate) sel_keys: SelKeys, -} - -#[repr(C)] -pub(crate) struct SelKeys(pub(crate) [c_int; MAX_SELKEY]); diff --git a/src/lib.rs b/src/lib.rs index 5bd60478f..d143c4c39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ #![deny(elided_lifetimes_in_paths)] #![deny(macro_use_extern_crate)] #![deny(missing_abi)] -// #![warn(missing_debug_implementations)] +#![warn(missing_debug_implementations)] // #![deny(missing_docs)] #![warn(noop_method_call)] #![warn(single_use_lifetimes)] @@ -12,7 +12,7 @@ #![warn(unused_macro_rules)] #![warn(unused_qualifications)] #![warn(unused_tuple_struct_fields)] -// #![warn(variant_size_differences)] +#![warn(variant_size_differences)] //! TODO: docs //! From c7630039addb6d70732127b9b67b1ec76bf354da Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Wed, 10 Jan 2024 21:17:55 +0800 Subject: [PATCH 2/9] docs(capi): reorganize the capi module and import API documents from texi --- src/capi/io.rs | 1 + src/capi/mod.rs | 792 ++++++++++++++++++++++++++++++++++++++++++++- src/capi/public.rs | 47 +-- 3 files changed, 818 insertions(+), 22 deletions(-) diff --git a/src/capi/io.rs b/src/capi/io.rs index 695bf11ab..0388cadc4 100644 --- a/src/capi/io.rs +++ b/src/capi/io.rs @@ -1546,6 +1546,7 @@ pub extern "C" fn chewing_cursor_Current(ctx: *const ChewingContext) -> c_int { ctx.editor.cursor() as c_int } +#[deprecated(note = "The chewing_cand_TotalPage function could achieve the same effect.")] #[tracing::instrument(skip(ctx), ret)] #[no_mangle] pub extern "C" fn chewing_cand_CheckDone(ctx: *const ChewingContext) -> c_int { diff --git a/src/capi/mod.rs b/src/capi/mod.rs index 8ca61ff72..7b5d40c29 100644 --- a/src/capi/mod.rs +++ b/src/capi/mod.rs @@ -1,6 +1,14 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(unsafe_code)] +//! C compatible APIs. +//! +//! All items in this module are available via the C header file ``. +//! Function symbols are exposed from the libchewing shared library. +//! +//! Functions are organized into several modules according to the services +//! provided by them. + mod ffi; mod io; mod public; @@ -8,7 +16,7 @@ mod public; #[doc(hidden)] pub mod internal; -/// Initializes chewing context and environment settings +/// Initializes chewing context and environment settings. /// /// Most of the Chewing IM APIs require a [ChewingContext]. To create a /// ChewingContext you must use the [chewing_new] function. @@ -73,7 +81,7 @@ pub mod setup { /// Releases the resources used by the given Chewing IM instance. pub use super::io::chewing_delete; - /// Sets the selectAreaLen, maxChiSymbolLen and selKey parameter from pcd + /// Sets the selectAreaLen, maxChiSymbolLen and selKey parameter from pcd. /// /// The pcd argument is a pointer to a Chewing configuration data structure. /// See also the ChewingConfigData data type. @@ -143,5 +151,781 @@ pub mod setup { pub use super::public::CHEWING_LOG_ERROR; } -pub use io::*; -pub use public::*; +/// Keyboard input handling. +/// +/// Functions to handle key strokes. The return value of these functions is 0 on +/// success and -1 on failure. +pub mod input { + /// Handles all keys that do not have dedicated methods. + pub use super::io::chewing_handle_Default; + + /// Handles the Backspace key. + pub use super::io::chewing_handle_Backspace; + + /// Handles the Capslock key. + pub use super::io::chewing_handle_Capslock; + + /// Handles any number key with the Ctrl modifier. + /// + /// The value of key should be in the range between ASCII character code + /// from 0 to 9. + pub use super::io::chewing_handle_CtrlNum; + + /// Handles the Delete key. + pub use super::io::chewing_handle_Del; + + /// Handles the Enter or Return key. + pub use super::io::chewing_handle_Enter; + + /// Handles the Esc key. + pub use super::io::chewing_handle_Esc; + + /// Handles the Space key. + pub use super::io::chewing_handle_Space; + + /// Handles the Tab key. + pub use super::io::chewing_handle_Tab; + + /// Handles the Home key. + pub use super::io::chewing_handle_Home; + + /// Handles the End key. + pub use super::io::chewing_handle_End; + + /// Handles the Left key. + pub use super::io::chewing_handle_Left; + + /// Handles the Right key. + pub use super::io::chewing_handle_Right; + + /// Handles the Up key. + /// + /// See also [chewing_cand_close][super::candidates::chewing_cand_close] keyboardless API to close candidate + /// window. + pub use super::io::chewing_handle_Up; + + /// Handles the Down key. + /// + /// See also [super::io::chewing_cand_open] keyboardless API to open candidate window. + pub use super::io::chewing_handle_Down; + + /// Handles the Left key with the Shift modifier. + pub use super::io::chewing_handle_ShiftLeft; + + /// Handles the Right key with the Shift modifier. + pub use super::io::chewing_handle_ShiftRight; + + /// Handles the Space key with the Shift modifier. + pub use super::io::chewing_handle_ShiftSpace; + + /// Handles the PageUp key. + pub use super::io::chewing_handle_PageUp; + + /// Handles the PageDown key. + pub use super::io::chewing_handle_PageDown; + + /// Handles tapping the Tab key twice quickly. + pub use super::io::chewing_handle_DblTab; + + /// Handles any numeric key from the keypad. + /// + /// The value of key should be in the range between ASCII character code + /// from 0 to 9. + pub use super::io::chewing_handle_Numlock; +} + +/// Keyboard layout and variants setting. +/// +/// The Chewing IM supports many different keyboard layout and variants. Use +/// functions in this module to set the current keyboard layout for the context. +pub mod layout { + /// Sets the current keyboard layout for ctx. + /// + /// The kbtype argument must be a value defined in [KB]. + /// + /// The return value is 0 on success and -1 on failure. The keyboard type + /// will set to KB_DEFAULT if return value is -1. + pub use super::io::chewing_set_KBType; + + /// Returns the current keyboard layout index for ctx. + /// + /// The return value is the layout index defined in [KB]. + pub use super::io::chewing_get_KBType; + + /// Returns the the current layout name string of ctx. + /// + /// The return value is the name of the current layout, see also function + /// [chewing_KBStr2Num]. + /// + /// The returned pointer must be freed by + /// [chewing_free][super::setup::chewing_free]. + /// + /// # Failures + /// + /// This function returns NULL when memory allocation fails. + pub use super::io::chewing_get_KBString; + + /// Converts the keyboard layout name from string to corresponding layout + /// index. + /// + /// If the string does not match any layout, this function returns + /// KB_DEFAULT. + /// + /// The string str might be one of the following layouts: + /// * KB_DEFAULT + /// * KB_HSU + /// * KB_IBM + /// * KB_GIN_YIEH + /// * KB_ET + /// * KB_ET26 + /// * KB_DVORAK + /// * KB_DVORAK_HSU + /// * KB_DVORAK_CP26 + /// * KB_HANYU_PINYIN + /// * KB_THL_PINYIN + /// * KB_MPS2_PINYIN + /// * KB_CARPALX + /// + /// See also [chewing_kbtype_Enumerate] for getting the list of supported + /// layouts programmatically. + pub use super::io::chewing_KBStr2Num; + + /// Returns the number of keyboard layouts supported by the Chewing IM. + pub use super::io::chewing_kbtype_Total; + + /// Starts the enumeration of the keyboard layouts. + /// + /// This function stores an iterator in the context. The iterator is only + /// destroyed after enumerate all keyboard layouts using + /// [chewing_kbtype_hasNext]. + pub use super::io::chewing_kbtype_Enumerate; + + /// Checks whether there are more keyboard layouts to enumerate. + /// + /// Returns 1 when there are more and 0 when it's the end of the iterator. + pub use super::io::chewing_kbtype_hasNext; + + /// Returns the current enumerated keyboard layout name. + /// + /// The returned string is emtpy string when enumeration is over. + /// + /// The returned value is a pointer to a character string. The memory must + /// be freed by the caller using function + /// [chewing_free][super::setup::chewing_free]. + /// + /// # Failures + /// + /// This function returns NULL when memory allocation fails. + pub use super::io::chewing_kbtype_String; + + /// Returns the current enumerated keyboard layout name. + /// + /// The returned string is emtpy string when enumeration is over. + /// + /// The return value is a const pointer to a character string. The pointer + /// is only valid immediately after checking the [chewing_kbtype_hasNext] + /// condition. + pub use super::io::chewing_kbtype_String_static; + + pub use super::public::KB; +} + +/// Input mode settings. +/// +/// The Chewing IM can switch between Chinese input mode or English mode. The +/// English mode supports input English characters directly. These functions set +/// the current input mode. +pub mod modes { + /// Sets the input mode to Chinese or English. + /// + /// The *mode* argument is one of the [CHINESE_MODE] and [SYMBOL_MODE] + /// constants. + pub use super::io::chewing_set_ChiEngMode; + + /// Returns the current Chinese/English mode setting. + pub use super::io::chewing_get_ChiEngMode; + + /// Sets the current punctuation input mode. + /// + /// The *mode* argument is one of the [FULLSHAPE_MODE] and [HALFSHAPE_MODE] + /// constants. + pub use super::io::chewing_set_ShapeMode; + + /// Returns the current punctuation mode. + pub use super::io::chewing_get_ShapeMode; + + pub use super::public::CHINESE_MODE; + pub use super::public::SYMBOL_MODE; + + pub use super::public::FULLSHAPE_MODE; + pub use super::public::HALFSHAPE_MODE; +} + +/// Candidate selection related functions. +/// +/// These functions can be used to transit the Chewing IM into candidate +/// selection state and enumerate candidates. +/// +/// # Keyboardless APIs +/// +/// The traditional chewing APIs are coupled to keyboards. They cause some +/// problems if the program like to design its own keyboard scheme, or if a +/// platform does not have certain keyboard keys (ex: mobile device). To +/// overcome these problems, the new keyboardless APIs are provided. With these +/// APIs, program can have better control over libchewing, instead of hacking +/// libchewing via fake keyboard event. +pub mod candidates { + /// Returns the number of pages of the candidates. + /// + /// If the return value is greater than zero, then the IM interface should + /// display a selection window of the candidates for the user to choose a + /// candidate. Otherwise hide the selection window. + pub use super::io::chewing_cand_TotalPage; + + /// Returns the current candidate page number. + /// + /// # Examples + /// + /// The candidates pagination could be displayed as: + /// + /// ```c + /// sprintf(buf, "[%d / %d]", + /// chewing_cand_CurrentPage(ctx), + /// chewing_cand_TotalPage(ctx)); + /// ``` + pub use super::io::chewing_cand_CurrentPage; + + /// Returns the number of the coices per page. + /// + /// See also the [chewing_set_candPerPage] function. + pub use super::io::chewing_cand_ChoicePerPage; + + /// Returns the total number of the available choices. + pub use super::io::chewing_cand_TotalChoice; + + /// Starts the enumeration of the candidates starting from the first one in + /// the current page. + /// + /// This function stores an iterator in the context. The iterator is only + /// destroyed after enumerate candidates using + /// [chewing_cand_hasNext]. + pub use super::io::chewing_cand_Enumerate; + + /// Checks if there are more candidates to enumerate. + /// + ///

+ /// ⚠ Warning: This function checks the end of total choices + /// instead of the end of current page. + ///

+ pub use super::io::chewing_cand_hasNext; + + /// Returns the current enumerated candidate string. + /// + /// The returned value is a pointer to a character string. The memory must + /// be freed by the caller using function + /// [chewing_free][super::setup::chewing_free]. + /// + /// # Failures + /// + /// This function returns NULL when memory allocation fails. + pub use super::io::chewing_cand_String; + + /// Returns the current enumerated candidate string. + /// + /// The returned string is emtpy string when enumeration is over. + /// + /// The return value is a const pointer to a character string. The pointer + /// is only valid immediately after checking the [chewing_cand_hasNext] + /// condition. + pub use super::io::chewing_cand_String_static; + + /// Checks if the candidates selection has finished. + /// + ///

+ /// ⚠ Warning: Not implemented. + ///

+ pub use super::io::chewing_cand_CheckDone; + + /// Sets the number of candidates returned per page. + /// + /// The setting is ignored if *n* is not between [MIN_SELKEY] and + /// [MAX_SELKEY] inclusive. + /// + /// The default value is MAX_SELKEY. + pub use super::io::chewing_set_candPerPage; + + /// Sets the key codes for candidate selection. + /// + /// *selkeys* is an ASCII code integer array of length [MAX_SELKEY]. The + /// second argument is unused. + /// + /// The default selection key is `1234567890`. + pub use super::io::chewing_set_selKey; + + /// Returns the current selection key setting. + /// + /// The returned value is a pointer to an integer array. The memory must + /// be freed by the caller using function + /// [chewing_free][super::setup::chewing_free]. + pub use super::io::chewing_get_selKey; + + /// This function is no-op now. Use [chewing_set_selKey] instead. + pub use super::io::chewing_set_hsuSelKeyType; + + /// This function is no-op now. Use [chewing_get_selKey] instead. + pub use super::io::chewing_get_hsuSelKeyType; + + /// Opens the candidate selection window. + /// + /// This operation is only allowed when the IM editor is in entering state. + /// + /// Returns 0 when success, -1 otherwise. + pub use super::io::chewing_cand_open; + + /// Closes the candidate selection window. + /// + /// Returns 0 when success, -1 otherwise. + pub use super::io::chewing_cand_close; + + /// Returns the candidate string by its index. + /// + /// The *index* must be between 0 and [chewing_cand_TotalChoice] inclusive. + /// + /// The return value is a const pointer to a character string. The pointer + /// is only valid immediately after calling this function. + pub use super::io::chewing_cand_string_by_index_static; + + /// Selects the candidate by its index. + /// + /// The *index* must be between 0 and [chewing_cand_TotalChoice] inclusive. + /// + /// Returns 0 when success, -1 otherwise. + /// + /// # Errors + /// + /// This function fails if the *index* is out of range or the candidate + /// selection window is not currently open. + pub use super::io::chewing_cand_choose_by_index; + + /// Sets the candidate list to the first (longest) candidate list. + /// + /// Returns 0 when success, -1 otherwise. + /// + /// # Errors + /// + /// This function fails if the candidate selection window is not currently + /// open. + pub use super::io::chewing_cand_list_first; + + /// Sets the candidate list to the last (shortest) candidate list. + /// + /// Returns 0 when success, -1 otherwise. + /// + /// # Errors + /// + /// This function fails if the candidate selection window is not currently + /// open. + pub use super::io::chewing_cand_list_last; + + /// Checks whether there is a next (shorter) candidate list. + /// + /// Returns 1 (true) when there is a next candidate list, 0 otherwise. + pub use super::io::chewing_cand_list_has_next; + + /// Checks whether there is a previous (longer) candidate list. + /// + /// Returns 1 (true) when there is a previous candidate list, 0 otherwise. + pub use super::io::chewing_cand_list_has_prev; + + /// Changes current candidate list to next candidate list. + /// + /// Returns 0 when success, -1 otherwise. + /// + /// # Errors + /// + /// This function fails if the candidate selection window is not currently + /// open. + pub use super::io::chewing_cand_list_next; + + /// Changes current candidate list to previous candidate list. + /// + /// Returns 0 when success, -1 otherwise. + /// + /// # Errors + /// + /// This function fails if the candidate selection window is not currently + /// open. + pub use super::io::chewing_cand_list_prev; + + pub use super::public::MAX_SELKEY; + pub use super::public::MIN_SELKEY; + + pub use super::public::HSU_SELKEY_TYPE1; + pub use super::public::HSU_SELKEY_TYPE2; +} + +/// Output handling. +pub mod output { + /// Checks whether the commit buffer has something to read. + /// + /// Returns 1 when true, 0 when false. + pub use super::io::chewing_commit_Check; + + /// Returns the string in the commit buffer. + /// + /// The returned value is a pointer to a character string. The memory must + /// be freed by the caller using function + /// [chewing_free][super::setup::chewing_free]. + /// + /// # Failures + /// + /// This function returns NULL when memory allocation fails. + pub use super::io::chewing_commit_String; + + /// Returns the string in the commit buffer. + /// + /// The return value is a const pointer to a character string. The pointer + /// is only valid immediately after checking the [chewing_commit_Check] + /// condition. + pub use super::io::chewing_commit_String_static; + + /// Checks whether the previous keystroke is ignored or not. + /// + /// Returns 1 when true, 0 when false. + pub use super::io::chewing_keystroke_CheckIgnore; + + /// Checks whether the previous keystroke is absorbed or not. + /// + /// Returns 1 when true, 0 when false. + /// + /// Absorbed key means the Chewing IM state machine has accepted the key and + /// changed its state accordingly. Caller should check various output + /// buffers to see if they need to update the display. + pub use super::io::chewing_keystroke_CheckAbsorb; + + /// Checks whether there is output in the pre-edit buffer. + /// + /// Returns 1 when true, 0 when false. + pub use super::io::chewing_buffer_Check; + + /// Returns the length of the string in current pre-edit buffer. + /// + ///

+ /// ⚠ Warning: The length is calculated in terms of + /// unicode characters. One character might occupy multiple bytes. + ///

+ pub use super::io::chewing_buffer_Len; + + /// Returns the current output in the pre-edit buffer. + /// + /// The returned value is a pointer to a character string. The memory must + /// be freed by the caller using function + /// [chewing_free][super::setup::chewing_free]. + /// + /// # Failures + /// + /// This function returns NULL when memory allocation fails. + pub use super::io::chewing_buffer_String; + + /// Returns the current output in the pre-edit buffer. + /// + /// The return value is a const pointer to a character string. The pointer + /// is only valid immediately after checking the [chewing_buffer_Check] + /// condition. + pub use super::io::chewing_buffer_String_static; + + /// Returns whether there are phonetic pre-edit string in the buffer. + /// + /// Returns 1 when true, 0 when false. + pub use super::io::chewing_bopomofo_Check; + + /// Returns whether there are phonetic pre-edit string in the buffer. Here + /// “zuin” means bopomofo, a phonetic system for transcribing Chinese, + /// especially Mandarin. + /// + /// Returns **0** when true, **1** when false. + /// + ///

+ /// ⚠ Warning: The return value of this function is + /// different from other newer functions that returns boolean value. + ///

+ pub use super::io::chewing_zuin_Check; + + /// Returns the phonetic characters in the pre-edit buffer. + /// + /// The return value is a const pointer to a character string. The pointer + /// is only valid immediately after checking the [chewing_bopomofo_Check] + /// condition. + pub use super::io::chewing_bopomofo_String_static; + + /// Returns the phonetic characters in the pre-edit buffer. + /// + /// The bopomofo_count argument is a output argument. It will contain the + /// number of phonetic characters in the returned string. + /// + /// The returned value is a pointer to a character string. The memory must + /// be freed by the caller using function + /// [chewing_free][super::setup::chewing_free]. + /// + /// # Failures + /// + /// This function returns NULL when memory allocation fails. + pub use super::io::chewing_zuin_String; + + /// Returns the current cursor position in the pre-edit buffer. + pub use super::io::chewing_cursor_Current; + + /// Starts the enumeration of intervals of recognized phrases. + /// + /// This function stores an iterator in the context. The iterator is only + /// destroyed after enumerate all intervals using + /// [chewing_interval_hasNext]. + pub use super::io::chewing_interval_Enumerate; + + /// Checks whether there are more intervals or not. + /// + /// Returns 1 when true, 0 when false. + pub use super::io::chewing_interval_hasNext; + + /// Returns the current enumerated interval. + /// + /// The *it* argument is an output argument. + pub use super::io::chewing_interval_Get; + + /// Returns whether there is auxiliary string in the auxiliary buffer. + /// + /// Returns 1 when true, 0 when false. + pub use super::io::chewing_aux_Check; + + /// Returns the length of the auxiliary string in the auxiliary buffer. + /// + ///

+ /// ⚠ Warning: The length is calculated in terms of + /// unicode characters. One character might occupy multiple bytes. + ///

+ pub use super::io::chewing_aux_Length; + + /// Returns the current auxiliary string. + /// + /// The returned value is a pointer to a character string. The memory must + /// be freed by the caller using function + /// [chewing_free][super::setup::chewing_free]. + /// + /// # Failures + /// + /// This function returns NULL when memory allocation fails. + pub use super::io::chewing_aux_String; + + /// Returns the current auxiliary string. + /// + /// The return value is a const pointer to a character string. The pointer + /// is only valid immediately after checking the [chewing_aux_Check] + /// condition. + pub use super::io::chewing_aux_String_static; + + /// Returns the phonetic sequence in the Chewing IM internal state machine. + /// + /// The return value is a pointer to a unsigned short array. The values in + /// the array is encoded Bopomofo phone. The memory must be freed by the + /// caller using function [chewing_free][super::setup::chewing_free]. + pub use super::io::chewing_get_phoneSeq; + + /// Returns the length of the phonetic sequence in the Chewing IM internal + /// state machine. + pub use super::io::chewing_get_phoneSeqLen; + + /// Converts the u16 encoded syllables to a bopomofo string. + /// + /// If both of the buf and the len are 0, this function will return buf + /// length for bopomofo including the null character so that caller can + /// prepare enough buffer for it. + /// + /// Returns 0 on success, -1 on failure. + pub use super::io::chewing_phone_to_bopomofo; + + /// Commits the current preedit buffer content to the commit buffer. + /// + /// Returns 0 when success, -1 otherwise. + /// + /// # Errors + /// + /// This function fails if the IM editor is not in entering state. + pub use super::io::chewing_commit_preedit_buf; + + /// Clears the current preedit buffer content. + /// + /// Returns 0 when success, -1 otherwise. + /// + /// # Errors + /// + /// This function fails if the IM editor is not in entering state. + pub use super::io::chewing_clean_preedit_buf; + + /// Clears the current bopomofo buffer content. + /// + /// Returns 0 when success, -1 otherwise. + /// + /// # Errors + /// + /// This function fails if the IM editor is not in entering state. + pub use super::io::chewing_clean_bopomofo_buf; + + pub use super::public::IntervalType; +} + +/// Userphrase handling. +pub mod userphrase { + /// Starts a userphrase enumeration. + /// + /// Caller shall call this function prior [chewing_userphrase_has_next] and + /// [chewing_userphrase_get] in order to enumerate userphrase correctly. + /// + /// This function stores an iterator in the context. The iterator is only + /// destroyed after enumerate all userphrases using + /// [chewing_userphrase_has_next]. + /// + /// Returns 0 on success, -1 on failure. + /// + /// # Examples + /// + /// ```c + /// chewing_userphrase_enumerate(ctx); + /// while (chewing_userphrase_has_next(ctx, &phrase_len, &bopomofo_len)) { + /// phrase = malloc(phrase_len); + /// if (!phrase) goto error; + /// bopomofo = malloc(bopomofo_len); + /// if (!bopomofo) goto error; + /// + /// chewing_userphrase_get(ctx, phrase, phrase_len, bopomofo, bopomofo_len); + /// /* do somthing */ + /// } + /// ``` + pub use super::io::chewing_userphrase_enumerate; + + /// Checks if there is another userphrase in current enumeration. + /// + /// The *phrase_len* and *bopomofo_len* are output buffer length needed by the userphrase and its bopomofo string. + /// + /// Returns 1 when true, 0 when false. + pub use super::io::chewing_userphrase_has_next; + + /// Gets the current enumerated userphrase. + /// + /// The *phrase_buf* and *bopomofo_buf* are userphrase and its bopomofo + /// buffer provided by caller. The length of the buffers can be retrived + /// from [chewing_userphrase_has_next]. + /// + /// Returns 0 on success, -1 on failure. + pub use super::io::chewing_userphrase_get; + + /// Adds new userphrase to the user dictionary. + /// + /// Returns how many phrases are added, -1 on failure. + pub use super::io::chewing_userphrase_add; + + /// Removes a userphrase from the user dictionary. + /// + /// Returns how many phrases are removed, -1 on failure. + pub use super::io::chewing_userphrase_remove; + + /// Searchs if a userphrase is in the user dictionary. + /// + /// Returns 1 when true, 0 when false. + pub use super::io::chewing_userphrase_lookup; +} + +/// Global settings. +/// +/// The Chewing IM could be customized in some small details. These functions +/// provide the configuration interfaces to the front-end. +pub mod globals { + /// Sets the maximum number of the Chinese characters allowed in the + /// pre-edit buffer. + /// + /// If the pre-edit string is longer than this number then the leading part + /// will be committed automatically. The range of n shall between + /// [MIN_CHI_SYMBOL_LEN] and [MAX_CHI_SYMBOL_LEN]. + pub use super::io::chewing_set_maxChiSymbolLen; + + /// Returns the maximum number of the Chinese characters allowed in the + /// pre-edit buffer. + pub use super::io::chewing_get_maxChiSymbolLen; + + /// Sets the direction to add new phrases when using CtrlNum. + /// + /// The direction argument is 0 when the direction is backward and 1 when + /// the direction is forward. + pub use super::io::chewing_set_addPhraseDirection; + + /// Returns the direction to add new phrases when using CtrlNum. + /// + /// The direction argument is 0 when the direction is backward and 1 when + /// the direction is forward. + pub use super::io::chewing_get_addPhraseDirection; + + /// Sets whether the Space key is treated as a selection key. + /// + /// When the mode argument is 1, the Space key will initiate the candidates + /// selection mode. + pub use super::io::chewing_set_spaceAsSelection; + + /// Returns whether the Space key is treated as a selection key. + /// + /// Returns 1 when the Space key will initiate the candidates selection + /// mode. + pub use super::io::chewing_get_spaceAsSelection; + + /// Sets whether the Esc key will flush the current pre-edit buffer. + /// + /// When the mode argument is 1, the Esc key will flush the pre-edit buffer. + pub use super::io::chewing_set_escCleanAllBuf; + + /// Returns whether the Esc key will flush the current pre-edit buffer. + /// + /// Returns 1 when the Esc key will flush the pre-edit buffer. + pub use super::io::chewing_get_escCleanAllBuf; + + /// Sets whether the Chewing IM will automatically shift cursor after + /// selection. + pub use super::io::chewing_set_autoShiftCur; + + /// Returns whether the Chewing IM will automatically shift cursor after + /// selection. + pub use super::io::chewing_get_autoShiftCur; + + /// Sets the current normal/easy symbol mode. + /// + /// In easy symbol mode, the key be will changed to its related easy symbol + /// in swkb.dat. The format of swkb.dat is key symbol pair per line. The + /// valid value of key is [0-9A-Z]. The lower case character in key will be + /// changed to upper case when loading swkb.dat. However, in easy symbol + /// mode, only [0-9A-Z] are accepted. + /// + /// The mode argument is 0 for normal mode or other for easy symbol mode. + pub use super::io::chewing_set_easySymbolInput; + + /// Sets whether the phrase for candidates selection is before the cursor or + /// after the cursor. + pub use super::io::chewing_set_phraseChoiceRearward; + + /// Returns the phrase choice rearward setting. + pub use super::io::chewing_get_phraseChoiceRearward; + + /// Sets enable or disable the automatic learning. + /// + /// The mode argument is be one of the [AUTOLEARN_ENABLED] and + /// [AUTOLEARN_DISABLED] constants. + pub use super::io::chewing_set_autoLearn; + + /// Returns whether the automatic learning is enabled or disabled. + pub use super::io::chewing_get_autoLearn; + + pub use super::public::MAX_CHI_SYMBOL_LEN; + pub use super::public::MIN_CHI_SYMBOL_LEN; + + pub use super::public::MAX_PHONE_SEQ_LEN; + pub use super::public::MAX_PHRASE_LEN; + + pub use super::public::AUTOLEARN_DISABLED; + pub use super::public::AUTOLEARN_ENABLED; +} diff --git a/src/capi/public.rs b/src/capi/public.rs index 0391295da..5f8d12c77 100644 --- a/src/capi/public.rs +++ b/src/capi/public.rs @@ -6,41 +6,52 @@ use crate::{ editor::{keyboard::AnyKeyboardLayout, syllable::KeyboardLayoutCompat, Editor}, }; -/// Indicates chewing will translate keystrokes to Chinese characters +/// Indicates chewing will translate keystrokes to Chinese characters. pub const CHINESE_MODE: c_int = 1; -/// Indicates the input mode is translating keystrokes to symbols +/// Indicates the input mode is translating keystrokes to symbols. pub const SYMBOL_MODE: c_int = 0; -/// Indicates chewing will translate latin and puctuation characters to double-with characters +/// Indicates chewing will translate latin and puctuation characters to +/// double-with characters. pub const FULLSHAPE_MODE: c_int = 1; -/// Indicates chewing will not translate latin and puctuation characters +/// Indicates chewing will not translate latin and puctuation characters. pub const HALFSHAPE_MODE: c_int = 0; -/// Indicates automatic user phrase learning is disabled +/// Indicates automatic user phrase learning is disabled. pub const AUTOLEARN_DISABLED: usize = 1; -/// Indicates automatic user phrase learning is enabled +/// Indicates automatic user phrase learning is enabled. pub const AUTOLEARN_ENABLED: usize = 0; +/// The minimal size of pre-edit buffer. +pub const MIN_CHI_SYMBOL_LEN: usize = 0; +/// The maximum size of pre-edit buffer. +pub const MAX_CHI_SYMBOL_LEN: usize = MAX_PHONE_SEQ_LEN - MAX_PHRASE_LEN; +/// The size of internal buffer for pre-edit buffer. +pub const MAX_PHONE_SEQ_LEN: usize = 50; +/// The maximum phrase size. +pub const MAX_PHRASE_LEN: usize = 11; -/// The number of minimum candidates that are selectable via shortcut keys +/// The number of minimum candidates that are selectable via shortcut keys. pub const MIN_SELKEY: usize = 1; -/// The number of maximum candidates that are selectable via shortcut keys +/// The number of maximum candidates that are selectable via shortcut keys. pub const MAX_SELKEY: usize = 10; -/// Log level +/// Log level. pub const CHEWING_LOG_VERBOSE: usize = 1; -/// Log level +/// Log level. pub const CHEWING_LOG_DEBUG: usize = 2; -/// Log level +/// Log level. pub const CHEWING_LOG_INFO: usize = 3; -/// Log level +/// Log level. pub const CHEWING_LOG_WARN: usize = 4; -/// Log level +/// Log level. pub const CHEWING_LOG_ERROR: usize = 5; -/// Use "asdfjkl789" as selection key +/// Use "asdfjkl789" as selection key. +#[deprecated] pub const HSU_SELKEY_TYPE1: usize = 1; -/// Use "asdfzxcv89" as selection key +/// Use "asdfzxcv89" as selection key. +#[deprecated] pub const HSU_SELKEY_TYPE2: usize = 2; -/// Configuration for chewing runtime features +/// Configuration for chewing runtime features. /// /// Deprecated, use chewing_set_ series of functions to set parameters instead. /// @@ -69,7 +80,7 @@ pub struct IntervalType { pub to: c_int, } -/// Keyboard layout index +/// Keyboard layout index. /// /// cbindgen:prefix-with-name /// cbindgen:enum-trailing-values=[TypeNum] @@ -91,7 +102,7 @@ pub enum KB { Carpalx, } -/// Opaque context handle used for chewing APIs +/// Opaque context handle used for chewing APIs. /// /// cbindgen:rename-all=None pub struct ChewingContext { From c054ce7b097cd49002237769d76cfbc9d017f38d Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Sun, 14 Jan 2024 08:25:16 +0800 Subject: [PATCH 3/9] refactor(dict): resolve some lint warnings --- src/capi/mod.rs | 1 + src/capi/public.rs | 20 +++++- src/dictionary/loader.rs | 130 ++++++++++++++++++++++++++------------- 3 files changed, 108 insertions(+), 43 deletions(-) diff --git a/src/capi/mod.rs b/src/capi/mod.rs index 7b5d40c29..e2622a729 100644 --- a/src/capi/mod.rs +++ b/src/capi/mod.rs @@ -1,5 +1,6 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(unsafe_code)] +#![allow(deprecated)] //! C compatible APIs. //! diff --git a/src/capi/public.rs b/src/capi/public.rs index 5f8d12c77..6085704ce 100644 --- a/src/capi/public.rs +++ b/src/capi/public.rs @@ -1,4 +1,4 @@ -use std::{ffi::c_int, iter::Peekable}; +use std::{ffi::c_int, fmt::Debug, iter::Peekable}; use crate::{ conversion::{ChewingEngine, Interval}, @@ -58,6 +58,7 @@ pub const HSU_SELKEY_TYPE2: usize = 2; /// cbindgen:rename-all=CamelCase #[repr(C)] #[deprecated] +#[derive(Debug)] pub struct ChewingConfigData { pub cand_per_page: c_int, pub max_chi_symbol_len: c_int, @@ -73,6 +74,7 @@ pub struct ChewingConfigData { /// Specifies the interval of a phrase segment in the pre-editng area #[repr(C)] +#[derive(Debug)] pub struct IntervalType { /// Starting position of certain interval pub from: c_int, @@ -116,5 +118,21 @@ pub struct ChewingContext { pub(crate) sel_keys: SelKeys, } +impl Debug for ChewingContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ChewingContext") + .field("kb_compat", &self.kb_compat) + .field("keyboard", &self.keyboard) + .field("editor", &self.editor) + .field("kbcompat_iter.is_some()", &self.kbcompat_iter.is_some()) + .field("cand_iter.is_some()", &self.cand_iter.is_some()) + .field("interval_iter.is_some()", &self.interval_iter.is_some()) + .field("userphrase_iter.is_some()", &self.userphrase_iter.is_some()) + .field("sel_keys", &self.sel_keys) + .finish_non_exhaustive() + } +} + #[repr(C)] +#[derive(Debug)] pub(crate) struct SelKeys(pub(crate) [c_int; MAX_SELKEY]); diff --git a/src/dictionary/loader.rs b/src/dictionary/loader.rs index a7ed4601b..a73863584 100644 --- a/src/dictionary/loader.rs +++ b/src/dictionary/loader.rs @@ -1,4 +1,7 @@ -use std::path::{Path, PathBuf}; +use std::{ + marker::PhantomData, + path::{Path, PathBuf}, +}; use crate::path::{find_path_by_files, sys_path_from_env_var, userphrase_path}; @@ -20,6 +23,13 @@ impl SystemDictionaryLoader { self } pub fn load(self) -> Option>> { + let mut db_loaders: Vec> = vec![]; + #[cfg(feature = "sqlite")] + { + db_loaders.push(LoaderWrapper::::new()); + } + db_loaders.push(LoaderWrapper::::new()); + let search_path = if let Some(sys_path) = self.sys_path { sys_path } else { @@ -27,35 +37,15 @@ impl SystemDictionaryLoader { }; let sys_path = find_path_by_files(&search_path, &["tsi.dat", "word.dat"])?; - let mut tsi_db_path = sys_path.clone(); - tsi_db_path.push("tsi.dat"); - let mut tsi_db = None; - #[cfg(feature = "sqlite")] - { - tsi_db = SqliteDictionary::open_read_only(&tsi_db_path) - .map(|db| Box::new(db) as Box) - .ok(); - } - if tsi_db.is_none() { - tsi_db = TrieDictionary::open(&tsi_db_path) - .map(|db| Box::new(db) as Box) - .ok(); - } + let tsi_db_path = sys_path.join("tsi.dat"); + let tsi_db = db_loaders + .iter() + .find_map(|loader| loader.open_read_only(&tsi_db_path)); - let mut word_db_path = sys_path; - word_db_path.push("word.dat"); - let mut word_db = None; - #[cfg(feature = "sqlite")] - { - word_db = SqliteDictionary::open_read_only(&word_db_path) - .map(|db| Box::new(db) as Box) - .ok(); - } - if word_db.is_none() { - word_db = TrieDictionary::open(&word_db_path) - .map(|db| Box::new(db) as Box) - .ok(); - } + let word_db_path = sys_path.join("word.dat"); + let word_db = db_loaders + .iter() + .find_map(|loader| loader.open_read_only(&word_db_path)); Some(vec![word_db.unwrap(), tsi_db.unwrap()]) } @@ -75,25 +65,81 @@ impl UserDictionaryLoader { self } pub fn load(self) -> Option> { + let mut db_loaders: Vec> = vec![]; + #[cfg(feature = "sqlite")] + { + db_loaders.push(LoaderWrapper::::new()); + } + db_loaders.push(LoaderWrapper::::new()); + let data_path = if let Some(data_path) = self.data_path { data_path } else { userphrase_path()? }; - let mut dict = None; - #[cfg(feature = "sqlite")] - { - dict = dbg!(SqliteDictionary::open(&data_path)) - .map(|db| Box::new(db) as Box) - .ok(); - } - if dict.is_none() { - dict = CdbDictionary::open(&data_path) - .map(|db| Box::new(db) as Box) - .ok(); - } + db_loaders + .iter() + .find_map(|loader| loader.open_read_only(&data_path)) + } +} + +trait DictionaryLoader { + fn open(&self, path: &PathBuf) -> Option>; + fn open_read_only(&self, path: &PathBuf) -> Option>; +} + +struct LoaderWrapper { + _marker: PhantomData, +} + +impl LoaderWrapper { + fn new() -> Box> { + Box::new(LoaderWrapper { + _marker: PhantomData, + }) + } +} + +#[cfg(feature = "sqlite")] +impl DictionaryLoader for LoaderWrapper { + fn open(&self, path: &PathBuf) -> Option> { + SqliteDictionary::open(path) + .map(|dict| Box::new(dict) as Box) + .ok() + } + + fn open_read_only(&self, path: &PathBuf) -> Option> { + SqliteDictionary::open_read_only(path) + .map(|dict| Box::new(dict) as Box) + .ok() + } +} + +impl DictionaryLoader for LoaderWrapper { + fn open(&self, path: &PathBuf) -> Option> { + TrieDictionary::open(path) + .map(|dict| Box::new(dict) as Box) + .ok() + } + + fn open_read_only(&self, path: &PathBuf) -> Option> { + TrieDictionary::open(path) + .map(|dict| Box::new(dict) as Box) + .ok() + } +} + +impl DictionaryLoader for LoaderWrapper { + fn open(&self, path: &PathBuf) -> Option> { + CdbDictionary::open(path) + .map(|dict| Box::new(dict) as Box) + .ok() + } - dict + fn open_read_only(&self, path: &PathBuf) -> Option> { + CdbDictionary::open(path) + .map(|dict| Box::new(dict) as Box) + .ok() } } From 76b6dbab59a2ead54e25bff52403bedbd07a5337 Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Sun, 14 Jan 2024 08:30:52 +0800 Subject: [PATCH 4/9] refactor(dict): user dict should open as read-write --- src/dictionary/loader.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/dictionary/loader.rs b/src/dictionary/loader.rs index a73863584..613b434a0 100644 --- a/src/dictionary/loader.rs +++ b/src/dictionary/loader.rs @@ -78,9 +78,7 @@ impl UserDictionaryLoader { userphrase_path()? }; - db_loaders - .iter() - .find_map(|loader| loader.open_read_only(&data_path)) + db_loaders.iter().find_map(|loader| loader.open(&data_path)) } } @@ -124,9 +122,7 @@ impl DictionaryLoader for LoaderWrapper { } fn open_read_only(&self, path: &PathBuf) -> Option> { - TrieDictionary::open(path) - .map(|dict| Box::new(dict) as Box) - .ok() + self.open(path) } } @@ -138,8 +134,6 @@ impl DictionaryLoader for LoaderWrapper { } fn open_read_only(&self, path: &PathBuf) -> Option> { - CdbDictionary::open(path) - .map(|dict| Box::new(dict) as Box) - .ok() + self.open(path) } } From 55af17ed6510013ff03fa6829e813f371d6d2fac Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Wed, 17 Jan 2024 07:21:17 +0800 Subject: [PATCH 5/9] build: build and install info by default --- CMakePresets.json | 15 +++++++++++++++ doc/CMakeLists.txt | 6 +----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 2d3061560..3adf22ef3 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -5,6 +5,14 @@ "minor": 21 }, "configurePresets": [ + { + "name": "default", + "displayName": "Default (Release)", + "description": "Default preset for release build", + "inherits": [ + "c99-release" + ] + }, { "name": "coverage-base", "displayName": "Enable Coverage", @@ -89,6 +97,7 @@ "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", + "BUILD_INFO": true, "WITH_RUST": false, "WITH_SQLITE3": true } @@ -145,5 +154,11 @@ "coverage-base" ] } + ], + "buildPresets": [ + { + "name": "default", + "configurePreset": "default" + } ] } \ No newline at end of file diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index e9c22791d..02b5e993d 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -15,9 +15,5 @@ if (BUILD_INFO AND MAKEINFO) add_custom_target(INFO ALL DEPENDS ${INFO_BIN}) add_dependencies(check INFO) - find_program(INSTALL_INFO NAMES ginstall-info install-info) - if (INSTALL_INFO) - install(FILES ${INFO_BIN} DESTINATION ${CMAKE_INSTALL_INFODIR}) - install(CODE "execute_process(COMMAND ${INSTALL_INFO} --info-dir=${CMAKE_INSTALL_INFODIR} ${INFO_BIN})") - endif() + install(FILES ${INFO_BIN} TYPE INFO) endif() \ No newline at end of file From 95da9801c3075100b62732e87232b0bab4a059f6 Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Wed, 17 Jan 2024 20:44:19 +0800 Subject: [PATCH 6/9] docs: replace @leq{} macro with plain <= --- doc/libchewing.texi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/libchewing.texi b/doc/libchewing.texi index 772e07ec2..1c590394b 100644 --- a/doc/libchewing.texi +++ b/doc/libchewing.texi @@ -640,8 +640,8 @@ This function checks if the candidates selection has finished. @section Candidates Behavior @deftypefun void chewing_set_candPerPage (ChewingContext *@var{ctx}, int @var{n}) -This function sets the candidates per page to @var{n}. If MIN_SELKEY @leq{} -@var{n} @leq{} @code{MAX_SELKEY} is not true or the number of selection keys is +This function sets the candidates per page to @var{n}. If MIN_SELKEY <= +@var{n} <= @code{MAX_SELKEY} is not true or the number of selection keys is not enough, the @var{n} will be ignored. The default candidates per page is @code{MAX_SELKEY}. @end deftypefun @@ -714,7 +714,7 @@ This function closes the candidate windows. It returns @code{0} when success, @deftypefun const char* chewing_cand_string_by_index_static (ChewingContext *@var{ctx}, int @var{index}) This function returns the candidate string by its index. The range of -@var{index} shall be @code{0} @leq{} @var{index} < +@var{index} shall be @code{0} <= @var{index} < @code{chewing_cand_TotalChoice}. This function returns @code{NULL} when no memory. @@ -725,7 +725,7 @@ be freed by the next time be used. @deftypefun int chewing_cand_choose_by_index (ChewingContext *@var{ctx}, int @var{index}) This function chooses the candidate by its index. The range of @var{index} -shall be @code{0} @leq{} @var{index} < @code{chewing_cand_TotalChoice}. This +shall be @code{0} <= @var{index} < @code{chewing_cand_TotalChoice}. This function returns @code{0} when success, @code{-1} otherwise. @end deftypefun From caabe788701ce3fad543c09007dfbb716a147278 Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Thu, 18 Jan 2024 08:18:26 +0800 Subject: [PATCH 7/9] docs: add basic crate overview doc --- Cargo.toml | 2 +- src/capi/io.rs | 3 ++- src/conversion/mod.rs | 2 +- src/dictionary/loader.rs | 16 +++++++++------ src/dictionary/mod.rs | 2 +- src/editor/composition_editor.rs | 2 +- src/editor/mod.rs | 29 +++++++++++++++++---------- src/lib.rs | 34 ++++++++++++++++++++++++++++---- src/zhuyin/mod.rs | 2 +- 9 files changed, 66 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6fa93f979..5272273da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,4 +50,4 @@ panic = "abort" [package.metadata.docs.rs] features = ["capi", "sqlite"] -rustdoc-args = ["-Zunstable-options", "--sort-modules-by-appearance"] +# rustdoc-args = ["-Zunstable-options", "--sort-modules-by-appearance"] diff --git a/src/capi/io.rs b/src/capi/io.rs index 0388cadc4..5ae076e51 100644 --- a/src/capi/io.rs +++ b/src/capi/io.rs @@ -22,9 +22,10 @@ use crate::{ keyboard::{AnyKeyboardLayout, KeyCode, KeyboardLayout, Modifiers, Qwerty}, syllable::{ DaiChien26, Et, Et26, GinYieh, Hsu, Ibm, KeyboardLayoutCompat, Pinyin, Standard, + SyllableEditor, }, BasicEditor, CharacterForm, Editor, EditorKeyBehavior, EditorOptions, LanguageMode, - LaxUserFreqEstimate, SyllableEditor, UserPhraseAddDirection, + LaxUserFreqEstimate, UserPhraseAddDirection, }, zhuyin::Syllable, }; diff --git a/src/conversion/mod.rs b/src/conversion/mod.rs index 330425413..2f6a11960 100644 --- a/src/conversion/mod.rs +++ b/src/conversion/mod.rs @@ -1,4 +1,4 @@ -//! TODO: docs +//! Algorithms to convert syllables to Chinese characters. mod chewing; mod symbol; diff --git a/src/dictionary/loader.rs b/src/dictionary/loader.rs index 613b434a0..7556c864f 100644 --- a/src/dictionary/loader.rs +++ b/src/dictionary/loader.rs @@ -22,7 +22,7 @@ impl SystemDictionaryLoader { self.sys_path = Some(path.into()); self } - pub fn load(self) -> Option>> { + pub fn load(self) -> Result>, &'static str> { let mut db_loaders: Vec> = vec![]; #[cfg(feature = "sqlite")] { @@ -35,7 +35,8 @@ impl SystemDictionaryLoader { } else { sys_path_from_env_var() }; - let sys_path = find_path_by_files(&search_path, &["tsi.dat", "word.dat"])?; + let sys_path = find_path_by_files(&search_path, &["tsi.dat", "word.dat"]) + .ok_or("SystemDictionaryNotFound")?; let tsi_db_path = sys_path.join("tsi.dat"); let tsi_db = db_loaders @@ -47,7 +48,7 @@ impl SystemDictionaryLoader { .iter() .find_map(|loader| loader.open_read_only(&word_db_path)); - Some(vec![word_db.unwrap(), tsi_db.unwrap()]) + Ok(vec![word_db.unwrap(), tsi_db.unwrap()]) } } @@ -64,7 +65,7 @@ impl UserDictionaryLoader { self.data_path = Some(path.as_ref().to_path_buf()); self } - pub fn load(self) -> Option> { + pub fn load(self) -> Result, &'static str> { let mut db_loaders: Vec> = vec![]; #[cfg(feature = "sqlite")] { @@ -75,10 +76,13 @@ impl UserDictionaryLoader { let data_path = if let Some(data_path) = self.data_path { data_path } else { - userphrase_path()? + userphrase_path().ok_or("UserDictionaryNotFound")? }; - db_loaders.iter().find_map(|loader| loader.open(&data_path)) + db_loaders + .iter() + .find_map(|loader| loader.open(&data_path)) + .ok_or("ErrorOpenUserDictionary") } } diff --git a/src/dictionary/mod.rs b/src/dictionary/mod.rs index 16cec8789..d260c7698 100644 --- a/src/dictionary/mod.rs +++ b/src/dictionary/mod.rs @@ -1,4 +1,4 @@ -//! Dictionaries for looking up phrases. +//! Systems and user phrase dictionaries. use std::{ any::Any, diff --git a/src/editor/composition_editor.rs b/src/editor/composition_editor.rs index 315d13261..b3d192e6f 100644 --- a/src/editor/composition_editor.rs +++ b/src/editor/composition_editor.rs @@ -6,7 +6,7 @@ use crate::conversion::{Break, Composition, Glue, Interval, Symbol}; /// TODO #[derive(Debug, Default, Clone)] -pub struct CompositionEditor { +pub(crate) struct CompositionEditor { /// TODO cursor: usize, cursor_stack: Vec, diff --git a/src/editor/mod.rs b/src/editor/mod.rs index e4c855686..d1aab6403 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -1,7 +1,7 @@ -//! TODO: doc +//! Abstract input method editors. mod abbrev; -pub mod composition_editor; +mod composition_editor; mod estimate; pub mod keyboard; mod selection; @@ -13,14 +13,14 @@ use std::{ }; pub use estimate::{EstimateError, LaxUserFreqEstimate, UserFreqEstimate}; -pub use syllable::SyllableEditor; use tracing::{debug, trace, warn}; use crate::{ conversion::{ - full_width_symbol_input, special_symbol_input, ConversionEngine, Interval, Symbol, + full_width_symbol_input, special_symbol_input, ChewingEngine, ConversionEngine, Interval, + Symbol, }, - dictionary::{Dictionary, LayeredDictionary}, + dictionary::{Dictionary, LayeredDictionary, SystemDictionaryLoader, UserDictionaryLoader}, editor::keyboard::KeyCode, zhuyin::{Syllable, SyllableSlice}, }; @@ -33,7 +33,7 @@ use self::{ phrase::PhraseSelector, symbol::{SpecialSymbolSelector, SymbolSelector}, }, - syllable::{KeyBehavior, Standard}, + syllable::{KeyBehavior, Standard, SyllableEditor}, }; #[derive(Debug, Clone, Copy)] @@ -107,10 +107,7 @@ pub enum EditorKeyBehavior { } #[derive(Debug)] -pub struct Editor -where - C: ConversionEngine, -{ +pub struct Editor { com: CompositionEditor, syl: Box, conv: C, @@ -126,6 +123,18 @@ where notice_buffer: String, } +impl Editor { + pub fn chewing() -> Result, Box> { + let system_dict = SystemDictionaryLoader::new().load()?; + let user_dict = UserDictionaryLoader::new().load()?; + let estimate = LaxUserFreqEstimate::open(user_dict.as_ref())?; + let dict = LayeredDictionary::new(system_dict, user_dict); + let conversion_engine = ChewingEngine::new(); + let editor = Editor::new(conversion_engine, dict, estimate); + Ok(editor) + } +} + impl Editor where C: ConversionEngine, diff --git a/src/lib.rs b/src/lib.rs index d143c4c39..7982f9c90 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,12 +14,38 @@ #![warn(unused_tuple_struct_fields)] #![warn(variant_size_differences)] -//! TODO: docs +//! The Chewing (酷音) intelligent phonetic input method library. //! -//! # Glossary +//! This crate provides the core algorithms and facilities that can be used to +//! implement input methods and manipulate user dictionaries. //! -//! 1. **Phonetic Key** - +//! # Behavior +//! +//! The [Editor][editor::Editor] implements the behavior of the Chewing input +//! method. Chewing is a bopomofo phonetics input method that can convert +//! keystrokes to Zhuyin/Bopomofo and then to Chinese characters. The state +//! machine powering the input method can detect the current input context and +//! translate the input to symbols, latin characters, or Chinese characters. The +//! Editor also has an option that is enabled by default to auto-learn new +//! phrases from users' input to provide more personalized intelligence. +//! +//! ```rust,no_run +//! # fn main() -> Result<(), Box> { +//! use chewing::editor::{BasicEditor, Editor}; +//! use chewing::editor::keyboard::{KeyboardLayout, KeyCode, Qwerty}; +//! +//! let keyboard = Qwerty; +//! let mut editor = Editor::chewing()?; +//! +//! editor.process_keyevent(keyboard.map(KeyCode::D)); +//! editor.process_keyevent(keyboard.map(KeyCode::J)); +//! editor.process_keyevent(keyboard.map(KeyCode::N4)); +//! editor.process_keyevent(keyboard.map(KeyCode::Down)); +//! editor.process_keyevent(keyboard.map(KeyCode::N3)); +//! +//! assert_eq!("酷", editor.display()); +//! # Ok(()) } +//! ``` #[cfg(feature = "capi")] pub mod capi; pub mod conversion; diff --git a/src/zhuyin/mod.rs b/src/zhuyin/mod.rs index b2bfa5b36..e00d38701 100644 --- a/src/zhuyin/mod.rs +++ b/src/zhuyin/mod.rs @@ -1,4 +1,4 @@ -//! TODO: docs +//! Chinese syllables and bopomofo phonetic symbols. mod bopomofo; mod syllable; From 21c5759b363bb88775810062433ad055c0b130e9 Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Thu, 18 Jan 2024 08:31:16 +0800 Subject: [PATCH 8/9] fix(editor): dict loader now returns Result --- src/capi/io.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/capi/io.rs b/src/capi/io.rs index 5ae076e51..266386782 100644 --- a/src/capi/io.rs +++ b/src/capi/io.rs @@ -133,8 +133,8 @@ pub extern "C" fn chewing_new2( SystemDictionaryLoader::new().sys_path(search_path).load() }; let dictionaries = match dictionaries { - Some(d) => d, - None => return null_mut(), + Ok(d) => d, + Err(_) => return null_mut(), }; let user_dictionary = if userpath.is_null() { UserDictionaryLoader::new().load() @@ -147,8 +147,8 @@ pub extern "C" fn chewing_new2( .load() }; let user_dictionary = match user_dictionary { - Some(d) => d, - None => return null_mut(), + Ok(d) => d, + Err(_) => return null_mut(), }; let estimate = LaxUserFreqEstimate::open(user_dictionary.as_ref()); From 7ecada3991f3ecd593eb49dee53490d20323f503 Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Thu, 18 Jan 2024 08:47:16 +0800 Subject: [PATCH 9/9] chore: bump crate version to 0.6.0-alpha.2 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1767b10d6..cad02c231 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,7 +124,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chewing" -version = "0.6.0-alpha.1" +version = "0.6.0-alpha.2" dependencies = [ "bytemuck", "cdb2", diff --git a/Cargo.toml b/Cargo.toml index 5272273da..aabdb7d77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "chewing" description = "The Chewing (酷音) intelligent Zhuyin input method." license = "LGPL-2.1-or-later" documentation = "https://docs.rs/chewing" -version = "0.6.0-alpha.1" +version = "0.6.0-alpha.2" rust-version = "1.70" edition = "2021"