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 fragment;
32mod inner;
33mod operation;
34
35// ── Re-exports from entity DTOs (enums that consumers need) ──────
36pub use frontend::block::dtos::{Alignment, MarkerType};
37pub use frontend::document::dtos::{TextDirection, WrapMode};
38pub use frontend::frame::dtos::FramePosition;
39pub use frontend::inline_element::dtos::{CharVerticalAlignment, InlineContent, UnderlineStyle};
40pub use frontend::list::dtos::ListStyle;
41pub use frontend::resource::dtos::ResourceType;
42
43// ── Error type ───────────────────────────────────────────────────
44pub type Result<T> = anyhow::Result<T>;
45
46// ── Public API types ─────────────────────────────────────────────
47pub use cursor::TextCursor;
48pub use document::TextDocument;
49pub use events::{DocumentEvent, Subscription};
50pub use fragment::DocumentFragment;
51pub use operation::{DocxExportResult, HtmlImportResult, MarkdownImportResult, Operation};
52
53// TextDocument and TextCursor are Send + Sync (all fields are Arc<Mutex<...>>).
54const _: () = {
55    #[allow(dead_code)]
56    fn assert_send_sync<T: Send + Sync>() {}
57    fn _assert_all() {
58        assert_send_sync::<TextDocument>();
59        assert_send_sync::<TextCursor>();
60    }
61};
62
63// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
64// Public format types
65// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
66
67/// Character/text formatting. All fields are optional: `None` means
68/// "not set — inherit from the block's default or the document's default."
69#[derive(Debug, Clone, Default, PartialEq)]
70pub struct TextFormat {
71    pub font_family: Option<String>,
72    pub font_point_size: Option<u32>,
73    pub font_weight: Option<u32>,
74    pub font_bold: Option<bool>,
75    pub font_italic: Option<bool>,
76    pub font_underline: Option<bool>,
77    pub font_overline: Option<bool>,
78    pub font_strikeout: Option<bool>,
79    pub letter_spacing: Option<i32>,
80    pub word_spacing: Option<i32>,
81    pub underline_style: Option<UnderlineStyle>,
82    pub vertical_alignment: Option<CharVerticalAlignment>,
83    pub anchor_href: Option<String>,
84    pub anchor_names: Vec<String>,
85    pub is_anchor: Option<bool>,
86    pub tooltip: Option<String>,
87}
88
89/// Block (paragraph) formatting. All fields are optional.
90#[derive(Debug, Clone, Default, PartialEq)]
91pub struct BlockFormat {
92    pub alignment: Option<Alignment>,
93    pub top_margin: Option<i32>,
94    pub bottom_margin: Option<i32>,
95    pub left_margin: Option<i32>,
96    pub right_margin: Option<i32>,
97    pub heading_level: Option<u8>,
98    pub indent: Option<u8>,
99    pub text_indent: Option<i32>,
100    pub marker: Option<MarkerType>,
101    pub tab_positions: Vec<i32>,
102}
103
104/// Frame formatting. All fields are optional.
105#[derive(Debug, Clone, Default, PartialEq)]
106pub struct FrameFormat {
107    pub height: Option<i32>,
108    pub width: Option<i32>,
109    pub top_margin: Option<i32>,
110    pub bottom_margin: Option<i32>,
111    pub left_margin: Option<i32>,
112    pub right_margin: Option<i32>,
113    pub padding: Option<i32>,
114    pub border: Option<i32>,
115    pub position: Option<FramePosition>,
116}
117
118// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
119// Enums for cursor movement
120// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
121
122/// Controls whether a movement collapses or extends the selection.
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum MoveMode {
125    /// Move both position and anchor — collapses selection.
126    MoveAnchor,
127    /// Move only position, keep anchor — creates or extends selection.
128    KeepAnchor,
129}
130
131/// Semantic cursor movement operations.
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133pub enum MoveOperation {
134    NoMove,
135    Start,
136    End,
137    StartOfLine,
138    EndOfLine,
139    StartOfBlock,
140    EndOfBlock,
141    StartOfWord,
142    EndOfWord,
143    PreviousBlock,
144    NextBlock,
145    PreviousCharacter,
146    NextCharacter,
147    PreviousWord,
148    NextWord,
149    Up,
150    Down,
151    Left,
152    Right,
153    WordLeft,
154    WordRight,
155}
156
157/// Quick-select a region around the cursor.
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub enum SelectionType {
160    WordUnderCursor,
161    LineUnderCursor,
162    BlockUnderCursor,
163    Document,
164}
165
166// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
167// Read-only info types
168// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
169
170/// Document-level statistics. O(1) cached.
171#[derive(Debug, Clone, PartialEq, Eq)]
172pub struct DocumentStats {
173    pub character_count: usize,
174    pub word_count: usize,
175    pub block_count: usize,
176    pub frame_count: usize,
177    pub image_count: usize,
178    pub list_count: usize,
179}
180
181/// Info about a block at a given position.
182#[derive(Debug, Clone, PartialEq, Eq)]
183pub struct BlockInfo {
184    pub block_id: usize,
185    pub block_number: usize,
186    pub start: usize,
187    pub length: usize,
188}
189
190/// A single search match.
191#[derive(Debug, Clone, PartialEq, Eq)]
192pub struct FindMatch {
193    pub position: usize,
194    pub length: usize,
195}
196
197/// Options for find / find_all / replace operations.
198#[derive(Debug, Clone, Default)]
199pub struct FindOptions {
200    pub case_sensitive: bool,
201    pub whole_word: bool,
202    pub use_regex: bool,
203    pub search_backward: bool,
204}