starsector/
tree.rs

1use std::borrow::Cow;
2
3use indextree::NodeId;
4use log::trace;
5use ropey::{Rope, RopeSlice};
6
7use crate::{emit::section_tree_to_rope, errors::*, iter::*, *};
8
9#[cfg(feature = "headline-parser")]
10use crate::headline::*;
11
12#[derive(Debug, PartialEq, Clone, Copy, Hash, Eq)]
13pub struct Section {
14    pub(crate) id: NodeId,
15}
16
17#[derive(Debug, PartialEq, Clone, Copy, Hash, Eq)]
18pub struct Document {
19    pub root: Section,
20
21    // This is only necessary to handle the annoying edge case where the
22    // document section is just "\n". Instead of turning text into an option, we
23    // use an empty string to represent both "\n" and "", since most uses will
24    // want uniform treatment of the two. When writing the file out, this will
25    // be special cased.
26    pub empty_root_section: bool,
27
28    // When this Section is written out, the terminal newline will be omitted if
29    // this is false and it is the root section.
30    pub terminal_newline: bool,
31}
32
33impl Document {
34    pub fn to_rope(&self, arena: &Arena) -> Rope {
35        section_tree_to_rope(
36            self.root.id,
37            arena,
38            self.terminal_newline,
39            self.empty_root_section,
40        )
41    }
42
43    pub fn at(&self, arena: &Arena, mut pos: usize) -> Option<(Section, usize)> {
44        let ct = self.to_rope(arena);
45        let k = ct.len_chars();
46        trace!("at: {} in buffer of length {}.", pos, k);
47
48        if pos >= k {
49            trace!("at: beyond end of buffer");
50            return None;
51        }
52
53        if pos == 0 && (!self.empty_root_section || ct == "\n") {
54            trace!("at: in implicit newline of newline empty root section");
55            return Some((self.root, pos));
56        }
57
58        let s = Section { id: self.root.id };
59        let terminal_newline_in_play = if pos == k - 1 && self.terminal_newline {
60            trace!("at: terminal newline in play; adjusting pos");
61            pos = k - 2;
62            true
63        } else {
64            trace!("at: terminal not newline in play");
65            false
66        };
67        if !self.empty_root_section && pos > 0 {
68            trace!(
69                "at: implicit newline of newline empty root section means we need to adjust pos"
70            );
71            pos -= 1;
72        }
73        trace!("at: delegating to section at function");
74        match s.at(arena, pos) {
75            Some((section, offset)) => {
76                if terminal_newline_in_play {
77                    Some((section, offset + 1))
78                } else {
79                    Some((section, offset))
80                }
81            }
82            None => None,
83        }
84    }
85
86    /// Returns the offset in chars of the descendant relative to the start of
87    /// this one, or None if it is not a descendant.
88    pub fn text_offset_of_child(&self, arena: &Arena, child: Section) -> Option<usize> {
89        match self.root.text_offset_of_child(arena, child) {
90            None => None,
91            Some(offset) => {
92                let entry = &arena.arena[self.root.id].get();
93                if entry.text.len_chars() == 0 && !self.empty_root_section && offset > 0 {
94                    // Handle edge case where Org document has a single blank
95                    // line before its first headline.
96                    Some(offset + 1)
97                } else {
98                    Some(offset)
99                }
100            }
101        }
102    }
103}
104
105// Misc methods.
106impl Section {
107    /// Formats the subtree rooted at `self` as a string.
108    pub fn to_rope(self, arena: &Arena) -> Rope {
109        section_tree_to_rope(
110            self.id, arena, /*terminal_newline=*/ true, /*empty_root_section=*/ true,
111        )
112    }
113
114    // FIXME: This is pretty inefficient.
115    pub fn at(self, arena: &Arena, mut pos: usize) -> Option<(Section, usize)> {
116        let root = &arena.arena[self.id].get();
117        let k = root.text.len_chars();
118        trace!("at: section at {} in {} char section", pos, k);
119        if pos < k {
120            trace!("at: in section text");
121            return Some((self, pos));
122        }
123        pos -= k;
124        for (j, child_id) in self.id.children(&arena.arena).enumerate() {
125            trace!(
126                "at: in child {} (id {}) of node id {}",
127                j,
128                child_id,
129                self.id
130            );
131            let k = Section { id: child_id }.to_rope(&arena).len_chars();
132            if pos < k {
133                let s = Section { id: child_id };
134                return match s.at(arena, pos) {
135                    Some((section, offset)) => Some((section, offset)),
136                    None => Some((s, pos)),
137                };
138            }
139            pos -= k;
140        }
141
142        trace!("Exhausted all children of node {}", self.id);
143
144        None
145    }
146
147    /// Creates a clone of the entire subtree rooted at `self`. Text is
148    /// copy-on-write, but nodes are independent.
149    pub fn clone_subtree(self, arena: &mut Arena) -> Section {
150        let new_self = arena.clone_section(self);
151        let mut stack = vec![(self, new_self)];
152        let mut scratch = Vec::default();
153        while let Some((old_parent, new_parent)) = stack.pop() {
154            scratch.extend(old_parent.children(arena));
155            for old_child in scratch.drain(..) {
156                let new_child = arena.clone_section(old_child);
157                new_parent.unchecked_append(arena, new_child);
158                stack.push((old_child, new_child));
159            }
160        }
161        new_self
162    }
163
164    /// Returns the offset in chars of the descendant relative to the start of
165    /// this one, or None if it is not a descendant.
166    pub fn text_offset_of_child(&self, arena: &Arena, child: Section) -> Option<usize> {
167        let entry = &arena.arena[self.id].get();
168
169        // In the ambiguous (due to our chosen representation) case of empty
170        // headline (only possible for document root), we assume that it is
171        // empty rather than "\n".
172        //
173        // Document has a version of this function that disambiguates this
174        // case.
175        let mut offset = entry.text.len_chars();
176        if offset > 0 {
177            offset += 1;
178        }
179
180        for c in self.descendants(arena) {
181            if c == child {
182                return Some(offset);
183            }
184
185            offset += arena.arena[c.id].get().text.len_chars() + 1;
186        }
187
188        None
189    }
190}
191
192// Non-mutating accessors.
193impl Section {
194    // FIXME: Look at macros/templates to generate these, or just expose the
195    // NodeIds directly to the user for use with the Arena.
196
197    pub fn level(self, arena: &Arena) -> u16 {
198        arena.arena[self.id].get().level
199    }
200
201    pub fn text<'a>(self, arena: &'a Arena) -> RopeSlice<'a> {
202        arena.arena[self.id].get().text.slice(..)
203    }
204
205    pub fn parent(self, arena: &Arena) -> Option<Section> {
206        arena.arena[self.id].parent().map(|p| Section { id: p })
207    }
208
209    pub fn children(self, arena: &Arena) -> Children {
210        Children {
211            children: self.id.children(&arena.arena),
212        }
213    }
214
215    pub fn ancestors(self, arena: &Arena) -> Ancestors {
216        Ancestors {
217            ancestors: self.id.ancestors(&arena.arena),
218        }
219    }
220
221    pub fn descendants(self, arena: &Arena) -> Descendants {
222        Descendants {
223            descendants: self.id.descendants(&arena.arena),
224        }
225    }
226
227    pub fn preceding_siblings(self, arena: &Arena) -> PrecedingSiblings {
228        PrecedingSiblings {
229            preceding_siblings: self.id.preceding_siblings(&arena.arena),
230        }
231    }
232
233    pub fn following_siblings(self, arena: &Arena) -> FollowingSiblings {
234        FollowingSiblings {
235            following_siblings: self.id.following_siblings(&arena.arena),
236        }
237    }
238
239    pub fn reverse_children(self, arena: &Arena) -> ReverseChildren {
240        ReverseChildren {
241            reverse_children: self.id.reverse_children(&arena.arena),
242        }
243    }
244}
245
246// Structure mutators
247impl Section {
248    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
249    /// adds it as the last child of `self`, setting its level to `self`'s + 1
250    /// if invalid.
251    pub fn append(self, arena: &mut Arena, new_child: Section) -> Result<(), StructureError> {
252        let min_level = arena.arena[self.id].get().level + 1;
253        arena.section_min_level(new_child, min_level);
254        Ok(self.id.checked_append(new_child.id, &mut arena.arena)?)
255    }
256
257    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
258    /// adds it as the first child of `self`, setting its level to `self`'s + 1
259    /// if invalid.
260    pub fn prepend(self, arena: &mut Arena, new_child: Section) -> Result<(), StructureError> {
261        let min_level = arena.arena[self.id].get().level + 1;
262        arena.section_min_level(new_child, min_level);
263        Ok(self.id.checked_prepend(new_child.id, &mut arena.arena)?)
264    }
265
266    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
267    /// adds it after `self`, setting its level to `self`'s if invalid.
268    pub fn insert_after(
269        self,
270        arena: &mut Arena,
271        new_sibling: Section,
272    ) -> Result<(), StructureError> {
273        let min_level = match self.parent(arena) {
274            Some(parent) => parent.level(arena) + 1,
275            None => {
276                return Err(StructureError::LevelError);
277            }
278        };
279
280        arena.section_min_level(new_sibling, min_level);
281        Ok(self
282            .id
283            .checked_insert_after(new_sibling.id, &mut arena.arena)?)
284    }
285
286    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
287    /// adds it before `self`, setting its level to `self`'s if invalid.
288    pub fn insert_before(
289        self,
290        arena: &mut Arena,
291        new_sibling: Section,
292    ) -> Result<(), StructureError> {
293        let min_level = match self.parent(arena) {
294            Some(parent) => parent.level(arena) + 1,
295            None => {
296                return Err(StructureError::LevelError);
297            }
298        };
299
300        arena.section_min_level(new_sibling, min_level);
301        Ok(self
302            .id
303            .checked_insert_before(new_sibling.id, &mut arena.arena)?)
304    }
305
306    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
307    /// adds it as the last child of `self`, returning an error if its level is
308    /// not strictly greater.
309    pub fn checked_append(
310        self,
311        arena: &mut Arena,
312        new_child: Section,
313    ) -> Result<(), StructureError> {
314        if arena.arena[new_child.id].get().level <= arena.arena[self.id].get().level {
315            return Err(StructureError::LevelError);
316        } else {
317            Ok(self.id.checked_append(new_child.id, &mut arena.arena)?)
318        }
319    }
320
321    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
322    /// adds it as the first child of `self`, returning an error if its level is
323    /// not strictly greater.
324    pub fn checked_prepend(
325        self,
326        arena: &mut Arena,
327        new_child: Section,
328    ) -> Result<(), StructureError> {
329        if arena.arena[new_child.id].get().level <= arena.arena[self.id].get().level {
330            return Err(StructureError::LevelError);
331        } else {
332            Ok(self.id.checked_prepend(new_child.id, &mut arena.arena)?)
333        }
334    }
335
336    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
337    /// adds it after `self`, returning an error if its level is not strictly
338    /// greater than its new parent's.
339    pub fn checked_insert_after(
340        self,
341        arena: &mut Arena,
342        new_sibling: Section,
343    ) -> Result<(), StructureError> {
344        if let Some(parent) = arena.arena[self.id].parent() {
345            if arena.arena[new_sibling.id].get().level <= arena.arena[parent].get().level {
346                return Err(StructureError::LevelError);
347            }
348        }
349
350        Ok(self
351            .id
352            .checked_insert_after(new_sibling.id, &mut arena.arena)?)
353    }
354
355    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
356    /// adds it before `self`, returning an error if its level is not strictly
357    /// greater than its new parent's.
358    pub fn checked_insert_before(
359        self,
360        arena: &mut Arena,
361        new_sibling: Section,
362    ) -> Result<(), StructureError> {
363        if let Some(parent) = arena.arena[self.id].parent() {
364            if arena.arena[new_sibling.id].get().level <= arena.arena[parent].get().level {
365                return Err(StructureError::LevelError);
366            }
367        }
368
369        Ok(self
370            .id
371            .checked_insert_before(new_sibling.id, &mut arena.arena)?)
372    }
373
374    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
375    /// adds it as the last child of `self`, panicking if its level is not
376    /// strictly greater.
377    pub fn unchecked_append(self, arena: &mut Arena, new_child: Section) {
378        self.checked_append(arena, new_child)
379            .expect("Checked append failed")
380    }
381
382    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
383    /// adds it as the first child of `self`, panicking if its level is not
384    /// strictly greater.
385    pub fn unchecked_insert_after(self, arena: &mut Arena, new_sibling: Section) {
386        self.checked_insert_after(arena, new_sibling)
387            .expect("Checked insert after failed")
388    }
389
390    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
391    /// adds it after `self`, panicking if its level is not strictly greater
392    /// than its new parent's.
393    pub fn unchecked_insert_before(self, arena: &mut Arena, new_sibling: Section) {
394        self.checked_insert_before(arena, new_sibling)
395            .expect("Checked insert before failed")
396    }
397
398    /// Detaches the subtree rooted at `new_child` from its parent (if any), and
399    /// adds it before `self`, panicking if its level is not strictly greater
400    /// than its new parent's.
401    pub fn unchecked_prepend(self, arena: &mut Arena, new_sibling: Section) {
402        self.checked_prepend(arena, new_sibling)
403            .expect("Checked prepend failed")
404    }
405
406    /// Detaches the subtree rooted at `new_child` from its parent (if any). The
407    /// nodes remain in the arena and may be reused, or their memory will be
408    /// freed when the document is emitted and reparsed.
409    pub fn remove_subtree(self, arena: &mut Arena) {
410        self.id.detach(&mut arena.arena)
411    }
412
413    /// Removes this node, attaching its children to its former parent in the
414    /// same place. The node remains in the arena and may be reused.
415    pub fn replace_with_children(self, arena: &mut Arena) {
416        self.id.remove(&mut arena.arena)
417    }
418
419    /// Detaches all children.
420    pub fn remove_children(self, arena: &mut Arena) {
421        while let Some(child) = self.children(arena).next() {
422            child.remove_subtree(arena);
423        }
424    }
425}
426
427// Convenience accessors that parse the headline to return the value.
428// It's about as efficient as https://www.youtube.com/watch?v=-DKCFjm0DvE
429// For more efficient use, call headline and use that to access multiple.
430//
431// Note that `set_raw` and `set_level` are available even without
432// `headline-parser` feature.
433#[cfg(feature = "headline-parser")]
434impl Section {
435    pub fn priority(
436        self,
437        arena: &Arena,
438        context: Option<&Context>,
439    ) -> Result<Option<char>, HeadlineError> {
440        match self.headline(arena, context) {
441            None => Err(HeadlineError::InvalidHeadlineError),
442            Some(h) => Ok(h.priority()),
443        }
444    }
445
446    pub fn raw_tags<'a>(
447        self,
448        arena: &'a Arena,
449        context: Option<&Context>,
450    ) -> Result<Cow<'a, str>, HeadlineError> {
451        match self.headline(arena, context) {
452            None => Err(HeadlineError::InvalidHeadlineError),
453            Some(h) => Ok(Cow::Owned(h.raw_tags().to_string())),
454        }
455    }
456
457    pub fn tags<'a>(
458        self,
459        arena: &'a Arena,
460        context: Option<&Context>,
461    ) -> Result<Vec<String>, HeadlineError> {
462        match self.headline(arena, context) {
463            None => Err(HeadlineError::InvalidHeadlineError),
464            Some(h) => Ok(h.tags().map(|s| s.to_string()).collect()),
465        }
466    }
467
468    pub fn has_tag(
469        self,
470        tag: &str,
471        arena: &Arena,
472        context: Option<&Context>,
473    ) -> Result<bool, HeadlineError> {
474        match self.headline(arena, context) {
475            None => Err(HeadlineError::InvalidHeadlineError),
476            Some(h) => Ok(h.has_tag(tag)),
477        }
478    }
479
480    pub fn keyword<'a>(
481        self,
482        arena: &'a Arena,
483        context: Option<&Context>,
484    ) -> Result<Option<Cow<'a, str>>, HeadlineError> {
485        match self.headline(arena, context) {
486            None => Err(HeadlineError::InvalidHeadlineError),
487            Some(h) => Ok(h.keyword().map(|s| Cow::Owned(s.to_string()))),
488        }
489    }
490
491    pub fn title<'a>(
492        self,
493        arena: &'a Arena,
494        context: Option<&Context>,
495    ) -> Result<Cow<'a, str>, HeadlineError> {
496        match self.headline(arena, context) {
497            None => Err(HeadlineError::InvalidHeadlineError),
498            Some(h) => Ok(Cow::Owned(h.title().to_string())),
499        }
500    }
501
502    pub fn commented(
503        self,
504        arena: &Arena,
505        context: Option<&Context>,
506    ) -> Result<bool, HeadlineError> {
507        match self.headline(arena, context) {
508            None => Err(HeadlineError::InvalidHeadlineError),
509            Some(h) => Ok(h.commented()),
510        }
511    }
512
513    pub fn planning(
514        &self,
515        arena: &Arena,
516        context: Option<&Context>,
517    ) -> Result<Planning<'static>, HeadlineError> {
518        match self.headline(arena, context) {
519            None => Err(HeadlineError::InvalidHeadlineError),
520            Some(h) => Ok(h.planning().clone().into_owned()),
521        }
522    }
523
524    pub fn scheduled(
525        &self,
526        arena: &Arena,
527        context: Option<&Context>,
528    ) -> Result<Option<Timestamp<'static>>, HeadlineError> {
529        self.planning(arena, context).map(|p| p.scheduled)
530    }
531
532    pub fn deadline(
533        &self,
534        arena: &Arena,
535        context: Option<&Context>,
536    ) -> Result<Option<Timestamp<'static>>, HeadlineError> {
537        self.planning(arena, context).map(|p| p.deadline)
538    }
539
540    pub fn closed(
541        &self,
542        arena: &Arena,
543        context: Option<&Context>,
544    ) -> Result<Option<Timestamp<'static>>, HeadlineError> {
545        self.planning(arena, context).map(|p| p.closed)
546    }
547
548    pub fn body<'a>(
549        self,
550        arena: &'a Arena,
551        context: Option<&Context>,
552    ) -> Result<Cow<'a, str>, HeadlineError> {
553        match self.headline(arena, context) {
554            None => Err(HeadlineError::InvalidHeadlineError),
555            Some(h) => Ok(Cow::Owned(h.body().to_string())),
556        }
557    }
558
559    #[cfg(feature = "orgize-integration")]
560    pub fn has_property(
561        &self,
562        arena: &Arena,
563        property: &str,
564        context: Option<&Context>,
565    ) -> Result<bool, HeadlineError> {
566        let org = self.orgize_headline(arena, context)?;
567        has_property_internal(property, &org)
568    }
569
570    #[cfg(feature = "orgize-integration")]
571    pub fn get_property(
572        &self,
573        arena: &Arena,
574        property: &str,
575        context: Option<&Context>,
576    ) -> Result<Option<Cow<'static, str>>, HeadlineError> {
577        let org = self.orgize_headline(arena, context)?;
578        get_property_internal(property, &org)
579    }
580
581    #[cfg(feature = "orgize-integration")]
582    pub fn get_id(
583        &self,
584        arena: &Arena,
585        context: Option<&Context>,
586    ) -> Result<Option<Cow<'static, str>>, HeadlineError> {
587        let org = self.orgize_headline(arena, context)?;
588        get_id_internal(&org)
589    }
590
591    #[cfg(feature = "orgize-integration")]
592    pub fn properties(
593        &self,
594        arena: &Arena,
595        context: Option<&Context>,
596    ) -> Result<indexmap::IndexMap<Cow<'static, str>, Cow<'static, str>>, HeadlineError> {
597        let org = self.orgize_headline(arena, context)?;
598        properties_internal(&org)
599    }
600
601    // Not public because we don't support Orgize keyword context.
602    #[cfg(feature = "orgize-integration")]
603    fn orgize_headline(
604        &self,
605        arena: &Arena,
606        context: Option<&Context>,
607    ) -> Result<orgize::Org, HeadlineError> {
608        match self.headline(arena, context) {
609            None => Err(HeadlineError::InvalidHeadlineError),
610            Some(h) => Ok(parse_orgize(&h.body())),
611        }
612    }
613}
614
615// Convenience mutators that change the headline in-place. These are somewhat
616// inefficient, as each time you call one, it will parse the entire headline,
617// change it, then generate it as text. Use a HeadlineBuilder if you care.
618#[cfg(feature = "headline-parser")]
619impl Section {
620    pub fn set_raw_tags(
621        self,
622        arena: &mut Arena,
623        raw_tags: &str,
624        context: Option<&Context>,
625    ) -> Result<(), HeadlineError> {
626        match self.headline(arena, context).map(|h| h.to_owned()) {
627            None => Err(HeadlineError::InvalidHeadlineError),
628            Some(h) => {
629                let mut h = h.to_builder();
630                h.set_raw_tags(raw_tags);
631                self.set_headline(arena, &h.headline(context)?)
632            }
633        }
634    }
635
636    pub fn set_tags<'a, I>(
637        self,
638        arena: &'a mut Arena,
639        tags: I,
640        context: Option<&Context>,
641    ) -> Result<(), HeadlineError>
642    where
643        I: Iterator<Item = Cow<'a, str>>,
644    {
645        match self.headline(arena, context).map(|h| h.to_owned()) {
646            None => Err(HeadlineError::InvalidHeadlineError),
647            Some(h) => {
648                let mut h = h.to_builder();
649                // FIXME: Figure out lifetime.
650                h.set_tags(tags.map(|s| s.to_owned()));
651                self.set_headline(arena, &h.headline(context)?)
652            }
653        }
654    }
655
656    pub fn update_tags<'a, I>(
657        self,
658        arena: &'a mut Arena,
659        tags: I,
660        context: Option<&Context>,
661    ) -> Result<(), HeadlineError>
662    where
663        I: Iterator<Item = Cow<'a, str>>,
664    {
665        match self.headline(arena, context).map(|h| h.to_owned()) {
666            None => Err(HeadlineError::InvalidHeadlineError),
667            Some(h) => {
668                let mut h = h.to_builder();
669                // FIXME: Figure out lifetime.
670                h.update_tags(tags.map(|s| s.to_owned()));
671                self.set_headline(arena, &h.headline(context)?)
672            }
673        }
674    }
675
676    pub fn remove_tags(
677        self,
678        arena: &mut Arena,
679        tags: &[&str],
680        context: Option<&Context>,
681    ) -> Result<(), HeadlineError> {
682        match self.headline(arena, context).map(|h| h.to_owned()) {
683            None => Err(HeadlineError::InvalidHeadlineError),
684            Some(h) => {
685                let mut h = h.to_builder();
686                h.remove_tags(tags);
687                self.set_headline(arena, &h.headline(context)?)
688            }
689        }
690    }
691
692    pub fn clear_tags(
693        self,
694        arena: &mut Arena,
695        context: Option<&Context>,
696    ) -> Result<(), crate::errors::HeadlineError> {
697        match self.headline(arena, None).map(|h| h.to_owned()) {
698            None => Err(HeadlineError::InvalidHeadlineError),
699            Some(h) => {
700                let mut h = h.to_builder();
701                h.clear_tags();
702                self.set_headline(arena, &h.headline(context)?)
703            }
704        }
705    }
706
707    pub fn add_tag(
708        self,
709        arena: &mut Arena,
710        tag: &str,
711        context: Option<&Context>,
712    ) -> Result<(), crate::errors::HeadlineError> {
713        match self.headline(arena, context).map(|h| h.to_owned()) {
714            None => Err(HeadlineError::InvalidHeadlineError),
715            Some(h) => {
716                let mut h = h.to_builder();
717                h.add_tag(tag);
718                self.set_headline(arena, &h.headline(context)?)
719            }
720        }
721    }
722
723    pub fn clear_tag<'a>(
724        self,
725        arena: &mut Arena,
726        tag: &str,
727        context: Option<&Context>,
728    ) -> Result<(), crate::errors::HeadlineError> {
729        match self.headline(arena, context).map(|h| h.to_owned()) {
730            None => Err(HeadlineError::InvalidHeadlineError),
731            Some(h) => {
732                let mut h = h.to_builder();
733                h.clear_tag(tag);
734                self.set_headline(arena, &h.headline(context)?)
735            }
736        }
737    }
738
739    pub fn set_keyword(
740        self,
741        arena: &mut Arena,
742        keyword: Option<Rope>,
743        context: Option<&Context>,
744    ) -> Result<(), crate::errors::HeadlineError> {
745        match self.headline(arena, context).map(|h| h.to_owned()) {
746            None => Err(HeadlineError::InvalidHeadlineError),
747            Some(h) => {
748                let mut h = h.to_builder();
749                h.keyword(keyword);
750                self.set_headline(arena, &h.headline(context)?)
751            }
752        }
753    }
754
755    pub fn set_title(
756        self,
757        arena: &mut Arena,
758        title: Rope,
759        context: Option<&Context>,
760    ) -> Result<(), crate::errors::HeadlineError> {
761        match self.headline(arena, context).map(|h| h.to_owned()) {
762            None => Err(HeadlineError::InvalidHeadlineError),
763            Some(h) => {
764                let mut h = h.to_builder();
765                h.title(title);
766                self.set_headline(arena, &h.headline(context)?)
767            }
768        }
769    }
770
771    pub fn set_commented(
772        self,
773        arena: &mut Arena,
774        commented: bool,
775        context: Option<&Context>,
776    ) -> Result<(), crate::errors::HeadlineError> {
777        match self.headline(arena, context).map(|h| h.to_owned()) {
778            None => Err(HeadlineError::InvalidHeadlineError),
779            Some(h) => {
780                let mut h = h.to_builder();
781                h.commented(commented);
782                self.set_headline(arena, &h.headline(context)?)
783            }
784        }
785    }
786
787    pub fn set_planning(
788        self,
789        arena: &mut Arena,
790        planning: Planning,
791        context: Option<&Context>,
792    ) -> Result<(), crate::errors::HeadlineError> {
793        match self.headline(arena, context) {
794            None => Err(HeadlineError::InvalidHeadlineError),
795            Some(h) => {
796                let mut h = h.to_builder();
797                h.planning(planning);
798                self.set_headline(arena, &h.headline(context)?)
799            }
800        }
801    }
802
803    pub fn set_scheduled(
804        self,
805        arena: &mut Arena,
806        scheduled: Option<Timestamp<'_>>,
807        context: Option<&Context>,
808    ) -> Result<(), crate::errors::HeadlineError> {
809        match self.headline(arena, context) {
810            None => Err(HeadlineError::InvalidHeadlineError),
811            Some(h) => {
812                let mut planning = h.planning().to_borrowed();
813                planning.scheduled = scheduled;
814                let mut h = h.to_builder();
815                h.planning(planning);
816                self.set_headline(arena, &h.headline(context)?)
817            }
818        }
819    }
820
821    pub fn set_deadline(
822        self,
823        arena: &mut Arena,
824        deadline: Option<Timestamp<'_>>,
825        context: Option<&Context>,
826    ) -> Result<(), crate::errors::HeadlineError> {
827        match self.headline(arena, context) {
828            None => Err(HeadlineError::InvalidHeadlineError),
829            Some(h) => {
830                let mut planning = h.planning().to_borrowed();
831                planning.deadline = deadline;
832                let mut h = h.to_builder();
833                h.planning(planning);
834                self.set_headline(arena, &h.headline(context)?)
835            }
836        }
837    }
838
839    pub fn set_closed(
840        self,
841        arena: &mut Arena,
842        closed: Option<Timestamp<'_>>,
843        context: Option<&Context>,
844    ) -> Result<(), crate::errors::HeadlineError> {
845        match self.headline(arena, context) {
846            None => Err(HeadlineError::InvalidHeadlineError),
847            Some(h) => {
848                let mut planning = h.planning().to_borrowed();
849                planning.closed = closed;
850                let mut h = h.to_builder();
851                h.planning(planning);
852                self.set_headline(arena, &h.headline(context)?)
853            }
854        }
855    }
856
857    pub fn set_body(
858        self,
859        arena: &mut Arena,
860        body: Rope,
861        context: Option<&Context>,
862    ) -> Result<(), crate::errors::HeadlineError> {
863        match self.headline(arena, context).map(|h| h.to_owned()) {
864            None => Err(HeadlineError::InvalidHeadlineError),
865            Some(h) => {
866                let mut h = h.to_builder();
867                h.body(body);
868                self.set_headline(arena, &h.headline(context)?)
869            }
870        }
871    }
872
873    #[cfg(feature = "orgize-integration")]
874    pub fn set_property(
875        self,
876        arena: &mut Arena,
877        property: &str,
878        value: &str,
879        context: Option<&Context>,
880    ) -> Result<(), crate::errors::HeadlineError> {
881        match self.headline(arena, context) {
882            None => Err(HeadlineError::InvalidHeadlineError),
883            Some(h) => {
884                let mut org = parse_orgize(h.body());
885                set_property_internal(&mut org, property, value)?;
886                let mut h = h.to_builder();
887                h.body(emit_orgize(&org));
888                let h = h.headline(context)?;
889                self.set_headline(arena, &h)
890            }
891        }
892    }
893
894    #[cfg(feature = "orgize-integration")]
895    pub fn clear_property(
896        self,
897        arena: &mut Arena,
898        property: &str,
899        context: Option<&Context>,
900    ) -> Result<(), crate::errors::HeadlineError> {
901        match self.headline(arena, context) {
902            None => Err(HeadlineError::InvalidHeadlineError),
903            Some(h) => {
904                let mut org = parse_orgize(h.body());
905                clear_property_internal(&mut org, property)?;
906                let mut h = h.to_builder();
907                h.body(emit_orgize(&org));
908                let h = h.headline(context)?;
909                self.set_headline(arena, &h)
910            }
911        }
912    }
913
914    #[cfg(feature = "orgize-integration")]
915    pub fn set_properties(
916        self,
917        arena: &mut Arena,
918        properties: indexmap::IndexMap<Cow<'static, str>, Cow<'static, str>>,
919        context: Option<&Context>,
920    ) -> Result<(), crate::errors::HeadlineError> {
921        match self.headline(arena, context) {
922            None => Err(HeadlineError::InvalidHeadlineError),
923            Some(h) => {
924                let mut org = parse_orgize(h.body());
925                set_properties_internal(&mut org, properties)?;
926                let mut h = h.to_builder();
927                h.body(emit_orgize(&org));
928                let h = h.headline(context)?;
929                self.set_headline(arena, &h)
930            }
931        }
932    }
933
934    #[cfg(feature = "orgize-integration")]
935    pub fn generate_id(
936        self,
937        arena: &mut Arena,
938        context: Option<&Context>,
939    ) -> Result<Cow<'static, str>, crate::errors::HeadlineError> {
940        match self.headline(arena, context) {
941            None => Err(HeadlineError::InvalidHeadlineError),
942            Some(h) => {
943                let mut org = parse_orgize(h.body());
944                if let Some(id) = get_property_internal("ID", &org)? {
945                    return Ok(id.to_owned());
946                }
947                let id = generate_id_internal(&mut org)?;
948                let mut h = h.to_builder();
949                h.body(emit_orgize(&org));
950                let h = h.headline(context)?;
951                self.set_headline(arena, &h)?;
952                Ok(id)
953            }
954        }
955    }
956}
957
958#[cfg(test)]
959mod tests {
960    use super::*;
961
962    #[test]
963    fn test_newline() {
964        let mut arena = Arena::default();
965        let doc = arena.parse_str("");
966
967        assert!(doc.at(&arena, 0).is_none());
968
969        let doc = arena.parse_str("\n");
970
971        let (section, offset) = doc.at(&arena, 0).unwrap();
972        assert_eq!(section.id, doc.root.id);
973        assert_eq!(offset, 0);
974
975        assert!(doc.at(&arena, 1).is_none());
976
977        let doc = arena.parse_str("\n\n");
978
979        let (section, offset) = doc.at(&arena, 0).unwrap();
980        assert_eq!(section.id, doc.root.id);
981        assert_eq!(offset, 0);
982
983        let (section, offset) = doc.at(&arena, 1).unwrap();
984        assert_eq!(section.id, doc.root.id);
985        assert_eq!(offset, 1);
986
987        assert!(doc.at(&arena, 2).is_none());
988    }
989
990    #[test]
991    fn test_empty() {
992        let mut arena = Arena::default();
993        let doc = arena.parse_str("");
994        assert!(doc.at(&arena, 0).is_none());
995    }
996
997    #[test]
998    fn test_at_o() {
999        let mut arena = Arena::default();
1000        let doc = arena.parse_str("* foo\n* bar\n");
1001
1002        let (section, offset) = doc.at(&arena, 0).unwrap();
1003        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1004        assert_eq!(offset, 0);
1005
1006        let (section, offset) = doc.at(&arena, 1).unwrap();
1007        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1008        assert_eq!(offset, 1);
1009
1010        let (section, offset) = doc.at(&arena, 2).unwrap();
1011        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1012        assert_eq!(offset, 2);
1013
1014        let (section, offset) = doc.at(&arena, 3).unwrap();
1015        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1016        assert_eq!(offset, 3);
1017
1018        let (section, offset) = doc.at(&arena, 4).unwrap();
1019        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1020        assert_eq!(offset, 4);
1021
1022        // The newline between "foo" and "bar".
1023        let (section, offset) = doc.at(&arena, 5).unwrap();
1024        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1025        assert_eq!(offset, 5);
1026
1027        let (section, offset) = doc.at(&arena, 6).unwrap();
1028        assert_eq!(
1029            section.id,
1030            doc.root.children(&arena).skip(1).next().unwrap().id
1031        );
1032        assert_eq!(offset, 0);
1033
1034        let (section, offset) = doc.at(&arena, 7).unwrap();
1035        assert_eq!(
1036            section.id,
1037            doc.root.children(&arena).skip(1).next().unwrap().id
1038        );
1039        assert_eq!(offset, 1);
1040
1041        let (section, offset) = doc.at(&arena, 8).unwrap();
1042        assert_eq!(
1043            section.id,
1044            doc.root.children(&arena).skip(1).next().unwrap().id
1045        );
1046        assert_eq!(offset, 2);
1047
1048        let (section, offset) = doc.at(&arena, 9).unwrap();
1049        assert_eq!(
1050            section.id,
1051            doc.root.children(&arena).skip(1).next().unwrap().id
1052        );
1053        assert_eq!(offset, 3);
1054
1055        let (section, offset) = doc.at(&arena, 10).unwrap();
1056        assert_eq!(
1057            section.id,
1058            doc.root.children(&arena).skip(1).next().unwrap().id
1059        );
1060        assert_eq!(offset, 4);
1061
1062        let (section, offset) = doc.at(&arena, 11).unwrap();
1063        assert_eq!(
1064            section.id,
1065            doc.root.children(&arena).skip(1).next().unwrap().id
1066        );
1067        assert_eq!(offset, 5);
1068
1069        assert!(doc.at(&arena, 12).is_none());
1070    }
1071
1072    #[test]
1073    fn test_at_two() {
1074        let mut arena = Arena::default();
1075        let doc = arena.parse_str("* foo\n* bar");
1076
1077        let (section, offset) = doc.at(&arena, 0).unwrap();
1078        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1079        assert_eq!(offset, 0);
1080
1081        let (section, offset) = doc.at(&arena, 1).unwrap();
1082        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1083        assert_eq!(offset, 1);
1084
1085        let (section, offset) = doc.at(&arena, 2).unwrap();
1086        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1087        assert_eq!(offset, 2);
1088
1089        let (section, offset) = doc.at(&arena, 3).unwrap();
1090        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1091        assert_eq!(offset, 3);
1092
1093        let (section, offset) = doc.at(&arena, 4).unwrap();
1094        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1095        assert_eq!(offset, 4);
1096
1097        // The newline between "foo" and "bar".
1098        let (section, offset) = doc.at(&arena, 5).unwrap();
1099        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1100        assert_eq!(offset, 5);
1101
1102        let (section, offset) = doc.at(&arena, 6).unwrap();
1103        assert_eq!(
1104            section.id,
1105            doc.root.children(&arena).skip(1).next().unwrap().id
1106        );
1107        assert_eq!(offset, 0);
1108
1109        let (section, offset) = doc.at(&arena, 7).unwrap();
1110        assert_eq!(
1111            section.id,
1112            doc.root.children(&arena).skip(1).next().unwrap().id
1113        );
1114        assert_eq!(offset, 1);
1115
1116        let (section, offset) = doc.at(&arena, 8).unwrap();
1117        assert_eq!(
1118            section.id,
1119            doc.root.children(&arena).skip(1).next().unwrap().id
1120        );
1121        assert_eq!(offset, 2);
1122
1123        let (section, offset) = doc.at(&arena, 9).unwrap();
1124        assert_eq!(
1125            section.id,
1126            doc.root.children(&arena).skip(1).next().unwrap().id
1127        );
1128        assert_eq!(offset, 3);
1129
1130        let (section, offset) = doc.at(&arena, 10).unwrap();
1131        assert_eq!(
1132            section.id,
1133            doc.root.children(&arena).skip(1).next().unwrap().id
1134        );
1135        assert_eq!(offset, 4);
1136
1137        assert!(doc.at(&arena, 11).is_none());
1138    }
1139
1140    #[test]
1141    fn test_at_thre() {
1142        let mut arena = Arena::default();
1143        let doc = arena.parse_str("\n* foo\n* bar");
1144
1145        let (section, offset) = doc.at(&arena, 0).unwrap();
1146        assert_eq!(section.id, doc.root.id);
1147        assert_eq!(offset, 0);
1148
1149        let (section, offset) = doc.at(&arena, 1).unwrap();
1150        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1151        assert_eq!(offset, 0);
1152
1153        let (section, offset) = doc.at(&arena, 2).unwrap();
1154        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1155        assert_eq!(offset, 1);
1156
1157        let (section, offset) = doc.at(&arena, 3).unwrap();
1158        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1159        assert_eq!(offset, 2);
1160
1161        let (section, offset) = doc.at(&arena, 4).unwrap();
1162        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1163        assert_eq!(offset, 3);
1164
1165        let (section, offset) = doc.at(&arena, 5).unwrap();
1166        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1167        assert_eq!(offset, 4);
1168
1169        // The newline between "foo" and "bar".
1170        let (section, offset) = doc.at(&arena, 6).unwrap();
1171        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1172        assert_eq!(offset, 5);
1173
1174        let (section, offset) = doc.at(&arena, 7).unwrap();
1175        assert_eq!(
1176            section.id,
1177            doc.root.children(&arena).skip(1).next().unwrap().id
1178        );
1179        assert_eq!(offset, 0);
1180
1181        let (section, offset) = doc.at(&arena, 8).unwrap();
1182        assert_eq!(
1183            section.id,
1184            doc.root.children(&arena).skip(1).next().unwrap().id
1185        );
1186        assert_eq!(offset, 1);
1187
1188        let (section, offset) = doc.at(&arena, 9).unwrap();
1189        assert_eq!(
1190            section.id,
1191            doc.root.children(&arena).skip(1).next().unwrap().id
1192        );
1193        assert_eq!(offset, 2);
1194
1195        let (section, offset) = doc.at(&arena, 10).unwrap();
1196        assert_eq!(
1197            section.id,
1198            doc.root.children(&arena).skip(1).next().unwrap().id
1199        );
1200        assert_eq!(offset, 3);
1201
1202        let (section, offset) = doc.at(&arena, 11).unwrap();
1203        assert_eq!(
1204            section.id,
1205            doc.root.children(&arena).skip(1).next().unwrap().id
1206        );
1207        assert_eq!(offset, 4);
1208
1209        assert!(doc.at(&arena, 12).is_none());
1210    }
1211
1212    #[test]
1213    fn test_at_fo() {
1214        let mut arena = Arena::default();
1215        let doc = arena.parse_str("\n* foo\n* bar\n");
1216
1217        let (section, offset) = doc.at(&arena, 0).unwrap();
1218        assert_eq!(section.id, doc.root.id);
1219        assert_eq!(offset, 0);
1220
1221        let (section, offset) = doc.at(&arena, 1).unwrap();
1222        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1223        assert_eq!(offset, 0);
1224
1225        let (section, offset) = doc.at(&arena, 2).unwrap();
1226        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1227        assert_eq!(offset, 1);
1228
1229        let (section, offset) = doc.at(&arena, 3).unwrap();
1230        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1231        assert_eq!(offset, 2);
1232
1233        let (section, offset) = doc.at(&arena, 4).unwrap();
1234        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1235        assert_eq!(offset, 3);
1236
1237        let (section, offset) = doc.at(&arena, 5).unwrap();
1238        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1239        assert_eq!(offset, 4);
1240
1241        // The newline between "foo" and "bar".
1242        let (section, offset) = doc.at(&arena, 6).unwrap();
1243        assert_eq!(section.id, doc.root.children(&arena).next().unwrap().id);
1244        assert_eq!(offset, 5);
1245
1246        let (section, offset) = doc.at(&arena, 7).unwrap();
1247        assert_eq!(
1248            section.id,
1249            doc.root.children(&arena).skip(1).next().unwrap().id
1250        );
1251        assert_eq!(offset, 0);
1252
1253        let (section, offset) = doc.at(&arena, 8).unwrap();
1254        assert_eq!(
1255            section.id,
1256            doc.root.children(&arena).skip(1).next().unwrap().id
1257        );
1258        assert_eq!(offset, 1);
1259
1260        let (section, offset) = doc.at(&arena, 9).unwrap();
1261        assert_eq!(
1262            section.id,
1263            doc.root.children(&arena).skip(1).next().unwrap().id
1264        );
1265        assert_eq!(offset, 2);
1266
1267        let (section, offset) = doc.at(&arena, 10).unwrap();
1268        assert_eq!(
1269            section.id,
1270            doc.root.children(&arena).skip(1).next().unwrap().id
1271        );
1272        assert_eq!(offset, 3);
1273
1274        let (section, offset) = doc.at(&arena, 11).unwrap();
1275        assert_eq!(
1276            section.id,
1277            doc.root.children(&arena).skip(1).next().unwrap().id
1278        );
1279        assert_eq!(offset, 4);
1280
1281        let (section, offset) = doc.at(&arena, 12).unwrap();
1282        assert_eq!(
1283            section.id,
1284            doc.root.children(&arena).skip(1).next().unwrap().id
1285        );
1286        assert_eq!(offset, 5);
1287
1288        assert!(doc.at(&arena, 13).is_none());
1289    }
1290
1291    #[test]
1292    fn test_at_nest() {
1293        let mut arena = Arena::default();
1294        let s = "* foo\n** bar\n*** baz\n* qux\n".to_string();
1295        let doc = arena.parse_str(&s);
1296        let foo = doc.root.children(&arena).next().unwrap();
1297        let bar = foo.children(&arena).next().unwrap();
1298        let baz = bar.children(&arena).next().unwrap();
1299
1300        let (section, offset) = doc.at(&arena, 0).unwrap();
1301        assert_eq!(section.id, foo.id);
1302        assert_eq!(offset, 0);
1303
1304        let (section, offset) = doc.at(&arena, 8).unwrap();
1305        assert_eq!(section.id, bar.id);
1306        assert_eq!(offset, 3);
1307
1308        let (section, offset) = doc.at(&arena, 18).unwrap();
1309        assert_eq!(section.id, baz.id);
1310        assert_eq!(offset, 7);
1311    }
1312}