]> git.donarmstrong.com Git - liborg-parser-perl.git/blob - lib/Org/Element/Headline.pm
Import original source of Org-Parser 0.23
[liborg-parser-perl.git] / lib / Org / Element / Headline.pm
1 package Org::Element::Headline;
2
3 use 5.010;
4 use locale;
5 use Moo;
6 extends 'Org::Element';
7
8 our $VERSION = '0.23'; # VERSION
9
10 has level => (is => 'rw');
11 has title => (is => 'rw');
12 has todo_priority => (is => 'rw');
13 has tags => (is => 'rw');
14 has is_todo => (is => 'rw');
15 has is_done => (is => 'rw');
16 has todo_state => (is => 'rw');
17 has progress => (is => 'rw');
18
19 sub header_as_string {
20     my ($self) = @_;
21     return $self->_str if defined $self->_str;
22     join("",
23          "*" x $self->level,
24          " ",
25          $self->is_todo ? $self->todo_state." " : "",
26          $self->todo_priority ? "[#".$self->todo_priority."] " : "",
27          $self->title->as_string,
28          $self->tags && @{$self->tags} ?
29              "  :".join(":", @{$self->tags}).":" : "",
30          "\n");
31 }
32
33 sub as_string {
34     my ($self) = @_;
35     $self->header_as_string . $self->children_as_string;
36 }
37
38 sub get_tags {
39     my ($self, $name, $search_parent) = @_;
40     my @res = @{ $self->tags // [] };
41     $self->walk_parents(
42         sub {
43             my ($el, $parent) = @_;
44             return 1 unless $parent->isa('Org::Element::Headline');
45             if ($parent->tags) {
46                 for (@{ $parent->tags }) {
47                     push @res, $_ unless $_ ~~ @res;
48                 }
49             }
50             1;
51         });
52     for (@{ $self->document->tags }) {
53         push @res, $_ unless $_ ~~ @res;
54     }
55     @res;
56 }
57
58 sub get_active_timestamp {
59     my ($self) = @_;
60
61     for my $s ($self->title, $self) {
62         my $ats;
63         $s->walk(
64             sub {
65                 my ($el) = @_;
66                 return if $ats;
67                 $ats = $el if $el->isa('Org::Element::Timestamp') &&
68                     $el->is_active;
69             }
70         );
71         return $ats if $ats;
72     }
73     return;
74 }
75
76 sub is_leaf {
77     my ($self) = @_;
78
79     return 1 unless $self->children;
80
81     my $res;
82     for my $child (@{ $self->children }) {
83         $child->walk(
84             sub {
85                 return if defined($res);
86                 my ($el) = @_;
87                 if ($el->isa('Org::Element::Headline')) {
88                     $res = 0;
89                     goto EXIT_WALK;
90                 }
91             }
92         );
93     }
94   EXIT_WALK:
95     $res //= 1;
96     $res;
97 }
98
99 sub promote_node {
100     my ($self, $num_levels) = @_;
101     $num_levels //= 1;
102     return if $num_levels == 0;
103     die "Please specify a positive number of levels" if $num_levels < 0;
104
105     for my $i (1..$num_levels) {
106
107         my $l = $self->level;
108         last if $l <= 1;
109         $l--;
110         $self->level($l);
111
112         $self->_str(undef);
113
114         my $parent = $self->parent;
115         my $siblings = $parent->children;
116         my $pos = $self->seniority;
117
118         # our children stay as children
119
120         # our right sibling headline(s) become children
121         while (1) {
122             my $s = $siblings->[$pos+1];
123             last unless $s && $s->isa('Org::Element::Headline')
124                 && $s->level > $l;
125             $self->children([]) unless defined $self->children;
126             push @{$self->children}, $s;
127             splice @$siblings, $pos+1, 1;
128             $s->parent($self);
129         }
130
131         # our parent headline can become sibling if level is the same
132         if ($parent->isa('Org::Element::Headline') && $parent->level == $l) {
133             splice @$siblings, $pos, 1;
134             my $gparent = $parent->parent;
135             splice @{$gparent->children}, $parent->seniority+1, 0, $self;
136             $self->parent($gparent);
137         }
138
139     }
140 }
141
142 sub demote_node {
143     my ($self, $num_levels) = @_;
144     $num_levels //= 1;
145     return if $num_levels == 0;
146     die "Please specify a positive number of levels" if $num_levels < 0;
147
148     for my $i (1..$num_levels) {
149
150         my $l = $self->level;
151         $l++;
152         $self->level($l);
153
154         $self->_str(undef);
155
156         # prev sibling can become parent
157         my $ps = $self->prev_sibling;
158         if ($ps && $ps->isa('Org::Element::Headline') && $ps->level < $l) {
159             splice @{$self->parent->children}, $self->seniority, 1;
160             $ps->children([]) if !defined($ps->children);
161             push @{$ps->children}, $self;
162             $self->parent($ps);
163         }
164
165     }
166 }
167
168 sub promote_branch {
169     my ($self, $num_levels) = @_;
170     $num_levels //= 1;
171     return if $num_levels == 0;
172     die "Please specify a positive number of levels" if $num_levels < 0;
173
174     for my $i (1..$num_levels) {
175         last if $self->level <= 1;
176         $_->promote_node() for $self->find('Headline');
177     }
178 }
179
180 sub demote_branch {
181     my ($self, $num_levels) = @_;
182     $num_levels //= 1;
183     return if $num_levels == 0;
184     die "Please specify a positive number of levels" if $num_levels < 0;
185
186     for my $i (1..$num_levels) {
187         $_->demote_node() for $self->find('Headline');
188     }
189 }
190
191 1;
192 # ABSTRACT: Represent Org headline
193
194
195 =pod
196
197 =head1 NAME
198
199 Org::Element::Headline - Represent Org headline
200
201 =head1 VERSION
202
203 version 0.23
204
205 =head1 DESCRIPTION
206
207 Derived from L<Org::Element>.
208
209 =head1 ATTRIBUTES
210
211 =head2 level => INT
212
213 Level of headline (e.g. 1, 2, 3). Corresponds to the number of bullet stars.
214
215 =head2 title => OBJ
216
217 L<Org::Element::Text> representing the headline title
218
219 =head2 todo_priority => STR
220
221 String (optional) representing priority.
222
223 =head2 tags => ARRAY
224
225 Arrayref (optional) containing list of defined tags.
226
227 =head2 is_todo => BOOL
228
229 Whether this headline is a TODO item.
230
231 =head2 is_done => BOOL
232
233 Whether this TODO item is in a done state (state which requires no more action,
234 e.g. DONE). Only meaningful if headline is a TODO item.
235
236 =head2 todo_state => STR
237
238 TODO state.
239
240 =head2 progress => STR
241
242 Progress.
243
244 =head1 METHODS
245
246 =for Pod::Coverage header_as_string as_string
247
248 =head2 $el->get_tags() => ARRAY
249
250 Get tags for this headline. A headline can define tags or inherit tags from its
251 parent headline (or from document).
252
253 =head2 $el->get_active_timestamp() => ELEMENT
254
255 Get the first active timestamp element for this headline, either in the title or
256 in the child elements.
257
258 =head2 $el->is_leaf() => BOOL
259
260 Returns true if element doesn't contain subtrees.
261
262 =head2 $el->promote_node([$num_levels])
263
264 Promote (decrease the level) of this headline node. $level specifies number of
265 levels, defaults to 1. Won't further promote if already at level 1.
266 Illustration:
267
268  * h1
269  ** h2   <-- promote 1 level
270  *** h3
271  *** h3b
272  ** h4
273  * h5
274
275 becomes:
276
277  * h1
278  * h2
279  *** h3
280  *** h3b
281  ** h4
282  * h5
283
284 =head2 $el->demote_node([$num_levels])
285
286 Does the opposite of promote_node().
287
288 =head2 $el->promote_branch([$num_levels])
289
290 Like promote_node(), but all children headlines will also be promoted.
291 Illustration:
292
293  * h1
294  ** h2   <-- promote 1 level
295  *** h3
296  **** grandkid
297  *** h3b
298
299  ** h4
300  * h5
301
302 becomes:
303
304  * h1
305  * h2
306  ** h3
307  *** grandkid
308  ** h3b
309
310  ** h4
311  * h5
312
313 =head2 $el->demote_branch([$num_levels])
314
315 Does the opposite of promote_branch().
316
317 =head1 AUTHOR
318
319 Steven Haryanto <stevenharyanto@gmail.com>
320
321 =head1 COPYRIGHT AND LICENSE
322
323 This software is copyright (c) 2012 by Steven Haryanto.
324
325 This is free software; you can redistribute it and/or modify it under
326 the same terms as the Perl 5 programming language system itself.
327
328 =cut
329
330
331 __END__
332