1mod table;
17
18use std::sync::Arc;
19use std::sync::atomic::{AtomicU64, Ordering};
20
21pub const SOFT: char = '\u{2028}';
23pub const OBJ: char = '\u{FFFC}';
25
26static NEXT_ID: AtomicU64 = AtomicU64::new(1);
27
28pub fn next_id() -> u64 {
30 NEXT_ID.fetch_add(1, Ordering::Relaxed)
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Hash)]
35pub struct Link {
36 pub url: String,
37 pub title: Option<String>,
38}
39
40impl Link {
41 pub fn new(url: impl Into<String>, title: Option<String>) -> Arc<Self> {
42 Arc::new(Self {
43 url: url.into(),
44 title: title.filter(|title| !title.is_empty()),
45 })
46 }
47}
48
49#[derive(Debug, Clone, Default, PartialEq, Eq)]
51pub struct Style {
52 pub bold: bool,
53 pub italic: bool,
54 pub strike: bool,
55 pub code: bool,
56 pub link: Option<Arc<Link>>,
57 pub image: Option<Arc<Link>>,
59 pub html: bool,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum Flag {
66 Bold,
67 Italic,
68 Strike,
69 Code,
70}
71
72impl Style {
73 pub fn get(&self, flag: Flag) -> bool {
74 match flag {
75 Flag::Bold => self.bold,
76 Flag::Italic => self.italic,
77 Flag::Strike => self.strike,
78 Flag::Code => self.code,
79 }
80 }
81
82 pub fn set(&mut self, flag: Flag, value: bool) {
83 match flag {
84 Flag::Bold => self.bold = value,
85 Flag::Italic => self.italic = value,
86 Flag::Strike => self.strike = value,
87 Flag::Code => self.code = value,
88 }
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub struct ListInfo {
95 pub id: u64,
96 pub ordered: bool,
97 pub start: u64,
99 pub tight: bool,
101}
102
103impl ListInfo {
104 pub fn new(ordered: bool, start: u64) -> Self {
105 Self {
106 id: next_id(),
107 ordered,
108 start,
109 tight: true,
110 }
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum Container {
117 Quote {
118 id: u64,
119 },
120 Item {
121 list: ListInfo,
122 id: u64,
123 task: Option<bool>,
125 },
126}
127
128impl Container {
129 pub fn quote() -> Self {
130 Container::Quote { id: next_id() }
131 }
132
133 pub fn item(list: ListInfo) -> Self {
134 Self::task_item(list, None)
135 }
136
137 pub fn task_item(list: ListInfo, task: Option<bool>) -> Self {
138 Container::Item {
139 list,
140 id: next_id(),
141 task,
142 }
143 }
144
145 pub fn same(&self, other: &Container) -> bool {
147 match (self, other) {
148 (Container::Quote { id: a }, Container::Quote { id: b }) => a == b,
149 (
150 Container::Item {
151 list: la, id: a, ..
152 },
153 Container::Item {
154 list: lb, id: b, ..
155 },
156 ) => la.id == lb.id && a == b,
157 _ => false,
158 }
159 }
160
161 pub fn list(&self) -> Option<ListInfo> {
162 match self {
163 Container::Item { list, .. } => Some(*list),
164 Container::Quote { .. } => None,
165 }
166 }
167
168 pub fn task(&self) -> Option<bool> {
169 match self {
170 Container::Item { task, .. } => *task,
171 Container::Quote { .. } => None,
172 }
173 }
174
175 pub fn is_quote(&self) -> bool {
176 matches!(self, Container::Quote { .. })
177 }
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
182pub enum ColumnAlign {
183 #[default]
185 None,
186 Left,
187 Center,
188 Right,
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195pub struct Cell {
196 pub table: u64,
197 pub row: u64,
198 pub header: bool,
199 pub align: ColumnAlign,
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
205pub enum Leaf {
206 Paragraph,
207 Heading(u8),
208 Code,
209 Html,
210 Rule,
211 Cell(Cell),
213 Line,
215}
216
217impl Leaf {
218 pub fn is_text(self) -> bool {
219 matches!(self, Leaf::Paragraph | Leaf::Heading(_) | Leaf::Cell(_))
220 }
221
222 pub fn cell(self) -> Option<Cell> {
223 match self {
224 Leaf::Cell(cell) => Some(cell),
225 _ => None,
226 }
227 }
228
229 pub fn is_raw(self) -> bool {
231 matches!(self, Leaf::Code | Leaf::Html)
232 }
233
234 pub fn is_plain(self) -> bool {
236 matches!(self, Leaf::Code | Leaf::Html | Leaf::Line)
237 }
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub enum BlockKind {
243 Paragraph,
244 Heading(u8),
245 Quote,
247 Bullet,
249 Ordered,
251 Task,
253 Code,
254 Rule,
255 Html,
257}
258
259impl BlockKind {
260 pub fn is_list(self) -> bool {
261 matches!(
262 self,
263 BlockKind::Bullet | BlockKind::Ordered | BlockKind::Task
264 )
265 }
266}
267
268#[derive(Debug, Clone)]
269pub struct Block {
270 pub containers: Vec<Container>,
271 pub leaf: Leaf,
272 pub chars: Vec<char>,
273 pub styles: Vec<Style>,
274 pub info: Option<Arc<str>>,
276}
277
278impl Block {
279 pub fn new(containers: Vec<Container>, leaf: Leaf) -> Self {
280 Self {
281 containers,
282 leaf,
283 chars: Vec::new(),
284 styles: Vec::new(),
285 info: None,
286 }
287 }
288
289 pub fn len(&self) -> usize {
290 self.chars.len()
291 }
292
293 pub fn is_empty(&self) -> bool {
294 self.chars.is_empty()
295 }
296
297 pub fn push(&mut self, c: char, style: Style) {
298 self.chars.push(c);
299 self.styles.push(style);
300 }
301
302 pub fn text(&self) -> String {
303 self.chars.iter().collect()
304 }
305
306 pub fn language(&self) -> Option<&str> {
308 self.info
309 .as_deref()
310 .and_then(|info| info.split_whitespace().next())
311 .filter(|language| !language.is_empty())
312 }
313
314 pub fn cell(&self) -> Option<Cell> {
315 self.leaf.cell()
316 }
317
318 pub fn inner(&self) -> Option<&Container> {
320 self.containers.last()
321 }
322
323 pub fn line_start(&self, offset: usize) -> usize {
326 let offset = offset.min(self.chars.len());
327 self.chars[..offset]
328 .iter()
329 .rposition(|c| *c == '\n')
330 .map(|i| i + 1)
331 .unwrap_or(0)
332 }
333
334 pub fn line_starts(&self) -> Vec<usize> {
336 let mut starts = vec![0];
337 for (i, c) in self.chars.iter().enumerate() {
338 if *c == '\n' {
339 starts.push(i + 1);
340 }
341 }
342 starts
343 }
344
345 pub fn kind(&self) -> BlockKind {
347 match self.leaf {
348 Leaf::Heading(level) => BlockKind::Heading(level),
349 Leaf::Code => BlockKind::Code,
350 Leaf::Rule => BlockKind::Rule,
351 Leaf::Html => BlockKind::Html,
352 Leaf::Line | Leaf::Cell(_) => BlockKind::Paragraph,
353 Leaf::Paragraph => match self.inner() {
354 Some(Container::Item { task: Some(_), .. }) => BlockKind::Task,
355 Some(Container::Item { list, .. }) if list.ordered => BlockKind::Ordered,
356 Some(Container::Item { .. }) => BlockKind::Bullet,
357 Some(Container::Quote { .. }) => BlockKind::Quote,
358 None => BlockKind::Paragraph,
359 },
360 }
361 }
362}
363
364#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
366pub struct Pos {
367 pub block: usize,
368 pub offset: usize,
369}
370
371impl Pos {
372 pub fn new(block: usize, offset: usize) -> Self {
373 Self { block, offset }
374 }
375}
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
379pub struct Selection {
380 pub anchor: Pos,
381 pub head: Pos,
382}
383
384impl Selection {
385 pub fn caret(pos: Pos) -> Self {
386 Self {
387 anchor: pos,
388 head: pos,
389 }
390 }
391
392 pub fn is_collapsed(&self) -> bool {
393 self.anchor == self.head
394 }
395
396 pub fn range(&self) -> (Pos, Pos) {
397 if self.anchor <= self.head {
398 (self.anchor, self.head)
399 } else {
400 (self.head, self.anchor)
401 }
402 }
403}
404
405pub fn is_word_char(c: char) -> bool {
406 c.is_alphanumeric() || c == '_' || c == '\'' || c == '\u{2019}'
407}
408
409fn is_break(c: char) -> bool {
410 c == '\n' || c == SOFT
411}
412
413#[derive(Debug, Clone)]
414pub struct Document {
415 pub blocks: Vec<Block>,
416}
417
418impl Default for Document {
419 fn default() -> Self {
420 Self {
421 blocks: vec![Block::new(Vec::new(), Leaf::Paragraph)],
422 }
423 }
424}
425
426impl Document {
427 pub fn start(&self) -> Pos {
428 Pos::new(0, 0)
429 }
430
431 pub fn end(&self) -> Pos {
432 let last = self.blocks.len() - 1;
433 Pos::new(last, self.blocks[last].len())
434 }
435
436 pub fn clamp(&self, pos: Pos) -> Pos {
437 let block = pos.block.min(self.blocks.len() - 1);
438 Pos::new(block, pos.offset.min(self.blocks[block].len()))
439 }
440
441 pub fn word_count(&self) -> usize {
442 self.blocks
443 .iter()
444 .map(|block| {
445 block
446 .text()
447 .split(|c: char| c.is_whitespace() || c == OBJ)
448 .filter(|word| !word.is_empty())
449 .count()
450 })
451 .sum()
452 }
453
454 pub fn spans(&self, a: Pos, b: Pos) -> Vec<(usize, usize, usize)> {
456 (a.block..=b.block)
457 .map(|index| {
458 let start = if index == a.block { a.offset } else { 0 };
459 let end = if index == b.block {
460 b.offset
461 } else {
462 self.blocks[index].len()
463 };
464 (index, start, end.max(start))
465 })
466 .collect()
467 }
468
469 pub fn container_start(&self, block: usize, depth: usize) -> usize {
472 let container = self.blocks[block].containers[depth];
473 let mut index = block;
474 while index > 0 {
475 let previous = &self.blocks[index - 1];
476 if previous
477 .containers
478 .get(depth)
479 .is_some_and(|other| other.same(&container))
480 {
481 index -= 1;
482 } else {
483 break;
484 }
485 }
486 index
487 }
488
489 pub fn container_end(&self, block: usize, depth: usize) -> usize {
491 let container = self.blocks[block].containers[depth];
492 let mut index = block + 1;
493 while index < self.blocks.len()
494 && self.blocks[index]
495 .containers
496 .get(depth)
497 .is_some_and(|other| other.same(&container))
498 {
499 index += 1;
500 }
501 index
502 }
503
504 pub fn set_task(&mut self, block: usize, depth: usize, task: Option<bool>) {
507 let start = self.container_start(block, depth);
508 let end = self.container_end(block, depth);
509 for block in &mut self.blocks[start..end] {
510 if let Some(Container::Item { task: value, .. }) = block.containers.get_mut(depth) {
511 *value = task;
512 }
513 }
514 }
515
516 pub fn delete_range(&mut self, a: Pos, b: Pos) -> Pos {
522 if a >= b {
523 return a;
524 }
525 if (a.block..=b.block).any(|index| self.blocks[index].cell().is_some()) {
526 return self.delete_range_in_tables(a, b);
527 }
528
529 if a.block == b.block {
530 let block = &mut self.blocks[a.block];
531 block.chars.drain(a.offset..b.offset);
532 block.styles.drain(a.offset..b.offset);
533 } else {
534 let tail_chars: Vec<char> = self.blocks[b.block].chars[b.offset..].to_vec();
535 let tail_styles: Vec<Style> = self.blocks[b.block].styles[b.offset..].to_vec();
536
537 {
538 let first = &mut self.blocks[a.block];
539 first.chars.truncate(a.offset);
540 first.styles.truncate(a.offset);
541 first.chars.extend(tail_chars);
542 first.styles.extend(tail_styles);
543 if first.leaf == Leaf::Rule && !first.chars.is_empty() {
544 first.leaf = Leaf::Paragraph;
545 }
546 }
547
548 self.blocks.drain(a.block + 1..=b.block);
549 }
550
551 a
552 }
553
554 pub fn insert_char(&mut self, at: Pos, c: char, style: &Style) -> Pos {
556 let block = &mut self.blocks[at.block];
557 if block.leaf == Leaf::Rule {
558 block.leaf = Leaf::Paragraph;
559 }
560 let style = if block.leaf.is_plain() {
561 Style::default()
562 } else {
563 style.clone()
564 };
565 block.chars.insert(at.offset, c);
566 block.styles.insert(at.offset, style);
567 Pos::new(at.block, at.offset + 1)
568 }
569
570 pub fn insert_text(&mut self, at: Pos, text: &str, style: &Style) -> Pos {
573 let mut pos = at;
574 for c in text.chars() {
575 match c {
576 '\r' => {}
577 '\n' if self.blocks[pos.block].cell().is_some() => {
578 pos = self.insert_char(pos, ' ', style);
580 }
581 '\n' => {
582 if self.blocks[pos.block].leaf.is_raw() {
583 pos = self.insert_char(pos, '\n', style);
584 } else {
585 pos = self.split_block(pos);
586 }
587 }
588 '\t' => pos = self.insert_char(pos, '\t', style),
589 c if c.is_control() => {}
590 c => pos = self.insert_char(pos, c, style),
591 }
592 }
593 pos
594 }
595
596 pub fn insert_spaces(&mut self, block: usize, offset: usize, count: usize) {
598 let block = &mut self.blocks[block];
599 let offset = offset.min(block.chars.len());
600 block
601 .chars
602 .splice(offset..offset, std::iter::repeat(' ').take(count));
603 block.styles.splice(
604 offset..offset,
605 std::iter::repeat(Style::default()).take(count),
606 );
607 }
608
609 pub fn split_block(&mut self, at: Pos) -> Pos {
613 let old = &self.blocks[at.block];
614 let len = old.len();
615 let leaf = match old.leaf {
616 Leaf::Heading(_) if at.offset >= len => Leaf::Paragraph,
617 Leaf::Rule => Leaf::Paragraph,
618 other => other,
619 };
620 let info = if leaf == Leaf::Code {
621 old.info.clone()
622 } else {
623 None
624 };
625 let mut containers = old.containers.clone();
626 let item_depth = containers
627 .iter()
628 .rposition(|c| matches!(c, Container::Item { .. }));
629 let mut moved: Option<(usize, Container, Container)> = None;
630 if let Some(depth) = item_depth.filter(|depth| *depth + 1 == containers.len()) {
631 if let Container::Item { list, task, .. } = containers[depth] {
632 let old_item = containers[depth];
633 let new_item = Container::task_item(list, task.map(|_| false));
634 containers[depth] = new_item;
635 moved = Some((depth, old_item, new_item));
636 }
637 }
638
639 let block = &mut self.blocks[at.block];
640 let chars = block.chars.split_off(at.offset);
641 let styles = block.styles.split_off(at.offset);
642
643 self.blocks.insert(
644 at.block + 1,
645 Block {
646 containers,
647 leaf,
648 chars,
649 styles,
650 info,
651 },
652 );
653
654 if let Some((depth, old_item, new_item)) = moved {
656 let mut index = at.block + 2;
657 while index < self.blocks.len()
658 && self.blocks[index]
659 .containers
660 .get(depth)
661 .is_some_and(|c| c.same(&old_item))
662 {
663 self.blocks[index].containers[depth] = new_item;
664 index += 1;
665 }
666 }
667
668 Pos::new(at.block + 1, 0)
669 }
670
671 pub fn merge_with_previous(&mut self, block: usize) -> Pos {
673 if block == 0 || block >= self.blocks.len() {
674 return Pos::new(block.min(self.blocks.len() - 1), 0);
675 }
676
677 if self.blocks[block - 1].leaf == Leaf::Rule {
678 self.blocks.remove(block - 1);
679 return Pos::new(block - 1, 0);
680 }
681
682 let previous_cell = self.blocks[block - 1].cell().is_some();
684 let current_cell = self.blocks[block].cell().is_some();
685 if previous_cell && !current_cell {
686 if self.blocks[block].is_empty() {
687 self.blocks.remove(block);
688 }
689 return Pos::new(block - 1, self.blocks[block - 1].len());
690 }
691 if current_cell {
692 if !previous_cell && self.blocks[block - 1].is_empty() {
693 self.blocks.remove(block - 1);
694 return Pos::new(block - 1, 0);
695 }
696 return Pos::new(block, 0);
697 }
698
699 let current = self.blocks.remove(block);
700 let previous = &mut self.blocks[block - 1];
701 let offset = previous.len();
702 let plain = previous.leaf.is_plain();
703 previous.chars.extend(current.chars);
704 if plain {
705 previous
706 .styles
707 .extend(std::iter::repeat(Style::default()).take(current.styles.len()));
708 } else {
709 previous.styles.extend(current.styles);
710 }
711 Pos::new(block - 1, offset)
712 }
713
714 pub fn all_have(&self, a: Pos, b: Pos, flag: Flag) -> bool {
719 let mut any = false;
720 for (index, start, end) in self.spans(a, b) {
721 let block = &self.blocks[index];
722 if block.leaf.is_plain() {
723 continue;
724 }
725 for (c, style) in block.chars[start..end]
726 .iter()
727 .zip(&block.styles[start..end])
728 {
729 if is_break(*c) {
730 continue;
731 }
732 any = true;
733 if !style.get(flag) {
734 return false;
735 }
736 }
737 }
738 any
739 }
740
741 pub fn set_flag(&mut self, a: Pos, b: Pos, flag: Flag, value: bool) {
742 for (index, start, end) in self.spans(a, b) {
743 if self.blocks[index].leaf.is_plain() {
744 continue;
745 }
746 for style in &mut self.blocks[index].styles[start..end] {
747 if !style.html {
748 style.set(flag, value);
749 }
750 }
751 }
752 }
753
754 pub fn set_link(&mut self, a: Pos, b: Pos, link: Option<Arc<Link>>) {
755 for (index, start, end) in self.spans(a, b) {
756 if self.blocks[index].leaf.is_plain() {
757 continue;
758 }
759 for style in &mut self.blocks[index].styles[start..end] {
760 if !style.html {
761 style.link = link.clone();
762 }
763 }
764 }
765 }
766
767 pub fn link_at(&self, pos: Pos) -> Option<Arc<Link>> {
768 self.blocks[pos.block]
769 .styles
770 .get(pos.offset)
771 .and_then(|style| style.link.clone())
772 }
773
774 pub fn typing_style(&self, pos: Pos) -> Style {
776 let block = &self.blocks[pos.block];
777 if block.leaf.is_plain() || block.styles.is_empty() {
778 return Style::default();
779 }
780
781 let mut style = if pos.offset == 0 {
782 let mut style = block.styles[0].clone();
783 style.link = None;
784 style
785 } else {
786 let mut style = block.styles[pos.offset - 1].clone();
787 let next_link = block
788 .styles
789 .get(pos.offset)
790 .and_then(|next| next.link.clone());
791 if style.link != next_link {
792 style.link = None;
793 }
794 style
795 };
796 style.image = None;
798 style.html = false;
799 style
800 }
801
802 pub fn plain_text(&self, a: Pos, b: Pos) -> String {
804 let mut out = String::new();
805 for (index, start, end) in self.spans(a, b) {
806 for c in &self.blocks[index].chars[start..end] {
807 match *c {
808 SOFT => out.push(' '),
809 OBJ => {}
810 c => out.push(c),
811 }
812 }
813 if index != b.block {
814 let same_row = match (self.blocks[index].cell(), self.blocks[index + 1].cell()) {
815 (Some(this), Some(next)) => this.row == next.row,
816 _ => false,
817 };
818 out.push(if same_row { '\t' } else { '\n' });
819 }
820 }
821 out
822 }
823
824 pub fn prev_pos(&self, pos: Pos) -> Pos {
829 if pos.offset > 0 {
830 Pos::new(pos.block, pos.offset - 1)
831 } else if pos.block > 0 {
832 Pos::new(pos.block - 1, self.blocks[pos.block - 1].len())
833 } else {
834 pos
835 }
836 }
837
838 pub fn next_pos(&self, pos: Pos) -> Pos {
839 if pos.offset < self.blocks[pos.block].len() {
840 Pos::new(pos.block, pos.offset + 1)
841 } else if pos.block + 1 < self.blocks.len() {
842 Pos::new(pos.block + 1, 0)
843 } else {
844 pos
845 }
846 }
847
848 pub fn word_left(&self, pos: Pos) -> Pos {
849 if pos.offset == 0 {
850 return self.prev_pos(pos);
851 }
852 let chars = &self.blocks[pos.block].chars;
853 let mut offset = pos.offset;
854 while offset > 0 && chars[offset - 1].is_whitespace() {
855 offset -= 1;
856 }
857 if offset > 0 && is_word_char(chars[offset - 1]) {
858 while offset > 0 && is_word_char(chars[offset - 1]) {
859 offset -= 1;
860 }
861 } else if offset > 0 {
862 offset -= 1;
863 }
864 Pos::new(pos.block, offset)
865 }
866
867 pub fn word_right(&self, pos: Pos) -> Pos {
868 let chars = &self.blocks[pos.block].chars;
869 let len = chars.len();
870 if pos.offset >= len {
871 return self.next_pos(pos);
872 }
873 let mut offset = pos.offset;
874 if is_word_char(chars[offset]) {
875 while offset < len && is_word_char(chars[offset]) {
876 offset += 1;
877 }
878 } else if !chars[offset].is_whitespace() {
879 offset += 1;
880 }
881 while offset < len && chars[offset].is_whitespace() {
882 offset += 1;
883 }
884 Pos::new(pos.block, offset)
885 }
886
887 pub fn word_at(&self, pos: Pos) -> (Pos, Pos) {
889 let chars = &self.blocks[pos.block].chars;
890 let mut start = pos.offset.min(chars.len());
891 let mut end = start;
892 while start > 0 && is_word_char(chars[start - 1]) {
893 start -= 1;
894 }
895 while end < chars.len() && is_word_char(chars[end]) {
896 end += 1;
897 }
898 if start == end && end < chars.len() {
899 end += 1;
900 }
901 (Pos::new(pos.block, start), Pos::new(pos.block, end))
902 }
903}