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}