Skip to main content

text_document/
lib.rs

1//! # text-document
2//!
3//! A rich text document model for Rust.
4//!
5//! Provides a [`TextDocument`] as the main entry point and [`TextCursor`] for
6//! cursor-based editing, inspired by Qt's QTextDocument/QTextCursor API.
7//!
8//! ```rust,no_run
9//! use text_document::{TextDocument, MoveMode, MoveOperation};
10//!
11//! let doc = TextDocument::new();
12//! doc.set_plain_text("Hello world").unwrap();
13//!
14//! let cursor = doc.cursor();
15//! cursor.move_position(MoveOperation::EndOfWord, MoveMode::KeepAnchor, 1);
16//! cursor.insert_text("Goodbye").unwrap(); // replaces "Hello"
17//!
18//! // Multiple cursors on the same document
19//! let c1 = doc.cursor();
20//! let c2 = doc.cursor_at(5);
21//! c1.insert_text("A").unwrap();
22//! // c2's position is automatically adjusted
23//!
24//! doc.undo().unwrap();
25//! ```
26
27mod convert;
28mod cursor;
29mod document;
30mod events;
31mod flow;
32mod fragment;
33mod highlight;
34mod inner;
35mod operation;
36mod text_block;
37mod text_frame;
38mod text_list;
39mod text_table;
40
41// ── Re-exports from entity DTOs (enums that consumers need) ──────
42pub use frontend::block::dtos::{Alignment, MarkerType};
43pub use frontend::document::dtos::{TextDirection, WrapMode};
44pub use frontend::frame::dtos::FramePosition;
45pub use frontend::inline_element::dtos::{CharVerticalAlignment, InlineContent, UnderlineStyle};
46pub use frontend::list::dtos::ListStyle;
47pub use frontend::resource::dtos::ResourceType;
48
49// ── Error type ───────────────────────────────────────────────────
50pub type Result<T> = anyhow::Result<T>;
51
52// ── Public API types ─────────────────────────────────────────────
53pub use cursor::TextCursor;
54pub use document::TextDocument;
55pub use events::{DocumentEvent, Subscription};
56pub use fragment::DocumentFragment;
57pub use highlight::{HighlightContext, HighlightFormat, HighlightSpan, SyntaxHighlighter};
58pub use operation::{DocxExportResult, HtmlImportResult, MarkdownImportResult, Operation};
59
60// ── Layout engine API types ─────────────────────────────────────
61pub use flow::{
62    BlockSnapshot, CellFormat, CellSnapshot, CellVerticalAlignment, FlowElement,
63    FlowElementSnapshot, FlowSnapshot, FormatChangeKind, FragmentContent, FrameSnapshot, ListInfo,
64    TableCellContext, TableCellRef, TableFormat, TableSnapshot,
65};
66pub use text_block::TextBlock;
67pub use text_frame::TextFrame;
68pub use text_list::TextList;
69pub use text_table::{TextTable, TextTableCell};
70
71// All public handle types are Send + Sync (all fields are Arc<Mutex<...>> + Copy).
72const _: () = {
73    #[allow(dead_code)]
74    fn assert_send_sync<T: Send + Sync>() {}
75    fn _assert_all() {
76        assert_send_sync::<TextDocument>();
77        assert_send_sync::<TextCursor>();
78        assert_send_sync::<TextBlock>();
79        assert_send_sync::<TextFrame>();
80        assert_send_sync::<TextTable>();
81        assert_send_sync::<TextTableCell>();
82        assert_send_sync::<TextList>();
83    }
84};
85
86// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
87// Color
88// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
89
90/// An RGBA color value. Each component is 0–255.
91#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
92pub struct Color {
93    pub red: u8,
94    pub green: u8,
95    pub blue: u8,
96    pub alpha: u8,
97}
98
99impl Color {
100    /// Create an opaque color (alpha = 255).
101    pub fn rgb(red: u8, green: u8, blue: u8) -> Self {
102        Self {
103            red,
104            green,
105            blue,
106            alpha: 255,
107        }
108    }
109
110    /// Create a color with explicit alpha.
111    pub fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
112        Self {
113            red,
114            green,
115            blue,
116            alpha,
117        }
118    }
119}
120
121// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
122// Public format types
123// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
124
125/// Character/text formatting. All fields are optional: `None` means
126/// "not set — inherit from the block's default or the document's default."
127#[derive(Debug, Clone, Default, PartialEq, Eq)]
128pub struct TextFormat {
129    pub font_family: Option<String>,
130    pub font_point_size: Option<u32>,
131    pub font_weight: Option<u32>,
132    pub font_bold: Option<bool>,
133    pub font_italic: Option<bool>,
134    pub font_underline: Option<bool>,
135    pub font_overline: Option<bool>,
136    pub font_strikeout: Option<bool>,
137    pub letter_spacing: Option<i32>,
138    pub word_spacing: Option<i32>,
139    pub underline_style: Option<UnderlineStyle>,
140    pub vertical_alignment: Option<CharVerticalAlignment>,
141    pub anchor_href: Option<String>,
142    pub anchor_names: Vec<String>,
143    pub is_anchor: Option<bool>,
144    pub tooltip: Option<String>,
145    pub foreground_color: Option<Color>,
146    pub background_color: Option<Color>,
147    pub underline_color: Option<Color>,
148}
149
150/// Block (paragraph) formatting. All fields are optional.
151#[derive(Debug, Clone, Default, PartialEq)]
152pub struct BlockFormat {
153    pub alignment: Option<Alignment>,
154    pub top_margin: Option<i32>,
155    pub bottom_margin: Option<i32>,
156    pub left_margin: Option<i32>,
157    pub right_margin: Option<i32>,
158    pub heading_level: Option<u8>,
159    pub indent: Option<u8>,
160    pub text_indent: Option<i32>,
161    pub marker: Option<MarkerType>,
162    pub tab_positions: Vec<i32>,
163    pub line_height: Option<f32>,
164    pub non_breakable_lines: Option<bool>,
165    pub direction: Option<TextDirection>,
166    pub background_color: Option<String>,
167}
168
169/// List formatting. All fields are optional: `None` means
170/// "not set — don't change this property."
171#[derive(Debug, Clone, Default, PartialEq, Eq)]
172pub struct ListFormat {
173    pub style: Option<ListStyle>,
174    pub indent: Option<u8>,
175    pub prefix: Option<String>,
176    pub suffix: Option<String>,
177}
178
179/// Frame formatting. All fields are optional.
180#[derive(Debug, Clone, Default, PartialEq, Eq)]
181pub struct FrameFormat {
182    pub height: Option<i32>,
183    pub width: Option<i32>,
184    pub top_margin: Option<i32>,
185    pub bottom_margin: Option<i32>,
186    pub left_margin: Option<i32>,
187    pub right_margin: Option<i32>,
188    pub padding: Option<i32>,
189    pub border: Option<i32>,
190    pub position: Option<FramePosition>,
191}
192
193// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
194// Enums for cursor movement
195// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
196
197/// Controls whether a movement collapses or extends the selection.
198#[derive(Debug, Clone, Copy, PartialEq, Eq)]
199pub enum MoveMode {
200    /// Move both position and anchor — collapses selection.
201    MoveAnchor,
202    /// Move only position, keep anchor — creates or extends selection.
203    KeepAnchor,
204}
205
206/// Semantic cursor movement operations.
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208pub enum MoveOperation {
209    NoMove,
210    Start,
211    End,
212    StartOfLine,
213    EndOfLine,
214    StartOfBlock,
215    EndOfBlock,
216    StartOfWord,
217    EndOfWord,
218    PreviousBlock,
219    NextBlock,
220    PreviousCharacter,
221    NextCharacter,
222    PreviousWord,
223    NextWord,
224    Up,
225    Down,
226    Left,
227    Right,
228    WordLeft,
229    WordRight,
230}
231
232/// Quick-select a region around the cursor.
233#[derive(Debug, Clone, Copy, PartialEq, Eq)]
234pub enum SelectionType {
235    WordUnderCursor,
236    LineUnderCursor,
237    BlockUnderCursor,
238    Document,
239}
240
241// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
242// Read-only info types
243// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
244
245/// Document-level statistics. O(1) cached.
246#[derive(Debug, Clone, PartialEq, Eq)]
247pub struct DocumentStats {
248    pub character_count: usize,
249    pub word_count: usize,
250    pub block_count: usize,
251    pub frame_count: usize,
252    pub image_count: usize,
253    pub list_count: usize,
254    pub table_count: usize,
255}
256
257/// Info about a block at a given position.
258#[derive(Debug, Clone, PartialEq, Eq)]
259pub struct BlockInfo {
260    pub block_id: usize,
261    pub block_number: usize,
262    pub start: usize,
263    pub length: usize,
264}
265
266/// A single search match.
267#[derive(Debug, Clone, PartialEq, Eq)]
268pub struct FindMatch {
269    pub position: usize,
270    pub length: usize,
271}
272
273/// Options for find / find_all / replace operations.
274#[derive(Debug, Clone, Default)]
275pub struct FindOptions {
276    pub case_sensitive: bool,
277    pub whole_word: bool,
278    pub use_regex: bool,
279    pub search_backward: bool,
280}