ailloli_ui_editor/input/ime.rs
1//! Non-mutating IME preedit projection into display buffers.
2
3use ailloli_ui_text::{TextBuffer, TextEditState};
4
5/// Display buffer used while IME preedit is active.
6///
7/// The buffer is an owned clone with non-empty preedit text inserted at the
8/// clamped caret. `caret_byte` is a UTF-8 byte offset into that display clone.
9///
10/// # Examples
11///
12/// ```
13/// use ailloli_ui_editor::input::ime::EditorDisplayBuffer;
14/// use ailloli_ui_text::TextBuffer;
15/// let display = EditorDisplayBuffer { buffer: TextBuffer::from_string("é"), caret_byte: 2 };
16/// assert_eq!(display.caret_byte, display.buffer.len_bytes());
17/// ```
18#[derive(Debug, Clone)]
19pub struct EditorDisplayBuffer {
20 /// Original text or an IME-preedit augmented clone.
21 pub buffer: TextBuffer,
22 /// Clamped display-buffer caret byte.
23 pub caret_byte: usize,
24}
25
26/// Produces text and caret geometry input for the current IME state.
27///
28/// No preedit or empty preedit returns an unchanged clone with the source caret
29/// clamped to buffer length. A preedit selection uses its end byte clamped to
30/// preedit length; absent selection positions after all preedit bytes.
31///
32/// # Examples
33///
34/// ```
35/// use ailloli_ui_editor::input::ime::display_buffer_for_edit;
36/// use ailloli_ui_text::{TextBuffer, TextEditState};
37/// let buffer = TextBuffer::from_string("hello");
38/// let display = display_buffer_for_edit(&buffer, &TextEditState::new());
39/// assert_eq!(display.buffer.as_str(), "hello");
40/// assert_eq!(display.caret_byte, 0);
41/// ```
42pub fn display_buffer_for_edit(buffer: &TextBuffer, edit: &TextEditState) -> EditorDisplayBuffer {
43 let Some(preedit) = edit.preedit.as_ref() else {
44 return EditorDisplayBuffer {
45 buffer: buffer.clone(),
46 caret_byte: edit.caret_byte.min(buffer.len_bytes()),
47 };
48 };
49 if preedit.text.is_empty() {
50 return EditorDisplayBuffer {
51 buffer: buffer.clone(),
52 caret_byte: edit.caret_byte.min(buffer.len_bytes()),
53 };
54 }
55 let mut display = buffer.clone();
56 let at = edit.caret_byte.min(display.len_bytes());
57 display.edit(at..at, &preedit.text);
58 let caret = preedit
59 .selection
60 .map(|(_, end)| at + end.min(preedit.text.len()))
61 .unwrap_or(at + preedit.text.len())
62 .min(display.len_bytes());
63 EditorDisplayBuffer {
64 buffer: display,
65 caret_byte: caret,
66 }
67}