1use alloc::boxed::Box;
31use alloc::format;
32use alloc::string::String;
33use alloc::vec::Vec;
34use core::sync::atomic::{AtomicU32, Ordering};
35
36use agent_core::json::{self, Limits, Value};
37
38use crate::ed_core::{TextPos, TextSel};
39use crate::history::{Edit, History};
40use crate::i18n::tr;
41
42pub const RF_BOLD: u8 = 1;
44pub const RF_ITALIC: u8 = 2;
46pub const RF_UNDER: u8 = 4;
48pub const RF_STRIKE: u8 = 8;
50pub const RF_ALL: u8 = 15;
52
53pub const RA_LEFT: u8 = 0;
55pub const RA_CENTER: u8 = 1;
57pub const RA_RIGHT: u8 = 2;
59pub const RA_JUSTIFY: u8 = 3;
61
62pub const LS_NONE: u8 = 0;
64pub const LS_BULLET: u8 = 1;
66pub const LS_NUMBER: u8 = 2;
68
69pub const PS_NORMAL: u8 = 0;
71pub const PS_H1: u8 = 1;
73pub const PS_H2: u8 = 2;
75pub const PS_H3: u8 = 3;
77pub const PS_QUOTE: u8 = 4;
79
80pub const LEVEL_MAX: u8 = 8;
82
83#[derive(Clone, Debug, Default, PartialEq, Eq)]
85pub struct Fmt {
86 pub flags: u8,
88 pub color: u32,
90 pub bg: u32,
92 pub size: u16,
94 pub font: String,
96 pub link: String,
98}
99
100impl Fmt {
101 pub fn flags(flags: u8) -> Fmt {
103 Fmt { flags, ..Fmt::default() }
104 }
105 pub fn bold() -> Fmt {
107 Fmt::flags(RF_BOLD)
108 }
109 pub fn italic() -> Fmt {
111 Fmt::flags(RF_ITALIC)
112 }
113 pub fn link(url: &str) -> Fmt {
115 Fmt { link: String::from(url), ..Fmt::default() }
116 }
117}
118
119static NEXT_VER: AtomicU32 = AtomicU32::new(1);
120
121fn next_ver() -> u32 {
122 NEXT_VER.fetch_add(1, Ordering::Relaxed)
123}
124
125#[derive(Clone, Debug)]
129pub struct Para {
130 text: String,
131 runs: Vec<(usize, Fmt)>,
133 pub mark: Fmt,
135 pub align: u8,
137 pub list: u8,
139 pub level: u8,
141 pub style: u8,
143 ver: u32,
145}
146
147impl PartialEq for Para {
148 fn eq(&self, o: &Para) -> bool {
149 self.text == o.text
150 && self.runs == o.runs
151 && self.mark == o.mark
152 && self.align == o.align
153 && self.list == o.list
154 && self.level == o.level
155 && self.style == o.style
156 }
157}
158
159impl Default for Para {
160 fn default() -> Para {
161 Para::new()
162 }
163}
164
165impl Para {
166 pub fn new() -> Para {
168 Para { text: String::new(), runs: Vec::new(), mark: Fmt::default(), align: RA_LEFT, list: LS_NONE, level: 0, style: PS_NORMAL, ver: next_ver() }
169 }
170
171 pub fn plain(text: &str) -> Para {
173 let mut p = Para::new();
174 p.push(text, &Fmt::default());
175 p
176 }
177
178 pub fn with(mut self, text: &str, fmt: Fmt) -> Para {
180 self.push(text, &fmt);
181 self
182 }
183
184 pub fn attrs(mut self, align: u8, list: u8, level: u8, style: u8) -> Para {
186 self.align = align.min(RA_JUSTIFY);
187 self.list = list.min(LS_NUMBER);
188 self.level = level.min(LEVEL_MAX);
189 self.style = style.min(PS_QUOTE);
190 self
191 }
192
193 pub fn push(&mut self, text: &str, fmt: &Fmt) {
195 let clean: String;
196 let text = if text.contains(['\n', '\r']) {
197 clean = text.chars().filter(|&c| c != '\n' && c != '\r').collect();
198 &clean
199 } else {
200 text
201 };
202 if text.is_empty() {
203 return;
204 }
205 self.text.push_str(text);
206 let end = self.text.len();
207 match self.runs.last_mut() {
208 Some(r) if r.1 == *fmt => r.0 = end,
209 _ => self.runs.push((end, fmt.clone())),
210 }
211 self.ver = next_ver();
212 }
213
214 pub fn text(&self) -> &str {
216 &self.text
217 }
218
219 pub fn len(&self) -> usize {
221 self.text.len()
222 }
223
224 pub fn is_empty(&self) -> bool {
226 self.text.is_empty()
227 }
228
229 pub fn runs(&self) -> impl Iterator<Item = (&str, &Fmt)> + '_ {
231 let mut start = 0;
232 self.runs.iter().map(move |(end, f)| {
233 let s = &self.text[start..*end];
234 start = *end;
235 (s, f)
236 })
237 }
238
239 pub fn spans(&self) -> impl Iterator<Item = (usize, usize, &Fmt)> + '_ {
241 let mut start = 0;
242 self.runs.iter().map(move |(end, f)| {
243 let s = start;
244 start = *end;
245 (s, *end, f)
246 })
247 }
248
249 pub fn version(&self) -> u32 {
251 self.ver
252 }
253
254 pub fn fmt_at(&self, byte: usize) -> &Fmt {
256 for (end, f) in &self.runs {
257 if byte < *end {
258 return f;
259 }
260 }
261 self.runs.last().map_or(&self.mark, |r| &r.1)
262 }
263
264 pub fn typing_fmt(&self, byte: usize) -> Fmt {
267 if self.text.is_empty() {
268 return self.mark.clone();
269 }
270 let mut f = if byte == 0 { self.fmt_at(0).clone() } else { self.fmt_at(byte - 1).clone() };
271 if !f.link.is_empty() && (byte == 0 || byte >= self.text.len() || self.fmt_at(byte).link != f.link) {
272 f.link.clear();
273 }
274 f
275 }
276
277 fn slice(&self, a: usize, b: usize) -> Para {
279 let mut p = Para {
280 text: String::new(),
281 runs: Vec::new(),
282 mark: self.mark.clone(),
283 align: self.align,
284 list: self.list,
285 level: self.level,
286 style: self.style,
287 ver: 0,
288 };
289 for (s, e, f) in self.spans() {
290 let (s, e) = (s.max(a), e.min(b));
291 if s < e {
292 p.push(&self.text[s..e], f);
293 }
294 }
295 if b < self.text.len() || self.text.is_empty() {
296 p.mark = if self.text.is_empty() { self.mark.clone() } else { self.typing_fmt(b) };
297 }
298 p.ver = next_ver();
299 p
300 }
301
302 fn append(&mut self, o: &Para) {
304 for (s, f) in o.runs() {
305 self.push(s, f);
306 }
307 }
308
309 fn map_fmt(&mut self, a: usize, b: usize, f: &dyn Fn(&mut Fmt)) {
311 if a >= b {
312 return;
313 }
314 let old = core::mem::take(&mut self.runs);
315 let text = core::mem::take(&mut self.text);
316 let mut start = 0;
317 for (end, fmt) in old {
318 for (s, e, inside) in [(start, end.min(a).max(start), false), (start.max(a), end.min(b), true), (b.max(start), end, false)] {
319 if s < e {
320 let mut g = fmt.clone();
321 if inside {
322 f(&mut g);
323 }
324 self.push(&text[s..e], &g);
325 }
326 }
327 start = end;
328 }
329 self.ver = next_ver();
330 }
331
332 fn touch(&mut self) {
333 self.ver = next_ver();
334 }
335
336 fn attrs_of(&mut self, o: &Para) {
338 self.align = o.align;
339 self.list = o.list;
340 self.level = o.level;
341 self.style = o.style;
342 }
343}
344
345#[derive(Clone)]
349pub(crate) struct RichBody {
350 paras: Vec<Para>,
351 sel: TextSel,
352}
353
354const K_NONE: u8 = 0;
356const K_TYPE: u8 = 1;
357const K_BACK: u8 = 2;
358const K_DEL: u8 = 3;
359
360struct ParaEdit {
362 first: usize,
363 old: Vec<Para>,
364 new: Vec<Para>,
365 sel0: TextSel,
366 sel1: TextSel,
367 label: String,
368 kind: u8,
369}
370
371impl Edit<RichBody> for ParaEdit {
372 fn apply(&mut self, s: &mut RichBody) {
373 s.paras.splice(self.first..self.first + self.old.len(), self.new.iter().cloned());
374 s.sel = self.sel1;
375 }
376 fn revert(&mut self, s: &mut RichBody) {
377 s.paras.splice(self.first..self.first + self.new.len(), self.old.iter().cloned());
378 s.sel = self.sel0;
379 }
380 fn label(&self) -> &str {
381 &self.label
382 }
383 fn merge(&mut self, next: &mut dyn Edit<RichBody>) -> bool {
384 if self.kind == K_NONE {
385 return false;
386 }
387 match next.value().and_then(|v| v.downcast_ref::<ParaEdit>()) {
388 Some(n) if n.kind == self.kind && n.first == self.first && n.sel0 == self.sel1 && n.old == self.new => {
389 self.new = n.new.clone();
390 self.sel1 = n.sel1;
391 true
392 }
393 _ => false,
394 }
395 }
396 fn value(&self) -> Option<&dyn core::any::Any> {
397 Some(self)
398 }
399}
400
401pub struct RichDoc {
403 body: RichBody,
404 hist: History<RichBody>,
405 rev: u64,
406 typing: Option<(Fmt, TextPos)>,
408 uid: u32,
410 pub font: String,
412 pub size: u16,
414}
415
416impl Default for RichDoc {
417 fn default() -> RichDoc {
418 RichDoc::new()
419 }
420}
421
422impl Clone for RichDoc {
423 fn clone(&self) -> RichDoc {
425 let mut d = RichDoc::from_paras(self.body.paras.clone());
426 d.body.sel = self.body.sel;
427 d.font = self.font.clone();
428 d.size = self.size;
429 d
430 }
431}
432
433const HIST_LIMIT: usize = 500;
434
435impl RichDoc {
436 pub fn new() -> RichDoc {
438 RichDoc::from_paras(Vec::new())
439 }
440
441 pub fn from_paras(mut paras: Vec<Para>) -> RichDoc {
443 if paras.is_empty() {
444 paras.push(Para::new());
445 }
446 RichDoc { body: RichBody { paras, sel: TextSel::default() }, hist: History::new(HIST_LIMIT), rev: 0, typing: None, uid: next_ver(), font: String::new(), size: 0 }
447 }
448
449 pub fn from_plain(s: &str) -> RichDoc {
451 RichDoc::from_paras(lines_of(s).map(Para::plain).collect())
452 }
453
454 pub fn paras(&self) -> &[Para] {
456 &self.body.paras
457 }
458
459 pub fn para(&self, i: usize) -> &Para {
461 &self.body.paras[i.min(self.body.paras.len() - 1)]
462 }
463
464 pub fn para_count(&self) -> usize {
466 self.body.paras.len()
467 }
468
469 pub fn revision(&self) -> u64 {
471 self.rev
472 }
473
474 pub(crate) fn uid(&self) -> u32 {
476 self.uid
477 }
478
479 pub fn selection(&self) -> TextSel {
481 self.body.sel
482 }
483
484 pub fn set_selection(&mut self, sel: TextSel) {
486 let sel = TextSel::new(self.clamp(sel.anchor), self.clamp(sel.caret));
487 self.body.sel = sel;
488 }
489
490 pub fn clamp(&self, p: TextPos) -> TextPos {
492 let line = p.line.min(self.body.paras.len() - 1);
493 let t = &self.body.paras[line].text;
494 let mut b = p.byte.min(t.len());
495 while !t.is_char_boundary(b) {
496 b -= 1;
497 }
498 TextPos::new(line, b)
499 }
500
501 pub fn end(&self) -> TextPos {
503 let l = self.body.paras.len() - 1;
504 TextPos::new(l, self.body.paras[l].len())
505 }
506
507 pub fn text(&self) -> String {
509 let mut s = String::new();
510 for (i, p) in self.body.paras.iter().enumerate() {
511 if i > 0 {
512 s.push('\n');
513 }
514 s.push_str(&p.text);
515 }
516 s
517 }
518
519 pub fn flat(&self, p: TextPos) -> usize {
521 let p = self.clamp(p);
522 self.body.paras[..p.line].iter().map(|q| q.text.len() + 1).sum::<usize>() + p.byte
523 }
524
525 pub fn unflat(&self, f: usize) -> TextPos {
527 let mut f = f;
528 for (i, p) in self.body.paras.iter().enumerate() {
529 if f <= p.text.len() {
530 return self.clamp(TextPos::new(i, f));
531 }
532 f -= p.text.len() + 1;
533 }
534 self.end()
535 }
536
537 pub fn text_range(&self, a: TextPos, b: TextPos) -> String {
539 let (a, b) = (self.clamp(a.min(b)), self.clamp(a.max(b)));
540 let mut s = String::new();
541 for l in a.line..=b.line {
542 let t = &self.body.paras[l].text;
543 let s0 = if l == a.line { a.byte } else { 0 };
544 let s1 = if l == b.line { b.byte } else { t.len() };
545 if l > a.line {
546 s.push('\n');
547 }
548 s.push_str(&t[s0..s1]);
549 }
550 s
551 }
552
553 pub fn paras_range(&self, a: TextPos, b: TextPos) -> Vec<Para> {
555 let (a, b) = (self.clamp(a.min(b)), self.clamp(a.max(b)));
556 (a.line..=b.line)
557 .map(|l| {
558 let p = &self.body.paras[l];
559 p.slice(if l == a.line { a.byte } else { 0 }, if l == b.line { b.byte } else { p.len() })
560 })
561 .collect()
562 }
563
564 pub fn selected_text(&self) -> String {
566 let (a, b) = self.body.sel.range();
567 self.text_range(a, b)
568 }
569
570 pub fn selected_paras(&self) -> Vec<Para> {
572 let (a, b) = self.body.sel.range();
573 self.paras_range(a, b)
574 }
575
576 pub fn current_para(&self) -> &Para {
578 &self.body.paras[self.body.sel.caret.line]
579 }
580
581 pub fn current_fmt(&self) -> Fmt {
584 let sel = self.body.sel;
585 if sel.is_empty() {
586 if let Some((f, at)) = &self.typing {
587 if *at == sel.caret {
588 return f.clone();
589 }
590 }
591 return self.body.paras[sel.caret.line].typing_fmt(sel.caret.byte);
592 }
593 let (a, b) = sel.range();
594 let mut first: Option<Fmt> = None;
595 let mut flags = RF_ALL;
596 for l in a.line..=b.line {
597 let p = &self.body.paras[l];
598 let (s0, s1) = (if l == a.line { a.byte } else { 0 }, if l == b.line { b.byte } else { p.len() });
599 for (s, e, f) in p.spans() {
600 if s.max(s0) < e.min(s1) {
601 flags &= f.flags;
602 if first.is_none() {
603 first = Some(f.clone());
604 }
605 }
606 }
607 }
608 let mut f = first.unwrap_or_else(|| self.body.paras[a.line].typing_fmt(a.byte));
609 f.flags &= flags;
610 f
611 }
612
613 pub fn link_at(&self, p: TextPos) -> Option<&str> {
615 let p = self.clamp(p);
616 let para = &self.body.paras[p.line];
617 if p.byte >= para.len() {
618 return None;
619 }
620 let l = ¶.fmt_at(p.byte).link;
621 (!l.is_empty()).then_some(l.as_str())
622 }
623
624 pub fn can_undo(&self) -> bool {
628 self.hist.can_undo()
629 }
630 pub fn can_redo(&self) -> bool {
632 self.hist.can_redo()
633 }
634 pub fn undo_label(&self) -> Option<&str> {
636 self.hist.undo_label()
637 }
638 pub fn redo_label(&self) -> Option<&str> {
640 self.hist.redo_label()
641 }
642 pub fn undo(&mut self) -> bool {
644 let r = self.hist.undo(&mut self.body);
645 if r {
646 self.rev += 1;
647 self.typing = None;
648 }
649 r
650 }
651 pub fn redo(&mut self) -> bool {
653 let r = self.hist.redo(&mut self.body);
654 if r {
655 self.rev += 1;
656 self.typing = None;
657 }
658 r
659 }
660 pub fn is_dirty(&self) -> bool {
662 self.hist.dirty()
663 }
664 pub fn mark_saved(&mut self) {
666 self.hist.mark_saved();
667 }
668 pub fn clear_history(&mut self) {
670 self.hist.clear();
671 }
672 pub fn group_begin(&mut self, label: &str) {
674 self.hist.group_begin(label);
675 }
676 pub fn group_end(&mut self) {
678 self.hist.group_end();
679 }
680
681 fn edit(&mut self, first: usize, n: usize, mut new: Vec<Para>, sel: TextSel, label: &str, kind: u8) -> bool {
683 if new.is_empty() {
684 new.push(Para::new());
685 }
686 let old: Vec<Para> = self.body.paras[first..first + n].to_vec();
687 if old == new {
688 self.body.sel = sel;
689 return false;
690 }
691 let e = ParaEdit { first, old, new, sel0: self.body.sel, sel1: sel, label: String::from(label), kind };
692 self.hist.apply(&mut self.body, Box::new(e));
693 self.rev += 1;
694 self.typing = None;
695 true
696 }
697
698 fn spliced(&self, a: TextPos, b: TextPos, frag: &[Para], attrs: bool, fill: Option<&Fmt>) -> (Vec<Para>, TextPos) {
703 let host = &self.body.paras[a.line];
704 let last = &self.body.paras[b.line];
705 let k = frag.len().max(1) - 1;
706 let empty = Para::new();
707 let f = |i: usize| frag.get(i).unwrap_or(&empty);
708 let mut out: Vec<Para> = Vec::with_capacity(k + 1);
709 let mut p0 = host.slice(0, a.byte);
710 p0.append(f(0));
711 if attrs && k > 0 && a.byte == 0 {
712 p0.attrs_of(f(0));
713 }
714 p0.mark = if k == 0 { last.mark.clone() } else { f(0).mark.clone() };
715 let end;
716 if k == 0 {
717 end = TextPos::new(a.line, p0.len());
718 p0.append(&last.slice(b.byte, last.len()));
719 out.push(p0);
720 } else {
721 out.push(p0);
722 for i in 1..k {
723 let mut p = f(i).clone();
724 if !attrs {
725 p.attrs_of(host);
726 }
727 p.touch();
728 out.push(p);
729 }
730 let mut pk = f(k).clone();
731 if !attrs {
732 pk.attrs_of(host);
733 }
734 end = TextPos::new(a.line + k, pk.len());
735 let tail = last.slice(b.byte, last.len());
736 pk.append(&tail);
737 pk.mark = tail.mark;
738 pk.touch();
739 out.push(pk);
740 }
741 if let Some(fm) = fill {
742 for p in out.iter_mut().filter(|p| p.is_empty()) {
743 p.mark = fm.clone();
744 }
745 }
746 (out, end)
747 }
748
749 pub fn insert_text(&mut self, s: &str) -> bool {
753 self.insert_text_kind(s, if s.contains(['\n', '\r']) { K_NONE } else { K_TYPE })
754 }
755
756 pub(crate) fn insert_text_kind(&mut self, s: &str, kind: u8) -> bool {
758 let sel = self.body.sel;
759 let fmt = match &self.typing {
760 Some((f, at)) if *at == sel.caret => f.clone(),
761 _ if sel.is_empty() => self.current_fmt(),
762 _ => {
763 let a = sel.start();
765 let p = &self.body.paras[a.line];
766 if a.byte < p.len() { p.fmt_at(a.byte).clone() } else { p.typing_fmt(a.byte) }
767 }
768 };
769 let frag: Vec<Para> = lines_of(s)
770 .map(|l| {
771 let mut p = Para::new();
772 p.push(l, &fmt);
773 p
774 })
775 .collect();
776 let (a, b) = self.body.sel.range();
777 let (new, end) = self.spliced(a, b, &frag, false, Some(&fmt));
778 let label = if kind == K_TYPE { tr("Schreiben") } else { tr("Einfügen") };
779 self.edit(a.line, b.line - a.line + 1, new, TextSel::at(end), label, kind)
780 }
781
782 pub fn insert_paras(&mut self, frag: &[Para]) -> bool {
785 if frag.is_empty() {
786 return self.delete_selection();
787 }
788 let (a, b) = self.body.sel.range();
789 let (new, end) = self.spliced(a, b, frag, frag.len() > 1, None);
790 self.edit(a.line, b.line - a.line + 1, new, TextSel::at(end), tr("Einfügen"), K_NONE)
791 }
792
793 pub fn delete_selection(&mut self) -> bool {
795 let (a, b) = self.body.sel.range();
796 self.delete(a, b)
797 }
798
799 pub fn delete(&mut self, a: TextPos, b: TextPos) -> bool {
801 self.delete_kind(a, b, K_NONE)
802 }
803
804 pub(crate) fn delete_kind(&mut self, a: TextPos, b: TextPos, kind: u8) -> bool {
806 let (a, b) = (self.clamp(a.min(b)), self.clamp(a.max(b)));
807 if a == b {
808 return false;
809 }
810 let fill = self.body.paras[a.line].typing_fmt(a.byte);
811 let (new, end) = self.spliced(a, b, &[], false, Some(&fill));
812 let mut new = new;
814 if a.byte == 0 && self.body.paras[a.line].is_empty() && b.line > a.line {
815 let src = &self.body.paras[b.line];
816 new[0].attrs_of(src);
817 }
818 self.edit(a.line, b.line - a.line + 1, new, TextSel::at(end), tr("Löschen"), kind)
819 }
820
821 pub fn split(&mut self) -> bool {
825 let (a, b) = self.body.sel.range();
826 let host = &self.body.paras[a.line];
827 if a == b && host.is_empty() && host.list != LS_NONE {
828 let mut p = host.clone();
829 if p.level > 0 {
830 p.level -= 1;
831 } else {
832 p.list = LS_NONE;
833 }
834 p.touch();
835 return self.edit(a.line, 1, alloc::vec![p], self.body.sel, tr("Liste"), K_NONE);
836 }
837 let fmt = self.current_fmt();
838 let mut e1 = Para::new();
839 e1.mark = fmt.clone();
840 let mut e2 = Para::new();
841 e2.mark = fmt.clone();
842 let at_end = b.byte >= self.body.paras[b.line].len();
843 let heading = host.style != PS_NORMAL && host.style != PS_QUOTE;
844 let (mut new, end) = self.spliced(a, b, &[e1, e2], false, Some(&fmt));
845 if heading && at_end {
846 if let Some(p) = new.last_mut() {
847 p.style = PS_NORMAL;
848 }
849 }
850 self.edit(a.line, b.line - a.line + 1, new, TextSel::at(end), tr("Absatz"), K_NONE)
851 }
852
853 fn fmt_range(&self, link_word: bool) -> Option<(TextPos, TextPos)> {
855 let sel = self.body.sel;
856 if !sel.is_empty() {
857 return Some(sel.range());
858 }
859 if link_word {
860 let p = self.body.paras.get(sel.caret.line)?;
861 let at = sel.caret.byte;
862 let link = if at < p.len() && !p.fmt_at(at).link.is_empty() {
863 p.fmt_at(at).link.clone()
864 } else if at > 0 && !p.fmt_at(at - 1).link.is_empty() {
865 p.fmt_at(at - 1).link.clone()
866 } else {
867 return None;
868 };
869 let spans: Vec<(usize, usize, bool)> = p.spans().map(|(s, e, f)| (s, e, f.link == link)).collect();
871 let i = spans.iter().position(|&(s, e, m)| m && s <= at && (at < e || (at == e && at > 0)))?;
872 let mut s = spans[i].0;
873 let mut e = spans[i].1;
874 let mut j = i;
875 while j > 0 && spans[j - 1].2 {
876 j -= 1;
877 s = spans[j].0;
878 }
879 let mut j = i;
880 while j + 1 < spans.len() && spans[j + 1].2 {
881 j += 1;
882 e = spans[j].1;
883 }
884 return Some((TextPos::new(sel.caret.line, s), TextPos::new(sel.caret.line, e)));
885 }
886 None
887 }
888
889 fn map_sel(&mut self, label: &str, link_word: bool, f: &dyn Fn(&mut Fmt)) -> bool {
891 let Some((a, b)) = self.fmt_range(link_word) else {
892 let mut t = self.current_fmt();
893 f(&mut t);
894 let caret = self.body.sel.caret;
895 if self.body.paras[caret.line].is_empty() {
897 let mut p = self.body.paras[caret.line].clone();
898 p.mark = t.clone();
899 p.touch();
900 let sel = self.body.sel;
901 self.edit(caret.line, 1, alloc::vec![p], sel, label, K_NONE);
902 }
903 self.typing = Some((t, caret));
904 return true;
905 };
906 let new: Vec<Para> = (a.line..=b.line)
907 .map(|l| {
908 let mut p = self.body.paras[l].clone();
909 let s0 = if l == a.line { a.byte } else { 0 };
910 let s1 = if l == b.line { b.byte } else { p.len() };
911 p.map_fmt(s0, s1, f);
912 if s1 >= p.len() || p.is_empty() {
913 f(&mut p.mark);
914 }
915 p
916 })
917 .collect();
918 let sel = self.body.sel;
919 self.edit(a.line, b.line - a.line + 1, new, sel, label, K_NONE)
920 }
921
922 pub fn toggle(&mut self, flag: u8) -> bool {
925 let on = self.current_fmt().flags & flag != flag;
926 let label = match flag {
927 RF_BOLD => tr("Fett"),
928 RF_ITALIC => tr("Kursiv"),
929 RF_UNDER => tr("Unterstrichen"),
930 _ => tr("Durchgestrichen"),
931 };
932 self.map_sel(label, false, &|f: &mut Fmt| if on { f.flags |= flag } else { f.flags &= !flag })
933 }
934
935 pub fn set_flags(&mut self, flags: u8, on: bool) -> bool {
937 self.map_sel(tr("Format"), false, &|f: &mut Fmt| if on { f.flags |= flags } else { f.flags &= !flags })
938 }
939
940 pub fn set_color(&mut self, argb: u32) -> bool {
942 self.map_sel(tr("Textfarbe"), false, &|f: &mut Fmt| f.color = argb)
943 }
944
945 pub fn set_bg(&mut self, argb: u32) -> bool {
947 self.map_sel(tr("Hervorhebung"), false, &|f: &mut Fmt| f.bg = argb)
948 }
949
950 pub fn set_size(&mut self, tenths: u16) -> bool {
952 let t = if tenths == 0 { 0 } else { tenths.clamp(10, 4000) };
953 self.map_sel(tr("Schriftgröße"), false, &|f: &mut Fmt| f.size = t)
954 }
955
956 pub fn set_font(&mut self, family: &str) -> bool {
958 let fam = String::from(family);
959 self.map_sel(tr("Schriftart"), false, &|f: &mut Fmt| f.font = fam.clone())
960 }
961
962 pub fn set_link(&mut self, url: &str) -> bool {
965 let u = String::from(url);
966 if self.body.sel.is_empty() && self.fmt_range(true).is_none() {
967 if url.is_empty() {
968 return false;
969 }
970 let mut f = self.current_fmt();
971 f.link = u.clone();
972 self.typing = Some((f, self.body.sel.caret));
973 return self.insert_text_kind(url, K_NONE);
974 }
975 self.map_sel(tr("Link"), true, &|f: &mut Fmt| f.link = u.clone())
976 }
977
978 pub fn clear_format(&mut self) -> bool {
980 self.map_sel(tr("Format löschen"), false, &|f: &mut Fmt| *f = Fmt { link: core::mem::take(&mut f.link), ..Fmt::default() })
981 }
982
983 fn map_paras(&mut self, label: &str, f: &dyn Fn(&mut Para)) -> bool {
987 let (a, b) = self.body.sel.range();
988 let new: Vec<Para> = (a.line..=b.line)
989 .map(|l| {
990 let mut p = self.body.paras[l].clone();
991 f(&mut p);
992 p.touch();
993 p
994 })
995 .collect();
996 let sel = self.body.sel;
997 self.edit(a.line, b.line - a.line + 1, new, sel, label, K_NONE)
998 }
999
1000 pub fn set_align(&mut self, align: u8) -> bool {
1002 let a = align.min(RA_JUSTIFY);
1003 self.map_paras(tr("Ausrichtung"), &|p: &mut Para| p.align = a)
1004 }
1005
1006 pub fn toggle_list(&mut self, kind: u8) -> bool {
1009 let (a, b) = self.body.sel.range();
1010 let all = (a.line..=b.line).all(|l| self.body.paras[l].list == kind);
1011 let k = if all { LS_NONE } else { kind.min(LS_NUMBER) };
1012 self.map_paras(tr("Liste"), &|p: &mut Para| p.list = k)
1013 }
1014
1015 pub fn set_list(&mut self, kind: u8) -> bool {
1017 let k = kind.min(LS_NUMBER);
1018 self.map_paras(tr("Liste"), &|p: &mut Para| p.list = k)
1019 }
1020
1021 pub fn indent(&mut self, delta: i32) -> bool {
1023 self.map_paras(tr("Einzug"), &|p: &mut Para| p.level = (p.level as i32).saturating_add(delta).clamp(0, LEVEL_MAX as i32) as u8)
1024 }
1025
1026 pub fn set_style(&mut self, style: u8) -> bool {
1028 let s = style.min(PS_QUOTE);
1029 self.map_paras(tr("Formatvorlage"), &|p: &mut Para| p.style = s)
1030 }
1031
1032 pub fn set_paras(&mut self, paras: Vec<Para>) -> bool {
1034 let n = self.body.paras.len();
1035 self.edit(0, n, paras, TextSel::default(), tr("Ersetzen"), K_NONE)
1036 }
1037
1038 pub fn to_json(&self) -> String {
1042 let mut paras = Vec::new();
1043 for p in &self.body.paras {
1044 let mut o = Value::Object(Vec::new());
1045 if p.align != RA_LEFT {
1046 o.insert("align", Value::string(["left", "center", "right", "justify"][p.align as usize & 3]));
1047 }
1048 if p.list != LS_NONE {
1049 o.insert("list", Value::string(if p.list == LS_BULLET { "bullet" } else { "number" }));
1050 }
1051 if p.level != 0 {
1052 o.insert("level", Value::u64(p.level as u64));
1053 }
1054 if p.style != PS_NORMAL {
1055 o.insert("style", Value::string(STYLE_NAMES[p.style as usize % STYLE_NAMES.len()]));
1056 }
1057 let runs: Vec<Value> = p
1058 .runs()
1059 .map(|(t, f)| {
1060 let mut r = fmt_json(f);
1061 if let Value::Object(v) = &mut r {
1062 v.insert(0, (String::from("t"), Value::string(t)));
1063 }
1064 r
1065 })
1066 .collect();
1067 o.insert("runs", Value::Array(runs));
1068 if p.mark != Fmt::default() && p.is_empty() {
1069 o.insert("mark", fmt_json(&p.mark));
1070 }
1071 paras.push(o);
1072 }
1073 let mut root = Value::object([("jfrich", Value::u64(1))]);
1074 if !self.font.is_empty() {
1075 root.insert("font", Value::string(self.font.clone()));
1076 }
1077 if self.size != 0 {
1078 root.insert("size", size_json(self.size));
1079 }
1080 root.insert("paras", Value::Array(paras));
1081 json::stringify_with_limits(&root, big_limits()).unwrap_or_default()
1082 }
1083
1084 pub fn from_json(s: &str) -> Option<RichDoc> {
1086 let v = json::parse_with_limits(s.as_bytes(), big_limits()).ok()?;
1087 v.get("jfrich")?.as_u64().filter(|&n| n == 1)?;
1088 let mut paras = Vec::new();
1089 for pv in v.get("paras")?.as_array()? {
1090 let mut p = Para::new();
1091 let s = |k: &str| pv.get(k).and_then(|x| x.as_str());
1092 p.align = match s("align") {
1093 Some("center") => RA_CENTER,
1094 Some("right") => RA_RIGHT,
1095 Some("justify") => RA_JUSTIFY,
1096 _ => RA_LEFT,
1097 };
1098 p.list = match s("list") {
1099 Some("bullet") => LS_BULLET,
1100 Some("number") => LS_NUMBER,
1101 _ => LS_NONE,
1102 };
1103 p.level = pv.get("level").and_then(|x| x.as_u64()).unwrap_or(0).min(LEVEL_MAX as u64) as u8;
1104 p.style = s("style").and_then(|n| STYLE_NAMES.iter().position(|&m| m == n)).unwrap_or(0) as u8;
1105 if let Some(runs) = pv.get("runs").and_then(|x| x.as_array()) {
1106 for r in runs {
1107 let t = r.get("t").and_then(|x| x.as_str()).unwrap_or("");
1108 p.push(t, &fmt_of_json(r));
1109 }
1110 }
1111 if let Some(m) = pv.get("mark") {
1112 p.mark = fmt_of_json(m);
1113 }
1114 paras.push(p);
1115 }
1116 let mut d = RichDoc::from_paras(paras);
1117 d.font = v.get("font").and_then(|x| x.as_str()).unwrap_or("").into();
1118 d.size = v.get("size").and_then(size_of_json).unwrap_or(0);
1119 Some(d)
1120 }
1121
1122 pub fn to_plain(&self) -> String {
1126 let marks = list_marks(&self.body.paras);
1127 let mut s = String::new();
1128 for (i, p) in self.body.paras.iter().enumerate() {
1129 if i > 0 {
1130 s.push('\n');
1131 }
1132 for _ in 0..p.level {
1133 s.push_str(" ");
1134 }
1135 if let Some(m) = &marks[i] {
1136 s.push_str(m);
1137 s.push(' ');
1138 }
1139 s.push_str(&p.text);
1140 }
1141 s
1142 }
1143}
1144
1145pub fn list_marks(paras: &[Para]) -> Vec<Option<String>> {
1149 let mut count = [0u32; LEVEL_MAX as usize + 1];
1150 let mut kind = [LS_NONE; LEVEL_MAX as usize + 1];
1151 paras
1152 .iter()
1153 .map(|p| {
1154 if p.list == LS_NONE {
1155 count = [0; LEVEL_MAX as usize + 1];
1156 kind = [LS_NONE; LEVEL_MAX as usize + 1];
1157 return None;
1158 }
1159 let l = (p.level as usize).min(LEVEL_MAX as usize);
1160 for d in l + 1..=LEVEL_MAX as usize {
1161 count[d] = 0;
1162 kind[d] = LS_NONE;
1163 }
1164 if kind[l] != p.list {
1165 count[l] = 0;
1166 kind[l] = p.list;
1167 }
1168 count[l] += 1;
1169 Some(if p.list == LS_BULLET {
1170 String::from(["•", "◦", "▪"][l % 3])
1171 } else {
1172 match l % 3 {
1173 0 => format!("{}.", count[l]),
1174 1 => format!("{}.", alpha(count[l])),
1175 _ => format!("{}.", roman(count[l])),
1176 }
1177 })
1178 })
1179 .collect()
1180}
1181
1182fn alpha(n: u32) -> String {
1184 let mut n = n.max(1);
1185 let mut v = Vec::new();
1186 while n > 0 {
1187 n -= 1;
1188 v.push(b'a' + (n % 26) as u8);
1189 n /= 26;
1190 }
1191 v.reverse();
1192 String::from_utf8(v).unwrap_or_default()
1193}
1194
1195fn roman(n: u32) -> String {
1197 const T: [(u32, &str); 13] =
1198 [(1000, "m"), (900, "cm"), (500, "d"), (400, "cd"), (100, "c"), (90, "xc"), (50, "l"), (40, "xl"), (10, "x"), (9, "ix"), (5, "v"), (4, "iv"), (1, "i")];
1199 let mut n = n.clamp(1, 3999);
1200 let mut s = String::new();
1201 for (v, t) in T {
1202 while n >= v {
1203 s.push_str(t);
1204 n -= v;
1205 }
1206 }
1207 s
1208}
1209
1210const STYLE_NAMES: [&str; 5] = ["normal", "h1", "h2", "h3", "quote"];
1211
1212pub(crate) fn lines_of(s: &str) -> impl Iterator<Item = &str> {
1214 s.split('\n').map(|l| l.strip_suffix('\r').unwrap_or(l)).flat_map(|l| l.split('\r'))
1215}
1216
1217fn big_limits() -> Limits {
1218 Limits { bytes: 64 << 20, depth: 8, values: 4 << 20, string_bytes: 16 << 20, members: 1 << 20 }
1219}
1220
1221pub(crate) fn color_hex(c: u32) -> String {
1223 let a = c >> 24;
1224 if a == 0xFF || a == 0 {
1225 format!("#{:06x}", c & 0xFF_FFFF)
1226 } else {
1227 format!("#{:06x}{:02x}", c & 0xFF_FFFF, a)
1228 }
1229}
1230
1231pub(crate) fn parse_hex_color(s: &str) -> Option<u32> {
1233 let h = s.trim().strip_prefix('#')?;
1234 if !h.bytes().all(|b| b.is_ascii_hexdigit()) {
1235 return None;
1236 }
1237 let v = u32::from_str_radix(h, 16).ok()?;
1238 match h.len() {
1239 3 => Some(0xFF00_0000 | (v >> 8 & 15) * 0x11_0000 | (v >> 4 & 15) * 0x1100 | (v & 15) * 0x11),
1240 6 => Some(0xFF00_0000 | v),
1241 8 => Some((v & 0xFF) << 24 | v >> 8),
1242 _ => None,
1243 }
1244}
1245
1246fn size_json(t: u16) -> Value {
1248 match json::Number::parse(&size_text(t)) {
1249 Some(n) => Value::Number(n),
1250 None => Value::u64((t / 10) as u64),
1251 }
1252}
1253
1254pub(crate) fn size_text(t: u16) -> String {
1256 if t % 10 == 0 { format!("{}", t / 10) } else { format!("{}.{}", t / 10, t % 10) }
1257}
1258
1259pub(crate) fn parse_tenths(s: &str) -> Option<u16> {
1261 let s = s.trim();
1262 let (i, f) = s.split_once('.').unwrap_or((s, ""));
1263 let i: u32 = i.parse().ok()?;
1264 let f: u32 = f.chars().next().map_or(Some(0), |c| c.to_digit(10))?;
1265 Some((i.min(u16::MAX as u32) * 10 + f).min(u16::MAX as u32) as u16)
1266}
1267
1268fn size_of_json(v: &Value) -> Option<u16> {
1269 match v {
1270 Value::Number(n) => parse_tenths(n.as_str()),
1271 _ => None,
1272 }
1273}
1274
1275fn fmt_json(f: &Fmt) -> Value {
1276 let mut o = Value::Object(Vec::new());
1277 for (bit, k) in [(RF_BOLD, "b"), (RF_ITALIC, "i"), (RF_UNDER, "u"), (RF_STRIKE, "s")] {
1278 if f.flags & bit != 0 {
1279 o.insert(k, Value::u64(1));
1280 }
1281 }
1282 if f.color != 0 {
1283 o.insert("color", Value::string(color_hex(f.color)));
1284 }
1285 if f.bg != 0 {
1286 o.insert("bg", Value::string(color_hex(f.bg)));
1287 }
1288 if f.size != 0 {
1289 o.insert("size", size_json(f.size));
1290 }
1291 if !f.font.is_empty() {
1292 o.insert("font", Value::string(f.font.clone()));
1293 }
1294 if !f.link.is_empty() {
1295 o.insert("link", Value::string(f.link.clone()));
1296 }
1297 o
1298}
1299
1300fn fmt_of_json(v: &Value) -> Fmt {
1301 let mut f = Fmt::default();
1302 for (bit, k) in [(RF_BOLD, "b"), (RF_ITALIC, "i"), (RF_UNDER, "u"), (RF_STRIKE, "s")] {
1303 let on = match v.get(k) {
1304 Some(Value::Bool(b)) => *b,
1305 Some(x) => x.as_u64().is_some_and(|n| n != 0),
1306 None => false,
1307 };
1308 if on {
1309 f.flags |= bit;
1310 }
1311 }
1312 let s = |k: &str| v.get(k).and_then(|x| x.as_str());
1313 f.color = s("color").and_then(parse_hex_color).unwrap_or(0);
1314 f.bg = s("bg").and_then(parse_hex_color).unwrap_or(0);
1315 f.size = v.get("size").and_then(size_of_json).unwrap_or(0);
1316 f.font = s("font").unwrap_or("").into();
1317 f.link = s("link").unwrap_or("").into();
1318 f
1319}
1320
1321pub(crate) const RK_TYPE: u8 = K_TYPE;
1323pub(crate) const RK_BACK: u8 = K_BACK;
1324pub(crate) const RK_DEL: u8 = K_DEL;