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    pub is_code_block: Option<bool>,
168    pub code_language: Option<String>,
169}
170
171/// List formatting. All fields are optional: `None` means
172/// "not set — don't change this property."
173#[derive(Debug, Clone, Default, PartialEq, Eq)]
174pub struct ListFormat {
175    pub style: Option<ListStyle>,
176    pub indent: Option<u8>,
177    pub prefix: Option<String>,
178    pub suffix: Option<String>,
179}
180
181/// Frame formatting. All fields are optional.
182#[derive(Debug, Clone, Default, PartialEq, Eq)]
183pub struct FrameFormat {
184    pub height: Option<i32>,
185    pub width: Option<i32>,
186    pub top_margin: Option<i32>,
187    pub bottom_margin: Option<i32>,
188    pub left_margin: Option<i32>,
189    pub right_margin: Option<i32>,
190    pub padding: Option<i32>,
191    pub border: Option<i32>,
192    pub position: Option<FramePosition>,
193    pub is_blockquote: Option<bool>,
194}
195
196// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
197// Enums for cursor movement
198// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
199
200/// Controls whether a movement collapses or extends the selection.
201#[derive(Debug, Clone, Copy, PartialEq, Eq)]
202pub enum MoveMode {
203    /// Move both position and anchor — collapses selection.
204    MoveAnchor,
205    /// Move only position, keep anchor — creates or extends selection.
206    KeepAnchor,
207}
208
209/// Semantic cursor movement operations.
210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211pub enum MoveOperation {
212    NoMove,
213    Start,
214    End,
215    StartOfLine,
216    EndOfLine,
217    StartOfBlock,
218    EndOfBlock,
219    StartOfWord,
220    EndOfWord,
221    PreviousBlock,
222    NextBlock,
223    PreviousCharacter,
224    NextCharacter,
225    PreviousWord,
226    NextWord,
227    Up,
228    Down,
229    Left,
230    Right,
231    WordLeft,
232    WordRight,
233}
234
235/// Quick-select a region around the cursor.
236#[derive(Debug, Clone, Copy, PartialEq, Eq)]
237pub enum SelectionType {
238    WordUnderCursor,
239    LineUnderCursor,
240    BlockUnderCursor,
241    Document,
242}
243
244// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
245// Read-only info types
246// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
247
248/// Document-level statistics. O(1) cached.
249#[derive(Debug, Clone, PartialEq, Eq)]
250pub struct DocumentStats {
251    pub character_count: usize,
252    pub word_count: usize,
253    pub block_count: usize,
254    pub frame_count: usize,
255    pub image_count: usize,
256    pub list_count: usize,
257    pub table_count: usize,
258}
259
260/// Info about a block at a given position.
261#[derive(Debug, Clone, PartialEq, Eq)]
262pub struct BlockInfo {
263    pub block_id: usize,
264    pub block_number: usize,
265    pub start: usize,
266    pub length: usize,
267}
268
269/// A single search match.
270#[derive(Debug, Clone, PartialEq, Eq)]
271pub struct FindMatch {
272    pub position: usize,
273    pub length: usize,
274}
275
276/// Options for find / find_all / replace operations.
277#[derive(Debug, Clone, Default)]
278pub struct FindOptions {
279    pub case_sensitive: bool,
280    pub whole_word: bool,
281    pub use_regex: bool,
282    pub search_backward: bool,
283}