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