· 9 years ago · Jan 14, 2017, 11:56 PM
1" Vimball Archiver by Charles E. Campbell, Jr., Ph.D.
2UseVimball
3finish
4doc/orgguide.txt [[[1
51508
6*orgguide.txt* For Vim version 7.3 Last change: 2014 December 26
7
8 _ _ ____ __ __ _____ ____ ___ __ __ _____ ____ ____
9 ( \/ )(_ _)( \/ ) ( _ )( _ \ / __)( \/ )( _ )( _ \( ___)
10 \ / _)(_ ) ( )(_)( ) /( (_-. ) ( )(_)( )(_) ))__)
11 \/ (____)(_/\/\_) (_____)(_)\_) \___/(_/\/\_)(_____)(____/(____)
12
13
14==============================================================================
15TABLE OF CONTENTS *org* *org-toc* *orgguide* *orgguide-toc*
16
17 1. About vim-orgmode guide |orgguide-about|
18 2. Introduction |orgguide-introduction|
19 3. Installation |orgguide-installation|
20 4. Document structure |orgguide-docstructure|
21 5. Tables |orgguide-tables|
22 6. Hyperlinks |orgguide-hyperlinks|
23 7. Todo items |orgguide-todo|
24 8. Tags |orgguide-tags|
25 9. Properties |orgguide-properties|
26 10. Dates and Times |orgguide-dates|
27 11. Capture - Refile - Archive |orgguide-capture|
28 12. Agenda views |orgguide-agenda|
29 13. Export/Markup for rich export |orgguide-export|
30 14. Publishing |orgguide-publishing|
31 15. Working with source code |orgguide-source|
32 16. Miscellaneous |orgguide-misc|
33 17. MobileOrg |orgguide-mobileorg|
34 18. Customization |orgguide-customization|
35 19. Development |orgguide-development|
36 20. License vim-orgmode |orgguide-license|
37 21. Contributors |orgguide-contributors|
38 22. Changelog |orgguide-changelog|
39 23. Links |orgguide-links|
40 24. Development |orgguide-development|
41
42==============================================================================
43ORG MODE GUIDE *orgguide-about*
44
45Copyright © 2010 Free Software Foundation
46
47 Permission is granted to copy, distribute and/or modify this document under
48 the terms of the GNU Free Documentation License, Version 1.3 or any later
49 version published by the Free Software Foundation; with no Invariant
50 Sections, with the Front-Cover texts being “A GNU Manual,†and with the
51 Back-Cover Texts as in (a) below. A copy of the license is included in the
52 section entitled “GNU Free Documentation License.â€
53
54 (a) The FSF’s Back-Cover Text is: “You have the freedom to copy and modify
55 this GNU manual. Buying copies from the FSF supports it in developing GNU
56 and promoting software freedom.â€
57
58 This document is part of a collection distributed under the GNU Free
59 Documentation License. If you want to distribute this document separately
60 from the collection, you can do so by adding a copy of the license to the
61 document, as described in section 6 of the license.
62
63==============================================================================
64INTRODUCTION *vim-orgmode* *orgguide-introduction*
65
66Vim-orgmode: Text outlining and task management for Vim based on Emacs'
67Org-Mode.
68
69The idea for this plugin was born by listening to the Floss Weekly podcast
70introducing Emacs' Org-Mode (http://twit.tv/floss136). Org-Mode has a lot of
71strong features like folding, views (sparse tree) and scheduling of tasks.
72These are completed by hyperlinks, tags, todo states, priorities aso.
73
74Vim-orgmode aims at providing the same functionality for Vim and for command
75line tools*.
76
77* WAITING for command line tools and other programs a library liborgmode is
78 provided. It encapsulates all functionality for parsing and modifying org
79 files.
80
81------------------------------------------------------------------------------
82Preface~
83 vim-orgmode is a file type plugin for keeping notes, maintaining TODO
84 lists, and doing project planning with a fast and effective plain-text
85 system. It is also an authoring and publishing system.
86
87 This document is a copy of the orgmode-guide for emacs
88 (http://orgmode.org/) with modifications for vim. It contains all basic
89 features and commands, along with important hints for customization.
90
91 To start create a new file with the extension ".org".
92
93------------------------------------------------------------------------------
94Features~
95 vim-orgmode is still very young but already quite usable. Here is a short
96 list of the already supported features:
97
98 - Cycle visibility of headings
99 - Navigate between headings
100 - Edit the structure of the document: add, move, promote, denote headings
101 and more
102 - Hyperlinks within vim-orgmode and outside (files, webpages, etc.)
103 - TODO list management
104 - Tags for headings
105 - Basic date handling
106 - Export (via emacs)
107
108 More features are coming...
109
110------------------------------------------------------------------------------
111Default mappings~
112 *org-mappings*
113Here is a short overview of the default mappings. They also can be invoked
114via the 'Org' menu. Most are only usable in command mode.
115
116 Show/Hide:~
117 <TAB> - Cycle Visibility
118
119 Editing Structure:~
120 <C-S-CR> - insert heading above
121 <S-CR> - insert heading below
122 <CR> - insert heading below
123 <C-CR> - insert heading above, after children
124
125 m} - move heading down
126 m{ - move heading up
127 m]] - move subtree down
128 m[[ - move subtree up
129
130 yah - yank heading
131 dah - delete heading
132 yar - yank subtree
133 dar - delete subtree
134 p - paste subtree
135
136 >> or >ah - demote heading
137 << or <ah - promote heading
138 >ar - demote subtree
139 <ar - promote subtree
140
141 Hyperlinks:~
142 gl - goto link
143 gyl - yank link
144 gil - insert new link
145
146 gn - next link
147 go - previous link
148
149 TODO Lists:~
150 <localleader>d - select keyword
151 <S-Left> - previous keyword
152 <S-Right> - next keyword
153 <C-S-Left> - previous keyword set
154 <C-S-Right> - next keyword set
155
156 Checkboxes:~
157 <localleader>cc - toggle status
158 <localleader>cn or <CR> - insert checkbox below
159 <localleader>cN or <C-S-CR> - insert checkbox above
160
161 TAGS and properties:~
162 <localleader>st - set tags
163
164 Dates:~
165 <localleader>sa - insert date
166 <localleader>si - insert inactive date
167 <localleader>pa - insert date by using calendar selection
168 <localleader>pi - insert inactive date by using calendar selection
169
170 Agenda:~
171 <localleader>caa - agenda for the week
172 <localleader>cat - agenda of all TODOs
173
174 Not yet implemented in vim-orgmode~
175 <localleader>caT - timeline for current buffer
176
177 Export:~
178 <localleader>ep - export as PDF
179 <localleader>eh - export as HTML
180 <localleader>el - export as LaTeX
181
182------------------------------------------------------------------------------
183Inline markup~
184
185 We support org authoring markup as closely as possible
186 (we're adding two markdown-like variants for =code= and blockquotes).
187
188 Inline markup:
189>
190 *bold*
191 /italic/
192 _underline_
193 +strike-through+
194 =code=
195 ~verbatim~
196<
197
198 Note:
199 - /italic/ is rendered as reverse in most terms (works fine in gVim, though)
200 - +strike-through+ doesn't work on Vim / gVim
201 - the non-standard `code' markup is also supported
202 - =code= and ~verbatim~ are also supported as block-level markup, see below.
203
204 Ref: http://orgmode.org/manual/Emphasis-and-monospace.html
205
206------------------------------------------------------------------------------
207INSTALLATION AND UPGRADE *orgguide-installation*
208
209 Download the latest stable release at
210 http://www.vim.org/scripts/script.php?script_id=3642
211
212 Open the vimball archive in vim and source it.
213
214 $ vim orgmode.vba
215>
216 :so %
217<
218
219 ATTENTION: All .pyc files of former versions of vim-orgmode need to be
220 deleted beforehand!
221
222 Add the following line to your .vimrc file to ensure that filetype plugins
223 are loaded properly:
224>
225 filetype plugin indent on
226<
227
228 Please install the Universal Text Linking
229 (http://www.vim.org/scripts/script.php?script_id=293) add-on, otherwise
230 hyperlinks won't work. Other plugins that integrate well with vim orgmode
231 are listed in the following section.
232
233 Note: If you want to insert timestamp by using a calendar, you should
234 install calendar(http://www.vim.org/scripts/script.php?script_id=52) to
235 enable this feature.
236
237------------------------------------------------------------------------------
238Suggested plugins~
239 Universal Text Linking~
240 (http://www.vim.org/scripts/script.php?script_id=293) general support for
241 text linking. The hyperlinks feature of vim-orgmode depends on this
242 plugin.
243
244 repeat~
245 (http://www.vim.org/scripts/script.php?script_id=2136)
246 Repeat actions that would not be repeatable otherwise. This plugin is
247 needed when you want to repeat the previous orgmode action.
248
249 taglist~
250 ([http://www.vim.org/scripts/script.php?script_id=273)
251 Display tags for the currently edited file. Vim-orgmode ships with support
252 for displaying the heading structure and hyperlinks in the taglist plugin.
253
254 tagbar~
255 (http://www.vim.org/scripts/script.php?script_id=3465)
256 A new approach to displaying tags for the currently edited file.
257 Vim-orgmode ships with support for displaying the heading structure and
258 hyperlinks in the tagbar plugin.
259
260 speeddating~
261 (http://www.vim.org/scripts/script.php?script_id=2120)
262 In-/decrease dates the vim way: C-a and C-x. Dates and times in the
263 orgmode format can be in-/decreased if this plugins is installed.
264
265 Narrow Region~
266 (http://www.vim.org/scripts/script.php?script_id=3075)
267 Emulation of Emacs' Narrow Region feature. It might be useful when dealing
268 with large orgmode files.
269
270 pathogen~
271 (http://www.vim.org/scripts/script.php?script_id=2332)
272 Easy management of multiple vim plugins.
273
274 calendar~
275 (http://www.vim.org/scripts/script.php?script_id=52)
276 This plugin will create a calendar window for timestamp insertion.
277
278 SyntaxRange~
279 (http://www.vim.org/scripts/script.php?script_id=4168)
280 Use proper syntax highlighting for code blocks such as: >
281 #+BEGIN_SRC cpp
282 int i = 1;
283 #+END_SRC
284<
285
286------------------------------------------------------------------------------
287Feedback~
288 If you find problems with vim-orgmode, or if you have questions, remarks,
289 or ideas about it, please create a ticket on
290 https://github.com/jceb/vim-orgmode
291
292==============================================================================
293DOCUMENT STRUCTURE *orgguide-docstructure*
294
295------------------------------------------------------------------------------
296Outlines~
297 Outlines allow a document to be organized in a hierarchical structure, which
298 (at least for me) is the best representation of notes and thoughts. An
299 overview of this structure is achieved by folding (hiding) large parts of
300 the document to show only the general document structure and the parts
301 currently being worked on. vim-orgmode greatly simplifies the use of
302 outlines by compressing the entire show/hide functionality into a single
303 command, <Plug>OrgToggleFolding, which is bound to the <TAB> key.
304
305------------------------------------------------------------------------------
306Headlines~
307 Headlines define the structure of an outline tree. The headlines in
308 vim-orgmode start with one or more stars, on the left margin. For example:
309>
310 * Top level headline
311 ** Second level
312 *** 3rd level
313 some text
314 *** 3rd level
315 more text
316
317 * Another top level headline
318<
319
320 Some people find the many stars too noisy and would prefer an outline
321 that has whitespace followed by a single star as headline starters.
322 |g:org_heading_shade_leading_stars| describes a setup to realize this.
323
324 Body text under headings is indented by default, but you can control this
325 with the |g:org_indent| variable.
326
327------------------------------------------------------------------------------
328Text objects~
329 Vim offers a mighty feature called |text-objects|. A text object is bound to
330 a certain character sequence that can be used in combination with all kinds
331 of editing and selection tasks.
332
333 vim-orgmode implements a number of text objects to make editing org files
334 easier:
335
336 ih inner heading, referring to the current heading
337 excluding the heading level characters (*)
338 ah a heading, referring to the current heading including
339 everything
340 ir inner subtree, starting with the current heading
341 ar a subtree, starting with the current heading
342 Oh inner outer heading, referring to the parent
343 Or inner outer heading, including subtree, referring to
344 the parent
345 OH an outer heading
346 OT an outer subtree
347
348 Motions can be used like text objects as well. See |orgguide-motion|.
349
350------------------------------------------------------------------------------
351Visibility cycling~
352 Outlines make it possible to hide parts of the text in the buffer.
353 vim-orgmode uses just two commands, bound to <Tab> and <S-Tab> to change the
354 visibility in the buffer.
355
356 <Tab> or *orgguide-Tab* or *orgguide-S-Tab*
357 <S-Tab> Subtree cycling: Rotate current subtree among the
358 states
359>
360 ,-> FOLDED -> CHILDREN -> SUBTREE --.
361 '-----------------------------------'
362<
363
364 When called with the shift key, global cycling is invoked.
365
366 <LocalLeader>, or *orgguide-<LocalLeader>,* or *orgguide-<LocalLeader>.*
367 <LocalLeader>. Global cycling: Rotate the entire buffer among the
368 states. The same can be achieved by using the
369 keybindings zm and zr.
370>
371 ,-> OVERVIEW -> CONTENTS -> SHOW ALL --.
372 '--------------------------------------'
373<
374
375 Vim-orgmode doesn't implement the following functionality, yet.~
376 When Emacs first visits an org file, the global state is set to
377 OVERVIEW, i.e. only the top level headlines are visible. This can be
378 configured through the variable =org-startup-folded=, or on a per-file
379 basis by adding a startup keyword =overview=, =content=, =showall=, like
380 this:
381>
382 #+STARTUP: content
383<
384------------------------------------------------------------------------------
385Motion~
386 *orgguide-motion*
387 The following commands jump to other headlines in the buffer.
388
389 } Next heading.
390
391 { Previous heading.
392
393 ]] Next heading same level.
394
395 [[ Previous heading same level.
396
397 g{ Backward to higher level heading.
398
399 g} Forward to higher level heading.
400
401------------------------------------------------------------------------------
402Structure editing~
403
404 *orgguide-S-CR*
405 <S-CR> Insert new heading with same level as current. If the
406 cursor is in a plain list item, a new item is created
407 (see section [[#Plain-lists][Plain lists]]). When this
408 command is used in the middle of a line, the line is
409 split and the rest of the line becomes the new
410 headline.
411
412 Not yet implemented in vim-orgmode~
413 M-S-<CR> Insert new TODO entry with same level as current
414 heading.
415
416 <Tab> or
417 <S-Tab> In a new entry with no text yet, <Tab> and <S-Tab>
418 will cycle through reasonable levels.
419
420 << or *orgguide-<<* or *orgguide-CTRL-d*
421 <C-d> (insert mode) Promote current heading by one level.
422
423 >> or *orgguide->>* or *orgguide-CTRL-t*
424 <C-t> (insert mode) Demote current heading by one level.
425
426 *orgguide-<[[*
427 <[[ Promote the current subtree by one level.
428
429 *orgguide->]]*
430 >]] Demote the current subtree by one level.
431
432 *orgguide-m{*
433 m{ Move heading up (swap with previous/next subtree of
434 same level).
435
436 *orgguide-m}*
437 m} Move heading down (swap with previous/next subtree of
438 same level).
439
440 *orgguide-m[[*
441 m[[ Move subtree up (swap with previous/next subtree of
442 same level).
443
444 *orgguide-m]]*
445 m]] Move subtree down (swap with previous/next subtree of
446 same level).
447
448 Not yet implemented in vim-orgmode~
449 C-c C-w Refile entry or region to a different location. See
450 section [[#Refiling-notes][Refiling notes]].
451
452 *orgguide-<Leader>nr*
453 <Leader>nr Narrow buffer to current subtree / widen it again
454 (only if NarrowRegion plugin is installed)
455
456 When there is an active region (Transient Mark mode), promotion and demotion
457 work on all headlines in the region.
458
459------------------------------------------------------------------------------
460Sparse trees~
461 Not yet implemented in vim-orgmode~
462
463------------------------------------------------------------------------------
464Plain lists~
465 *orgguide-plain-list*
466 Within an entry of the outline tree, hand-formatted lists can provide
467 additional structure.
468
469 They also provide a way to create lists of checkboxes (see section
470 |orgguide-checkboxes|).
471
472 vim-orgmode supports editing such lists, and the exporter (see section
473 |orgguide-export|) parses and formats them.
474
475 vim-orgmode knows ordered lists, unordered lists, and description lists:
476 - 'Unordered' list items start with ‘-’, ‘+’, or ‘*’ as bullets.
477 - 'Ordered' list items start with ‘1.’ or ‘1)’.
478 - 'Description' list use ‘ :: ’ to separate the 'term' from the
479 description.
480
481 Items belonging to the same list must have the same indentation on the
482 first line. An item ends before the next line that is indented like its
483 bullet/number, or less. A list ends when all items are closed, or before
484 two blank lines. An example:
485>
486 ** Lord of the Rings
487 My favorite scenes are (in this order)
488 1. The attack of the Rohirrim
489 2. Eowyn's fight with the witch king
490 + this was already my favorite scene in the book
491 + I really like Miranda Otto.
492 Important actors in this film are:
493 - Elijah Wood :: He plays Frodo
494 - Sean Austin :: He plays Sam, Frodo's friend.
495<
496
497 The following commands act on items when the cursor is in the first line
498 of an item (the line with the bullet or number).
499
500 Not yet implemented in vim-orgmode~
501 The following commands act on items when the cursor is in the first line of
502 an item (the line with the bullet or number).
503
504------------------------------------------------------------------------------
505Footnotes~
506 Not yet implemented in vim-orgmode~
507
508==============================================================================
509TABLES *orgguide-tables*
510 Not yet implemented in vim-orgmode~
511
512==============================================================================
513HYPERLINKS *orgguide-hyperlinks*
514
515NOTE: The |utl| plugin is used for this feature and needs to be installed.
516 http://www.vim.org/scripts/script.php?script_id=293
517
518Like HTML, vim-orgmode provides links inside a file, external links to other
519files, Usenet articles, emails, and much more.
520
521------------------------------------------------------------------------------
522Link format~
523 *orgguide-linkformat*
524 vim-orgmode will recognize plain URL-like links and activate them as links.
525 The general link format, however, looks like this:
526>
527 [[link][description]] or alternatively [[link]]
528<
529
530 If vim was compiled with |+conceal|, vim-orgmode will shorten this format to
531 just display 'description' or 'link' once the link was completely entered
532 (that is, if all brackets are present) and you've left insert mode or
533 you're editing another line.
534 To edit the invisible ‘link’ part, go into insert mode, or call the
535 'Insert/edit Link' command by pressing 'gil'.
536
537------------------------------------------------------------------------------
538Internal links~
539 Not yet implemented in vim-orgmode~
540
541------------------------------------------------------------------------------
542External links~
543 |utl| supports links to files and websites. Others can be added by extending
544 utl (see |utl-smartSamples|). External links are URL-like locators. They
545 start with a short identifying string followed by a colon. There can be no
546 space after the colon. Here are some examples:
547>
548 http://www.astro.uva.nl/~dominik on the web
549 file:/home/dominik/images/jupiter.jpg file, absolute path
550 /home/dominik/images/jupiter.jpg same as above
551<
552
553 A link should be enclosed in double brackets and may contain a descriptive
554 text to be displayed instead of the URL (see section |orgguide-linkformat|),
555 for example:
556>
557 [[http://www.vim.org/][VIM]]
558<
559
560------------------------------------------------------------------------------
561Handling links~
562 vim-orgmode provides methods to create a link in the correct syntax, to
563 insert it into an org file, and to follow the link.
564
565 Not yet implemented in vim-orgmode~
566 C-c l Store a link to the current location. This is a
567 /global/ command (you must create the key binding
568 yourself) which can be used in any buffer to create a
569 link. The link will be stored for later insertion into
570 an org buffer (see below).
571
572 *orgguide-gil*
573 gil Insert a link. This prompts for a link to be inserted
574 into the buffer. You can just type a link, or use
575 history keys <Up> and <Down> to access stored links.
576 You will be prompted for the description part of the
577 link. File name completion is enabled to link to a
578 local file. In addition vim-orgmode provides the
579 command :OrgHyperlinkInsert to insert a link from
580 command line.
581
582 gil When the cursor is on an existing link, gil allows you
583 to edit the link and description parts of the link.
584
585 Not yet implemented in vim-orgmode~
586 C-c C-o or mouse-1 or mouse-2 Open link at point.
587
588 Not yet implemented in vim-orgmode~
589 C-c & Jump back to a recorded position. A position is
590 recorded by the commands following internal links, and
591 by C-c %. Using this command several times in direct
592 succession moves through a ring of previously recorded
593 positions.
594
595------------------------------------------------------------------------------
596Targeted links~
597 Not yet implemented in vim-orgmode~
598
599==============================================================================
600TODO ITEMS *orgguide-todo*
601
602vim-orgmode does not maintain TODO lists as separate documents. Instead, TODO
603items are an integral part of the notes file, because TODO items usually come
604up while taking notes! With vim-orgmode, simply mark any entry in a tree as
605being a TODO item. In this way, information is not duplicated, and the entire
606context from which the TODO item emerged is always present.
607
608Of course, this technique for managing TODO items scatters them throughout
609your notes file. vim-orgmode compensates for this by providing methods to give
610you an overview of all the things that you have to do.
611
612------------------------------------------------------------------------------
613Using TODO states~
614
615 Any headline becomes a TODO item when it starts with the word ‘TODO’,
616 for example:
617>
618 *** TODO Write letter to Sam Fortune
619<
620
621 The most important commands to work with TODO entries are:
622
623 <LocalLeader>ct Rotate the TODO state of the current item among. See
624 |orgguide-tags-settings|for more information.
625>
626 ,-> (unmarked) -> TODO -> DONE --.
627 '--------------------------------'
628<
629
630 Not yet implemented in vim-orgmode~
631 The same rotation can also be done “remotely†from the timeline and
632 agenda buffers with the t command key (see section
633 |orgguide-agenda-commands|).
634
635 <S-right> or <S-left> Select the following/preceding TODO state, similar to
636 cycling.
637
638 Not yet implemented in vim-orgmode~
639 C-c / t View TODO items in a /sparse tree/ (see section
640 [[#Sparse-trees][Sparse trees]]). Folds the buffer,
641 but shows all TODO items and the headings hierarchy
642 above them.
643
644 <LocalLeader>cat Show the global TODO list. This collects the TODO
645 items from all agenda files (see section
646 |orgguide-agenda-views|) into a single buffer.
647
648 Not yet implemented in vim-orgmode~
649 S-M-<CR> Insert a new TODO entry below the current one.
650
651------------------------------------------------------------------------------
652Multi-state workflows~
653
654 You can use TODO keywords to indicate different 'sequential' states in
655 the process of working on an item, for example:
656>
657 :let g:org_todo_keywords=['TODO', 'FEEDBACK', 'VERIFY', '|', 'DONE', 'DELEGATED']
658<
659
660 The vertical bar separates the TODO keywords (states that 'need action')
661 from the DONE states (which need 'no further action'). If you don’t
662 provide the separator bar, the last state is used as the DONE state.
663 With this setup, the command <S-Right> will cycle an entry from TODO to
664 FEEDBACK, then to VERIFY, and finally to DONE and DELEGATED.
665
666 Sometimes you may want to use different sets of TODO keywords in
667 parallel. For example, you may want to have the basic TODO/DONE, but
668 also a workflow for bug fixing, and a separate state indicating that an
669 item has been canceled (so it is not DONE, but also does not require
670 action). Your setup would then look like this:
671>
672 :let g:org_todo_keywords = [['TODO(t)', '|', 'DONE(d)'],
673 \ ['REPORT(r)', 'BUG(b)', 'KNOWNCAUSE(k)', '|', 'FIXED(f)'],
674 \ ['CANCELED(c)']]
675<
676 The keywords should all be different, this helps vim-orgmode to keep track
677 of which subsequence should be used for a given entry. The example also
678 shows how to define keys for fast access of a particular state, by
679 adding a letter in parenthesis after each keyword - you will be prompted
680 for the key after pressing ,d.
681
682 *orgguide-<LocalLeader>d*
683 <LocalLeader>d prompt for fast access of a todo state
684
685 Not yet implemented in vim-orgmode~
686 To define TODO keywords that are valid only in a single file, use the
687 following text anywhere in the file.
688
689>
690 #+BEGIN_EXAMPLE
691 #+TODO: TODO(t) | DONE(d)
692 #+TODO: REPORT(r) BUG(b) KNOWNCAUSE(k) | FIXED(f)
693 #+TODO: | CANCELED(c)
694 #+END_EXAMPLE
695<
696
697 After changing one of these lines, use C-c C-c with the cursor still in
698 the line to make the changes known to vim-orgmode.
699
700------------------------------------------------------------------------------
701Progress logging~
702 Not yet implemented in vim-orgmode~
703
704------------------------------------------------------------------------------
705Priorities~
706 Not yet implemented in vim-orgmode~
707
708------------------------------------------------------------------------------
709Breaking tasks down into subtasks~
710
711It is often advisable to break down large tasks into smaller, manageable
712subtasks. You can do this by creating an outline tree below a TODO item,
713with detailed subtasks on the tree. To keep the overview over the
714fraction of subtasks that are already completed, insert either ‘[/]’ or
715‘[%]’ anywhere in the headline. These cookies will be updated each time
716the TODO status of a child changes, or when pressing C-c C-c on the
717cookie. For example:
718
719>
720 * Organize Party [33%]
721 ** TODO Call people [1/2]
722 *** TODO Peter
723 *** DONE Sarah
724 ** TODO Buy food
725 ** DONE Talk to neighbor
726<
727
728<localleader>c# Update the checkboxes status of current heading. It
729 also update the heading status too.
730
731------------------------------------------------------------------------------
732Checkboxes~
733 *orgguide-checkboxes*
734
735Every item in a plain list (see section |orgguide-plain-list|)
736can be made into a checkbox by starting it with the string ‘[ ]’.
737Checkboxes are not included into the global TODO list, so they are often
738great to split a task into a number of simple steps. Here is an example
739of a checkbox list.
740
741>
742 * TODO Organize party [1/3]
743 - [-] call people [1/2]
744 - [ ] Peter
745 - [X] Sarah
746 - [X] order food
747 - [ ] think about what music to play
748<
749
750Checkboxes work hierarchically, so if a checkbox item has children that
751are checkboxes, toggling one of the children checkboxes will make the
752parent checkbox reflect if none, some, or all of the children are
753checked.
754
755The following commands work with checkboxes:
756
757<localleader>cc Toggle checkbox status or (with prefix arg) checkbox
758 presence at point.
759
760<localleader>cn or
761<CR> or <S-CR> Insert a new checkbox below current line.
762
763<localleader>cN or
764<C-S-CR> Insert a new checkbox above current line.
765
766==============================================================================
767TAGS *orgguide-tags*
768
769An excellent way to implement labels and contexts for cross-correlating
770information is to assign 'tags' to headlines. vim-orgmode has extensive
771support for tags.
772
773Every headline can contain a list of tags; they occur at the end of the
774headline. Tags are normal words containing letters, numbers, ‘_’, and
775‘@’. Tags must be preceded and followed by a single colon, e.g.,
776‘:work:’. Several tags can be specified, as in ‘:work:urgent:’. Tags
777will by default be in bold face with the same color as the headline.
778
779------------------------------------------------------------------------------
780Tag inheritance~
781 *orgguide-tags-inheritance*
782 Not yet implemented in vim-orgmode~
783
784------------------------------------------------------------------------------
785Setting tags~
786 *orgguide-tags-settings*
787 Tags can simply be typed into the buffer at the end of a headline. After
788 a colon, <TAB> offers completion on tags. There is also a special
789 command for inserting tags:
790
791 *orgguide-<LocalLeader>st*
792 <LocalLeader>st Enter new tags for the current headline. vim-orgmode
793 will either offer completion or a special single-key
794 interface for setting tags, see below. After pressing
795 <CR>, the tags will be inserted and aligned to
796 'org-tags-column'.
797
798 *orgguide-<LocalLeader>ft*
799 <LocalLeader>ft Find tags in the current file.
800
801 vim-orgmode will support tag insertion based on a 'list of tags'. By default
802 this list is constructed dynamically, containing all tags currently used
803 in the buffer.
804
805------------------------------------------------------------------------------
806Tag searches~
807 *orgguide-tags-search*
808 Not yet implemented in vim-orgmode~
809
810==============================================================================
811PROPERTIES *orgguide-properties*
812
813 Not yet implemented in vim-orgmode~
814
815==============================================================================
816DATES AND TIMES *orgguide-dates*
817
818To assist project planning, TODO items can be labeled with a date and/or
819a time. The specially formatted string carrying the date and time
820information is called a 'timestamp' in vim-orgmode.
821
822------------------------------------------------------------------------------
823Timestamps~
824
825 A timestamp is a specification of a date (possibly with a time or a range of
826 times) in a special format, either <2003-09-16 Tue> or <2003-09-16 Tue
827 09:39> or <2003-09-16 Tue 12:00-12:30>. A timestamp can appear anywhere in
828 the headline or body of an org tree entry. Its presence causes entries to
829 be shown on specific dates in the agenda (see section |orgguide-agenda|). We
830 distinguish:
831
832 Plain timestamp; Event; Appointment ~
833 A simple timestamp just assigns a date/time to an item. This is just like
834 writing down an appointment or event in a paper agenda.
835>
836 * Meet Peter at the movies <2006-11-01 Wed 19:15>
837 * Discussion on climate change <2006-11-02 Thu 20:00-22:00>
838<
839 Timestamp with repeater interval ~
840 Not yet implemented in vim-orgmode~
841
842 Diary-style sexp entries ~
843 Not yet implemented in vim-orgmode~
844
845 Time/Date range~
846 Two timestamps connected by ‘--’ denote a range.
847>
848 ** Meeting in Amsterdam
849 <2004-08-23 Mon>--<2004-08-26 Thu>
850<
851 Inactive timestamp~
852 Just like a plain timestamp, but with square brackets instead of angular
853 ones. These timestamps are inactive in the sense that they do 'not'
854 trigger an entry to show up in the agenda.
855>
856 * Gillian comes late for the fifth time [2006-11-01 Wed]
857<
858------------------------------------------------------------------------------
859Creating timestamps~
860
861 For vim-orgmode to recognize timestamps, they need to be in the specific
862 format. All commands listed below produce timestamps in the correct format.
863
864 *orgmode-<LocalLeader>-sa*
865 <LocalLeader>sa Prompt for a date and insert a corresponding
866 timestamp.
867
868 Not yet implemented in vim-orgmode~
869 When the cursor is at an existing timestamp in the
870 buffer, the command is used to modify this timestamp
871 instead of inserting a new one.
872
873 Not yet implemented in vim-orgmode~
874 When this command is used twice in succession, a time
875 range is inserted. With a prefix, also add the current
876 time.
877
878 *orgmode-<LocalLeader>si*
879 <LocalLeader>si Like |orgmode-<LocalLeader>-sa|, but insert an inactive
880 timestamp that will not cause an agenda entry.
881
882 *orgmode-ctrl-a* or *orgmode-ctrl-x*
883 CTRL-A or CTRL-X Change the item under the cursor in a timestamp.
884 The cursor can be on a year, month, day, hour or
885 minute. NOTE: The plugin 'speeddating' should be
886 installed for this feature.
887
888 Not yet implemented in vim-orgmode~
889 When the timestamp contains a time range like
890 ‘15:30-16:30’, modifying the first time will also
891 shift the second, shifting the time block with
892 constant length. To change the length, modify the
893 second time.
894
895 When vim-orgmode prompts for a date/time, it will accept any string
896 containing some date and/or time information, and intelligently interpret
897 the string, deriving defaults for unspecified information from the current
898 date and time. See the manual for more information on how exactly the
899 date/time prompt works.
900
901 You can also select a date in the pop-up calendar.
902 NOTE: The plugin 'calendar' should be installed for this feature.
903
904 *orgmode-<LocalLeader>pa*
905 <LocalLeader>pa Open a calendar and prompt a user selected date, then
906 insert a corresponding timestamp.
907
908 *orgmode-<LocalLeader>pi*
909 <LocalLeader>pi Like |orgmode-<LocalLeader>-pa|, but insert an inactive
910 timestamp that will not cause an agenda entry.
911
912------------------------------------------------------------------------------
913Deadlines and scheduling~
914 Not yet implemented in vim-orgmode~
915
916------------------------------------------------------------------------------
917Clocking work time~
918 Not yet implemented in vim-orgmode~
919
920==============================================================================
921CAPTURE - REFILE - ARCHIVE *orgguide-capture*
922
923 Not yet implemented in vim-orgmode~
924
925==============================================================================
926AGENDA VIEWS *orgguide-agenda*
927
928Due to the way vim-orgmode works, TODO items, time-stamped items, and tagged
929headlines can be scattered throughout a file or even a number of files. To get
930an overview of open action items, or of events that are important for a
931particular date, this information must be collected, sorted and displayed in
932an organized way. There are several different views, see below.
933
934The extracted information is displayed in a special agenda buffer. This
935buffer is read-only.
936
937Not yet implemented in vim-orgmode~
938... but provides commands to visit the corresponding locations in the original
939org files, and even to edit these files remotely. Remote editing from the
940agenda buffer means, for example, that you can change the dates of deadlines
941and appointments from the agenda buffer. The commands available in the Agenda
942buffer are listed in |orgguide-agenda-commands|.
943
944- |orgguide-agenda-files| Files being searched for agenda information
945- |orgguide-agenda-dispatcher| Keyboard access to agenda views
946- |orgguide-agenda-views| What is available out of the box?
947- |orgguide-agenda-commands| Remote editing of org trees
948- |orgguide-agenda-custom| Defining special searches and views
949
950------------------------------------------------------------------------------
951Agenda files~
952 *g:org_agenda_files* *orgguide-agenda-files*
953 Default: []
954 The information to be shown is normally collected from all 'agendafiles',
955 the files listed in the variable g:org_agenda_files.
956
957 You can change the list of agenda files like this:
958>
959 let g:org_agenda_files = ['~/org/index.org', ~/org/project.org']
960<
961
962 Also globbing is allowed. This makes it easy to use ALL *.org files in a
963 folder. Using all *.org files in ~/org/ is done like this:
964>
965 let g:org_agenda_files = ['~/org/*.org']
966<
967
968 WARNING: This might be slow if you have a lot of org files.
969
970------------------------------------------------------------------------------
971The agenda dispatcher ~
972 *orgguide-agenda-dispatcher*
973 Not yet implemented in vim-orgmode~
974
975------------------------------------------------------------------------------
976The built-in agenda views ~
977 *orgguide-agenda-views*
978
979 The weekly/daily agenda~
980 The purpose of the weekly/daily 'agenda' is to act like a page of a
981 paper agenda, showing all the tasks for the current week or day.
982
983 *orgguide-<LocalLeader>caa*
984 <LocalLeader>caa Compile an agenda for the current week from a list of
985 org files. The agenda shows the entries for each day.
986
987 The global TODO list~
988 The global TODO list contains all unfinished TODO items formatted and
989 collected into a single place.
990
991 Not yet implemented in vim-orgmode~
992 Remote editing of TODO items lets you change the state of a TODO entry
993 with a single key press. The commands available in the TODO list are
994 described in |agenda-commands|
995
996 *orgguide-<LocalLeader>cat*
997 <LocalLeader>cat Show the global TODO list. This collects the TODO
998 items from all agenda files into a single buffer.
999
1000 Not yet implemented in vim-orgmode~
1001 *orgguide-<LocalLeader>caT*
1002 <LocalLeader>caT Like the above, but allows selection of a specific
1003 TODO keyword.
1004
1005 Matching tags and properties~
1006 Not yet implemented in vim-orgmode~
1007
1008 Timeline for a single file~
1009 The timeline summarizes all time-stamped items from a single vim-orgmode
1010 file in a /time-sorted view/. The main purpose of this command is to
1011 give an overview over events in a project.
1012
1013 *orgguide-<LocalLeader>caL*
1014 <LocalLeader>caL Show a time-sorted view of the vim-orgmode, with all
1015 time-stamped items.
1016
1017 Search view~
1018 Not yet implemented in vim-orgmode~
1019
1020------------------------------------------------------------------------------
1021Commands in the agenda buffer~
1022 *orgguide-agenda-commands*
1023 Entries in the agenda buffer are linked back to the org file where they
1024 originate. Commands are provided to show and jump to the
1025 original entry location, and to edit the org files “remotely†from the
1026 agenda buffer.
1027
1028 Not yet implemented in vim-orgmode~
1029 only partly implemented
1030
1031 Motion~
1032 Not yet implemented in vim-orgmode~
1033
1034 View/Go to org file~
1035 *orgguide-agenda-Tab*
1036 <Tab> Go to the original location of the item in an
1037 alternative window.
1038
1039 *orgguide-agenda-CR*
1040 <CR> Go to the original location of the item and stay in
1041 the same/the agenda window.
1042
1043 *orgguide-agenda-S-CR*
1044 <S-CR> Go to the original location of the item in a new split
1045 window.
1046
1047 Not yet implemented in vim-orgmode~
1048
1049 Change display~
1050 Not yet implemented in vim-orgmode~
1051
1052------------------------------------------------------------------------------
1053Custom agenda views~
1054 *orgguide-agenda-custom*
1055 Not yet implemented in vim-orgmode~
1056
1057==============================================================================
1058EXPORTING *orgguide-export*
1059
1060NOTE: vim-orgmode relies on Emacs for this feature. Emacs _and_ Emacs'
1061 org-mode need to be installed! For PDF export a Latex environment
1062 is needed as well!
1063
1064vim-orgmode documents can be exported into a variety of other formats:
1065ASCII export for inclusion into emails, HTML to publish on the web,
1066LaTeX/PDF for beautiful printed documents and DocBook to enter the world
1067of many other formats using DocBook tools. There is also export to
1068iCalendar format so that planning information can be incorporated into
1069desktop calendars.
1070
1071Currently, the export to pdf, html, latex and markdown is supported via the
1072following commands and the 'export' menu:
1073>
1074 :OrgExportToPDF
1075 :OrgExportToHTML
1076 :OrgExportToLaTeX
1077 :OrgExportToMarkdown
1078<
1079
1080Make sure that you have configured your emacs accordingly, as for instance
1081the markdown exporter is not loaded by default. To load it, add
1082
1083>
1084 (eval-after-load "org"
1085 '(require 'ox-md nil t))
1086<
1087
1088to your init.el. Make also sure to specify your path by using the
1089|g:org_export_init_script| option.
1090
1091 *g:org_export_emacs*
1092Default: "/usr/bin/emacs"
1093Path to Emacs executable. Example:
1094>
1095 :let g:org_export_emacs="~/bin/emcas"
1096<
1097
1098 *g:org_export_verbose*
1099Default: 0
1100If set, Emacs' export output is displayed.
1101>
1102 :let g:org_export_verbose=1
1103<
1104
1105 *g:org_export_init_script*
1106Default: ""
1107For the export via Emacs a separate configuration file can be sourced to
1108determine Emacs' export behavior. Examples:
1109
1110Source the ~/.emacs configuration file:
1111>
1112 :let g:org_export_init_script="~/.emacs"
1113<
1114
1115Or source a different file:
1116>
1117 :let g:org_export_init_script="~/.emacs_org_init"
1118<
1119
1120==============================================================================
1121PUBLISHING *orgguide-publishing*
1122
1123 Not yet implemented in vim-orgmode~
1124
1125==============================================================================
1126WORKING WITH SOURCE CODE *orgguide-source*
1127
1128 Not yet implemented in vim-orgmode~
1129
1130==============================================================================
1131MISCELLANEOUS *orgguide-misc*
1132
1133 Not yet implemented in vim-orgmode~
1134
1135==============================================================================
1136MOBILEORG *orgguide-mobileorg*
1137
1138 Not yet implemented in vim-orgmode~
1139
1140==============================================================================
1141CUSTOMIZATION *orgguide-customization*
1142
1143------------------------------------------------------------------------------
1144Remapping shortcuts~
1145 vim-orgmode provides an easy way for remapping the default keyboard
1146 shortcuts. For this task it relies on vim's <Plug> mappings. All shortcuts
1147 of vim-orgmode are accessible by <Plug>s.
1148
1149 To change a keyboard shortcut the name of the related <Plug> is needed.
1150 First we need to look up the current mapping in the Org menu. The following
1151 command reveals the <Plug>'s name:
1152>
1153 :map <current_mapping>
1154<
1155
1156 The result should look something like this:
1157>
1158 :map ,t
1159 n ,t @<Plug>OrgSetTags
1160<
1161
1162 Now we can create an alternate mapping:
1163>
1164 nmap <new_mapping> <the_plug>
1165<
1166
1167 To change the mapping for editing tags to <leader>t the vimrc entry would
1168 look like this:
1169>
1170 nmap <leader>t @<Plug>OrgSetTags
1171<
1172
1173------------------------------------------------------------------------------
1174Syntax highlighting and indentation~
1175 Syntax highlighting is customizable to fit nicely with the user's
1176 colorscheme.
1177
1178 *g:org_heading_highlight_colors*
1179 Default: ['Title', 'Constant', 'Identifier', 'Statement', 'PreProc', 'Type',
1180 \ 'Special']
1181 Define the highlighting colors/group names for headings. Example:
1182>
1183 let g:org_heading_highlight_colors = ['Title', 'Constant', 'Identifier',
1184 \ 'Statement', 'PreProc', 'Type', 'Special']
1185<
1186
1187 *g:org_heading_highlight_levels*
1188 Default: len(g:org_heading_highlight_colors)
1189 Definie the number of levels of highlighting. If this number is bigger than
1190 the list of colors defined in of g:org_heading_highlight_colors the colors
1191 of g:org_heading_highlight_colors get repeated. Example:
1192>
1193 let g:org_heading_highlight_levels = len(g:org_heading_highlight_colors)
1194<
1195
1196 *g:org_heading_shade_leading_stars*
1197 Default: 1
1198 Defines if leading stars are displayed in the color of the heading or if a
1199 special NonText highlighting is used that hides them from user. Example:
1200>
1201 let g:org_heading_shade_leading_stars = 1
1202<
1203
1204 *g:org_todo_keywords*
1205 Default: ['TODO', '|', 'DONE']
1206 Defines the keywords that are highlighted in headings. For more information
1207 about this variable, please consult the org-mode documentation
1208 (http://orgmode.org/org.html#index-org_002dtodo_002dkeywords-511). Example:
1209>
1210 let g:org_todo_keywords = ['TODO', '|', 'DONE']
1211<
1212
1213 *g:org_todo_keyword_faces*
1214 Default: []
1215 Defines special faces (styles) for displaying g:org_todo_keywords. Please
1216 refer to vim documentation (topic |attr-list|) for allowed values for
1217 :weight, :slant, :decoration. Muliple colors can be separated by comma for
1218 :foreground and :background faces to provide different colors for GUI and
1219 terminal mode. Example:
1220>
1221 let g:org_todo_keyword_faces = []
1222<
1223
1224 *g:org_indent*
1225 Default: 1
1226 Defines if body text is indented. By default text is indented according to
1227 heading level (heading.level + 1). You can disable it by setting:
1228>
1229 let g:org_indent = 0
1230<
1231
1232 Syntax Highlighting Examples~
1233 Define an additionaly keyword 'WAITING' and set the foreground color to
1234 'cyan'. Define another keyword 'CANCELED' and set the foreground color to
1235 red, background to black and the weight to normal, slant to italc and
1236 decoration to underline:
1237
1238>
1239 let g:org_todo_keywords = [['TODO', 'WAITING', '|', 'DONE'],
1240 \ ['|', 'CANCELED']]
1241 let g:org_todo_keyword_faces = [['WAITING', 'cyan'], ['CANCELED',
1242 \ [':foreground red', ':background black', ':weight bold',
1243 \ ':slant italic', ':decoration underline']]]
1244<
1245
1246==============================================================================
1247DEVELOPMENT *orgguide-development*
1248
1249The development of vim-orgmode is coordinated via github:
1250 https://github.com/jceb/vim-orgmode
1251
1252If you like this project, have questions, suggestions or problems, simply drop
1253us a line and open an issue. Patches are very welcome!
1254
1255Here is a quick start about the vim-orgmode development.
1256
1257------------------------------------------------------------------------------
1258Structure and Source Code~
1259 The majority of the source code is stored in folder ftplugin/orgmode. This
1260 is where the actual functionality of the plugin is located.
1261
1262 I choose to implement vim-orgmode mainly in Python. I hope this will ease
1263 the implementation especially with the functionality of the Python standard
1264 library at hand.
1265
1266 Right below the directory ftplugin/orgmode the basic implementation of
1267 vim-orgmode is found. This basic functionality provides everything for
1268 higher level implementations that modify the buffer, provide a menu and
1269 keybindings to the user and everything else that is needed.
1270
1271 Below the directory ftplugin/orgmode/plugins the plugins are located. Every
1272 plugin must provide a class equal to its filename with the .py-extension.
1273 An example for a plugin can be found in file
1274 ftplugin/orgmode/plugins/Example.py.
1275
1276 *g:org_plugins*
1277 Default: ['ShowHide', '|', 'Navigator', 'EditStructure', '|', 'Hyperlinks',
1278 \ '|', 'Todo', 'TagsProperties', 'Date', 'Agenda', 'Misc', '|',
1279 \ 'Export']
1280 Every plugin must be enabled by the user by setting the g:org_plugins
1281 variable. By default all shipped plugins are enabled. Example:
1282>
1283 let g:org_plugins = ['ShowHide', '|', 'Navigator', 'EditStructure']
1284<
1285
1286 Files and folders~
1287 .
1288 ├── debian - files needed for building a Debian package
1289 ├── doc - vim documentation
1290 ├── documentation - development documentation
1291 ├── examples - example of aplugin
1292 ├── ftdetect - Filetype detection for orgmode files
1293 ├── ftplugin - Home of the main part of vim-orgmode
1294 │ └── orgmode - Home for all Python code
1295 │ ├── liborgmode - vim unrelated part of vim-orgmde. Contains
1296 │ │ basic data structures and algorithms to
1297 │ │ parse and edit orgfiles.
1298 │ └── plugins - Home for all orgmode plugins
1299 ├── indent - Indentation for orgmode files
1300 ├── syntax - Syntax highlighting
1301 ├── tests - Tests to verify the consistency and
1302 │ correctness of orgmode and the plugins
1303 ├── build_vmb.vim - Build file for creating a Vimball
1304 ├── install-vbm.vim - Local installation of vbm via make target
1305 ├── LICENSE - License Information
1306 ├── README.org - README :)
1307 └── Makefile - make commands
1308
1309------------------------------------------------------------------------------
1310Writing a plugin~
1311 To write a plugin:
1312 1. copy file ftplugin/orgmode/plugins/Example.py to
1313 ftplugin/orgmode/plugins/YourPlugin.py
1314 2. Change class name to "YourPlugin"
1315 3. Set the menu name, it doesn't need to match the filename anymore, e.g.
1316 "Your Plugin"
1317 4. Prepare keybindings in function register by defining a proper action and
1318 a key this action should be mapped to. For further information refer to
1319 section Keybindings.
1320 5. Register your plugin:
1321>
1322 let g:org_plugins = ['ShowHide', '|', 'Navigator', 'EditStructure',
1323 \ 'YourPlugin']
1324<
1325
1326 6. Write unittests and implement YourPlugin.
1327
1328------------------------------------------------------------------------------
1329Keybindings~
1330 Keybindings alias mappings are described very well in the vim
1331 documentation, see |map-modes|. vim-orgmode tries to make it easy for the
1332 developer to register new keybindings, make them customizable and provide
1333 menu entries so that the user can access the functionality like in original
1334 orgmode.
1335
1336 This is done by providing three classes: Keybinding, Plug and ActionEntry
1337
1338 Keybinding~
1339 This is the basic class that encapsulates a single keybinding consisting
1340 of a key/mapping and an action. Several options can be set when creating
1341 the object to specify the mode and all kinds of other things.
1342
1343 If a Plug is given instead of an action string the Plug is bound to the
1344 key. All relevant data is read from the Plug, e.g. name, mode aso.
1345
1346 Example~
1347 Map g{ to moving to parent heading in normal mode:
1348>
1349 Keybinding('g{', \
1350 ':py ORGMODE.plugins["Navigator"].parent(mode="normal")<CR>', \
1351 mode=MODE_NORMAL)
1352
1353 vim -> :nmap g{
1354 \ :py ORGMODE.plugins["Navigator"].parent(mode="normal")<CR>
1355<
1356
1357 Map g{ to moving to parent heading in normal mode by using a Plug:
1358>
1359 Keybinding('g{', Plug('OrgJumpToParentNormal', \
1360 ':py ORGMODE.plugins["Navigator"].parent(mode="normal")<CR>'))
1361
1362 vim -> :nnoremap <Plug>OrgJumpToParentNormal :py
1363 \ ORGMODE.plugins["Navigator"].parent(mode="normal")<CR>
1364 vim -> :nmap g{ <Plug>OrgJumpToParentNormal
1365<
1366
1367 Plug~
1368 A Plug is a unique keybinding that can not be executed by pressing
1369 any key. This makes it a special Keybinding that takes a name and
1370 an action to create an object. A plug normally goes together with a
1371 regular Keybinding to bind the Plug to a key.
1372
1373 This special behavior is needed to ensure that keybindings are
1374 customizable by the user. If the user creates a keybinding to a
1375 Plug the Keybinding object makes sure that the users keybinding is
1376 used and the keybinding specified by the plugin is not used.
1377
1378 Example~
1379 Map g{ to moving to parent heading in normal mode by using a Plug:
1380>
1381 Keybinding('g{', Plug('OrgJumpToParentNormal', \
1382 ':py ORGMODE.plugins["Navigator"].parent(mode="normal")<CR>'))
1383
1384 vim -> :nnoremap <Plug>OrgJumpToParentNormal
1385 \ :py ORGMODE.plugins["Navigator"].parent(mode="normal")<CR>
1386 vim -> :nmap g{ <Plug>OrgJumpToParentNormal
1387<
1388
1389 ActionEntry~
1390 An ActionEntry makes Keybindings accessible by the vim menu. It takes a
1391 description and a Keybinding object and builds a menu entry from this. The
1392 resulting object can be added to a Submenu object by using the + operator.
1393
1394 Example~
1395 Map g{ to moving to parent heading in normal mode by using a Plug:
1396>
1397 k = Keybinding('g{', Plug('OrgJumpToParentNormal', \
1398 ':py ORGMODE.plugins["Navigator"].parent(mode="normal")<CR>'))
1399
1400 vim -> :nnoremap <Plug>OrgJumpToParentNormal
1401 \ :py ORGMODE.plugins["Navigator"].parent(mode="normal")<CR>
1402 vim -> :nmap g{ <Plug>OrgJumpToParentNormal
1403
1404 menu + ActionEntry('&Up', k)
1405 vim -> :nmenu &Org.&Naviagte Headings.&Up<Tab>g{
1406 \ <Plug>OrgJumpToParentNormal
1407>
1408
1409------------------------------------------------------------------------------
1410Building a Vimball~
1411 Vimball is an archive format for vim plugins. It's of use when you want to
1412 install vim-orgmode for a single user. To build a Vimball just run the
1413 following command in the root folder of this plugin. Please make sure that
1414 vim is installed on your computer:
1415>
1416 make vbm
1417<
1418
1419 For installing the plugin form the resulting orgmode.vbm.gz file, please
1420 refer to the Installation section.
1421
1422------------------------------------------------------------------------------
1423Building a Debian Package~
1424 A Debian package is of use when you want to make vim-orgmode available to
1425 all users on your computer. Make sure you've debhelper and vim installed,
1426 than run the following command from the root directory of this plugin to
1427 build the debian package:
1428>
1429 dpkg-buildpackage -us -uc
1430<
1431
1432 For installing the plugin form the resulting vim-orgmode_X.X.X-X.deb file,
1433 please refer to the Installation section.
1434
1435------------------------------------------------------------------------------
1436Creating Tests Cases~
1437 For every plugin it's important to write automated test cases. This is
1438 important to ensure that little changes don't break things at the other end
1439 of the project.
1440
1441 vim-orgmode relies on Pyunit (http://docs.python.org/library/unittest.html).
1442 All tests are located in the tests directory. Run
1443>
1444 make test
1445<
1446
1447 to run all tests. To create a new test the test should be added to the
1448 corresponding test file.
1449
1450 In case a new plugin is created a new test file needs to be created as well.
1451 The test needs to be added to the test suite located in the file
1452 tests/run_tests.py.
1453
1454 Finally the
1455>
1456 make coverage
1457<
1458
1459 should be run. The result shows the test coverage of all project files. One
1460 hundred percent (100%) is of course the goal :-)
1461
1462==============================================================================
1463LINKS *orgguide-links*
1464
1465- Original org-mode for Emacs (http://orgmode.org)
1466
1467- VimOrganizer, another vim port of Emacs org-mode
1468 (http://www.vim.org/scripts/script.php?script_id=3342)
1469
1470==============================================================================
1471CHANGELOG *orgguide-changelog*
1472
1473Is found in file CHANGELOG.org
1474
1475==============================================================================
1476CONTRIBUTORS *orgguide-contributors*
1477
1478Thanks to all how contributed to vim-orgmode. All contributors are name here
1479in alphabetic order:
1480
1481- Stefan Otte
1482- Aleksandar Dimitrov
1483
1484==============================================================================
1485LICENSE VIM-ORGMODE *orgguide-license*
1486
1487Copyright (C) 2010, 2011 Jan Christoph Ebersbach
1488
1489http://www.e-jc.de/
1490
1491All rights reserved.
1492
1493The source code of this program is made available under the terms of the GNU
1494Affero General Public License version 3 (GNU AGPL V3) as published by the Free
1495Software Foundation.
1496
1497Binary versions of this program provided by Univention to you as well as other
1498copyrighted, protected or trademarked materials like Logos, graphics, fonts,
1499specific documentations and configurations, cryptographic keys etc. are
1500subject to a license agreement between you and Univention and not subject to
1501the GNU AGPL V3.
1502
1503In the case you use this program under the terms of the GNU AGPL V3, the
1504program is provided in the hope that it will be useful, but WITHOUT ANY
1505WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
1506A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
1507details.
1508
1509You should have received a copy of the GNU Affero General Public License with
1510the Debian GNU/Linux or Univention distribution in file
1511/usr/share/common-licenses/AGPL-3; if not, see <http://www.gnu.org/licenses/>.
1512
1513vim:tw=78:ts=2:sw=2:expandtab:ft=help:norl:
1514indent/org.vim [[[1
1515124
1516" Delete the next line to avoid the special indention of items
1517if !exists("g:org_indent")
1518 let g:org_indent = 1
1519endif
1520
1521setlocal foldtext=GetOrgFoldtext()
1522setlocal fillchars-=fold:-
1523setlocal fillchars+=fold:\
1524setlocal foldexpr=GetOrgFolding()
1525setlocal foldmethod=expr
1526setlocal indentexpr=GetOrgIndent()
1527setlocal nolisp
1528setlocal nosmartindent
1529setlocal autoindent
1530
1531function! GetOrgIndent()
1532 if g:org_indent == 0
1533 return -1
1534 endif
1535
1536python << EOF
1537from orgmode._vim import indent_orgmode
1538indent_orgmode()
1539EOF
1540 if exists('b:indent_level')
1541 let l:tmp = b:indent_level
1542 unlet b:indent_level
1543 return l:tmp
1544 else
1545 return -1
1546 endif
1547endfunction
1548
1549function! GetOrgFolding()
1550 let l:mode = mode()
1551 if l:mode == 'i'
1552 " the cache size is limited to 3, because vim queries the current and
1553 " both surrounding lines when the user is typing in insert mode. The
1554 " cache is shared between GetOrgFolding and GetOrgFoldtext
1555 if ! exists('b:org_folding_cache')
1556 let b:org_folding_cache = {}
1557 endif
1558
1559 if has_key(b:org_folding_cache, v:lnum)
1560 if match(b:org_folding_cache[v:lnum], '^>') == 0 &&
1561 \ match(getline(v:lnum), '^\*\+\s') != 0
1562 " when the user pastes text or presses enter, it happens that
1563 " the cache starts to confuse vim's folding abilities
1564 " these entries can safely be removed
1565 unlet b:org_folding_cache[v:lnum]
1566
1567 " the fold text cache is probably also damaged, delete it as
1568 " well
1569 unlet! b:org_foldtext_cache
1570 else
1571 return b:org_folding_cache[v:lnum]
1572 endif
1573 endif
1574python << EOF
1575from orgmode._vim import fold_orgmode
1576fold_orgmode(allow_dirty=True)
1577EOF
1578 else
1579python << EOF
1580from orgmode._vim import fold_orgmode
1581fold_orgmode()
1582EOF
1583 endif
1584
1585 if exists('b:fold_expr')
1586 let l:tmp = b:fold_expr
1587 unlet b:fold_expr
1588 if l:mode == 'i'
1589 if ! has_key(b:org_folding_cache, v:lnum)
1590 if len(b:org_folding_cache) > 3
1591 let b:org_folding_cache = {}
1592 endif
1593 let b:org_folding_cache[v:lnum] = l:tmp
1594 endif
1595 endif
1596 return l:tmp
1597 else
1598 return -1
1599 endif
1600endfunction
1601
1602function! SetOrgFoldtext(text)
1603 let b:foldtext = a:text
1604endfunction
1605
1606function! GetOrgFoldtext()
1607 let l:mode = mode()
1608 if l:mode == 'i'
1609 " add a separate cache for fold text
1610 if ! exists('b:org_foldtext_cache') ||
1611 \ ! has_key(b:org_foldtext_cache, 'timestamp') ||
1612 \ b:org_foldtext_cache['timestamp'] > (localtime() + 10)
1613 let b:org_foldtext_cache = {'timestamp': localtime()}
1614 endif
1615
1616 if has_key(b:org_foldtext_cache, v:foldstart)
1617 return b:org_foldtext_cache[v:foldstart]
1618 endif
1619python << EOF
1620from orgmode._vim import fold_text
1621fold_text(allow_dirty=True)
1622EOF
1623 else
1624 unlet! b:org_foldtext_cache
1625python << EOF
1626from orgmode._vim import fold_text
1627fold_text()
1628EOF
1629 endif
1630
1631 if exists('b:foldtext')
1632 let l:tmp = b:foldtext
1633 unlet b:foldtext
1634 if l:mode == 'i'
1635 let b:org_foldtext_cache[v:foldstart] = l:tmp
1636 endif
1637 return l:tmp
1638 endif
1639endfunction
1640ftdetect/org.vim [[[1
16412
1642autocmd BufNewFile,BufRead *.org setfiletype org
1643"autocmd BufNewFile,BufReadPost org:todo* setfiletype orgtodo
1644ftplugin/orgmode/liborgmode/__init__.py [[[1
16451
1646# -*- coding: utf-8 -*-
1647ftplugin/orgmode/liborgmode/agenda.py [[[1
164866
1649# -*- coding: utf-8 -*-
1650
1651u"""
1652 Agenda
1653 ~~~~~~~~~~~~~~~~~~
1654
1655 The agenda is one of the main concepts of orgmode. It allows to
1656 collect TODO items from multiple org documents in an agenda view.
1657
1658 Features:
1659 * filtering
1660 * sorting
1661"""
1662
1663from orgmode.liborgmode.agendafilter import filter_items
1664from orgmode.liborgmode.agendafilter import is_within_week_and_active_todo
1665from orgmode.liborgmode.agendafilter import contains_active_todo
1666from orgmode.liborgmode.agendafilter import contains_active_date
1667
1668
1669class AgendaManager(object):
1670 u"""Simple parsing of Documents to create an agenda."""
1671
1672 def __init__(self):
1673 super(AgendaManager, self).__init__()
1674
1675 def get_todo(self, documents):
1676 u"""
1677 Get the todo agenda for the given documents (list of document).
1678 """
1679 filtered = []
1680 for i, document in enumerate(documents):
1681 # filter and return headings
1682 tmp = filter_items(document.all_headings(), [contains_active_todo])
1683 filtered.extend(tmp)
1684 return sorted(filtered)
1685
1686 def get_next_week_and_active_todo(self, documents):
1687 u"""
1688 Get the agenda for next week for the given documents (list of
1689 document).
1690 """
1691 filtered = []
1692 for i, document in enumerate(documents):
1693 # filter and return headings
1694 tmp = filter_items(
1695 document.all_headings(),
1696 [is_within_week_and_active_todo])
1697 filtered.extend(tmp)
1698 return sorted(filtered)
1699
1700 def get_timestamped_items(self, documents):
1701 u"""
1702 Get all time-stamped items in a time-sorted way for the given
1703 documents (list of document).
1704 """
1705 filtered = []
1706 for i, document in enumerate(documents):
1707 # filter and return headings
1708 tmp = filter_items(
1709 document.all_headings(),
1710 [contains_active_date])
1711 filtered.extend(tmp)
1712 return sorted(filtered)
1713
1714# vim: set noexpandtab:
1715ftplugin/orgmode/liborgmode/agendafilter.py [[[1
171678
1717# -*- coding: utf-8 -*-
1718
1719u"""
1720 agendafilter
1721 ~~~~~~~~~~~~~~~~
1722
1723 AgendaFilter contains all the filters that can be applied to create the
1724 agenda.
1725
1726
1727 All functions except filter_items() in the module are filters. Given a
1728 heading they return if the heading meets the critera of the filter.
1729
1730 The function filter_items() can combine different filters and only returns
1731 the filtered headings.
1732"""
1733
1734from datetime import datetime
1735from datetime import timedelta
1736
1737
1738def filter_items(headings, filters):
1739 u"""
1740 Filter the given headings. Return the list of headings which were not
1741 filtered.
1742
1743 :headings: is an list of headings
1744 :filters: is the list of filters that are to be applied. all function in
1745 this module (except this function) are filters.
1746
1747 You can use it like this:
1748
1749 >>> filtered = filter_items(headings, [contains_active_date,
1750 contains_active_todo])
1751
1752 """
1753 filtered = headings
1754 for f in filters:
1755 filtered = filter(f, filtered)
1756 return filtered
1757
1758
1759def is_within_week(heading):
1760 u"""
1761 Return True if the date in the deading is within a week in the future (or
1762 older.
1763 """
1764 if contains_active_date(heading):
1765 next_week = datetime.today() + timedelta(days=7)
1766 if heading.active_date < next_week:
1767 return True
1768
1769
1770def is_within_week_and_active_todo(heading):
1771 u"""
1772 Return True if heading contains an active TODO and the date is within a
1773 week.
1774 """
1775 return is_within_week(heading) and contains_active_todo(heading)
1776
1777
1778def contains_active_todo(heading):
1779 u"""
1780 Return True if heading contains an active TODO.
1781
1782 FIXME: the todo checking should consider a number of different active todo
1783 states
1784 """
1785 return heading.todo == u"TODO"
1786
1787
1788def contains_active_date(heading):
1789 u"""
1790 Return True if heading contains an active date.
1791 """
1792 return not(heading.active_date is None)
1793
1794# vim: set noexpandtab:
1795ftplugin/orgmode/liborgmode/base.py [[[1
1796163
1797# -*- coding: utf-8 -*-
1798
1799"""
1800 base
1801 ~~~~~~~~~~
1802
1803 Here are some really basic data structures that are used throughout
1804 the liborgmode.
1805"""
1806
1807from UserList import UserList
1808
1809
1810def flatten_list(l):
1811 """TODO"""
1812 res = []
1813 if type(l) in (tuple, list) or isinstance(l, UserList):
1814 for i in l:
1815 if type(i) in (list, tuple) or isinstance(i, UserList):
1816 res.extend(flatten_list(i))
1817 else:
1818 res.append(i)
1819 return res
1820
1821
1822class Direction():
1823 u"""
1824 Direction is used to indicate the direction of certain actions.
1825
1826 Example: it defines the direction headings get parted in.
1827 """
1828 FORWARD = 1
1829 BACKWARD = 2
1830
1831
1832class MultiPurposeList(UserList):
1833 u"""
1834 A Multi Purpose List is a list that calls a user defined hook on
1835 change. The implementation is very basic - the hook is called without any
1836 parameters. Otherwise the Multi Purpose List can be used like any other
1837 list.
1838
1839 The member element "data" can be used to fill the list without causing the
1840 list to be marked dirty. This should only be used during initialization!
1841 """
1842
1843 def __init__(self, initlist=None, on_change=None):
1844 UserList.__init__(self, initlist)
1845 self._on_change = on_change
1846
1847 def _changed(self):
1848 u"""
1849 Call hook
1850 """
1851 if callable(self._on_change):
1852 self._on_change()
1853
1854 def __setitem__(self, i, item):
1855 UserList.__setitem__(self, i, item)
1856 self._changed()
1857
1858 def __delitem__(self, i):
1859 UserList.__delitem__(self, i)
1860 self._changed()
1861
1862 def __setslice__(self, i, j, other):
1863 UserList.__setslice__(self, i, j, other)
1864 self._changed()
1865
1866 def __delslice__(self, i, j):
1867 UserList.__delslice__(self, i, j)
1868 self._changed()
1869
1870 def __getslice__(self, i, j):
1871 # fix UserList - don't return a new list of the same type but just the
1872 # normal list item
1873 i = max(i, 0)
1874 j = max(j, 0)
1875 return self.data[i:j]
1876
1877 def __iadd__(self, other):
1878 res = UserList.__iadd__(self, other)
1879 self._changed()
1880 return res
1881
1882 def __imul__(self, n):
1883 res = UserList.__imul__(self, n)
1884 self._changed()
1885 return res
1886
1887 def append(self, item):
1888 UserList.append(self, item)
1889 self._changed()
1890
1891 def insert(self, i, item):
1892 UserList.insert(self, i, item)
1893 self._changed()
1894
1895 def pop(self, i=-1):
1896 item = self[i]
1897 del self[i]
1898 return item
1899
1900 def remove(self, item):
1901 self.__delitem__(self.index(item))
1902
1903 def reverse(self):
1904 UserList.reverse(self)
1905 self._changed()
1906
1907 def sort(self, *args, **kwds):
1908 UserList.sort(self, *args, **kwds)
1909 self._changed()
1910
1911 def extend(self, other):
1912 UserList.extend(self, other)
1913 self._changed()
1914
1915
1916def get_domobj_range(content=[], position=0, direction=Direction.FORWARD, identify_fun=None):
1917 u"""
1918 Get the start and end line number of the dom obj lines from content.
1919
1920 :content: String to be recognized dom obj
1921 :positon: Line number in content
1922 :direction: Search direction
1923 :identify_fun: A identify function to recognize dom obj(Heading, Checkbox) title string.
1924
1925 :return: Start and end line number for the recognized dom obj.
1926 """
1927 len_cb = len(content)
1928
1929 if position < 0 or position > len_cb:
1930 return (None, None)
1931
1932 tmp_line = position
1933 start = None
1934 end = None
1935
1936 if direction == Direction.FORWARD:
1937 while tmp_line < len_cb:
1938 if identify_fun(content[tmp_line]) is not None:
1939 if start is None:
1940 start = tmp_line
1941 elif end is None:
1942 end = tmp_line - 1
1943 if start is not None and end is not None:
1944 break
1945 tmp_line += 1
1946 else:
1947 while tmp_line >= 0 and tmp_line < len_cb:
1948 if identify_fun(content[tmp_line]) is not None:
1949 if start is None:
1950 start = tmp_line
1951 elif end is None:
1952 end = tmp_line - 1
1953 if start is not None and end is not None:
1954 break
1955 tmp_line -= 1 if start is None else -1
1956
1957 return (start, end)
1958
1959# vim: set noexpandtab:
1960ftplugin/orgmode/liborgmode/checkboxes.py [[[1
1961413
1962# -*- coding: utf-8 -*-
1963
1964"""
1965 checkboxes
1966 ~~~~~~~~~
1967
1968 TODO: explain this :)
1969"""
1970
1971import re
1972from UserList import UserList
1973
1974import vim
1975from orgmode.liborgmode.base import MultiPurposeList, flatten_list
1976from orgmode.liborgmode.orgdate import OrgTimeRange
1977from orgmode.liborgmode.orgdate import get_orgdate
1978from orgmode.liborgmode.dom_obj import DomObj, DomObjList, REGEX_SUBTASK, REGEX_SUBTASK_PERCENT, REGEX_HEADING, REGEX_CHECKBOX
1979
1980
1981class Checkbox(DomObj):
1982 u""" Structural checkbox object """
1983 STATUS_ON = u'[X]'
1984 STATUS_OFF = u'[ ]'
1985 # intermediate status
1986 STATUS_INT = u'[-]'
1987
1988 def __init__(self, level=1, type=u'-', title=u'', status=u'[ ]', body=None):
1989 u"""
1990 :level: Indent level of the checkbox
1991 :type: Type of the checkbox list (-, +, *)
1992 :title: Title of the checkbox
1993 :status: Status of the checkbox ([ ], [X], [-])
1994 :body: Body of the checkbox
1995 """
1996 DomObj.__init__(self, level=level, title=title, body=body)
1997
1998 # heading
1999 self._heading = None
2000
2001 self._children = CheckboxList(obj=self)
2002 self._dirty_checkbox = False
2003 # list type
2004 self._type = u'-'
2005 if type:
2006 self.type = type
2007 # status
2008 self._status = Checkbox.STATUS_OFF
2009 if status:
2010 self.status = status
2011
2012 def __unicode__(self):
2013 return u' ' * self.level + self.type + u' ' + \
2014 (self.status + u' ' if self.status else u'') + self.title
2015
2016 def __str__(self):
2017 return self.__unicode__().encode(u'utf-8')
2018
2019 def __len__(self):
2020 # 1 is for the heading's title
2021 return 1 + len(self.body)
2022
2023 def copy(self, including_children=True, parent=None):
2024 u"""
2025 Create a copy of the current checkbox. The checkbox will be completely
2026 detached and not even belong to a document anymore.
2027
2028 :including_children: If True a copy of all children is create as
2029 well. If False the returned checkbox doesn't
2030 have any children.
2031 :parent: Don't use this parameter. It's set
2032 automatically.
2033 """
2034 checkbox = self.__class__(
2035 level=self.level, title=self.title,
2036 body=self.body[:])
2037 if parent:
2038 parent.children.append(checkbox)
2039 if including_children and self.children:
2040 for item in self.children:
2041 item.copy(
2042 including_children=including_children,
2043 parent=checkbox)
2044 checkbox._orig_start = self._orig_start
2045 checkbox._orig_len = self._orig_len
2046
2047 checkbox._dirty_heading = self.is_dirty_checkbox
2048
2049 return checkbox
2050
2051 @classmethod
2052 def parse_checkbox_from_data(cls, data, heading=None, orig_start=None):
2053 u""" Construct a new checkbox from the provided data
2054
2055 :data: List of lines
2056 :heading: The heading object this checkbox belongs to
2057 :orig_start: The original start of the heading in case it was read
2058 from a document. If orig_start is provided, the
2059 resulting heading will not be marked dirty.
2060
2061 :returns: The newly created checkbox
2062 """
2063 def parse_title(heading_line):
2064 # checkbox is not heading
2065 if REGEX_HEADING.match(heading_line) is not None:
2066 return None
2067 m = REGEX_CHECKBOX.match(heading_line)
2068 if m:
2069 r = m.groupdict()
2070 return (len(r[u'level']), r[u'type'], r[u'status'], r[u'title'])
2071
2072 return None
2073
2074 if not data:
2075 raise ValueError(u'Unable to create checkbox, no data provided.')
2076
2077 # create new checkbox
2078 nc = cls()
2079 nc.level, nc.type, nc.status, nc.title = parse_title(data[0])
2080 nc.body = data[1:]
2081 if orig_start is not None:
2082 nc._dirty_heading = False
2083 nc._dirty_body = False
2084 nc._orig_start = orig_start
2085 nc._orig_len = len(nc)
2086 if heading:
2087 nc._heading = heading
2088
2089 return nc
2090
2091 def update_subtasks(self, total=0, on=0):
2092 if total != 0:
2093 percent = (on * 100) / total
2094 else:
2095 percent = 0
2096
2097 count = "%d/%d" % (on, total)
2098 self.title = REGEX_SUBTASK.sub("[%s]" % (count), self.title)
2099 self.title = REGEX_SUBTASK_PERCENT.sub("[%d%%]" % (percent), self.title)
2100 d = self._heading.document.write_checkbox(self, including_children=False)
2101
2102 @classmethod
2103 def identify_checkbox(cls, line):
2104 u""" Test if a certain line is a checkbox or not.
2105
2106 :line: the line to check
2107
2108 :returns: indent_level
2109 """
2110 # checkbox is not heading
2111 if REGEX_HEADING.match(line) is not None:
2112 return None
2113 m = REGEX_CHECKBOX.match(line)
2114 if m:
2115 r = m.groupdict()
2116 return len(r[u'level'])
2117
2118 return None
2119
2120 @property
2121 def is_dirty(self):
2122 u""" Return True if the heading's body is marked dirty """
2123 return self._dirty_checkbox or self._dirty_body
2124
2125 @property
2126 def is_dirty_checkbox(self):
2127 u""" Return True if the heading is marked dirty """
2128 return self._dirty_checkbox
2129
2130 def get_index_in_parent_list(self):
2131 """ Retrieve the index value of current checkbox in the parents list of
2132 checkboxes. This works also for top level checkboxes.
2133
2134 :returns: Index value or None if heading doesn't have a
2135 parent/document or is not in the list of checkboxes
2136 """
2137 if self.parent:
2138 return super(Checkbox, self).get_index_in_parent_list()
2139 elif self.document:
2140 l = self.get_parent_list()
2141 if l:
2142 return l.index(self)
2143
2144 def get_parent_list(self):
2145 """ Retrieve the parents' list of headings. This works also for top
2146 level headings.
2147
2148 :returns: List of headings or None if heading doesn't have a
2149 parent/document or is not in the list of headings
2150 """
2151 if self.parent:
2152 return super(Checkbox, self).get_parent_list()
2153 elif self.document:
2154 if self in self.document.checkboxes:
2155 return self.document.checkboxes
2156
2157 def set_dirty(self):
2158 u""" Mark the heading and body dirty so that it will be rewritten when
2159 saving the document """
2160 self._dirty_checkbox = True
2161 self._dirty_body = True
2162 if self._document:
2163 self._document.set_dirty_document()
2164
2165 def set_dirty_checkbox(self):
2166 u""" Mark the checkbox dirty so that it will be rewritten when saving the
2167 document """
2168 self._dirty_checkbox = True
2169 if self._document:
2170 self._document.set_dirty_document()
2171
2172 @property
2173 def previous_checkbox(self):
2174 u""" Serialized access to the previous checkbox """
2175 return super(Checkbox, self).previous_item
2176
2177 @property
2178 def next_checkbox(self):
2179 u""" Serialized access to the next checkbox """
2180 return super(Checkbox, self).next_item
2181
2182 @property
2183 def first_checkbox(self):
2184 u""" Access to the first child heading or None if no children exist """
2185 if self.children:
2186 return self.children[0]
2187
2188 @property
2189 def start(self):
2190 u""" Access to the starting line of the checkbox """
2191 if self.document is None:
2192 return self._orig_start
2193
2194 # static computation of start
2195 if not self.document.is_dirty:
2196 return self._orig_start
2197
2198 # dynamic computation of start, really slow!
2199 def compute_start(h):
2200 if h:
2201 return len(h) + compute_start(h.previous_checkbox)
2202 return compute_start(self.previous_checkbox)
2203
2204 def toggle(self):
2205 u""" Toggle status of this checkbox """
2206 if self.status == Checkbox.STATUS_OFF or self.status is None:
2207 self.status = Checkbox.STATUS_ON
2208 else:
2209 self.status = Checkbox.STATUS_OFF
2210 self.set_dirty()
2211
2212 def all_siblings(self):
2213 if not self.parent:
2214 p = self._heading
2215 else:
2216 p = self.parent
2217 if not p.children:
2218 raise StopIteration()
2219
2220 c = p.first_checkbox
2221 while c:
2222 yield c
2223 c = c.next_sibling
2224 raise StopIteration()
2225
2226 def all_children(self):
2227 if not self.children:
2228 raise StopIteration()
2229
2230 c = self.first_checkbox
2231 while c:
2232 yield c
2233 for d in c.all_children():
2234 yield d
2235 c = c.next_sibling
2236
2237 raise StopIteration()
2238
2239 def all_siblings_status(self):
2240 u""" Return checkboxes status for currnet checkbox's all siblings
2241
2242 :return: (total, on)
2243 total: total # of checkboxes
2244 on: # of checkboxes which are on
2245 """
2246 total, on = 0, 0
2247 for c in self.all_siblings():
2248 if c.status is not None:
2249 total += 1
2250
2251 if c.status == Checkbox.STATUS_ON:
2252 on += 1
2253
2254 return (total, on)
2255
2256 def are_children_all(self, status):
2257 u""" Check all children checkboxes status """
2258 clen = len(self.children)
2259 for i in range(clen):
2260 if self.children[i].status != status:
2261 return False
2262 # recursively check children's status
2263 if not self.children[i].are_children_all(status):
2264 return False
2265
2266 return True
2267
2268 def is_child_one(self, status):
2269 u""" Return true, if there is one child with given status """
2270 clen = len(self.children)
2271 for i in range(clen):
2272 if self.children[i].status == status:
2273 return True
2274
2275 return False
2276
2277 def are_siblings_all(self, status):
2278 u""" Check all sibling checkboxes status """
2279 for c in self.all_siblings():
2280 if c.status != status:
2281 return False
2282
2283 return True
2284
2285 def level():
2286 u""" Access to the checkbox indent level """
2287 def fget(self):
2288 return self._level
2289
2290 def fset(self, value):
2291 self._level = int(value)
2292 self.set_dirty_checkbox()
2293
2294 def fdel(self):
2295 self.level = None
2296
2297 return locals()
2298 level = property(**level())
2299
2300 def title():
2301 u""" Title of current checkbox """
2302 def fget(self):
2303 return self._title.strip()
2304
2305 def fset(self, value):
2306 if type(value) not in (unicode, str):
2307 raise ValueError(u'Title must be a string.')
2308 v = value
2309 if type(v) == str:
2310 v = v.decode(u'utf-8')
2311 self._title = v.strip()
2312 self.set_dirty_checkbox()
2313
2314 def fdel(self):
2315 self.title = u''
2316
2317 return locals()
2318 title = property(**title())
2319
2320 def status():
2321 u""" status of current checkbox """
2322 def fget(self):
2323 return self._status
2324
2325 def fset(self, value):
2326 self._status = value
2327 self.set_dirty()
2328
2329 def fdel(self):
2330 self._status = u''
2331
2332 return locals()
2333 status = property(**status())
2334
2335 def type():
2336 u""" type of current checkbox list type """
2337 def fget(self):
2338 return self._type
2339
2340 def fset(self, value):
2341 self._type = value
2342
2343 def fdel(self):
2344 self._type = u''
2345
2346 return locals()
2347 type = property(**type())
2348
2349
2350class CheckboxList(DomObjList):
2351 u"""
2352 Checkbox List
2353 """
2354 def __init__(self, initlist=None, obj=None):
2355 """
2356 :initlist: Initial data
2357 :obj: Link to a concrete Checkbox or Document object
2358 """
2359 # it's not necessary to register a on_change hook because the heading
2360 # list will itself take care of marking headings dirty or adding
2361 # headings to the deleted headings list
2362 DomObjList.__init__(self, initlist, obj)
2363
2364 @classmethod
2365 def is_checkbox(cls, obj):
2366 return CheckboxList.is_domobj(obj)
2367
2368 def _get_heading(self):
2369 if self.__class__.is_checkbox(self._obj):
2370 return self._obj._document
2371 return self._obj
2372
2373
2374# vim: set noexpandtab:
2375ftplugin/orgmode/liborgmode/documents.py [[[1
2376291
2377# -*- coding: utf-8 -*-
2378
2379"""
2380 documents
2381 ~~~~~~~~~
2382
2383 TODO: explain this :)
2384"""
2385
2386from UserList import UserList
2387
2388from orgmode.liborgmode.base import MultiPurposeList, flatten_list, Direction, get_domobj_range
2389from orgmode.liborgmode.headings import Heading, HeadingList
2390
2391
2392class Document(object):
2393 u"""
2394 Representation of a whole org-mode document.
2395
2396 A Document consists basically of headings (see Headings) and some metadata.
2397
2398 TODO: explain the 'dirty' mechanism
2399 """
2400
2401 def __init__(self):
2402 u"""
2403 Don't call this constructor directly but use one of the concrete
2404 implementations.
2405
2406 TODO: what are the concrete implementatiions?
2407 """
2408 object.__init__(self)
2409
2410 # is a list - only the Document methods should work on this list!
2411 self._content = None
2412 self._dirty_meta_information = False
2413 self._dirty_document = False
2414 self._meta_information = MultiPurposeList(on_change=self.set_dirty_meta_information)
2415 self._orig_meta_information_len = None
2416 self._headings = HeadingList(obj=self)
2417 self._deleted_headings = []
2418
2419 # settings needed to align tags properly
2420 self._tabstop = 8
2421 self._tag_column = 77
2422
2423 self.todo_states = [u'TODO', u'DONE']
2424
2425 def __unicode__(self):
2426 if self.meta_information is None:
2427 return u'\n'.join(self.all_headings())
2428 return u'\n'.join(self.meta_information) + u'\n' + u'\n'.join([u'\n'.join([unicode(i)] + i.body) for i in self.all_headings()])
2429
2430 def __str__(self):
2431 return self.__unicode__().encode(u'utf-8')
2432
2433 def get_all_todo_states(self):
2434 u""" Convenience function that returns all todo and done states and
2435 sequences in one big list.
2436
2437 :returns: [all todo/done states]
2438 """
2439 return flatten_list(self.get_todo_states())
2440
2441 def get_todo_states(self):
2442 u""" Returns a list containing a tuple of two lists of allowed todo
2443 states split by todo and done states. Multiple todo-done state
2444 sequences can be defined.
2445
2446 :returns: [([todo states], [done states]), ..]
2447 """
2448 return self.todo_states
2449
2450 def tabstop():
2451 u""" Tabstop for this document """
2452 def fget(self):
2453 return self._tabstop
2454
2455 def fset(self, value):
2456 self._tabstop = value
2457
2458 return locals()
2459 tabstop = property(**tabstop())
2460
2461 def tag_column():
2462 u""" The column all tags are right-aligned to """
2463 def fget(self):
2464 return self._tag_column
2465
2466 def fset(self, value):
2467 self._tag_column = value
2468
2469 return locals()
2470 tag_column = property(**tag_column())
2471
2472 def init_dom(self, heading=Heading):
2473 u""" Initialize all headings in document - build DOM. This method
2474 should be call prior to accessing the document.
2475
2476 :returns: self
2477 """
2478 def init_heading(_h):
2479 u"""
2480 :returns the initialized heading
2481 """
2482 start = _h.end + 1
2483 prev_heading = None
2484 while True:
2485 new_heading = self.find_heading(start, heading=heading)
2486
2487 # * Heading 1 <- heading
2488 # * Heading 1 <- sibling
2489 # or
2490 # * Heading 2 <- heading
2491 # * Heading 1 <- parent's sibling
2492 if not new_heading or \
2493 new_heading.level <= _h.level:
2494 break
2495
2496 # * Heading 1 <- heading
2497 # * Heading 2 <- first child
2498 # * Heading 2 <- another child
2499 new_heading._parent = _h
2500 if prev_heading:
2501 prev_heading._next_sibling = new_heading
2502 new_heading._previous_sibling = prev_heading
2503 _h.children.data.append(new_heading)
2504 # the start and end computation is only
2505 # possible when the new heading was properly
2506 # added to the document structure
2507 init_heading(new_heading)
2508 if new_heading.children:
2509 # skip children
2510 start = new_heading.end_of_last_child + 1
2511 else:
2512 start = new_heading.end + 1
2513 prev_heading = new_heading
2514
2515 return _h
2516
2517 h = self.find_heading(heading=heading)
2518 # initialize meta information
2519 if h:
2520 self._meta_information.data.extend(self._content[:h._orig_start])
2521 else:
2522 self._meta_information.data.extend(self._content[:])
2523 self._orig_meta_information_len = len(self.meta_information)
2524
2525 # initialize dom tree
2526 prev_h = None
2527 while h:
2528 if prev_h:
2529 prev_h._next_sibling = h
2530 h._previous_sibling = prev_h
2531 self.headings.data.append(h)
2532 init_heading(h)
2533 prev_h = h
2534 h = self.find_heading(h.end_of_last_child + 1, heading=heading)
2535
2536 return self
2537
2538 def meta_information():
2539 u"""
2540 Meta information is text that precedes all headings in an org-mode
2541 document. It might contain additional information about the document,
2542 e.g. author
2543 """
2544 def fget(self):
2545 return self._meta_information
2546
2547 def fset(self, value):
2548 if self._orig_meta_information_len is None:
2549 self._orig_meta_information_len = len(self.meta_information)
2550 if type(value) in (list, tuple) or isinstance(value, UserList):
2551 self._meta_information[:] = flatten_list(value)
2552 elif type(value) in (str, ):
2553 self._meta_information[:] = value.decode(u'utf-8').split(u'\n')
2554 elif type(value) in (unicode, ):
2555 self._meta_information[:] = value.split(u'\n')
2556 self.set_dirty_meta_information()
2557
2558 def fdel(self):
2559 self.meta_information = u''
2560
2561 return locals()
2562 meta_information = property(**meta_information())
2563
2564 def headings():
2565 u""" List of top level headings """
2566 def fget(self):
2567 return self._headings
2568
2569 def fset(self, value):
2570 self._headings[:] = value
2571
2572 def fdel(self):
2573 del self.headings[:]
2574
2575 return locals()
2576 headings = property(**headings())
2577
2578 def write(self):
2579 u""" write the document
2580
2581 :returns: True if something was written, otherwise False
2582 """
2583 raise NotImplementedError(u'Abstract method, please use concrete impelementation!')
2584
2585 def set_dirty_meta_information(self):
2586 u""" Mark the meta information dirty so that it will be rewritten when
2587 saving the document """
2588 self._dirty_meta_information = True
2589
2590 def set_dirty_document(self):
2591 u""" Mark the whole document dirty. When changing a heading this
2592 method must be executed in order to changed computation of start and
2593 end positions from a static to a dynamic computation """
2594 self._dirty_document = True
2595
2596 @property
2597 def is_dirty(self):
2598 u"""
2599 Return information about unsaved changes for the document and all
2600 related headings.
2601
2602 :returns: Return True if document contains unsaved changes.
2603 """
2604 if self.is_dirty_meta_information:
2605 return True
2606
2607 if self.is_dirty_document:
2608 return True
2609
2610 if self._deleted_headings:
2611 return True
2612
2613 return False
2614
2615 @property
2616 def is_dirty_meta_information(self):
2617 u""" Return True if the meta information is marked dirty """
2618 return self._dirty_meta_information
2619
2620 @property
2621 def is_dirty_document(self):
2622 u""" Return True if the document is marked dirty """
2623 return self._dirty_document
2624
2625 def all_headings(self):
2626 u""" Iterate over all headings of the current document in serialized
2627 order
2628
2629 :returns: Returns an iterator object which returns all headings of
2630 the current file in serialized order
2631 """
2632 if not self.headings:
2633 raise StopIteration()
2634
2635 h = self.headings[0]
2636 while h:
2637 yield h
2638 h = h.next_heading
2639 raise StopIteration()
2640
2641 def find_heading(
2642 self, position=0, direction=Direction.FORWARD,
2643 heading=Heading, connect_with_document=True):
2644 u""" Find heading in the given direction
2645
2646 :postition: starting line, counting from 0 (in vim you start
2647 counting from 1, don't forget)
2648 :direction: downwards == Direction.FORWARD,
2649 upwards == Direction.BACKWARD
2650 :heading: Heading class from which new heading objects will be
2651 instanciated
2652 :connect_with_document: if True, the newly created heading will be
2653 connected with the document, otherwise not
2654
2655 :returns: New heading object or None
2656 """
2657 (start, end) = get_domobj_range(content=self._content, position=position, direction=direction, identify_fun=heading.identify_heading)
2658
2659 if start is not None and end is None:
2660 end = len(self._content) - 1
2661 if start is not None and end is not None:
2662 return heading.parse_heading_from_data(
2663 self._content[start:end + 1], self.get_all_todo_states(),
2664 document=self if connect_with_document else None, orig_start=start)
2665
2666
2667# vim: set noexpandtab:
2668ftplugin/orgmode/liborgmode/dom_obj.py [[[1
2669486
2670# -*- coding: utf-8 -*-
2671
2672"""
2673 dom object
2674 ~~~~~~~~~~
2675
2676 TODO: explain this :)
2677"""
2678
2679import re
2680from UserList import UserList
2681from orgmode.liborgmode.base import MultiPurposeList, flatten_list
2682
2683# breaking down tasks regex
2684REGEX_SUBTASK = re.compile(r'\[(\d*)/(\d*)\]')
2685REGEX_SUBTASK_PERCENT = re.compile(r'\[(\d*)%\]')
2686
2687# heading regex
2688REGEX_HEADING = re.compile(
2689 r'^(?P<level>\*+)(\s+(?P<title>.*?))?\s*(\s(?P<tags>:[\w_:@]+:))?$',
2690 flags=re.U | re.L)
2691REGEX_TAG = re.compile(
2692 r'^\s*((?P<title>[^\s]*?)\s+)?(?P<tags>:[\w_:@]+:)$',
2693 flags=re.U | re.L)
2694REGEX_TODO = re.compile(r'^[^\s]*$')
2695
2696# checkbox regex:
2697# - [ ] checkbox item
2698# - [X] checkbox item
2699# - [ ]
2700# - no status checkbox
2701UnOrderListType = [u'-', u'+', u'*']
2702OrderListType = [u'.', u')']
2703REGEX_CHECKBOX = re.compile(
2704 r'^(?P<level>\s*)(?P<type>[%s]|([a-zA-Z]|[\d]+)[%s])(\s+(?P<status>\[.\]))?\s*(?P<title>.*)$'
2705 % (''.join(UnOrderListType), ''.join(OrderListType)), flags=re.U | re.L)
2706
2707
2708class DomObj(object):
2709 u"""
2710 A DomObj is DOM structure element, like Heading and Checkbox.
2711 Its purpose is to abstract the same parts of Heading and Checkbox objects,
2712 and make code reusable.
2713
2714 All methods and properties are extracted from Heading object.
2715 Heading and Checkbox objects inherit from DomObj, and override some specific
2716 methods in their own objects.
2717
2718 Normally, we don't intend to use DomObj directly. However, we can add some more
2719 DOM structure element based on this class to make code more concise.
2720 """
2721
2722 def __init__(self, level=1, title=u'', body=None):
2723 u"""
2724 :level: Level of the dom object
2725 :title: Title of the dom object
2726 :body: Body of the dom object
2727 """
2728 object.__init__(self)
2729
2730 self._document = None
2731 self._parent = None
2732 self._previous_sibling = None
2733 self._next_sibling = None
2734 self._children = MultiPurposeList()
2735 self._orig_start = None
2736 self._orig_len = 0
2737
2738 self._level = level
2739 # title
2740 self._title = u''
2741 if title:
2742 self.title = title
2743
2744 # body
2745 self._dirty_body = False
2746 self._body = MultiPurposeList(on_change=self.set_dirty_body)
2747 if body:
2748 self.body = body
2749
2750 def __unicode__(self):
2751 return u'<dom obj level=%s, title=%s>' % (level, title)
2752
2753 def __str__(self):
2754 return self.__unicode__().encode(u'utf-8')
2755
2756 def __len__(self):
2757 # 1 is for the heading's title
2758 return 1 + len(self.body)
2759
2760 @property
2761 def is_dirty(self):
2762 u""" Return True if the dom obj body is marked dirty """
2763 return self._dirty_body
2764
2765 @property
2766 def is_dirty_body(self):
2767 u""" Return True if the dom obj body is marked dirty """
2768 return self._dirty_body
2769
2770 def get_index_in_parent_list(self):
2771 """ Retrieve the index value of current dom obj in the parents list of
2772 dom objs. This works also for top level dom objs.
2773
2774 :returns: Index value or None if dom obj doesn't have a
2775 parent/document or is not in the list of dom objs
2776 """
2777 l = self.get_parent_list()
2778 if l:
2779 return l.index(self)
2780
2781 def get_parent_list(self):
2782 """ Retrieve the parents list of dom objs. This works also for top
2783 level dom objs.
2784
2785 :returns: List of dom objs or None if dom objs doesn't have a
2786 parent/document or is not in the list of dom objs
2787 """
2788 if self.parent:
2789 if self in self.parent.children:
2790 return self.parent.children
2791
2792 def set_dirty(self):
2793 u""" Mark the dom objs and body dirty so that it will be rewritten when
2794 saving the document """
2795 if self._document:
2796 self._document.set_dirty_document()
2797
2798 def set_dirty_body(self):
2799 u""" Mark the dom objs' body dirty so that it will be rewritten when
2800 saving the document """
2801 self._dirty_body = True
2802 if self._document:
2803 self._document.set_dirty_document()
2804
2805 @property
2806 def document(self):
2807 u""" Read only access to the document. If you want to change the
2808 document, just assign the dom obj to another document """
2809 return self._document
2810
2811 @property
2812 def parent(self):
2813 u""" Access to the parent dom obj """
2814 return self._parent
2815
2816 @property
2817 def number_of_parents(self):
2818 u""" Access to the number of parent dom objs before reaching the root
2819 document """
2820 def count_parents(h):
2821 if h.parent:
2822 return 1 + count_parents(h.parent)
2823 else:
2824 return 0
2825 return count_parents(self)
2826
2827 @property
2828 def previous_sibling(self):
2829 u""" Access to the previous dom obj that's a sibling of the current one
2830 """
2831 return self._previous_sibling
2832
2833 @property
2834 def next_sibling(self):
2835 u""" Access to the next dom obj that's a sibling of the current one """
2836 return self._next_sibling
2837
2838 @property
2839 def previous_item(self):
2840 u""" Serialized access to the previous dom obj """
2841 if self.previous_sibling:
2842 h = self.previous_sibling
2843 while h.children:
2844 h = h.children[-1]
2845 return h
2846 elif self.parent:
2847 return self.parent
2848
2849 @property
2850 def next_item(self):
2851 u""" Serialized access to the next dom obj """
2852 if self.children:
2853 return self.children[0]
2854 elif self.next_sibling:
2855 return self.next_sibling
2856 else:
2857 h = self.parent
2858 while h:
2859 if h.next_sibling:
2860 return h.next_sibling
2861 else:
2862 h = h.parent
2863
2864 @property
2865 def start(self):
2866 u""" Access to the starting line of the dom obj """
2867 if self.document is None:
2868 return self._orig_start
2869
2870 # static computation of start
2871 if not self.document.is_dirty:
2872 return self._orig_start
2873
2874 # dynamic computation of start, really slow!
2875 def compute_start(h):
2876 if h:
2877 return len(h) + compute_start(h.previous_item)
2878 return compute_start(self.previous_item)
2879
2880 @property
2881 def start_vim(self):
2882 if self.start is not None:
2883 return self.start + 1
2884
2885 @property
2886 def end(self):
2887 u""" Access to the ending line of the dom obj """
2888 if self.start is not None:
2889 return self.start + len(self.body)
2890
2891 @property
2892 def end_vim(self):
2893 if self.end is not None:
2894 return self.end + 1
2895
2896 @property
2897 def end_of_last_child(self):
2898 u""" Access to end of the last child """
2899 if self.children:
2900 child = self.children[-1]
2901 while child.children:
2902 child = child.children[-1]
2903 return child.end
2904 return self.end
2905
2906 @property
2907 def end_of_last_child_vim(self):
2908 return self.end_of_last_child + 1
2909
2910 def children():
2911 u""" Subheadings of the current dom obj """
2912 def fget(self):
2913 return self._children
2914
2915 def fset(self, value):
2916 v = value
2917 if type(v) in (list, tuple) or isinstance(v, UserList):
2918 v = flatten_list(v)
2919 self._children[:] = v
2920
2921 def fdel(self):
2922 del self.children[:]
2923
2924 return locals()
2925 children = property(**children())
2926
2927 @property
2928 def first_child(self):
2929 u""" Access to the first child dom obj or None if no children exist """
2930 if self.children:
2931 return self.children[0]
2932
2933 @property
2934 def last_child(self):
2935 u""" Access to the last child dom obj or None if no children exist """
2936 if self.children:
2937 return self.children[-1]
2938
2939 def level():
2940 u""" Access to the dom obj level """
2941 def fget(self):
2942 return self._level
2943
2944 def fset(self, value):
2945 self._level = int(value)
2946 self.set_dirty()
2947
2948 def fdel(self):
2949 self.level = None
2950
2951 return locals()
2952 level = property(**level())
2953
2954 def title():
2955 u""" Title of current dom object """
2956 def fget(self):
2957 return self._title.strip()
2958
2959 def fset(self, value):
2960 if type(value) not in (unicode, str):
2961 raise ValueError(u'Title must be a string.')
2962 v = value
2963 if type(v) == str:
2964 v = v.decode(u'utf-8')
2965 self._title = v.strip()
2966 self.set_dirty()
2967
2968 def fdel(self):
2969 self.title = u''
2970
2971 return locals()
2972 title = property(**title())
2973
2974 def body():
2975 u""" Holds the content belonging to the heading """
2976 def fget(self):
2977 return self._body
2978
2979 def fset(self, value):
2980 if type(value) in (list, tuple) or isinstance(value, UserList):
2981 self._body[:] = flatten_list(value)
2982 elif type(value) in (str, ):
2983 self._body[:] = value.decode('utf-8').split(u'\n')
2984 elif type(value) in (unicode, ):
2985 self._body[:] = value.split(u'\n')
2986 else:
2987 self.body = list(unicode(value))
2988
2989 def fdel(self):
2990 self.body = []
2991
2992 return locals()
2993 body = property(**body())
2994
2995
2996class DomObjList(MultiPurposeList):
2997 u"""
2998 A Dom Obj List
2999 """
3000 def __init__(self, initlist=None, obj=None):
3001 """
3002 :initlist: Initial data
3003 :obj: Link to a concrete Heading or Document object
3004 """
3005 # it's not necessary to register a on_change hook because the heading
3006 # list will itself take care of marking headings dirty or adding
3007 # headings to the deleted headings list
3008 MultiPurposeList.__init__(self)
3009
3010 self._obj = obj
3011
3012 # initialization must be done here, because
3013 # self._document is not initialized when the
3014 # constructor of MultiPurposeList is called
3015 if initlist:
3016 self.extend(initlist)
3017
3018 @classmethod
3019 def is_domobj(cls, obj):
3020 return isinstance(obj, DomObj)
3021
3022 def _get_document(self):
3023 if self.__class__.is_domobj(self._obj):
3024 return self._obj._document
3025 return self._obj
3026
3027 def __setitem__(self, i, item):
3028 if not self.__class__.is_domobj(item):
3029 raise ValueError(u'Item is not a Dom obj!')
3030 if item in self:
3031 raise ValueError(u'Dom obj is already part of this list!')
3032 # self._add_to_deleted_domobjs(self[i])
3033
3034 # self._associate_domobj(item, \
3035 # self[i - 1] if i - 1 >= 0 else None, \
3036 # self[i + 1] if i + 1 < len(self) else None)
3037 MultiPurposeList.__setitem__(self, i, item)
3038
3039 def __setslice__(self, i, j, other):
3040 o = other
3041 if self.__class__.is_domobj(o):
3042 o = (o, )
3043 o = flatten_list(o)
3044 for item in o:
3045 if not self.__class__.is_domobj(item):
3046 raise ValueError(u'List contains items that are not a Dom obj!')
3047 i = max(i, 0)
3048 j = max(j, 0)
3049 # self._add_to_deleted_domobjs(self[i:j])
3050 # self._associate_domobj(o, \
3051 # self[i - 1] if i - 1 >= 0 and i < len(self) else None, \
3052 # self[j] if j >= 0 and j < len(self) else None)
3053 MultiPurposeList.__setslice__(self, i, j, o)
3054
3055 def __delitem__(self, i, taint=True):
3056 item = self[i]
3057 if item.previous_sibling:
3058 item.previous_sibling._next_sibling = item.next_sibling
3059 if item.next_sibling:
3060 item.next_sibling._previous_sibling = item.previous_sibling
3061
3062 # if taint:
3063 # self._add_to_deleted_domobjs(item)
3064 MultiPurposeList.__delitem__(self, i)
3065
3066 def __delslice__(self, i, j, taint=True):
3067 i = max(i, 0)
3068 j = max(j, 0)
3069 items = self[i:j]
3070 if items:
3071 first = items[0]
3072 last = items[-1]
3073 if first.previous_sibling:
3074 first.previous_sibling._next_sibling = last.next_sibling
3075 if last.next_sibling:
3076 last.next_sibling._previous_sibling = first.previous_sibling
3077 # if taint:
3078 # self._add_to_deleted_domobjs(items)
3079 MultiPurposeList.__delslice__(self, i, j)
3080
3081 def __iadd__(self, other):
3082 o = other
3083 if self.__class__.is_domobj(o):
3084 o = (o, )
3085 for item in flatten_list(o):
3086 if not self.__class__.is_domobj(item):
3087 raise ValueError(u'List contains items that are not a Dom obj!')
3088 # self._associate_domobj(o, self[-1] if len(self) > 0 else None, None)
3089 return MultiPurposeList.__iadd__(self, o)
3090
3091 def __imul__(self, n):
3092 # TODO das müsste eigentlich ein klonen von objekten zur Folge haben
3093 return MultiPurposeList.__imul__(self, n)
3094
3095 def append(self, item, taint=True):
3096 if not self.__class__.is_domobj(item):
3097 raise ValueError(u'Item is not a heading!')
3098 if item in self:
3099 raise ValueError(u'Heading is already part of this list!')
3100 # self._associate_domobj(
3101 # item, self[-1] if len(self) > 0 else None,
3102 # None, taint=taint)
3103 MultiPurposeList.append(self, item)
3104
3105 def insert(self, i, item, taint=True):
3106 # self._associate_domobj(
3107 # item,
3108 # self[i - 1] if i - 1 >= 0 and i - 1 < len(self) else None,
3109 # self[i] if i >= 0 and i < len(self) else None, taint=taint)
3110 MultiPurposeList.insert(self, i, item)
3111
3112 def pop(self, i=-1):
3113 item = self[i]
3114 # self._add_to_deleted_domobjs(item)
3115 del self[i]
3116 return item
3117
3118 def remove_slice(self, i, j, taint=True):
3119 self.__delslice__(i, j, taint=taint)
3120
3121 def remove(self, item, taint=True):
3122 self.__delitem__(self.index(item), taint=taint)
3123
3124 def reverse(self):
3125 MultiPurposeList.reverse(self)
3126 prev_h = None
3127 for h in self:
3128 h._previous_sibling = prev_h
3129 h._next_sibling = None
3130 prev_h._next_sibling = h
3131 h.set_dirty()
3132 prev_h = h
3133
3134 def sort(self, *args, **kwds):
3135 MultiPurposeList.sort(*args, **kwds)
3136 prev_h = None
3137 for h in self:
3138 h._previous_sibling = prev_h
3139 h._next_sibling = None
3140 prev_h._next_sibling = h
3141 h.set_dirty()
3142 prev_h = h
3143
3144 def extend(self, other):
3145 o = other
3146 if self.__class__.is_domobj(o):
3147 o = (o, )
3148 for item in o:
3149 if not self.__class__.is_domobj(item):
3150 raise ValueError(u'List contains items that are not a heading!')
3151 # self._associate_domobj(o, self[-1] if len(self) > 0 else None, None)
3152 MultiPurposeList.extend(self, o)
3153
3154
3155# vim: set noexpandtab:
3156ftplugin/orgmode/liborgmode/headings.py [[[1
3157907
3158# -*- coding: utf-8 -*-
3159
3160"""
3161 headings
3162 ~~~~~~~~~
3163
3164 TODO: explain this :)
3165"""
3166
3167import re
3168from UserList import UserList
3169
3170import vim
3171from orgmode.liborgmode.base import MultiPurposeList, flatten_list, Direction, get_domobj_range
3172from orgmode.liborgmode.orgdate import OrgTimeRange
3173from orgmode.liborgmode.orgdate import get_orgdate
3174from orgmode.liborgmode.checkboxes import Checkbox, CheckboxList
3175from orgmode.liborgmode.dom_obj import DomObj, DomObjList, REGEX_SUBTASK, REGEX_SUBTASK_PERCENT, REGEX_HEADING, REGEX_TAG, REGEX_TODO
3176
3177
3178class Heading(DomObj):
3179 u""" Structural heading object """
3180
3181 def __init__(self, level=1, title=u'', tags=None, todo=None, body=None, active_date=None):
3182 u"""
3183 :level: Level of the heading
3184 :title: Title of the heading
3185 :tags: Tags of the heading
3186 :todo: Todo state of the heading
3187 :body: Body of the heading
3188 :active_date: active date that is used in the agenda
3189 """
3190 DomObj.__init__(self, level=level, title=title, body=body)
3191
3192 self._children = HeadingList(obj=self)
3193 self._dirty_heading = False
3194
3195 # todo
3196 self._todo = None
3197 if todo:
3198 self.todo = todo
3199
3200 # tags
3201 self._tags = MultiPurposeList(on_change=self.set_dirty_heading)
3202 if tags:
3203 self.tags = tags
3204
3205 # active date
3206 self._active_date = active_date
3207 if active_date:
3208 self.active_date = active_date
3209
3210 # checkboxes
3211 self._checkboxes = CheckboxList(obj=self)
3212 self._cached_checkbox = None
3213
3214 def __unicode__(self):
3215 res = u'*' * self.level
3216 if self.todo:
3217 res = u' '.join((res, self.todo))
3218 if self.title:
3219 res = u' '.join((res, self.title))
3220
3221 # compute position of tags
3222 if self.tags:
3223 tabs = 0
3224 spaces = 2
3225 tags = u':%s:' % (u':'.join(self.tags), )
3226
3227 # FIXME this is broken because of missing associations for headings
3228 ts = 6
3229 tag_column = 77
3230 if self.document:
3231 ts = self.document.tabstop
3232 tag_column = self.document.tag_column
3233
3234 len_heading = len(res)
3235 len_tags = len(tags)
3236 if len_heading + spaces + len_tags < tag_column:
3237 spaces_to_next_tabstop = ts - divmod(len_heading, ts)[1]
3238
3239 if len_heading + spaces_to_next_tabstop + len_tags < tag_column:
3240 tabs, spaces = divmod(
3241 tag_column - (len_heading + spaces_to_next_tabstop + len_tags),
3242 ts)
3243
3244 if spaces_to_next_tabstop:
3245 tabs += 1
3246 else:
3247 spaces = tag_column - (len_heading + len_tags)
3248
3249 res += u'\t' * tabs + u' ' * spaces + tags
3250
3251 # append a trailing space when there are just * and no text
3252 if len(res) == self.level:
3253 res += u' '
3254 return res
3255
3256 def __str__(self):
3257 return self.__unicode__().encode(u'utf-8')
3258
3259 def __len__(self):
3260 # 1 is for the heading's title
3261 return 1 + len(self.body)
3262
3263 def __lt__(self, other):
3264 """
3265 Headings can be sorted by date.
3266 """
3267 try:
3268 if self.active_date < other.active_date:
3269 return True
3270 elif self.active_date == other.active_date:
3271 return False
3272 elif self.active_date > other.active_date:
3273 return False
3274 except:
3275 if self.active_date and not other.active_date:
3276 return True
3277 elif not self.active_date and other.active_date:
3278 return False
3279 elif not self.active_date and not other.active:
3280 return False
3281
3282 def __le__(self, other):
3283 """
3284 Headings can be sorted by date.
3285 """
3286 try:
3287 if self.active_date < other.active_date:
3288 return True
3289 elif self.active_date == other.active_date:
3290 return True
3291 elif self.active_date > other.active_date:
3292 return False
3293 except:
3294 if self.active_date and not other.active_date:
3295 return True
3296 elif not self.active_date and other.active_date:
3297 return False
3298 elif not self.active_date and not other.active:
3299 return True
3300
3301 def __ge__(self, other):
3302 """
3303 Headings can be sorted by date.
3304 """
3305 try:
3306 if self.active_date > other.active_date:
3307 return True
3308 elif self.active_date == other.active_date:
3309 return True
3310 elif self.active_date < other.active_date:
3311 return False
3312 except:
3313 if not self.active_date and other.active_date:
3314 return True
3315 elif self.active_date and not other.active_date:
3316 return False
3317 elif not self.active_date and not other.active:
3318 return True
3319
3320 def __gt__(self, other):
3321 """
3322 Headings can be sorted by date.
3323 """
3324 try:
3325 if self.active_date > other.active_date:
3326 return True
3327 elif self.active_date == other.active_date:
3328 return False
3329 elif self.active_date < other.active_date:
3330 return False
3331 except:
3332 if not self.active_date and other.active_date:
3333 return True
3334 elif self.active_date and not other.active_date:
3335 return False
3336 elif not self.active_date and not other.active:
3337 return False
3338
3339 def copy(self, including_children=True, parent=None):
3340 u"""
3341 Create a copy of the current heading. The heading will be completely
3342 detached and not even belong to a document anymore.
3343
3344 :including_children: If True a copy of all children is create as
3345 well. If False the returned heading doesn't
3346 have any children.
3347 :parent: Don't use this parameter. It's set
3348 automatically.
3349 """
3350 heading = self.__class__(
3351 level=self.level, title=self.title,
3352 tags=self.tags, todo=self.todo, body=self.body[:])
3353 if parent:
3354 parent.children.append(heading)
3355 if including_children and self.children:
3356 for item in self.children:
3357 item.copy(
3358 including_children=including_children,
3359 parent=heading)
3360 heading._orig_start = self._orig_start
3361 heading._orig_len = self._orig_len
3362
3363 heading._dirty_heading = self.is_dirty_heading
3364
3365 return heading
3366
3367 def all_checkboxes(self):
3368 u""" Iterate over all checkboxes of the current heading in serialized
3369 order
3370
3371 :returns: Returns an iterator object which returns all checkboxes of
3372 the current heading in serialized order
3373 """
3374 if not self.checkboxes:
3375 raise StopIteration()
3376
3377 c = self.first_checkbox
3378 while c:
3379 yield c
3380 c = c.next_checkbox
3381 raise StopIteration()
3382
3383 def all_toplevel_checkboxes(self):
3384 u""" return all top level checkboxes for current heading """
3385 if not self.checkboxes:
3386 raise StopIteration()
3387
3388 c = self.first_checkbox
3389 while c:
3390 yield c
3391 c = c.next_sibling
3392 raise StopIteration()
3393
3394 def find_checkbox(self, position=0, direction=Direction.FORWARD,
3395 checkbox=Checkbox, connect_with_heading=True):
3396 u""" Find checkbox in the given direction
3397
3398 :postition: starting line, counting from 0 (in vim you start
3399 counting from 1, don't forget)
3400 :direction: downwards == Direction.FORWARD,
3401 upwards == Direction.BACKWARD
3402 :checkbox: Checkbox class from which new checkbox objects will be
3403 instanciated
3404 :connect_with_heading: if True, the newly created checkbox will be
3405 connected with the heading, otherwise not
3406
3407 :returns: New checkbox object or None
3408 """
3409 doc = self.document
3410 (start, end) = get_domobj_range(content=doc._content, position=position, direction=direction, identify_fun=checkbox.identify_checkbox)
3411 # if out of current headinig range, reutrn None
3412 heading_end = self.start + len(self) - 1
3413 if start > heading_end:
3414 return None
3415
3416 if end > heading_end:
3417 end = heading_end
3418
3419 if start is not None and end is None:
3420 end = heading_end
3421 if start is not None and end is not None:
3422 return checkbox.parse_checkbox_from_data(
3423 doc._content[start:end + 1],
3424 heading=self if connect_with_heading else None, orig_start=start)
3425
3426 def init_checkboxes(self, checkbox=Checkbox):
3427 u""" Initialize all checkboxes in current heading - build DOM.
3428
3429 :returns: self
3430 """
3431 def init_checkbox(_c):
3432 u"""
3433 :returns the initialized checkbox
3434 """
3435 start = _c.end + 1
3436 prev_checkbox = None
3437 while True:
3438 new_checkbox = self.find_checkbox(start, checkbox=checkbox)
3439
3440 # * Checkbox 1 <- checkbox
3441 # * Checkbox 1 <- sibling
3442 # or
3443 # * Checkbox 2 <- checkbox
3444 # * Checkbox 1 <- parent's sibling
3445 if not new_checkbox or \
3446 new_checkbox.level <= _c.level:
3447 break
3448
3449 # * Checkbox 1 <- heading
3450 # * Checkbox 2 <- first child
3451 # * Checkbox 2 <- another child
3452 new_checkbox._parent = _c
3453 if prev_checkbox:
3454 prev_checkbox._next_sibling = new_checkbox
3455 new_checkbox._previous_sibling = prev_checkbox
3456 _c.children.data.append(new_checkbox)
3457 # the start and end computation is only
3458 # possible when the new checkbox was properly
3459 # added to the document structure
3460 init_checkbox(new_checkbox)
3461 if new_checkbox.children:
3462 # skip children
3463 start = new_checkbox.end_of_last_child + 1
3464 else:
3465 start = new_checkbox.end + 1
3466 prev_checkbox = new_checkbox
3467
3468 return _c
3469
3470 c = self.find_checkbox(checkbox=checkbox, position=self.start)
3471
3472 # initialize dom tree
3473 prev_c = None
3474 while c:
3475 if prev_c and prev_c.level == c.level:
3476 prev_c._next_sibling = c
3477 c._previous_sibling = prev_c
3478 self.checkboxes.data.append(c)
3479 init_checkbox(c)
3480 prev_c = c
3481 c = self.find_checkbox(c.end_of_last_child + 1, checkbox=checkbox)
3482
3483 return self
3484
3485 def current_checkbox(self, position=None):
3486 u""" Find the current checkbox (search backward) and return the related object
3487 :returns: Checkbox object or None
3488 """
3489 if position is None:
3490 position = vim.current.window.cursor[0] - 1
3491
3492 if not self.checkboxes:
3493 return
3494
3495 def binaryFindInHeading():
3496 hi = len(self.checkboxes)
3497 lo = 0
3498 while lo < hi:
3499 mid = (lo + hi) // 2
3500 c = self.checkboxes[mid]
3501 if c.end_of_last_child < position:
3502 lo = mid + 1
3503 elif c.start > position:
3504 hi = mid
3505 else:
3506 return binaryFindCheckbox(c)
3507
3508 def binaryFindCheckbox(checkbox):
3509 if not checkbox.children or checkbox.end >= position:
3510 return checkbox
3511
3512 hi = len(checkbox.children)
3513 lo = 0
3514 while lo < hi:
3515 mid = (lo + hi) // 2
3516 c = checkbox.children[mid]
3517 if c.end_of_last_child < position:
3518 lo = mid + 1
3519 elif c.start > position:
3520 hi = mid
3521 else:
3522 return binaryFindCheckbox(c)
3523
3524 # look at the cache to find the heading
3525 c_tmp = self._cached_checkbox
3526 if c_tmp is not None:
3527 if c_tmp.end_of_last_child > position and \
3528 c_tmp.start < position:
3529 if c_tmp.end < position:
3530 self._cached_checkbox = binaryFindCheckbox(c_tmp)
3531 return self._cached_checkbox
3532
3533 self._cached_checkbox = binaryFindInHeading()
3534 return self._cached_checkbox
3535
3536 @property
3537 def first_checkbox(self):
3538 u""" Access to the first child checkbox or None if no children exist """
3539 if self.checkboxes:
3540 return self.checkboxes[0]
3541
3542 @classmethod
3543 def parse_heading_from_data(
3544 cls, data, allowed_todo_states, document=None,
3545 orig_start=None):
3546 u""" Construct a new heading from the provided data
3547
3548 :data: List of lines
3549 :allowed_todo_states: TODO???
3550 :document: The document object this heading belongs to
3551 :orig_start: The original start of the heading in case it was read
3552 from a document. If orig_start is provided, the
3553 resulting heading will not be marked dirty.
3554
3555 :returns: The newly created heading
3556 """
3557 test_not_empty = lambda x: x != u''
3558
3559 def parse_title(heading_line):
3560 # WARNING this regular expression fails if there is just one or no
3561 # word in the heading but a tag!
3562 m = REGEX_HEADING.match(heading_line)
3563 if m:
3564 r = m.groupdict()
3565 level = len(r[u'level'])
3566 todo = None
3567 title = u''
3568 tags = filter(test_not_empty, r[u'tags'].split(u':')) if r[u'tags'] else []
3569
3570 # if there is just one or no word in the heading, redo the parsing
3571 mt = REGEX_TAG.match(r[u'title'])
3572 if not tags and mt:
3573 r = mt.groupdict()
3574 tags = filter(test_not_empty, r[u'tags'].split(u':')) if r[u'tags'] else []
3575 if r[u'title'] is not None:
3576 _todo_title = [i.strip() for i in r[u'title'].split(None, 1)]
3577 if _todo_title and _todo_title[0] in allowed_todo_states:
3578 todo = _todo_title[0]
3579 if len(_todo_title) > 1:
3580 title = _todo_title[1]
3581 else:
3582 title = r[u'title'].strip()
3583
3584 return (level, todo, title, tags)
3585 raise ValueError(u'Data doesn\'t start with a heading definition.')
3586
3587 if not data:
3588 raise ValueError(u'Unable to create heading, no data provided.')
3589
3590 # create new heaing
3591 new_heading = cls()
3592 new_heading.level, new_heading.todo, new_heading.title, new_heading.tags = parse_title(data[0])
3593 new_heading.body = data[1:]
3594 if orig_start is not None:
3595 new_heading._dirty_heading = False
3596 new_heading._dirty_body = False
3597 new_heading._orig_start = orig_start
3598 new_heading._orig_len = len(new_heading)
3599 if document:
3600 new_heading._document = document
3601
3602 # try to find active dates
3603 tmp_orgdate = get_orgdate(data)
3604 if tmp_orgdate and tmp_orgdate.active \
3605 and not isinstance(tmp_orgdate, OrgTimeRange):
3606 new_heading.active_date = tmp_orgdate
3607 else:
3608 new_heading.active_date = None
3609
3610 return new_heading
3611
3612 def update_subtasks(self, total=0, on=0):
3613 u""" Update subtask information for current heading
3614 :total: total # of top level checkboxes
3615 :on: # of top level checkboxes which are on
3616 """
3617 if total != 0:
3618 percent = (on * 100) / total
3619 else:
3620 percent = 0
3621
3622 count = "%d/%d" % (on, total)
3623 self.title = REGEX_SUBTASK.sub("[%s]" % (count), self.title)
3624 self.title = REGEX_SUBTASK_PERCENT.sub("[%d%%]" % (percent), self.title)
3625 self.document.write_heading(self, including_children=False)
3626
3627 @classmethod
3628 def identify_heading(cls, line):
3629 u""" Test if a certain line is a heading or not.
3630
3631 :line: the line to check
3632
3633 :returns: level
3634 """
3635 level = 0
3636 if not line:
3637 return None
3638 for i in xrange(0, len(line)):
3639 if line[i] == u'*':
3640 level += 1
3641 if len(line) > (i + 1) and line[i + 1] in (u'\t', u' '):
3642 return level
3643 else:
3644 return None
3645
3646 @property
3647 def is_dirty(self):
3648 u""" Return True if the heading's body is marked dirty """
3649 return self._dirty_heading or self._dirty_body
3650
3651 @property
3652 def is_dirty_heading(self):
3653 u""" Return True if the heading is marked dirty """
3654 return self._dirty_heading
3655
3656 def get_index_in_parent_list(self):
3657 """ Retrieve the index value of current heading in the parents list of
3658 headings. This works also for top level headings.
3659
3660 :returns: Index value or None if heading doesn't have a
3661 parent/document or is not in the list of headings
3662 """
3663 if self.parent:
3664 return super(Heading, self).get_index_in_parent_list()
3665 elif self.document:
3666 l = self.get_parent_list()
3667 if l:
3668 return l.index(self)
3669
3670 def get_parent_list(self):
3671 """ Retrieve the parents' list of headings. This works also for top
3672 level headings.
3673
3674 :returns: List of headings or None if heading doesn't have a
3675 parent/document or is not in the list of headings
3676 """
3677 if self.parent:
3678 return super(Heading, self).get_parent_list()
3679 elif self.document:
3680 if self in self.document.headings:
3681 return self.document.headings
3682
3683 def set_dirty(self):
3684 u""" Mark the heading and body dirty so that it will be rewritten when
3685 saving the document """
3686 self._dirty_heading = True
3687 self._dirty_body = True
3688 if self._document:
3689 self._document.set_dirty_document()
3690
3691 def set_dirty_heading(self):
3692 u""" Mark the heading dirty so that it will be rewritten when saving the
3693 document """
3694 self._dirty_heading = True
3695 if self._document:
3696 self._document.set_dirty_document()
3697
3698 @property
3699 def previous_heading(self):
3700 u""" Serialized access to the previous heading """
3701 return super(Heading, self).previous_item
3702
3703 @property
3704 def next_heading(self):
3705 u""" Serialized access to the next heading """
3706 return super(Heading, self).next_item
3707
3708 @property
3709 def start(self):
3710 u""" Access to the starting line of the heading """
3711 if self.document is None:
3712 return self._orig_start
3713
3714 # static computation of start
3715 if not self.document.is_dirty:
3716 return self._orig_start
3717
3718 # dynamic computation of start, really slow!
3719 def compute_start(h):
3720 if h:
3721 return len(h) + compute_start(h.previous_heading)
3722 return len(self.document.meta_information) if \
3723 self.document.meta_information else 0
3724 return compute_start(self.previous_heading)
3725
3726 def level():
3727 u""" Access to the heading level """
3728 def fget(self):
3729 return self._level
3730
3731 def fset(self, value):
3732 self._level = int(value)
3733 self.set_dirty_heading()
3734
3735 def fdel(self):
3736 self.level = None
3737
3738 return locals()
3739 level = property(**level())
3740
3741 def todo():
3742 u""" Todo state of current heading. When todo state is set, it will be
3743 converted to uppercase """
3744 def fget(self):
3745 # extract todo state from heading
3746 return self._todo
3747
3748 def fset(self, value):
3749 # update todo state
3750 if type(value) not in (unicode, str, type(None)):
3751 raise ValueError(u'Todo state must be a string or None.')
3752 if value and not REGEX_TODO.match(value):
3753 raise ValueError(u'Found non allowed character in todo state! %s' % value)
3754 if not value:
3755 self._todo = None
3756 else:
3757 v = value
3758 if type(v) == str:
3759 v = v.decode(u'utf-8')
3760 self._todo = v.upper()
3761 self.set_dirty_heading()
3762
3763 def fdel(self):
3764 self.todo = None
3765
3766 return locals()
3767 todo = property(**todo())
3768
3769 def active_date():
3770 u"""
3771 active date of the hearing.
3772
3773 active dates are used in the agenda view. they can be part of the
3774 heading and/or the body.
3775 """
3776 def fget(self):
3777 return self._active_date
3778
3779 def fset(self, value):
3780 self._active_date = value
3781
3782 def fdel(self):
3783 self._active_date = None
3784 return locals()
3785 active_date = property(**active_date())
3786
3787 def title():
3788 u""" Title of current heading """
3789 def fget(self):
3790 return self._title.strip()
3791
3792 def fset(self, value):
3793 if type(value) not in (unicode, str):
3794 raise ValueError(u'Title must be a string.')
3795 v = value
3796 if type(v) == str:
3797 v = v.decode(u'utf-8')
3798 self._title = v.strip()
3799 self.set_dirty_heading()
3800
3801 def fdel(self):
3802 self.title = u''
3803
3804 return locals()
3805 title = property(**title())
3806
3807 def tags():
3808 u""" Tags of the current heading """
3809 def fget(self):
3810 return self._tags
3811
3812 def fset(self, value):
3813 v = value
3814 if type(v) in (unicode, str):
3815 v = list(unicode(v))
3816 if type(v) not in (list, tuple) and not isinstance(v, UserList):
3817 v = list(unicode(v))
3818 v = flatten_list(v)
3819 v_decoded = []
3820 for i in v:
3821 if type(i) not in (unicode, str):
3822 raise ValueError(u'Found non string value in tags! %s' % unicode(i))
3823 if u':' in i:
3824 raise ValueError(u'Found non allowed character in tag! %s' % i)
3825 i_tmp = i.strip().replace(' ', '_').replace('\t', '_')
3826 if type(i) == str:
3827 i_tmp = i.decode(u'utf-8')
3828 v_decoded.append(i_tmp)
3829
3830 self._tags[:] = v_decoded
3831
3832 def fdel(self):
3833 self.tags = []
3834
3835 return locals()
3836 tags = property(**tags())
3837
3838 def checkboxes():
3839 u""" All checkboxes in current heading """
3840 def fget(self):
3841 return self._checkboxes
3842
3843 def fset(self, value):
3844 self._checkboxes[:] = value
3845
3846 def fdel(self):
3847 del self.checkboxes[:]
3848
3849 return locals()
3850 checkboxes = property(**checkboxes())
3851
3852
3853class HeadingList(DomObjList):
3854 u"""
3855 A Heading List just contains headings. It's used for documents to store top
3856 level headings and for headings to store subheadings.
3857
3858 A Heading List must be linked to a Document or Heading!
3859
3860 See documenatation of MultiPurposeList for more information.
3861 """
3862 def __init__(self, initlist=None, obj=None):
3863 """
3864 :initlist: Initial data
3865 :obj: Link to a concrete Heading or Document object
3866 """
3867 # it's not necessary to register a on_change hook because the heading
3868 # list will itself take care of marking headings dirty or adding
3869 # headings to the deleted headings list
3870 DomObjList.__init__(self, initlist, obj)
3871
3872 @classmethod
3873 def is_heading(cls, obj):
3874 return HeadingList.is_domobj(obj)
3875
3876 def _get_document(self):
3877 if self.__class__.is_heading(self._obj):
3878 return self._obj._document
3879 return self._obj
3880
3881 def _add_to_deleted_headings(self, item):
3882 u"""
3883 Serialize headings so that all subheadings are also marked for deletion
3884 """
3885 if not self._get_document():
3886 # HeadingList has not yet been associated
3887 return
3888
3889 if type(item) in (list, tuple) or isinstance(item, UserList):
3890 for i in flatten_list(item):
3891 self._add_to_deleted_headings(i)
3892 else:
3893 self._get_document()._deleted_headings.append(
3894 item.copy(including_children=False))
3895 self._add_to_deleted_headings(item.children)
3896 self._get_document().set_dirty_document()
3897
3898 def _associate_heading(
3899 self, heading, previous_sibling, next_sibling,
3900 children=False, taint=True):
3901 """
3902 :heading: The heading or list to associate with the current heading
3903 :previous_sibling: The previous sibling of the current heading. If
3904 heading is a list the first heading will be
3905 connected with the previous sibling and the last
3906 heading with the next sibling. The items in between
3907 will be linked with one another.
3908 :next_sibling: The next sibling of the current heading. If
3909 heading is a list the first heading will be
3910 connected with the previous sibling and the last
3911 heading with the next sibling. The items in between
3912 will be linked with one another.
3913 :children: Marks whether children are processed in the current
3914 iteration or not (should not be use, it's set
3915 automatically)
3916 :taint: If not True, the heading is not marked dirty at the end
3917 of the association process and its orig_start and
3918 orig_len values are not updated.
3919 """
3920 # TODO this method should be externalized and moved to the Heading class
3921 if type(heading) in (list, tuple) or isinstance(heading, UserList):
3922 prev = previous_sibling
3923 current = None
3924 for _next in flatten_list(heading):
3925 if current:
3926 self._associate_heading(
3927 current, prev, _next,
3928 children=children, taint=taint)
3929 prev = current
3930 current = _next
3931 if current:
3932 self._associate_heading(
3933 current, prev, next_sibling,
3934 children=children, taint=taint)
3935 else:
3936 if taint:
3937 heading._orig_start = None
3938 heading._orig_len = None
3939 d = self._get_document()
3940 if heading._document != d:
3941 heading._document = d
3942 if not children:
3943 # connect heading with previous and next headings
3944 heading._previous_sibling = previous_sibling
3945 if previous_sibling:
3946 previous_sibling._next_sibling = heading
3947 heading._next_sibling = next_sibling
3948 if next_sibling:
3949 next_sibling._previous_sibling = heading
3950
3951 if d == self._obj:
3952 # self._obj is a Document
3953 heading._parent = None
3954 elif heading._parent != self._obj:
3955 # self._obj is a Heading
3956 heading._parent = self._obj
3957 if taint:
3958 heading.set_dirty()
3959
3960 self._associate_heading(
3961 heading.children, None, None,
3962 children=True, taint=taint)
3963
3964 def __setitem__(self, i, item):
3965 if not self.__class__.is_heading(item):
3966 raise ValueError(u'Item is not a heading!')
3967 if item in self:
3968 raise ValueError(u'Heading is already part of this list!')
3969 self._add_to_deleted_headings(self[i])
3970
3971 self._associate_heading(
3972 item,
3973 self[i - 1] if i - 1 >= 0 else None,
3974 self[i + 1] if i + 1 < len(self) else None)
3975 MultiPurposeList.__setitem__(self, i, item)
3976
3977 def __setslice__(self, i, j, other):
3978 o = other
3979 if self.__class__.is_heading(o):
3980 o = (o, )
3981 o = flatten_list(o)
3982 for item in o:
3983 if not self.__class__.is_heading(item):
3984 raise ValueError(u'List contains items that are not a heading!')
3985 i = max(i, 0)
3986 j = max(j, 0)
3987 self._add_to_deleted_headings(self[i:j])
3988 self._associate_heading(
3989 o,
3990 self[i - 1] if i - 1 >= 0 and i < len(self) else None,
3991 self[j] if j >= 0 and j < len(self) else None)
3992 MultiPurposeList.__setslice__(self, i, j, o)
3993
3994 def __delitem__(self, i, taint=True):
3995 item = self[i]
3996 if item.previous_sibling:
3997 item.previous_sibling._next_sibling = item.next_sibling
3998 if item.next_sibling:
3999 item.next_sibling._previous_sibling = item.previous_sibling
4000
4001 if taint:
4002 self._add_to_deleted_headings(item)
4003 MultiPurposeList.__delitem__(self, i)
4004
4005 def __delslice__(self, i, j, taint=True):
4006 i = max(i, 0)
4007 j = max(j, 0)
4008 items = self[i:j]
4009 if items:
4010 first = items[0]
4011 last = items[-1]
4012 if first.previous_sibling:
4013 first.previous_sibling._next_sibling = last.next_sibling
4014 if last.next_sibling:
4015 last.next_sibling._previous_sibling = first.previous_sibling
4016 if taint:
4017 self._add_to_deleted_headings(items)
4018 MultiPurposeList.__delslice__(self, i, j)
4019
4020 def __iadd__(self, other):
4021 o = other
4022 if self.__class__.is_heading(o):
4023 o = (o, )
4024 for item in flatten_list(o):
4025 if not self.__class__.is_heading(item):
4026 raise ValueError(u'List contains items that are not a heading!')
4027 self._associate_heading(o, self[-1] if len(self) > 0 else None, None)
4028 return MultiPurposeList.__iadd__(self, o)
4029
4030 def append(self, item, taint=True):
4031 if not self.__class__.is_heading(item):
4032 raise ValueError(u'Item is not a heading!')
4033 if item in self:
4034 raise ValueError(u'Heading is already part of this list!')
4035 self._associate_heading(
4036 item, self[-1] if len(self) > 0 else None,
4037 None, taint=taint)
4038 MultiPurposeList.append(self, item)
4039
4040 def insert(self, i, item, taint=True):
4041 self._associate_heading(
4042 item,
4043 self[i - 1] if i - 1 >= 0 and i - 1 < len(self) else None,
4044 self[i] if i >= 0 and i < len(self) else None, taint=taint)
4045 MultiPurposeList.insert(self, i, item)
4046
4047 def pop(self, i=-1):
4048 item = self[i]
4049 self._add_to_deleted_headings(item)
4050 del self[i]
4051 return item
4052
4053 def extend(self, other):
4054 o = other
4055 if self.__class__.is_heading(o):
4056 o = (o, )
4057 for item in o:
4058 if not self.__class__.is_heading(item):
4059 raise ValueError(u'List contains items that are not a heading!')
4060 self._associate_heading(o, self[-1] if len(self) > 0 else None, None)
4061 MultiPurposeList.extend(self, o)
4062
4063
4064# vim: set noexpandtab:
4065ftplugin/orgmode/liborgmode/orgdate.py [[[1
4066286
4067# -*- coding: utf-8 -*-
4068u"""
4069 OrgDate
4070 ~~~~~~~~~~~~~~~~~~
4071
4072 This module contains all date/time/timerange representations that exist in
4073 orgmode.
4074
4075 There exist three different kinds:
4076
4077 * OrgDate: is similar to a date object in python and it looks like
4078 '2011-09-07 Wed'.
4079
4080 * OrgDateTime: is similar to a datetime object in python and looks like
4081 '2011-09-07 Wed 10:30'
4082
4083 * OrgTimeRange: indicates a range of time. It has a start and and end date:
4084 * <2011-09-07 Wed>--<2011-09-08 Fri>
4085 * <2011-09-07 Wed 10:00-13:00>
4086
4087 All OrgTime oblects can be active or inactive.
4088"""
4089
4090import datetime
4091import re
4092
4093# <2011-09-12 Mon>
4094_DATE_REGEX = re.compile(r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>")
4095# [2011-09-12 Mon]
4096_DATE_PASSIVE_REGEX = re.compile(r"\[(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w\]")
4097
4098# <2011-09-12 Mon 10:20>
4099_DATETIME_REGEX = re.compile(
4100 r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d{1,2}):(\d\d)>")
4101# [2011-09-12 Mon 10:20]
4102_DATETIME_PASSIVE_REGEX = re.compile(
4103 r"\[(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d{1,2}):(\d\d)\]")
4104
4105# <2011-09-12 Mon>--<2011-09-13 Tue>
4106_DATERANGE_REGEX = re.compile(
4107 # <2011-09-12 Mon>--
4108 r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>--"
4109 # <2011-09-13 Tue>
4110 "<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>")
4111# <2011-09-12 Mon 10:00>--<2011-09-12 Mon 11:00>
4112_DATETIMERANGE_REGEX = re.compile(
4113 # <2011-09-12 Mon 10:00>--
4114 r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)>--"
4115 # <2011-09-12 Mon 11:00>
4116 "<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)>")
4117# <2011-09-12 Mon 10:00--12:00>
4118_DATETIMERANGE_SAME_DAY_REGEX = re.compile(
4119 r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)-(\d\d):(\d\d)>")
4120
4121
4122def get_orgdate(data):
4123 u"""
4124 Parse the given data (can be a string or list). Return an OrgDate if data
4125 contains a string representation of an OrgDate; otherwise return None.
4126
4127 data can be a string or a list containing strings.
4128 """
4129 if isinstance(data, list):
4130 return _findfirst(_text2orgdate, data)
4131 else:
4132 return _text2orgdate(data)
4133 # if no dates found
4134 return None
4135
4136
4137def _findfirst(f, seq):
4138 u"""
4139 Return first item in sequence seq where f(item) == True.
4140
4141 TODO: this is a general help function and it should be moved somewhere
4142 else; preferably into the standard lib :)
4143 """
4144 for found in (f(item) for item in seq if f(item)):
4145 return found
4146
4147
4148def _text2orgdate(string):
4149 u"""
4150 Transform the given string into an OrgDate.
4151 Return an OrgDate if data contains a string representation of an OrgDate;
4152 otherwise return None.
4153 """
4154 # handle active datetime with same day
4155 result = _DATETIMERANGE_SAME_DAY_REGEX.search(string)
4156 if result:
4157 try:
4158 (syear, smonth, sday, shour, smin, ehour, emin) = \
4159 [int(m) for m in result.groups()]
4160 start = datetime.datetime(syear, smonth, sday, shour, smin)
4161 end = datetime.datetime(syear, smonth, sday, ehour, emin)
4162 return OrgTimeRange(True, start, end)
4163 except Exception:
4164 return None
4165
4166 # handle active datetime
4167 result = _DATETIMERANGE_REGEX.search(string)
4168 if result:
4169 try:
4170 tmp = [int(m) for m in result.groups()]
4171 (syear, smonth, sday, shour, smin, eyear, emonth, eday, ehour, emin) = tmp
4172 start = datetime.datetime(syear, smonth, sday, shour, smin)
4173 end = datetime.datetime(eyear, emonth, eday, ehour, emin)
4174 return OrgTimeRange(True, start, end)
4175 except Exception:
4176 return None
4177
4178 # handle active datetime
4179 result = _DATERANGE_REGEX.search(string)
4180 if result:
4181 try:
4182 tmp = [int(m) for m in result.groups()]
4183 syear, smonth, sday, eyear, emonth, ehour = tmp
4184 start = datetime.date(syear, smonth, sday)
4185 end = datetime.date(eyear, emonth, ehour)
4186 return OrgTimeRange(True, start, end)
4187 except Exception:
4188 return None
4189
4190 # handle active datetime
4191 result = _DATETIME_REGEX.search(string)
4192 if result:
4193 try:
4194 year, month, day, hour, minutes = [int(m) for m in result.groups()]
4195 return OrgDateTime(True, year, month, day, hour, minutes)
4196 except Exception:
4197 return None
4198
4199 # handle passive datetime
4200 result = _DATETIME_PASSIVE_REGEX.search(string)
4201 if result:
4202 try:
4203 year, month, day, hour, minutes = [int(m) for m in result.groups()]
4204 return OrgDateTime(False, year, month, day, hour, minutes)
4205 except Exception:
4206 return None
4207
4208 # handle passive dates
4209 result = _DATE_PASSIVE_REGEX.search(string)
4210 if result:
4211 try:
4212 year, month, day = [int(m) for m in result.groups()]
4213 return OrgDate(False, year, month, day)
4214 except Exception:
4215 return None
4216
4217 # handle active dates
4218 result = _DATE_REGEX.search(string)
4219 if result:
4220 try:
4221 year, month, day = [int(m) for m in result.groups()]
4222 return OrgDate(True, year, month, day)
4223 except Exception:
4224 return None
4225
4226
4227class OrgDate(datetime.date):
4228 u"""
4229 OrgDate represents a normal date like '2011-08-29 Mon'.
4230
4231 OrgDates can be active or inactive.
4232
4233 NOTE: date is immutable. Thats why there needs to be __new__().
4234 See: http://docs.python.org/reference/datamodel.html#object.__new__
4235 """
4236 def __init__(self, active, year, month, day):
4237 self.active = active
4238 pass
4239
4240 def __new__(cls, active, year, month, day):
4241 return datetime.date.__new__(cls, year, month, day)
4242
4243 def __unicode__(self):
4244 u"""
4245 Return a string representation.
4246 """
4247 if self.active:
4248 return self.strftime(u'<%Y-%m-%d %a>')
4249 else:
4250 return self.strftime(u'[%Y-%m-%d %a]')
4251
4252 def __str__(self):
4253 return self.__unicode__().encode(u'utf-8')
4254
4255
4256class OrgDateTime(datetime.datetime):
4257 u"""
4258 OrgDateTime represents a normal date like '2011-08-29 Mon'.
4259
4260 OrgDateTime can be active or inactive.
4261
4262 NOTE: date is immutable. Thats why there needs to be __new__().
4263 See: http://docs.python.org/reference/datamodel.html#object.__new__
4264 """
4265
4266 def __init__(self, active, year, month, day, hour, mins):
4267 self.active = active
4268
4269 def __new__(cls, active, year, month, day, hour, minute):
4270 return datetime.datetime.__new__(cls, year, month, day, hour, minute)
4271
4272 def __unicode__(self):
4273 u"""
4274 Return a string representation.
4275 """
4276 if self.active:
4277 return self.strftime(u'<%Y-%m-%d %a %H:%M>')
4278 else:
4279 return self.strftime(u'[%Y-%m-%d %a %H:%M]')
4280
4281 def __str__(self):
4282 return self.__unicode__().encode(u'utf-8')
4283
4284
4285class OrgTimeRange(object):
4286 u"""
4287 OrgTimeRange objects have a start and an end. Start and ent can be date
4288 or datetime. Start and end have to be the same type.
4289
4290 OrgTimeRange objects look like this:
4291 * <2011-09-07 Wed>--<2011-09-08 Fri>
4292 * <2011-09-07 Wed 20:00>--<2011-09-08 Fri 10:00>
4293 * <2011-09-07 Wed 10:00-13:00>
4294 """
4295
4296 def __init__(self, active, start, end):
4297 u"""
4298 stat and end must be datetime.date or datetime.datetime (both of the
4299 same type).
4300 """
4301 super(OrgTimeRange, self).__init__()
4302 self.start = start
4303 self.end = end
4304 self.active = active
4305
4306 def __unicode__(self):
4307 u"""
4308 Return a string representation.
4309 """
4310 # active
4311 if self.active:
4312 # datetime
4313 if isinstance(self.start, datetime.datetime):
4314 # if start and end are on same the day
4315 if self.start.year == self.end.year and\
4316 self.start.month == self.end.month and\
4317 self.start.day == self.end.day:
4318 return u"<%s-%s>" % (
4319 self.start.strftime(u'%Y-%m-%d %a %H:%M'),
4320 self.end.strftime(u'%H:%M'))
4321 else:
4322 return u"<%s>--<%s>" % (
4323 self.start.strftime(u'%Y-%m-%d %a %H:%M'),
4324 self.end.strftime(u'%Y-%m-%d %a %H:%M'))
4325 # date
4326 if isinstance(self.start, datetime.date):
4327 return u"<%s>--<%s>" % (
4328 self.start.strftime(u'%Y-%m-%d %a'),
4329 self.end.strftime(u'%Y-%m-%d %a'))
4330 # inactive
4331 else:
4332 if isinstance(self.start, datetime.datetime):
4333 # if start and end are on same the day
4334 if self.start.year == self.end.year and\
4335 self.start.month == self.end.month and\
4336 self.start.day == self.end.day:
4337 return u"[%s-%s]" % (
4338 self.start.strftime(u'%Y-%m-%d %a %H:%M'),
4339 self.end.strftime(u'%H:%M'))
4340 else:
4341 return u"[%s]--[%s]" % (
4342 self.start.strftime(u'%Y-%m-%d %a %H:%M'),
4343 self.end.strftime(u'%Y-%m-%d %a %H:%M'))
4344 if isinstance(self.start, datetime.date):
4345 return u"[%s]--[%s]" % (
4346 self.start.strftime(u'%Y-%m-%d %a'),
4347 self.end.strftime(u'%Y-%m-%d %a'))
4348
4349 def __str__(self):
4350 return self.__unicode__().encode(u'utf-8')
4351
4352# vim: set noexpandtab:
4353ftplugin/orgmode/plugins/Agenda.py [[[1
4354270
4355# -*- coding: utf-8 -*-
4356
4357from datetime import date
4358import os
4359import glob
4360
4361import vim
4362
4363from orgmode._vim import ORGMODE, get_bufnumber, get_bufname, echoe
4364from orgmode import settings
4365from orgmode.keybinding import Keybinding, Plug, Command
4366from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu
4367
4368
4369class Agenda(object):
4370 u"""
4371 The Agenda Plugin uses liborgmode.agenda to display the agenda views.
4372
4373 The main task is to format the agenda from liborgmode.agenda.
4374 Also all the mappings: jump from agenda to todo, etc are realized here.
4375 """
4376
4377 def __init__(self):
4378 u""" Initialize plugin """
4379 object.__init__(self)
4380 # menu entries this plugin should create
4381 self.menu = ORGMODE.orgmenu + Submenu(u'Agenda')
4382
4383 # key bindings for this plugin
4384 # key bindings are also registered through the menu so only additional
4385 # bindings should be put in this variable
4386 self.keybindings = []
4387
4388 # commands for this plugin
4389 self.commands = []
4390
4391 @classmethod
4392 def _switch_to(cls, bufname, vim_commands=None):
4393 u"""
4394 Swicht to the buffer with bufname.
4395
4396 A list of vim.commands (if given) gets executed as well.
4397
4398 TODO: this should be extracted and imporved to create an easy to use
4399 way to create buffers/jump to buffers. Otherwise there are going to be
4400 quite a few ways to open buffers in vimorgmode.
4401 """
4402 cmds = [
4403 u'botright split org:%s' % bufname,
4404 u'setlocal buftype=nofile',
4405 u'setlocal modifiable',
4406 u'setlocal nonumber',
4407 # call opendoc() on enter the original todo item
4408 u'nnoremap <silent> <buffer> <CR> :exec "py ORGMODE.plugins[u\'Agenda\'].opendoc()"<CR>',
4409 u'nnoremap <silent> <buffer> <TAB> :exec "py ORGMODE.plugins[u\'Agenda\'].opendoc(switch=True)"<CR>',
4410 u'nnoremap <silent> <buffer> <S-CR> :exec "py ORGMODE.plugins[u\'Agenda\'].opendoc(split=True)"<CR>',
4411 # statusline
4412 u'setlocal statusline=Org\\ %s' % bufname]
4413 if vim_commands:
4414 cmds.extend(vim_commands)
4415 for cmd in cmds:
4416 vim.command(cmd.encode(u'utf-8'))
4417
4418 @classmethod
4419 def _get_agendadocuments(self):
4420 u"""
4421 Return the org documents of the agenda files; return None if no
4422 agenda documents are defined.
4423
4424 TODO: maybe turn this into an decorator?
4425 """
4426 # load org files of agenda
4427 agenda_files = settings.get(u'org_agenda_files', u',')
4428 if not agenda_files or agenda_files == ',':
4429 echoe((
4430 u"No org_agenda_files defined. Use :let "
4431 u"g:org_agenda_files=['~/org/index.org'] to add "
4432 u"files to the agenda view."))
4433 return
4434
4435 # glob for files in agenda_files
4436 resolved_files = []
4437 for f in agenda_files:
4438 f = glob.glob(os.path.join(
4439 os.path.expanduser(os.path.dirname(f)),
4440 os.path.basename(f)))
4441 resolved_files.extend(f)
4442
4443 agenda_files = [os.path.realpath(f) for f in resolved_files]
4444
4445 # load the agenda files into buffers
4446 for agenda_file in agenda_files:
4447 vim.command((u'badd %s' % agenda_file.replace(" ", "\ ")).encode(u'utf-8'))
4448
4449 # determine the buffer nr of the agenda files
4450 agenda_nums = [get_bufnumber(fn) for fn in agenda_files]
4451
4452 # collect all documents of the agenda files and create the agenda
4453 return [ORGMODE.get_document(i) for i in agenda_nums if i is not None]
4454
4455 @classmethod
4456 def opendoc(cls, split=False, switch=False):
4457 u"""
4458 If you are in the agenda view jump to the document the item in the
4459 current line belongs to. cls.line2doc is used for that.
4460
4461 :split: if True, open the document in a new split window.
4462 :switch: if True, switch to another window and open the the document
4463 there.
4464 """
4465 row, _ = vim.current.window.cursor
4466 try:
4467 bufname, bufnr, destrow = cls.line2doc[row]
4468 except:
4469 return
4470
4471 # reload source file if it is not loaded
4472 if get_bufname(bufnr) is None:
4473 vim.command((u'badd %s' % bufname).encode(u'utf-8'))
4474 bufnr = get_bufnumber(bufname)
4475 tmp = cls.line2doc[row]
4476 cls.line2doc[bufnr] = tmp
4477 # delete old endry
4478 del cls.line2doc[row]
4479
4480 if split:
4481 vim.command((u"sbuffer %s" % bufnr).encode(u'utf-8'))
4482 elif switch:
4483 vim.command(u"wincmd w".encode(u'utf-8'))
4484 vim.command((u"buffer %d" % bufnr).encode(u'utf-8'))
4485 else:
4486 vim.command((u"buffer %s" % bufnr).encode(u'utf-8'))
4487 vim.command((u"normal! %dgg <CR>" % (destrow + 1)).encode(u'utf-8'))
4488
4489 @classmethod
4490 def list_next_week(cls):
4491 agenda_documents = cls._get_agendadocuments()
4492 if not agenda_documents:
4493 return
4494 raw_agenda = ORGMODE.agenda_manager.get_next_week_and_active_todo(
4495 agenda_documents)
4496
4497 # create buffer at bottom
4498 cmd = [u'setlocal filetype=orgagenda', ]
4499 cls._switch_to(u'AGENDA', cmd)
4500
4501 # line2doc is a dic with the mapping:
4502 # line in agenda buffer --> source document
4503 # It's easy to jump to the right document this way
4504 cls.line2doc = {}
4505 # format text for agenda
4506 last_date = raw_agenda[0].active_date
4507 final_agenda = [u'Week Agenda:', unicode(last_date)]
4508 for i, h in enumerate(raw_agenda):
4509 # insert date information for every new date (not datetime)
4510 if unicode(h.active_date)[1:11] != unicode(last_date)[1:11]:
4511 today = date.today()
4512 # insert additional "TODAY" string
4513 if h.active_date.year == today.year and \
4514 h.active_date.month == today.month and \
4515 h.active_date.day == today.day:
4516 section = unicode(h.active_date) + u" TODAY"
4517 today_row = len(final_agenda) + 1
4518 else:
4519 section = unicode(h.active_date)
4520 final_agenda.append(section)
4521
4522 # update last_date
4523 last_date = h.active_date
4524
4525 bufname = os.path.basename(vim.buffers[h.document.bufnr].name)
4526 bufname = bufname[:-4] if bufname.endswith(u'.org') else bufname
4527 formated = u" %(bufname)s (%(bufnr)d) %(todo)s %(title)s" % {
4528 'bufname': bufname,
4529 'bufnr': h.document.bufnr,
4530 'todo': h.todo,
4531 'title': h.title
4532 }
4533 final_agenda.append(formated)
4534 cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
4535
4536 # show agenda
4537 vim.current.buffer[:] = [i.encode(u'utf-8') for i in final_agenda]
4538 vim.command(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'.encode(u'utf-8'))
4539 # try to jump to the positon of today
4540 try:
4541 vim.command((u'normal! %sgg<CR>' % today_row).encode(u'utf-8'))
4542 except:
4543 pass
4544
4545 @classmethod
4546 def list_all_todos(cls):
4547 u"""
4548 List all todos in all agenda files in one buffer.
4549 """
4550 agenda_documents = cls._get_agendadocuments()
4551 if not agenda_documents:
4552 return
4553 raw_agenda = ORGMODE.agenda_manager.get_todo(agenda_documents)
4554
4555 cls.line2doc = {}
4556 # create buffer at bottom
4557 cmd = [u'setlocal filetype=orgagenda']
4558 cls._switch_to(u'AGENDA', cmd)
4559
4560 # format text of agenda
4561 final_agenda = []
4562 for i, h in enumerate(raw_agenda):
4563 tmp = u"%s %s" % (h.todo, h.title)
4564 final_agenda.append(tmp)
4565 cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
4566
4567 # show agenda
4568 vim.current.buffer[:] = [i.encode(u'utf-8') for i in final_agenda]
4569 vim.command(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'.encode(u'utf-8'))
4570
4571 @classmethod
4572 def list_timeline(cls):
4573 """
4574 List a timeline of the current buffer to get an overview of the
4575 current file.
4576 """
4577 raw_agenda = ORGMODE.agenda_manager.get_timestamped_items(
4578 [ORGMODE.get_document()])
4579
4580 # create buffer at bottom
4581 cmd = [u'setlocal filetype=orgagenda']
4582 cls._switch_to(u'AGENDA', cmd)
4583
4584 cls.line2doc = {}
4585 # format text of agenda
4586 final_agenda = []
4587 for i, h in enumerate(raw_agenda):
4588 tmp = u"%s %s" % (h.todo, h.title)
4589 final_agenda.append(tmp)
4590 cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
4591
4592 # show agenda
4593 vim.current.buffer[:] = [i.encode(u'utf-8') for i in final_agenda]
4594 vim.command(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'.encode(u'utf-8'))
4595
4596 def register(self):
4597 u"""
4598 Registration of the plugin.
4599
4600 Key bindings and other initialization should be done here.
4601 """
4602 add_cmd_mapping_menu(
4603 self,
4604 name=u"OrgAgendaTodo",
4605 function=u':py ORGMODE.plugins[u"Agenda"].list_all_todos()',
4606 key_mapping=u'<localleader>cat',
4607 menu_desrc=u'Agenda for all TODOs'
4608 )
4609 add_cmd_mapping_menu(
4610 self,
4611 name=u"OrgAgendaWeek",
4612 function=u':py ORGMODE.plugins[u"Agenda"].list_next_week()',
4613 key_mapping=u'<localleader>caa',
4614 menu_desrc=u'Agenda for the week'
4615 )
4616 add_cmd_mapping_menu(
4617 self,
4618 name=u'OrgAgendaTimeline',
4619 function=u':py ORGMODE.plugins[u"Agenda"].list_timeline()',
4620 key_mapping=u'<localleader>caL',
4621 menu_desrc=u'Timeline for this buffer'
4622 )
4623
4624# vim: set noexpandtab:
4625ftplugin/orgmode/plugins/Date.py [[[1
4626306
4627# -*- coding: utf-8 -*-
4628import re
4629from datetime import timedelta, date, datetime
4630
4631import vim
4632
4633from orgmode._vim import ORGMODE, echom, insert_at_cursor, get_user_input
4634from orgmode import settings
4635from orgmode.keybinding import Keybinding, Plug
4636from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu
4637
4638
4639class Date(object):
4640 u"""
4641 Handles all date and timestamp related tasks.
4642
4643 TODO: extend functionality (calendar, repetitions, ranges). See
4644 http://orgmode.org/guide/Dates-and-Times.html#Dates-and-Times
4645 """
4646
4647 date_regex = r"\d\d\d\d-\d\d-\d\d"
4648 datetime_regex = r"[A-Z]\w\w \d\d\d\d-\d\d-\d\d \d\d:\d\d>"
4649
4650 month_mapping = {
4651 u'jan': 1, u'feb': 2, u'mar': 3, u'apr': 4, u'may': 5,
4652 u'jun': 6, u'jul': 7, u'aug': 8, u'sep': 9, u'oct': 10, u'nov': 11,
4653 u'dec': 12}
4654
4655 def __init__(self):
4656 u""" Initialize plugin """
4657 object.__init__(self)
4658 # menu entries this plugin should create
4659 self.menu = ORGMODE.orgmenu + Submenu(u'Dates and Scheduling')
4660
4661 # key bindings for this plugin
4662 # key bindings are also registered through the menu so only additional
4663 # bindings should be put in this variable
4664 self.keybindings = []
4665
4666 # commands for this plugin
4667 self.commands = []
4668
4669 # set speeddating format that is compatible with orgmode
4670 try:
4671 if int(vim.eval(u'exists(":SpeedDatingFormat")'.encode(u'utf-8'))) == 2:
4672 vim.command(u':1SpeedDatingFormat %Y-%m-%d %a'.encode(u'utf-8'))
4673 vim.command(u':1SpeedDatingFormat %Y-%m-%d %a %H:%M'.encode(u'utf-8'))
4674 else:
4675 echom(u'Speeddating plugin not installed. Please install it.')
4676 except:
4677 echom(u'Speeddating plugin not installed. Please install it.')
4678
4679 @classmethod
4680 def _modify_time(cls, startdate, modifier):
4681 u"""Modify the given startdate according to modifier. Return the new
4682 date or datetime.
4683
4684 See http://orgmode.org/manual/The-date_002ftime-prompt.html
4685 """
4686 if modifier is None or modifier == '' or modifier == '.':
4687 return startdate
4688
4689 # rm crap from modifier
4690 modifier = modifier.strip()
4691
4692 # check real date
4693 date_regex = r"(\d\d\d\d)-(\d\d)-(\d\d)"
4694 match = re.search(date_regex, modifier)
4695 if match:
4696 year, month, day = match.groups()
4697 newdate = date(int(year), int(month), int(day))
4698
4699 # check abbreviated date, seperated with '-'
4700 date_regex = u"(\d{1,2})-(\d+)-(\d+)"
4701 match = re.search(date_regex, modifier)
4702 if match:
4703 year, month, day = match.groups()
4704 newdate = date(2000 + int(year), int(month), int(day))
4705
4706 # check abbreviated date, seperated with '/'
4707 # month/day
4708 date_regex = u"(\d{1,2})/(\d{1,2})"
4709 match = re.search(date_regex, modifier)
4710 if match:
4711 month, day = match.groups()
4712 newdate = date(startdate.year, int(month), int(day))
4713 # date should be always in the future
4714 if newdate < startdate:
4715 newdate = date(startdate.year + 1, int(month), int(day))
4716
4717 # check full date, seperated with 'space'
4718 # month day year
4719 # 'sep 12 9' --> 2009 9 12
4720 date_regex = u"(\w\w\w) (\d{1,2}) (\d{1,2})"
4721 match = re.search(date_regex, modifier)
4722 if match:
4723 gr = match.groups()
4724 day = int(gr[1])
4725 month = int(cls.month_mapping[gr[0]])
4726 year = 2000 + int(gr[2])
4727 newdate = date(year, int(month), int(day))
4728
4729 # check days as integers
4730 date_regex = u"^(\d{1,2})$"
4731 match = re.search(date_regex, modifier)
4732 if match:
4733 newday, = match.groups()
4734 newday = int(newday)
4735 if newday > startdate.day:
4736 newdate = date(startdate.year, startdate.month, newday)
4737 else:
4738 # TODO: DIRTY, fix this
4739 # this does NOT cover all edge cases
4740 newdate = startdate + timedelta(days=28)
4741 newdate = date(newdate.year, newdate.month, newday)
4742
4743 # check for full days: Mon, Tue, Wed, Thu, Fri, Sat, Sun
4744 modifier_lc = modifier.lower()
4745 match = re.search(u'mon|tue|wed|thu|fri|sat|sun', modifier_lc)
4746 if match:
4747 weekday_mapping = {
4748 u'mon': 0, u'tue': 1, u'wed': 2, u'thu': 3,
4749 u'fri': 4, u'sat': 5, u'sun': 6}
4750 diff = (weekday_mapping[modifier_lc] - startdate.weekday()) % 7
4751 # use next weeks weekday if current weekday is the same as modifier
4752 if diff == 0:
4753 diff = 7
4754 newdate = startdate + timedelta(days=diff)
4755
4756 # check for days modifier with appended d
4757 match = re.search(u'\+(\d*)d', modifier)
4758 if match:
4759 days = int(match.groups()[0])
4760 newdate = startdate + timedelta(days=days)
4761
4762 # check for days modifier without appended d
4763 match = re.search(u'\+(\d*) |\+(\d*)$', modifier)
4764 if match:
4765 try:
4766 days = int(match.groups()[0])
4767 except:
4768 days = int(match.groups()[1])
4769 newdate = startdate + timedelta(days=days)
4770
4771 # check for week modifier
4772 match = re.search(u'\+(\d+)w', modifier)
4773 if match:
4774 weeks = int(match.groups()[0])
4775 newdate = startdate + timedelta(weeks=weeks)
4776
4777 # check for week modifier
4778 match = re.search(u'\+(\d+)m', modifier)
4779 if match:
4780 months = int(match.groups()[0])
4781 newdate = date(startdate.year, startdate.month + months, startdate.day)
4782
4783 # check for year modifier
4784 match = re.search(u'\+(\d*)y', modifier)
4785 if match:
4786 years = int(match.groups()[0])
4787 newdate = date(startdate.year + years, startdate.month, startdate.day)
4788
4789 # check for month day
4790 match = re.search(
4791 u'(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) (\d{1,2})',
4792 modifier.lower())
4793 if match:
4794 month = cls.month_mapping[match.groups()[0]]
4795 day = int(match.groups()[1])
4796 newdate = date(startdate.year, int(month), int(day))
4797 # date should be always in the future
4798 if newdate < startdate:
4799 newdate = date(startdate.year + 1, int(month), int(day))
4800
4801 # check abbreviated date, seperated with '/'
4802 # month/day/year
4803 date_regex = u"(\d{1,2})/(\d+)/(\d+)"
4804 match = re.search(date_regex, modifier)
4805 if match:
4806 month, day, year = match.groups()
4807 newdate = date(2000 + int(year), int(month), int(day))
4808
4809 # check for month day year
4810 # sep 12 2011
4811 match = re.search(
4812 u'(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) (\d{1,2}) (\d{1,4})',
4813 modifier.lower())
4814 if match:
4815 month = int(cls.month_mapping[match.groups()[0]])
4816 day = int(match.groups()[1])
4817 if len(match.groups()[2]) < 4:
4818 year = 2000 + int(match.groups()[2])
4819 else:
4820 year = int(match.groups()[2])
4821 newdate = date(year, month, day)
4822
4823 # check for time: HH:MM
4824 # '12:45' --> datetime(2006, 06, 13, 12, 45))
4825 match = re.search(u'(\d{1,2}):(\d\d)$', modifier)
4826 if match:
4827 try:
4828 startdate = newdate
4829 except:
4830 pass
4831 return datetime(
4832 startdate.year, startdate.month, startdate.day,
4833 int(match.groups()[0]), int(match.groups()[1]))
4834
4835 try:
4836 return newdate
4837 except:
4838 return startdate
4839
4840 @classmethod
4841 def insert_timestamp(cls, active=True):
4842 u"""
4843 Insert a timestamp at the cursor position.
4844
4845 TODO: show fancy calendar to pick the date from.
4846 TODO: add all modifier of orgmode.
4847 """
4848 today = date.today()
4849 msg = u''.join([
4850 u'Inserting ',
4851 unicode(today.strftime(u'%Y-%m-%d %a'), u'utf-8'),
4852 u' | Modify date'])
4853 modifier = get_user_input(msg)
4854
4855 # abort if the user canceled the input promt
4856 if modifier is None:
4857 return
4858
4859 newdate = cls._modify_time(today, modifier)
4860
4861 # format
4862 if isinstance(newdate, datetime):
4863 newdate = newdate.strftime(
4864 u'%Y-%m-%d %a %H:%M'.encode(u'utf-8')).decode(u'utf-8')
4865 else:
4866 newdate = newdate.strftime(
4867 u'%Y-%m-%d %a'.encode(u'utf-8')).decode(u'utf-8')
4868 timestamp = u'<%s>' % newdate if active else u'[%s]' % newdate
4869
4870 insert_at_cursor(timestamp)
4871
4872 @classmethod
4873 def insert_timestamp_with_calendar(cls, active=True):
4874 u"""
4875 Insert a timestamp at the cursor position.
4876 Show fancy calendar to pick the date from.
4877
4878 TODO: add all modifier of orgmode.
4879 """
4880 if int(vim.eval(u'exists(":CalendarH")'.encode(u'utf-8'))) != 2:
4881 vim.command("echo 'Please install plugin Calendar to enable this function'")
4882 return
4883 vim.command("CalendarH")
4884 # backup calendar_action
4885 calendar_action = vim.eval("g:calendar_action")
4886 vim.command("let g:org_calendar_action_backup = '" + calendar_action + "'")
4887 vim.command("let g:calendar_action = 'CalendarAction'")
4888
4889 timestamp_template = u'<%s>' if active else u'[%s]'
4890 # timestamp template
4891 vim.command("let g:org_timestamp_template = '" + timestamp_template + "'")
4892
4893 def register(self):
4894 u"""
4895 Registration of the plugin.
4896
4897 Key bindings and other initialization should be done here.
4898 """
4899 add_cmd_mapping_menu(
4900 self,
4901 name=u'OrgDateInsertTimestampActiveCmdLine',
4902 key_mapping=u'<localleader>sa',
4903 function=u':py ORGMODE.plugins[u"Date"].insert_timestamp()',
4904 menu_desrc=u'Timest&'
4905 )
4906 add_cmd_mapping_menu(
4907 self,
4908 name=u'OrgDateInsertTimestampInactiveCmdLine',
4909 key_mapping='<localleader>si',
4910 function=u':py ORGMODE.plugins[u"Date"].insert_timestamp(False)',
4911 menu_desrc=u'Timestamp (&inactive)'
4912 )
4913 add_cmd_mapping_menu(
4914 self,
4915 name=u'OrgDateInsertTimestampActiveWithCalendar',
4916 key_mapping=u'<localleader>pa',
4917 function=u':py ORGMODE.plugins[u"Date"].insert_timestamp_with_calendar()',
4918 menu_desrc=u'Timestamp with Calendar'
4919 )
4920 add_cmd_mapping_menu(
4921 self,
4922 name=u'OrgDateInsertTimestampInactiveWithCalendar',
4923 key_mapping=u'<localleader>pi',
4924 function=u':py ORGMODE.plugins[u"Date"].insert_timestamp_with_calendar(False)',
4925 menu_desrc=u'Timestamp with Calendar(inactive)'
4926 )
4927
4928 submenu = self.menu + Submenu(u'Change &Date')
4929 submenu + ActionEntry(u'Day &Earlier', u'<C-x>', u'<C-x>')
4930 submenu + ActionEntry(u'Day &Later', u'<C-a>', u'<C-a>')
4931
4932# vim: set noexpandtab:
4933ftplugin/orgmode/plugins/EditCheckbox.py [[[1
4934291
4935# -*- coding: utf-8 -*-
4936
4937import vim
4938from orgmode._vim import echo, echom, echoe, ORGMODE, apply_count, repeat, insert_at_cursor, indent_orgmode
4939from orgmode.menu import Submenu, Separator, ActionEntry, add_cmd_mapping_menu
4940from orgmode.keybinding import Keybinding, Plug, Command
4941from orgmode.liborgmode.checkboxes import Checkbox
4942from orgmode.liborgmode.dom_obj import OrderListType
4943
4944
4945class EditCheckbox(object):
4946 u"""
4947 Checkbox plugin.
4948 """
4949
4950 def __init__(self):
4951 u""" Initialize plugin """
4952 object.__init__(self)
4953 # menu entries this plugin should create
4954 self.menu = ORGMODE.orgmenu + Submenu(u'Edit Checkbox')
4955
4956 # key bindings for this plugin
4957 # key bindings are also registered through the menu so only additional
4958 # bindings should be put in this variable
4959 self.keybindings = []
4960
4961 # commands for this plugin
4962 self.commands = []
4963
4964 @classmethod
4965 def new_checkbox(cls, below=None):
4966 d = ORGMODE.get_document()
4967 h = d.current_heading()
4968 if h is None:
4969 return
4970 # init checkboxes for current heading
4971 h.init_checkboxes()
4972 c = h.current_checkbox()
4973
4974 nc = Checkbox()
4975 nc._heading = h
4976
4977 # default checkbox level
4978 level = h.level + 1
4979 start = vim.current.window.cursor[0] - 1
4980 # if no checkbox is found, insert at current line with indent level=1
4981 if c is None:
4982 h.checkboxes.append(nc)
4983 else:
4984 l = c.get_parent_list()
4985 idx = c.get_index_in_parent_list()
4986 if l is not None and idx is not None:
4987 l.insert(idx + (1 if below else 0), nc)
4988 # workaround for broken associations, Issue #165
4989 nc._parent = c.parent
4990 if below:
4991 if c.next_sibling:
4992 c.next_sibling._previous_sibling = nc
4993 nc._next_sibling = c.next_sibling
4994 c._next_sibling = nc
4995 nc._previous_sibling = c
4996 else:
4997 if c.previous_sibling:
4998 c.previous_sibling._next_sibling = nc
4999 nc._next_sibling = c
5000 nc._previous_sibling = c.previous_sibling
5001 c._previous_sibling = nc
5002
5003 t = c.type
5004 # increase key for ordered lists
5005 if t[-1] in OrderListType:
5006 try:
5007 num = int(t[:-1]) + (1 if below else -1)
5008 if num < 0:
5009 # don't decrease to numbers below zero
5010 echom(u"Can't decrement further than '0'")
5011 return
5012 t = '%d%s' % (num, t[-1])
5013 except ValueError:
5014 try:
5015 char = ord(t[:-1]) + (1 if below else -1)
5016 if below:
5017 if char == 91:
5018 # stop incrementing at Z (90)
5019 echom(u"Can't increment further than 'Z'")
5020 return
5021 elif char == 123:
5022 # increment from z (122) to A
5023 char = 65
5024 else:
5025 if char == 96:
5026 # stop decrementing at a (97)
5027 echom(u"Can't decrement further than 'a'")
5028 return
5029 elif char == 64:
5030 # decrement from A (65) to z
5031 char = 122
5032 t = u'%s%s' % (chr(char), t[-1])
5033 except ValueError:
5034 pass
5035 nc.type = t
5036 if not c.status:
5037 nc.status = None
5038 level = c.level
5039
5040 if below:
5041 start = c.end_of_last_child
5042 else:
5043 start = c.start
5044 nc.level = level
5045
5046 if below:
5047 start += 1
5048 # vim's buffer behave just opposite to Python's list when inserting a
5049 # new item. The new entry is appended in vim put prepended in Python!
5050 vim.current.buffer[start:start] = [unicode(nc)]
5051
5052 # update checkboxes status
5053 cls.update_checkboxes_status()
5054
5055 vim.command((u'exe "normal %dgg"|startinsert!' % (start + 1, )).encode(u'utf-8'))
5056
5057 @classmethod
5058 def toggle(cls, checkbox=None):
5059 u"""
5060 Toggle the checkbox given in the parameter.
5061 If the checkbox is not given, it will toggle the current checkbox.
5062 """
5063 d = ORGMODE.get_document()
5064 current_heading = d.current_heading()
5065 # init checkboxes for current heading
5066 if current_heading is None:
5067 return
5068 current_heading = current_heading.init_checkboxes()
5069
5070 if checkbox is None:
5071 # get current_checkbox
5072 c = current_heading.current_checkbox()
5073 # no checkbox found
5074 if c is None:
5075 cls.update_checkboxes_status()
5076 return
5077 else:
5078 c = checkbox
5079
5080 if c.status == Checkbox.STATUS_OFF or c.status is None:
5081 # set checkbox status on if all children are on
5082 if not c.children or c.are_children_all(Checkbox.STATUS_ON):
5083 c.toggle()
5084 d.write_checkbox(c)
5085 elif c.status is None:
5086 c.status = Checkbox.STATUS_OFF
5087 d.write_checkbox(c)
5088
5089 elif c.status == Checkbox.STATUS_ON:
5090 if not c.children or c.is_child_one(Checkbox.STATUS_OFF):
5091 c.toggle()
5092 d.write_checkbox(c)
5093
5094 elif c.status == Checkbox.STATUS_INT:
5095 # can't toggle intermediate state directly according to emacs orgmode
5096 pass
5097 # update checkboxes status
5098 cls.update_checkboxes_status()
5099
5100 @classmethod
5101 def _update_subtasks(cls):
5102 d = ORGMODE.get_document()
5103 h = d.current_heading()
5104 # init checkboxes for current heading
5105 h.init_checkboxes()
5106 # update heading subtask info
5107 c = h.first_checkbox
5108 if c is None:
5109 return
5110 total, on = c.all_siblings_status()
5111 h.update_subtasks(total, on)
5112 # update all checkboxes under current heading
5113 cls._update_checkboxes_subtasks(c)
5114
5115 @classmethod
5116 def _update_checkboxes_subtasks(cls, checkbox):
5117 # update checkboxes
5118 for c in checkbox.all_siblings():
5119 if c.children:
5120 total, on = c.first_child.all_siblings_status()
5121 c.update_subtasks(total, on)
5122 cls._update_checkboxes_subtasks(c.first_child)
5123
5124 @classmethod
5125 def update_checkboxes_status(cls):
5126 d = ORGMODE.get_document()
5127 h = d.current_heading()
5128 if h is None:
5129 return
5130 # init checkboxes for current heading
5131 h.init_checkboxes()
5132
5133 cls._update_checkboxes_status(h.first_checkbox)
5134 cls._update_subtasks()
5135
5136 @classmethod
5137 def _update_checkboxes_status(cls, checkbox=None):
5138 u""" helper function for update checkboxes status
5139 :checkbox: The first checkbox of this indent level
5140 :return: The status of the parent checkbox
5141 """
5142 if checkbox is None:
5143 return
5144
5145 status_off, status_on, status_int, total = 0, 0, 0, 0
5146 # update all top level checkboxes' status
5147 for c in checkbox.all_siblings():
5148 current_status = c.status
5149 # if this checkbox is not leaf, its status should determine by all its children
5150 if c.children:
5151 current_status = cls._update_checkboxes_status(c.first_child)
5152
5153 # don't update status if the checkbox has no status
5154 if c.status is None:
5155 current_status = None
5156 # the checkbox needs to have status
5157 else:
5158 total += 1
5159
5160 # count number of status in this checkbox level
5161 if current_status == Checkbox.STATUS_OFF:
5162 status_off += 1
5163 elif current_status == Checkbox.STATUS_ON:
5164 status_on += 1
5165 elif current_status == Checkbox.STATUS_INT:
5166 status_int += 1
5167
5168 # write status if any update
5169 if current_status is not None and c.status != current_status:
5170 c.status = current_status
5171 d = ORGMODE.get_document()
5172 d.write_checkbox(c)
5173
5174 parent_status = Checkbox.STATUS_INT
5175 # all silbing checkboxes are off status
5176 if status_off == total:
5177 parent_status = Checkbox.STATUS_OFF
5178 # all silbing checkboxes are on status
5179 elif status_on == total:
5180 parent_status = Checkbox.STATUS_ON
5181 # one silbing checkbox is on or int status
5182 elif status_on != 0 or status_int != 0:
5183 parent_status = Checkbox.STATUS_INT
5184 # other cases
5185 else:
5186 parent_status = None
5187
5188 return parent_status
5189
5190 def register(self):
5191 u"""
5192 Registration of the plugin.
5193
5194 Key bindings and other initialization should be done here.
5195 """
5196 add_cmd_mapping_menu(
5197 self,
5198 name=u'OrgCheckBoxNewAbove',
5199 function=u':py ORGMODE.plugins[u"EditCheckbox"].new_checkbox()<CR>',
5200 key_mapping=u'<localleader>cN',
5201 menu_desrc=u'New CheckBox Above'
5202 )
5203 add_cmd_mapping_menu(
5204 self,
5205 name=u'OrgCheckBoxNewBelow',
5206 function=u':py ORGMODE.plugins[u"EditCheckbox"].new_checkbox(below=True)<CR>',
5207 key_mapping=u'<localleader>cn',
5208 menu_desrc=u'New CheckBox Below'
5209 )
5210 add_cmd_mapping_menu(
5211 self,
5212 name=u'OrgCheckBoxToggle',
5213 function=u':silent! py ORGMODE.plugins[u"EditCheckbox"].toggle()<CR>',
5214 key_mapping=u'<localleader>cc',
5215 menu_desrc=u'Toggle Checkbox'
5216 )
5217 add_cmd_mapping_menu(
5218 self,
5219 name=u'OrgCheckBoxUpdate',
5220 function=u':silent! py ORGMODE.plugins[u"EditCheckbox"].update_checkboxes_status()<CR>',
5221 key_mapping=u'<localleader>c#',
5222 menu_desrc=u'Update Subtasks'
5223 )
5224
5225# vim: set noexpandtab:
5226ftplugin/orgmode/plugins/EditStructure.py [[[1
5227413
5228# -*- coding: utf-8 -*-
5229
5230import vim
5231
5232from orgmode._vim import ORGMODE, apply_count, repeat, realign_tags
5233from orgmode import settings
5234from orgmode.exceptions import HeadingDomError
5235from orgmode.keybinding import Keybinding, Plug, MODE_INSERT, MODE_NORMAL
5236from orgmode.menu import Submenu, Separator, ActionEntry
5237from orgmode.liborgmode.base import Direction
5238from orgmode.liborgmode.headings import Heading
5239
5240
5241class EditStructure(object):
5242 u""" EditStructure plugin """
5243
5244 def __init__(self):
5245 u""" Initialize plugin """
5246 object.__init__(self)
5247 # menu entries this plugin should create
5248 self.menu = ORGMODE.orgmenu + Submenu(u'&Edit Structure')
5249
5250 # key bindings for this plugin
5251 # key bindings are also registered through the menu so only additional
5252 # bindings should be put in this variable
5253 self.keybindings = []
5254
5255 @classmethod
5256 def new_heading(cls, below=None, insert_mode=False, end_of_last_child=False):
5257 u"""
5258 :below: True, insert heading below current heading, False,
5259 insert heading above current heading, None, special
5260 behavior for insert mode, use the current text as
5261 heading
5262 :insert_mode: True, if action is performed in insert mode
5263 :end_of_last_child: True, insert heading at the end of last child,
5264 otherwise the newly created heading will "take
5265 over" the current heading's children
5266 """
5267 d = ORGMODE.get_document()
5268 current_heading = d.current_heading()
5269 cursor = vim.current.window.cursor[:]
5270 if not current_heading:
5271 # the user is in meta data region
5272 pos = cursor[0] - 1
5273 heading = Heading(title=d.meta_information[pos], body=d.meta_information[pos + 1:])
5274 d.headings.insert(0, heading)
5275 del d.meta_information[pos:]
5276 d.write()
5277 vim.command((u'exe "normal %dgg"|startinsert!' % (heading.start_vim, )).encode(u'utf-8'))
5278 return heading
5279
5280 # check for plain list(checkbox)
5281 current_heading.init_checkboxes()
5282 c = current_heading.current_checkbox()
5283 if c is not None:
5284 ORGMODE.plugins[u"EditCheckbox"].new_checkbox(below)
5285 return
5286
5287 heading = Heading(level=current_heading.level)
5288
5289 # it's weird but this is the behavior of original orgmode
5290 if below is None:
5291 below = cursor[1] != 0 or end_of_last_child
5292
5293 # insert newly created heading
5294 l = current_heading.get_parent_list()
5295 idx = current_heading.get_index_in_parent_list()
5296 if l is not None and idx is not None:
5297 l.insert(idx + (1 if below else 0), heading)
5298 else:
5299 raise HeadingDomError(u'Current heading is not properly linked in DOM')
5300
5301 if below and not end_of_last_child:
5302 # append heading at the end of current heading and also take
5303 # over the children of current heading
5304 for child in current_heading.children:
5305 heading.children.append(child, taint=False)
5306 current_heading.children.remove_slice(
5307 0, len(current_heading.children),
5308 taint=False)
5309
5310 # if cursor is currently on a heading, insert parts of it into the
5311 # newly created heading
5312 if insert_mode and cursor[1] != 0 and cursor[0] == current_heading.start_vim:
5313 offset = cursor[1] - current_heading.level - 1 - (
5314 len(current_heading.todo) + 1 if current_heading.todo else 0)
5315 if offset < 0:
5316 offset = 0
5317 if int(settings.get(u'org_improve_split_heading', u'1')) and \
5318 offset > 0 and len(current_heading.title) == offset + 1 \
5319 and current_heading.title[offset - 1] not in (u' ', u'\t'):
5320 offset += 1
5321 heading.title = current_heading.title[offset:]
5322 current_heading.title = current_heading.title[:offset]
5323 heading.body = current_heading.body[:]
5324 current_heading.body = []
5325
5326 d.write()
5327 vim.command((u'exe "normal %dgg"|startinsert!' % (heading.start_vim, )).encode(u'utf-8'))
5328
5329 # return newly created heading
5330 return heading
5331
5332 @classmethod
5333 def _append_heading(cls, heading, parent):
5334 if heading.level <= parent.level:
5335 raise ValueError('Heading level not is lower than parent level: %d ! > %d' % (heading.level, parent.level))
5336
5337 if parent.children and parent.children[-1].level < heading.level:
5338 cls._append_heading(heading, parent.children[-1])
5339 else:
5340 parent.children.append(heading, taint=False)
5341
5342 @classmethod
5343 def _change_heading_level(cls, level, including_children=True, on_heading=False, insert_mode=False):
5344 u"""
5345 Change level of heading realtively with or without including children.
5346
5347 :level: the number of levels to promote/demote heading
5348 :including_children: True if should should be included in promoting/demoting
5349 :on_heading: True if promoting/demoting should only happen when the cursor is on the heading
5350 :insert_mode: True if vim is in insert mode
5351 """
5352 d = ORGMODE.get_document()
5353 current_heading = d.current_heading()
5354 if not current_heading or on_heading and current_heading.start_vim != vim.current.window.cursor[0]:
5355 # TODO figure out the actually pressed keybinding and feed these
5356 # keys instead of making keys up like this
5357 if level > 0:
5358 if insert_mode:
5359 vim.eval(u'feedkeys("\<C-t>", "n")'.encode(u'utf-8'))
5360 elif including_children:
5361 vim.eval(u'feedkeys(">]]", "n")'.encode(u'utf-8'))
5362 elif on_heading:
5363 vim.eval(u'feedkeys(">>", "n")'.encode(u'utf-8'))
5364 else:
5365 vim.eval(u'feedkeys(">}", "n")'.encode(u'utf-8'))
5366 else:
5367 if insert_mode:
5368 vim.eval(u'feedkeys("\<C-d>", "n")'.encode(u'utf-8'))
5369 elif including_children:
5370 vim.eval(u'feedkeys("<]]", "n")'.encode(u'utf-8'))
5371 elif on_heading:
5372 vim.eval(u'feedkeys("<<", "n")'.encode(u'utf-8'))
5373 else:
5374 vim.eval(u'feedkeys("<}", "n")'.encode(u'utf-8'))
5375 # return True because otherwise apply_count will not work
5376 return True
5377
5378 # don't allow demotion below level 1
5379 if current_heading.level == 1 and level < 1:
5380 return False
5381
5382 # reduce level of demotion to a minimum heading level of 1
5383 if (current_heading.level + level) < 1:
5384 level = 1
5385
5386 def indent(heading, ic):
5387 if not heading:
5388 return
5389 heading.level += level
5390
5391 if ic:
5392 for child in heading.children:
5393 indent(child, ic)
5394
5395 # save cursor position
5396 c = vim.current.window.cursor[:]
5397
5398 # indent the promoted/demoted heading
5399 indent_end_vim = current_heading.end_of_last_child_vim if including_children else current_heading.end_vim
5400 indent(current_heading, including_children)
5401
5402 # when changing the level of a heading, its position in the DOM
5403 # needs to be updated. It's likely that the heading gets a new
5404 # parent and new children when demoted or promoted
5405
5406 # find new parent
5407 p = current_heading.parent
5408 pl = current_heading.get_parent_list()
5409 ps = current_heading.previous_sibling
5410 nhl = current_heading.level
5411
5412 if level > 0:
5413 # demotion
5414 # subheading or top level heading
5415 if ps and nhl > ps.level:
5416 pl.remove(current_heading, taint=False)
5417 # find heading that is the new parent heading
5418 oh = ps
5419 h = ps
5420 while nhl > h.level:
5421 oh = h
5422 if h.children:
5423 h = h.children[-1]
5424 else:
5425 break
5426 np = h if nhl > h.level else oh
5427
5428 # append current heading to new heading
5429 np.children.append(current_heading, taint=False)
5430
5431 # if children are not included, distribute them among the
5432 # parent heading and it's siblings
5433 if not including_children:
5434 for h in current_heading.children[:]:
5435 if h and h.level <= nhl:
5436 cls._append_heading(h, np)
5437 current_heading.children.remove(h, taint=False)
5438 else:
5439 # promotion
5440 if p and nhl <= p.level:
5441 idx = current_heading.get_index_in_parent_list() + 1
5442 # find the new parent heading
5443 oh = p
5444 h = p
5445 while nhl <= h.level:
5446 # append new children to current heading
5447 for child in h.children[idx:]:
5448 cls._append_heading(child, current_heading)
5449 h.children.remove_slice(idx, len(h.children), taint=False)
5450 idx = h.get_index_in_parent_list() + 1
5451 if h.parent:
5452 h = h.parent
5453 else:
5454 break
5455 ns = oh.next_sibling
5456 while ns and ns.level > current_heading.level:
5457 nns = ns.next_sibling
5458 cls._append_heading(ns, current_heading)
5459 ns = nns
5460
5461 # append current heading to new parent heading / document
5462 pl.remove(current_heading, taint=False)
5463 if nhl > h.level:
5464 h.children.insert(idx, current_heading, taint=False)
5465 else:
5466 d.headings.insert(idx, current_heading, taint=False)
5467
5468 d.write()
5469 if indent_end_vim != current_heading.start_vim:
5470 vim.command((u'normal %dggV%dgg=' % (current_heading.start_vim, indent_end_vim)).encode(u'utf-8'))
5471 # restore cursor position
5472 vim.current.window.cursor = (c[0], c[1] + level)
5473
5474 return True
5475
5476 @classmethod
5477 @realign_tags
5478 @repeat
5479 @apply_count
5480 def demote_heading(cls, including_children=True, on_heading=False, insert_mode=False):
5481 if cls._change_heading_level(1, including_children=including_children, on_heading=on_heading, insert_mode=insert_mode):
5482 if including_children:
5483 return u'OrgDemoteSubtree'
5484 return u'OrgDemoteHeading'
5485
5486 @classmethod
5487 @realign_tags
5488 @repeat
5489 @apply_count
5490 def promote_heading(cls, including_children=True, on_heading=False, insert_mode=False):
5491 if cls._change_heading_level(-1, including_children=including_children, on_heading=on_heading, insert_mode=insert_mode):
5492 if including_children:
5493 return u'OrgPromoteSubtreeNormal'
5494 return u'OrgPromoteHeadingNormal'
5495
5496 @classmethod
5497 def _move_heading(cls, direction=Direction.FORWARD, including_children=True):
5498 u""" Move heading up or down
5499
5500 :returns: heading or None
5501 """
5502 d = ORGMODE.get_document()
5503 current_heading = d.current_heading()
5504 if not current_heading or \
5505 (direction == Direction.FORWARD and not current_heading.next_sibling) or \
5506 (direction == Direction.BACKWARD and not current_heading.previous_sibling):
5507 return None
5508
5509 cursor_offset = vim.current.window.cursor[0] - (current_heading._orig_start + 1)
5510 l = current_heading.get_parent_list()
5511 if l is None:
5512 raise HeadingDomError(u'Current heading is not properly linked in DOM')
5513
5514 if not including_children:
5515 if current_heading.previous_sibling:
5516 npl = current_heading.previous_sibling.children
5517 for child in current_heading.children:
5518 npl.append(child, taint=False)
5519 elif current_heading.parent:
5520 # if the current heading doesn't have a previous sibling it
5521 # must be the first heading
5522 np = current_heading.parent
5523 for child in current_heading.children:
5524 cls._append_heading(child, np)
5525 else:
5526 # if the current heading doesn't have a parent, its children
5527 # must be added as top level headings to the document
5528 npl = l
5529 for child in current_heading.children[::-1]:
5530 npl.insert(0, child, taint=False)
5531 current_heading.children.remove_slice(0, len(current_heading.children), taint=False)
5532
5533 idx = current_heading.get_index_in_parent_list()
5534 if idx is None:
5535 raise HeadingDomError(u'Current heading is not properly linked in DOM')
5536
5537 offset = 1 if direction == Direction.FORWARD else -1
5538 del l[idx]
5539 l.insert(idx + offset, current_heading)
5540
5541 d.write()
5542
5543 vim.current.window.cursor = (
5544 current_heading.start_vim + cursor_offset,
5545 vim.current.window.cursor[1])
5546
5547 return True
5548
5549 @classmethod
5550 @repeat
5551 @apply_count
5552 def move_heading_upward(cls, including_children=True):
5553 if cls._move_heading(direction=Direction.BACKWARD, including_children=including_children):
5554 if including_children:
5555 return u'OrgMoveSubtreeUpward'
5556 return u'OrgMoveHeadingUpward'
5557
5558 @classmethod
5559 @repeat
5560 @apply_count
5561 def move_heading_downward(cls, including_children=True):
5562 if cls._move_heading(direction=Direction.FORWARD, including_children=including_children):
5563 if including_children:
5564 return u'OrgMoveSubtreeDownward'
5565 return u'OrgMoveHeadingDownward'
5566
5567 def register(self):
5568 u"""
5569 Registration of plugin. Key bindings and other initialization should be done.
5570 """
5571 settings.set(u'org_improve_split_heading', u'1')
5572
5573 self.keybindings.append(Keybinding(u'<C-S-CR>', Plug(u'OrgNewHeadingAboveNormal', u':silent! py ORGMODE.plugins[u"EditStructure"].new_heading(below=False)<CR>')))
5574 self.menu + ActionEntry(u'New Heading &above', self.keybindings[-1])
5575 self.keybindings.append(Keybinding(u'<localleader>hN', u'<Plug>OrgNewHeadingAboveNormal', mode=MODE_NORMAL))
5576 self.keybindings.append(Keybinding(u'<localleader><CR>', u'<Plug>OrgNewHeadingAboveNormal', mode=MODE_NORMAL))
5577
5578 self.keybindings.append(Keybinding(u'<S-CR>', Plug(u'OrgNewHeadingBelowNormal', u':silent! py ORGMODE.plugins[u"EditStructure"].new_heading(below=True)<CR>')))
5579 self.menu + ActionEntry(u'New Heading &below', self.keybindings[-1])
5580 self.keybindings.append(Keybinding(u'<localleader>hh', u'<Plug>OrgNewHeadingBelowNormal', mode=MODE_NORMAL))
5581 self.keybindings.append(Keybinding(u'<leader><CR>', u'<Plug>OrgNewHeadingBelowNormal', mode=MODE_NORMAL))
5582
5583 self.keybindings.append(Keybinding(u'<C-CR>', Plug(u'OrgNewHeadingBelowAfterChildrenNormal', u':silent! py ORGMODE.plugins[u"EditStructure"].new_heading(below=True, end_of_last_child=True)<CR>')))
5584 self.menu + ActionEntry(u'New Heading below, after &children', self.keybindings[-1])
5585 self.keybindings.append(Keybinding(u'<localleader>hn', u'<Plug>OrgNewHeadingBelowAfterChildrenNormal', mode=MODE_NORMAL))
5586 self.keybindings.append(Keybinding(u'<CR>', u'<Plug>OrgNewHeadingBelowAfterChildrenNormal', mode=MODE_NORMAL))
5587
5588 self.keybindings.append(Keybinding(u'<C-S-CR>', Plug(u'OrgNewHeadingAboveInsert', u'<C-o>:<C-u>silent! py ORGMODE.plugins[u"EditStructure"].new_heading(below=False, insert_mode=True)<CR>', mode=MODE_INSERT)))
5589 self.keybindings.append(Keybinding(u'<S-CR>', Plug(u'OrgNewHeadingBelowInsert', u'<C-o>:<C-u>silent! py ORGMODE.plugins[u"EditStructure"].new_heading(below=True, insert_mode=True)<CR>', mode=MODE_INSERT)))
5590 self.keybindings.append(Keybinding(u'<C-CR>', Plug(u'OrgNewHeadingBelowAfterChildrenInsert', u'<C-o>:<C-u>silent! py ORGMODE.plugins[u"EditStructure"].new_heading(insert_mode=True, end_of_last_child=True)<CR>', mode=MODE_INSERT)))
5591
5592 self.menu + Separator()
5593
5594 self.keybindings.append(Keybinding(u'm{', Plug(u'OrgMoveHeadingUpward', u':py ORGMODE.plugins[u"EditStructure"].move_heading_upward(including_children=False)<CR>')))
5595 self.keybindings.append(Keybinding(u'm[[', Plug(u'OrgMoveSubtreeUpward', u':py ORGMODE.plugins[u"EditStructure"].move_heading_upward()<CR>')))
5596 self.menu + ActionEntry(u'Move Subtree &Up', self.keybindings[-1])
5597 self.keybindings.append(Keybinding(u'm}', Plug(u'OrgMoveHeadingDownward', u':py ORGMODE.plugins[u"EditStructure"].move_heading_downward(including_children=False)<CR>')))
5598 self.keybindings.append(Keybinding(u'm]]', Plug(u'OrgMoveSubtreeDownward', u':py ORGMODE.plugins[u"EditStructure"].move_heading_downward()<CR>')))
5599 self.menu + ActionEntry(u'Move Subtree &Down', self.keybindings[-1])
5600
5601 self.menu + Separator()
5602
5603 self.menu + ActionEntry(u'&Copy Heading', u'yah', u'yah')
5604 self.menu + ActionEntry(u'C&ut Heading', u'dah', u'dah')
5605
5606 self.menu + Separator()
5607
5608 self.menu + ActionEntry(u'&Copy Subtree', u'yar', u'yar')
5609 self.menu + ActionEntry(u'C&ut Subtree', u'dar', u'dar')
5610 self.menu + ActionEntry(u'&Paste Subtree', u'p', u'p')
5611
5612 self.menu + Separator()
5613
5614 self.keybindings.append(Keybinding(u'<ah', Plug(u'OrgPromoteHeadingNormal', u':silent! py ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False)<CR>')))
5615 self.menu + ActionEntry(u'&Promote Heading', self.keybindings[-1])
5616 self.keybindings.append(Keybinding(u'<<', Plug(u'OrgPromoteOnHeadingNormal', u':silent! py ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False, on_heading=True)<CR>')))
5617 self.keybindings.append(Keybinding(u'<{', u'<Plug>OrgPromoteHeadingNormal', mode=MODE_NORMAL))
5618 self.keybindings.append(Keybinding(u'<ih', u'<Plug>OrgPromoteHeadingNormal', mode=MODE_NORMAL))
5619
5620 self.keybindings.append(Keybinding(u'<ar', Plug(u'OrgPromoteSubtreeNormal', u':silent! py ORGMODE.plugins[u"EditStructure"].promote_heading()<CR>')))
5621 self.menu + ActionEntry(u'&Promote Subtree', self.keybindings[-1])
5622 self.keybindings.append(Keybinding(u'<[[', u'<Plug>OrgPromoteSubtreeNormal', mode=MODE_NORMAL))
5623 self.keybindings.append(Keybinding(u'<ir', u'<Plug>OrgPromoteSubtreeNormal', mode=MODE_NORMAL))
5624
5625 self.keybindings.append(Keybinding(u'>ah', Plug(u'OrgDemoteHeadingNormal', u':silent! py ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False)<CR>')))
5626 self.menu + ActionEntry(u'&Demote Heading', self.keybindings[-1])
5627 self.keybindings.append(Keybinding(u'>>', Plug(u'OrgDemoteOnHeadingNormal', u':silent! py ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False, on_heading=True)<CR>')))
5628 self.keybindings.append(Keybinding(u'>}', u'<Plug>OrgDemoteHeadingNormal', mode=MODE_NORMAL))
5629 self.keybindings.append(Keybinding(u'>ih', u'<Plug>OrgDemoteHeadingNormal', mode=MODE_NORMAL))
5630
5631 self.keybindings.append(Keybinding(u'>ar', Plug(u'OrgDemoteSubtreeNormal', u':silent! py ORGMODE.plugins[u"EditStructure"].demote_heading()<CR>')))
5632 self.menu + ActionEntry(u'&Demote Subtree', self.keybindings[-1])
5633 self.keybindings.append(Keybinding(u'>]]', u'<Plug>OrgDemoteSubtreeNormal', mode=MODE_NORMAL))
5634 self.keybindings.append(Keybinding(u'>ir', u'<Plug>OrgDemoteSubtreeNormal', mode=MODE_NORMAL))
5635
5636 # other keybindings
5637 self.keybindings.append(Keybinding(u'<C-d>', Plug(u'OrgPromoteOnHeadingInsert', u'<C-o>:silent! py ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False, on_heading=True, insert_mode=True)<CR>', mode=MODE_INSERT)))
5638 self.keybindings.append(Keybinding(u'<C-t>', Plug(u'OrgDemoteOnHeadingInsert', u'<C-o>:silent! py ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False, on_heading=True, insert_mode=True)<CR>', mode=MODE_INSERT)))
5639
5640# vim: set noexpandtab:
5641ftplugin/orgmode/plugins/Hyperlinks.py [[[1
5642215
5643# -*- coding: utf-8 -*-
5644
5645import re
5646
5647import vim
5648
5649from orgmode._vim import echom, ORGMODE, realign_tags
5650from orgmode.menu import Submenu, Separator, ActionEntry
5651from orgmode.keybinding import Keybinding, Plug, Command
5652
5653
5654class Hyperlinks(object):
5655 u""" Hyperlinks plugin """
5656
5657 def __init__(self):
5658 u""" Initialize plugin """
5659 object.__init__(self)
5660 # menu entries this plugin should create
5661 self.menu = ORGMODE.orgmenu + Submenu(u'Hyperlinks')
5662
5663 # key bindings for this plugin
5664 # key bindings are also registered through the menu so only additional
5665 # bindings should be put in this variable
5666 self.keybindings = []
5667
5668 # commands for this plugin
5669 self.commands = []
5670
5671 uri_match = re.compile(
5672 r'^\[{2}(?P<uri>[^][]*)(\]\[(?P<description>[^][]*))?\]{2}')
5673
5674 @classmethod
5675 def _get_link(cls, cursor=None):
5676 u"""
5677 Get the link the cursor is on and return it's URI and description
5678
5679 :cursor: None or (Line, Column)
5680 :returns: None if no link was found, otherwise {uri:URI,
5681 description:DESCRIPTION, line:LINE, start:START, end:END}
5682 or uri and description could be None if not set
5683 """
5684 cursor = cursor if cursor else vim.current.window.cursor
5685 line = vim.current.buffer[cursor[0] - 1].decode(u'utf-8')
5686
5687 # if the cursor is on the last bracket, it's not recognized as a hyperlink
5688 start = line.rfind(u'[[', 0, cursor[1])
5689 if start == -1:
5690 start = line.rfind(u'[[', 0, cursor[1] + 2)
5691 end = line.find(u']]', cursor[1])
5692 if end == -1:
5693 end = line.find(u']]', cursor[1] - 1)
5694
5695 # extract link
5696 if start != -1 and end != -1:
5697 end += 2
5698 match = Hyperlinks.uri_match.match(line[start:end])
5699
5700 res = {
5701 u'line': line,
5702 u'start': start,
5703 u'end': end,
5704 u'uri': None,
5705 u'description': None}
5706 if match:
5707 res.update(match.groupdict())
5708 return res
5709
5710 @classmethod
5711 def follow(cls, action=u'openLink', visual=u''):
5712 u""" Follow hyperlink. If called on a regular string UTL determines the
5713 outcome. Normally a file with that name will be opened.
5714
5715 :action: "copy" if the link should be copied to clipboard, otherwise
5716 the link will be opened
5717 :visual: "visual" if Universal Text Linking should be triggered in
5718 visual mode
5719
5720 :returns: URI or None
5721 """
5722 if not int(vim.eval(u'exists(":Utl")')):
5723 echom(u'Universal Text Linking plugin not installed, unable to proceed.')
5724 return
5725
5726 action = u'copyLink' \
5727 if (action and action.startswith(u'copy')) \
5728 else u'openLink'
5729 visual = u'visual' if visual and visual.startswith(u'visual') else u''
5730
5731 link = Hyperlinks._get_link()
5732
5733 if link and link[u'uri'] is not None:
5734 # call UTL with the URI
5735 vim.command((
5736 u'Utl %s %s %s' % (action, visual, link[u'uri'])).encode(u'utf-8'))
5737 return link[u'uri']
5738 else:
5739 # call UTL and let it decide what to do
5740 vim.command((u'Utl %s %s' % (action, visual)).encode(u'utf-8'))
5741
5742 @classmethod
5743 @realign_tags
5744 def insert(cls, uri=None, description=None):
5745 u""" Inserts a hyperlink. If no arguments are provided, an interactive
5746 query will be started.
5747
5748 :uri: The URI that will be opened
5749 :description: An optional description that will be displayed instead of
5750 the URI
5751
5752 :returns: (URI, description)
5753 """
5754 link = Hyperlinks._get_link()
5755 if link:
5756 if uri is None and link[u'uri'] is not None:
5757 uri = link[u'uri']
5758 if description is None and link[u'description'] is not None:
5759 description = link[u'description']
5760
5761 if uri is None:
5762 uri = vim.eval(u'input("Link: ", "", "file")')
5763 elif link:
5764 uri = vim.eval(u'input("Link: ", "%s", "file")' % link[u'uri'])
5765 if uri is None:
5766 return
5767 else:
5768 uri = uri.decode(u'utf-8')
5769
5770 if description is None:
5771 description = vim.eval(u'input("Description: ")').decode(u'utf-8')
5772 elif link:
5773 description = vim.eval(
5774 u'input("Description: ", "%s")' %
5775 link[u'description']).decode(u'utf-8')
5776 if description is None:
5777 return
5778
5779 cursor = vim.current.window.cursor
5780 cl = vim.current.buffer[cursor[0] - 1].decode(u'utf-8')
5781 head = cl[:cursor[1] + 1] if not link else cl[:link[u'start']]
5782 tail = cl[cursor[1] + 1:] if not link else cl[link[u'end']:]
5783
5784 separator = u''
5785 if description:
5786 separator = u']['
5787
5788 if uri or description:
5789 vim.current.buffer[cursor[0] - 1] = \
5790 (u''.join((head, u'[[%s%s%s]]' %
5791 (uri, separator, description), tail))).encode(u'utf-8')
5792 elif link:
5793 vim.current.buffer[cursor[0] - 1] = \
5794 (u''.join((head, tail))).encode(u'utf-8')
5795
5796 def register(self):
5797 u"""
5798 Registration of plugin. Key bindings and other initialization should be done.
5799 """
5800 cmd = Command(
5801 u'OrgHyperlinkFollow',
5802 u':py ORGMODE.plugins[u"Hyperlinks"].follow()')
5803 self.commands.append(cmd)
5804 self.keybindings.append(
5805 Keybinding(u'gl', Plug(u'OrgHyperlinkFollow', self.commands[-1])))
5806 self.menu + ActionEntry(u'&Follow Link', self.keybindings[-1])
5807
5808 cmd = Command(
5809 u'OrgHyperlinkCopy',
5810 u':py ORGMODE.plugins[u"Hyperlinks"].follow(action=u"copy")')
5811 self.commands.append(cmd)
5812 self.keybindings.append(
5813 Keybinding(u'gyl', Plug(u'OrgHyperlinkCopy', self.commands[-1])))
5814 self.menu + ActionEntry(u'&Copy Link', self.keybindings[-1])
5815
5816 cmd = Command(
5817 u'OrgHyperlinkInsert',
5818 u':py ORGMODE.plugins[u"Hyperlinks"].insert(<f-args>)',
5819 arguments=u'*')
5820 self.commands.append(cmd)
5821 self.keybindings.append(
5822 Keybinding(u'gil', Plug(u'OrgHyperlinkInsert', self.commands[-1])))
5823 self.menu + ActionEntry(u'&Insert Link', self.keybindings[-1])
5824
5825 self.menu + Separator()
5826
5827 # find next link
5828 cmd = Command(
5829 u'OrgHyperlinkNextLink',
5830 u":if search('\[\{2}\zs[^][]*\(\]\[[^][]*\)\?\ze\]\{2}', 's') == 0 | echo 'No further link found.' | endif")
5831 self.commands.append(cmd)
5832 self.keybindings.append(
5833 Keybinding(u'gn', Plug(u'OrgHyperlinkNextLink', self.commands[-1])))
5834 self.menu + ActionEntry(u'&Next Link', self.keybindings[-1])
5835
5836 # find previous link
5837 cmd = Command(
5838 u'OrgHyperlinkPreviousLink',
5839 u":if search('\[\{2}\zs[^][]*\(\]\[[^][]*\)\?\ze\]\{2}', 'bs') == 0 | echo 'No further link found.' | endif")
5840 self.commands.append(cmd)
5841 self.keybindings.append(
5842 Keybinding(u'go', Plug(u'OrgHyperlinkPreviousLink', self.commands[-1])))
5843 self.menu + ActionEntry(u'&Previous Link', self.keybindings[-1])
5844
5845 self.menu + Separator()
5846
5847 # Descriptive Links
5848 cmd = Command(u'OrgHyperlinkDescriptiveLinks', u':setlocal cole=2')
5849 self.commands.append(cmd)
5850 self.menu + ActionEntry(u'&Descriptive Links', self.commands[-1])
5851
5852 # Literal Links
5853 cmd = Command(u'OrgHyperlinkLiteralLinks', u':setlocal cole=0')
5854 self.commands.append(cmd)
5855 self.menu + ActionEntry(u'&Literal Links', self.commands[-1])
5856
5857# vim: set noexpandtab:
5858ftplugin/orgmode/plugins/LoggingWork.py [[[1
585941
5860# -*- coding: utf-8 -*-
5861
5862import vim
5863
5864from orgmode._vim import echo, echom, echoe, ORGMODE, apply_count, repeat
5865from orgmode.menu import Submenu, Separator, ActionEntry
5866from orgmode.keybinding import Keybinding, Plug, Command
5867
5868
5869class LoggingWork(object):
5870 u""" LoggingWork plugin """
5871
5872 def __init__(self):
5873 u""" Initialize plugin """
5874 object.__init__(self)
5875 # menu entries this plugin should create
5876 self.menu = ORGMODE.orgmenu + Submenu(u'&Logging work')
5877
5878 # key bindings for this plugin
5879 # key bindings are also registered through the menu so only additional
5880 # bindings should be put in this variable
5881 self.keybindings = []
5882
5883 # commands for this plugin
5884 self.commands = []
5885
5886 @classmethod
5887 def action(cls):
5888 u""" Some kind of action
5889
5890 :returns: TODO
5891 """
5892 pass
5893
5894 def register(self):
5895 u"""
5896 Registration of plugin. Key bindings and other initialization should be done.
5897 """
5898 # an Action menu entry which binds "keybinding" to action ":action"
5899 self.commands.append(Command(u'OrgLoggingRecordDoneTime', u':py ORGMODE.plugins[u"LoggingWork"].action()'))
5900 self.menu + ActionEntry(u'&Record DONE time', self.commands[-1])
5901ftplugin/orgmode/plugins/Misc.py [[[1
5902177
5903# -*- coding: utf-8 -*-
5904
5905import vim
5906
5907from orgmode._vim import ORGMODE, apply_count
5908from orgmode.menu import Submenu
5909from orgmode.keybinding import Keybinding, Plug, MODE_VISUAL, MODE_OPERATOR
5910
5911
5912class Misc(object):
5913 u""" Miscellaneous functionality """
5914
5915 def __init__(self):
5916 u""" Initialize plugin """
5917 object.__init__(self)
5918 # menu entries this plugin should create
5919 self.menu = ORGMODE.orgmenu + Submenu(u'Misc')
5920
5921 # key bindings for this plugin
5922 # key bindings are also registered through the menu so only additional
5923 # bindings should be put in this variable
5924 self.keybindings = []
5925
5926 @classmethod
5927 def jump_to_first_character(cls):
5928 heading = ORGMODE.get_document().current_heading()
5929 if not heading or heading.start_vim != vim.current.window.cursor[0]:
5930 vim.eval(u'feedkeys("^", "n")'.encode(u'utf-8'))
5931 return
5932
5933 vim.current.window.cursor = (vim.current.window.cursor[0], heading.level + 1)
5934
5935 @classmethod
5936 def edit_at_first_character(cls):
5937 heading = ORGMODE.get_document().current_heading()
5938 if not heading or heading.start_vim != vim.current.window.cursor[0]:
5939 vim.eval(u'feedkeys("I", "n")'.encode(u'utf-8'))
5940 return
5941
5942 vim.current.window.cursor = (vim.current.window.cursor[0], heading.level + 1)
5943 vim.command(u'startinsert'.encode(u'utf-8'))
5944
5945 # @repeat
5946 @classmethod
5947 @apply_count
5948 def i_heading(cls, mode=u'visual', selection=u'inner', skip_children=False):
5949 u"""
5950 inner heading text object
5951 """
5952 heading = ORGMODE.get_document().current_heading()
5953 if heading:
5954 if selection != u'inner':
5955 heading = heading if not heading.parent else heading.parent
5956
5957 line_start, col_start = [int(i) for i in vim.eval(u'getpos("\'<")'.encode(u'utf-8'))[1:3]]
5958 line_end, col_end = [int(i) for i in vim.eval(u'getpos("\'>")'.encode(u'utf-8'))[1:3]]
5959
5960 if mode != u'visual':
5961 line_start = vim.current.window.cursor[0]
5962 line_end = line_start
5963
5964 start = line_start
5965 end = line_end
5966 move_one_character_back = u'' if mode == u'visual' else u'h'
5967
5968 if heading.start_vim < line_start:
5969 start = heading.start_vim
5970 if heading.end_vim > line_end and not skip_children:
5971 end = heading.end_vim
5972 elif heading.end_of_last_child_vim > line_end and skip_children:
5973 end = heading.end_of_last_child_vim
5974
5975 if mode != u'visual' and not vim.current.buffer[end - 1]:
5976 end -= 1
5977 move_one_character_back = u''
5978
5979 swap_cursor = u'o' if vim.current.window.cursor[0] == line_start else u''
5980
5981 if selection == u'inner' and vim.current.window.cursor[0] != line_start:
5982 h = ORGMODE.get_document().current_heading()
5983 if h:
5984 heading = h
5985
5986 visualmode = vim.eval(u'visualmode()').decode(u'utf-8') if mode == u'visual' else u'v'
5987
5988 if line_start == start and line_start != heading.start_vim:
5989 if col_start in (0, 1):
5990 vim.command(
5991 (u'normal! %dgg0%s%dgg$%s%s' %
5992 (start, visualmode, end, move_one_character_back, swap_cursor)).encode(u'utf-8'))
5993 else:
5994 vim.command(
5995 (u'normal! %dgg0%dl%s%dgg$%s%s' %
5996 (start, col_start - 1, visualmode, end, move_one_character_back, swap_cursor)).encode(u'utf-8'))
5997 else:
5998 vim.command(
5999 (u'normal! %dgg0%dl%s%dgg$%s%s' %
6000 (start, heading.level + 1, visualmode, end, move_one_character_back, swap_cursor)).encode(u'utf-8'))
6001
6002 if selection == u'inner':
6003 if mode == u'visual':
6004 return u'OrgInnerHeadingVisual' if not skip_children else u'OrgInnerTreeVisual'
6005 else:
6006 return u'OrgInnerHeadingOperator' if not skip_children else u'OrgInnerTreeOperator'
6007 else:
6008 if mode == u'visual':
6009 return u'OrgOuterHeadingVisual' if not skip_children else u'OrgOuterTreeVisual'
6010 else:
6011 return u'OrgOuterHeadingOperator' if not skip_children else u'OrgOuterTreeOperator'
6012 elif mode == u'visual':
6013 vim.command(u'normal! gv'.encode(u'utf-8'))
6014
6015 # @repeat
6016 @classmethod
6017 @apply_count
6018 def a_heading(cls, selection=u'inner', skip_children=False):
6019 u"""
6020 a heading text object
6021 """
6022 heading = ORGMODE.get_document().current_heading()
6023 if heading:
6024 if selection != u'inner':
6025 heading = heading if not heading.parent else heading.parent
6026
6027 line_start, col_start = [int(i) for i in vim.eval(u'getpos("\'<")'.encode(u'utf-8'))[1:3]]
6028 line_end, col_end = [int(i) for i in vim.eval(u'getpos("\'>")'.encode(u'utf-8'))[1:3]]
6029
6030 start = line_start
6031 end = line_end
6032
6033 if heading.start_vim < line_start:
6034 start = heading.start_vim
6035 if heading.end_vim > line_end and not skip_children:
6036 end = heading.end_vim
6037 elif heading.end_of_last_child_vim > line_end and skip_children:
6038 end = heading.end_of_last_child_vim
6039
6040 swap_cursor = u'o' if vim.current.window.cursor[0] == line_start else u''
6041
6042 vim.command(
6043 (u'normal! %dgg%s%dgg$%s' %
6044 (start, vim.eval(u'visualmode()'.encode(u'utf-8')), end, swap_cursor)).encode(u'utf-8'))
6045 if selection == u'inner':
6046 return u'OrgAInnerHeadingVisual' if not skip_children else u'OrgAInnerTreeVisual'
6047 else:
6048 return u'OrgAOuterHeadingVisual' if not skip_children else u'OrgAOuterTreeVisual'
6049 else:
6050 vim.command(u'normal! gv'.encode(u'utf-8'))
6051
6052 def register(self):
6053 u"""
6054 Registration of plugin. Key bindings and other initialization should be done.
6055 """
6056 self.keybindings.append(Keybinding(u'^', Plug(u'OrgJumpToFirstCharacter', u':py ORGMODE.plugins[u"Misc"].jump_to_first_character()<CR>')))
6057 self.keybindings.append(Keybinding(u'I', Plug(u'OrgEditAtFirstCharacter', u':py ORGMODE.plugins[u"Misc"].edit_at_first_character()<CR>')))
6058
6059 self.keybindings.append(Keybinding(u'ih', Plug(u'OrgInnerHeadingVisual', u':<C-u>py ORGMODE.plugins[u"Misc"].i_heading()<CR>', mode=MODE_VISUAL)))
6060 self.keybindings.append(Keybinding(u'ah', Plug(u'OrgAInnerHeadingVisual', u':<C-u>py ORGMODE.plugins[u"Misc"].a_heading()<CR>', mode=MODE_VISUAL)))
6061 self.keybindings.append(Keybinding(u'Oh', Plug(u'OrgOuterHeadingVisual', u':<C-u>py ORGMODE.plugins[u"Misc"].i_heading(selection=u"outer")<CR>', mode=MODE_VISUAL)))
6062 self.keybindings.append(Keybinding(u'OH', Plug(u'OrgAOuterHeadingVisual', u':<C-u>py ORGMODE.plugins[u"Misc"].a_heading(selection=u"outer")<CR>', mode=MODE_VISUAL)))
6063
6064 self.keybindings.append(Keybinding(u'ih', Plug(u'OrgInnerHeadingOperator', u':<C-u>py ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator")<CR>', mode=MODE_OPERATOR)))
6065 self.keybindings.append(Keybinding(u'ah', u':normal Vah<CR>', mode=MODE_OPERATOR))
6066 self.keybindings.append(Keybinding(u'Oh', Plug(u'OrgOuterHeadingOperator', ':<C-u>py ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator", selection=u"outer")<CR>', mode=MODE_OPERATOR)))
6067 self.keybindings.append(Keybinding(u'OH', u':normal VOH<CR>', mode=MODE_OPERATOR))
6068
6069 self.keybindings.append(Keybinding(u'ir', Plug(u'OrgInnerTreeVisual', u':<C-u>py ORGMODE.plugins[u"Misc"].i_heading(skip_children=True)<CR>', mode=MODE_VISUAL)))
6070 self.keybindings.append(Keybinding(u'ar', Plug(u'OrgAInnerTreeVisual', u':<C-u>py ORGMODE.plugins[u"Misc"].a_heading(skip_children=True)<CR>', mode=MODE_VISUAL)))
6071 self.keybindings.append(Keybinding(u'Or', Plug(u'OrgOuterTreeVisual', u'<:<C-u>py ORGMODE.plugins[u"Misc"].i_heading(selection=u"outer", skip_children=True)<CR>', mode=MODE_VISUAL)))
6072 self.keybindings.append(Keybinding(u'OR', Plug(u'OrgAOuterTreeVisual', u':<C-u>py ORGMODE.plugins[u"Misc"].a_heading(selection=u"outer", skip_children=True)<CR>', mode=MODE_VISUAL)))
6073
6074 self.keybindings.append(Keybinding(u'ir', Plug(u'OrgInnerTreeOperator', u':<C-u>py ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator")<CR>', mode=MODE_OPERATOR)))
6075 self.keybindings.append(Keybinding(u'ar', u':normal Var<CR>', mode=MODE_OPERATOR))
6076 self.keybindings.append(Keybinding(u'Or', Plug(u'OrgOuterTreeOperator', u':<C-u>py ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator", selection=u"outer", skip_children=True)<CR>', mode=MODE_OPERATOR)))
6077 self.keybindings.append(Keybinding(u'OR', u':normal VOR<CR>', mode=MODE_OPERATOR))
6078
6079# vim: set noexpandtab:
6080ftplugin/orgmode/plugins/Navigator.py [[[1
6081317
6082# -*- coding: utf-8 -*-
6083
6084import vim
6085
6086from orgmode._vim import echo, ORGMODE, apply_count
6087from orgmode.menu import Submenu, ActionEntry
6088from orgmode.keybinding import Keybinding, MODE_VISUAL, MODE_OPERATOR, Plug
6089from orgmode.liborgmode.documents import Direction
6090
6091
6092class Navigator(object):
6093 u""" Implement navigation in org-mode documents """
6094
6095 def __init__(self):
6096 object.__init__(self)
6097 self.menu = ORGMODE.orgmenu + Submenu(u'&Navigate Headings')
6098 self.keybindings = []
6099
6100 @classmethod
6101 @apply_count
6102 def parent(cls, mode):
6103 u"""
6104 Focus parent heading
6105
6106 :returns: parent heading or None
6107 """
6108 heading = ORGMODE.get_document().current_heading()
6109 if not heading:
6110 if mode == u'visual':
6111 vim.command(u'normal! gv'.encode(u'utf-8'))
6112 else:
6113 echo(u'No heading found')
6114 return
6115
6116 if not heading.parent:
6117 if mode == u'visual':
6118 vim.command(u'normal! gv'.encode(u'utf-8'))
6119 else:
6120 echo(u'No parent heading found')
6121 return
6122
6123 p = heading.parent
6124
6125 if mode == u'visual':
6126 cls._change_visual_selection(heading, p, direction=Direction.BACKWARD, parent=True)
6127 else:
6128 vim.current.window.cursor = (p.start_vim, p.level + 1)
6129 return p
6130
6131 @classmethod
6132 @apply_count
6133 def parent_next_sibling(cls, mode):
6134 u"""
6135 Focus the parent's next sibling
6136
6137 :returns: parent's next sibling heading or None
6138 """
6139 heading = ORGMODE.get_document().current_heading()
6140 if not heading:
6141 if mode == u'visual':
6142 vim.command(u'normal! gv'.encode(u'utf-8'))
6143 else:
6144 echo(u'No heading found')
6145 return
6146
6147 if not heading.parent or not heading.parent.next_sibling:
6148 if mode == u'visual':
6149 vim.command(u'normal! gv'.encode(u'utf-8'))
6150 else:
6151 echo(u'No parent heading found')
6152 return
6153
6154 ns = heading.parent.next_sibling
6155
6156 if mode == u'visual':
6157 cls._change_visual_selection(heading, ns, direction=Direction.FORWARD, parent=False)
6158 elif mode == u'operator':
6159 vim.current.window.cursor = (ns.start_vim, 0)
6160 else:
6161 vim.current.window.cursor = (ns.start_vim, ns.level + 1)
6162 return ns
6163
6164 @classmethod
6165 def _change_visual_selection(cls, current_heading, heading, direction=Direction.FORWARD, noheadingfound=False, parent=False):
6166 current = vim.current.window.cursor[0]
6167 line_start, col_start = [int(i) for i in vim.eval(u'getpos("\'<")'.encode(u'utf-8'))[1:3]]
6168 line_end, col_end = [int(i) for i in vim.eval(u'getpos("\'>")'.encode(u'utf-8'))[1:3]]
6169
6170 f_start = heading.start_vim
6171 f_end = heading.end_vim
6172 swap_cursor = True
6173
6174 # << |visual start
6175 # selection end >>
6176 if current == line_start:
6177 if (direction == Direction.FORWARD and line_end < f_start) or noheadingfound and not direction == Direction.BACKWARD:
6178 swap_cursor = False
6179
6180 # focus heading HERE
6181 # << |visual start
6182 # selection end >>
6183
6184 # << |visual start
6185 # focus heading HERE
6186 # selection end >>
6187 if f_start < line_start and direction == Direction.BACKWARD:
6188 if current_heading.start_vim < line_start and not parent:
6189 line_start = current_heading.start_vim
6190 else:
6191 line_start = f_start
6192
6193 elif (f_start < line_start or f_start < line_end) and not noheadingfound:
6194 line_start = f_start
6195
6196 # << |visual start
6197 # selection end >>
6198 # focus heading HERE
6199 else:
6200 if direction == Direction.FORWARD:
6201 if line_end < f_start and not line_start == f_start - 1 and current_heading:
6202 # focus end of previous heading instead of beginning of next heading
6203 line_start = line_end
6204 line_end = f_start - 1
6205 else:
6206 # focus end of next heading
6207 line_start = line_end
6208 line_end = f_end
6209 elif direction == Direction.BACKWARD:
6210 if line_end < f_end:
6211 pass
6212 else:
6213 line_start = line_end
6214 line_end = f_end
6215
6216 # << visual start
6217 # selection end| >>
6218 else:
6219 # focus heading HERE
6220 # << visual start
6221 # selection end| >>
6222 if line_start > f_start and line_end > f_end and not parent:
6223 line_end = f_end
6224 swap_cursor = False
6225
6226 elif (line_start > f_start or line_start == f_start) and \
6227 line_end <= f_end and direction == Direction.BACKWARD:
6228 line_end = line_start
6229 line_start = f_start
6230
6231 # << visual start
6232 # selection end and focus heading end HERE| >>
6233
6234 # << visual start
6235 # focus heading HERE
6236 # selection end| >>
6237
6238 # << visual start
6239 # selection end| >>
6240 # focus heading HERE
6241 else:
6242 if direction == Direction.FORWARD:
6243 if line_end < f_start - 1:
6244 # focus end of previous heading instead of beginning of next heading
6245 line_end = f_start - 1
6246 else:
6247 # focus end of next heading
6248 line_end = f_end
6249 else:
6250 line_end = f_end
6251 swap_cursor = False
6252
6253 move_col_start = u'%dl' % (col_start - 1) if (col_start - 1) > 0 and (col_start - 1) < 2000000000 else u''
6254 move_col_end = u'%dl' % (col_end - 1) if (col_end - 1) > 0 and (col_end - 1) < 2000000000 else u''
6255 swap = u'o' if swap_cursor else u''
6256
6257 vim.command((
6258 u'normal! %dgg%s%s%dgg%s%s' %
6259 (line_start, move_col_start, vim.eval(u'visualmode()'.encode(u'utf-8')), line_end, move_col_end, swap)).encode(u'utf-8'))
6260
6261 @classmethod
6262 def _focus_heading(cls, mode, direction=Direction.FORWARD, skip_children=False):
6263 u"""
6264 Focus next or previous heading in the given direction
6265
6266 :direction: True for next heading, False for previous heading
6267 :returns: next heading or None
6268 """
6269 d = ORGMODE.get_document()
6270 current_heading = d.current_heading()
6271 heading = current_heading
6272 focus_heading = None
6273 # FIXME this is just a piece of really ugly and unmaintainable code. It
6274 # should be rewritten
6275 if not heading:
6276 if direction == Direction.FORWARD and d.headings \
6277 and vim.current.window.cursor[0] < d.headings[0].start_vim:
6278 # the cursor is in the meta information are, therefore focus
6279 # first heading
6280 focus_heading = d.headings[0]
6281 if not (heading or focus_heading):
6282 if mode == u'visual':
6283 # restore visual selection when no heading was found
6284 vim.command(u'normal! gv'.encode(u'utf-8'))
6285 else:
6286 echo(u'No heading found')
6287 return
6288 elif direction == Direction.BACKWARD:
6289 if vim.current.window.cursor[0] != heading.start_vim:
6290 # the cursor is in the body of the current heading, therefore
6291 # the current heading will be focused
6292 if mode == u'visual':
6293 line_start, col_start = [int(i) for i in vim.eval(u'getpos("\'<")'.encode(u'utf-8'))[1:3]]
6294 line_end, col_end = [int(i) for i in vim.eval(u'getpos("\'>")'.encode(u'utf-8'))[1:3]]
6295 if line_start >= heading.start_vim and line_end > heading.start_vim:
6296 focus_heading = heading
6297 else:
6298 focus_heading = heading
6299
6300 # so far no heading has been found that the next focus should be on
6301 if not focus_heading:
6302 if not skip_children and direction == Direction.FORWARD and heading.children:
6303 focus_heading = heading.children[0]
6304 elif direction == Direction.FORWARD and heading.next_sibling:
6305 focus_heading = heading.next_sibling
6306 elif direction == Direction.BACKWARD and heading.previous_sibling:
6307 focus_heading = heading.previous_sibling
6308 if not skip_children:
6309 while focus_heading.children:
6310 focus_heading = focus_heading.children[-1]
6311 else:
6312 if direction == Direction.FORWARD:
6313 focus_heading = current_heading.next_heading
6314 else:
6315 focus_heading = current_heading.previous_heading
6316
6317 noheadingfound = False
6318 if not focus_heading:
6319 if mode in (u'visual', u'operator'):
6320 # the cursor seems to be on the last or first heading of this
6321 # document and performes another next/previous operation
6322 focus_heading = heading
6323 noheadingfound = True
6324 else:
6325 if direction == Direction.FORWARD:
6326 echo(u'Already focussing last heading')
6327 else:
6328 echo(u'Already focussing first heading')
6329 return
6330
6331 if mode == u'visual':
6332 cls._change_visual_selection(current_heading, focus_heading, direction=direction, noheadingfound=noheadingfound)
6333 elif mode == u'operator':
6334 if direction == Direction.FORWARD and vim.current.window.cursor[0] >= focus_heading.start_vim:
6335 vim.current.window.cursor = (focus_heading.end_vim, len(vim.current.buffer[focus_heading.end].decode(u'utf-8')))
6336 else:
6337 vim.current.window.cursor = (focus_heading.start_vim, 0)
6338 else:
6339 vim.current.window.cursor = (focus_heading.start_vim, focus_heading.level + 1)
6340 if noheadingfound:
6341 return
6342 return focus_heading
6343
6344 @classmethod
6345 @apply_count
6346 def previous(cls, mode, skip_children=False):
6347 u"""
6348 Focus previous heading
6349 """
6350 return cls._focus_heading(mode, direction=Direction.BACKWARD, skip_children=skip_children)
6351
6352 @classmethod
6353 @apply_count
6354 def next(cls, mode, skip_children=False):
6355 u"""
6356 Focus next heading
6357 """
6358 return cls._focus_heading(mode, direction=Direction.FORWARD, skip_children=skip_children)
6359
6360 def register(self):
6361 # normal mode
6362 self.keybindings.append(Keybinding(u'g{', Plug('OrgJumpToParentNormal', u':py ORGMODE.plugins[u"Navigator"].parent(mode=u"normal")<CR>')))
6363 self.menu + ActionEntry(u'&Up', self.keybindings[-1])
6364 self.keybindings.append(Keybinding(u'g}', Plug('OrgJumpToParentsSiblingNormal', u':py ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"normal")<CR>')))
6365 self.menu + ActionEntry(u'&Down', self.keybindings[-1])
6366 self.keybindings.append(Keybinding(u'{', Plug(u'OrgJumpToPreviousNormal', u':py ORGMODE.plugins[u"Navigator"].previous(mode=u"normal")<CR>')))
6367 self.menu + ActionEntry(u'&Previous', self.keybindings[-1])
6368 self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextNormal', u':py ORGMODE.plugins[u"Navigator"].next(mode=u"normal")<CR>')))
6369 self.menu + ActionEntry(u'&Next', self.keybindings[-1])
6370
6371 # visual mode
6372 self.keybindings.append(Keybinding(u'g{', Plug(u'OrgJumpToParentVisual', u'<Esc>:<C-u>py ORGMODE.plugins[u"Navigator"].parent(mode=u"visual")<CR>', mode=MODE_VISUAL)))
6373 self.keybindings.append(Keybinding(u'g}', Plug('OrgJumpToParentsSiblingVisual', u'<Esc>:<C-u>py ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"visual")<CR>', mode=MODE_VISUAL)))
6374 self.keybindings.append(Keybinding(u'{', Plug(u'OrgJumpToPreviousVisual', u'<Esc>:<C-u>py ORGMODE.plugins[u"Navigator"].previous(mode=u"visual")<CR>', mode=MODE_VISUAL)))
6375 self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextVisual', u'<Esc>:<C-u>py ORGMODE.plugins[u"Navigator"].next(mode=u"visual")<CR>', mode=MODE_VISUAL)))
6376
6377 # operator-pending mode
6378 self.keybindings.append(Keybinding(u'g{', Plug(u'OrgJumpToParentOperator', u':<C-u>py ORGMODE.plugins[u"Navigator"].parent(mode=u"operator")<CR>', mode=MODE_OPERATOR)))
6379 self.keybindings.append(Keybinding(u'g}', Plug('OrgJumpToParentsSiblingOperator', u':<C-u>py ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"operator")<CR>', mode=MODE_OPERATOR)))
6380 self.keybindings.append(Keybinding(u'{', Plug(u'OrgJumpToPreviousOperator', u':<C-u>py ORGMODE.plugins[u"Navigator"].previous(mode=u"operator")<CR>', mode=MODE_OPERATOR)))
6381 self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextOperator', u':<C-u>py ORGMODE.plugins[u"Navigator"].next(mode=u"operator")<CR>', mode=MODE_OPERATOR)))
6382
6383 # section wise movement (skip children)
6384 # normal mode
6385 self.keybindings.append(Keybinding(u'[[', Plug(u'OrgJumpToPreviousSkipChildrenNormal', u':py ORGMODE.plugins[u"Navigator"].previous(mode=u"normal", skip_children=True)<CR>')))
6386 self.menu + ActionEntry(u'Ne&xt Same Level', self.keybindings[-1])
6387 self.keybindings.append(Keybinding(u']]', Plug(u'OrgJumpToNextSkipChildrenNormal', u':py ORGMODE.plugins[u"Navigator"].next(mode=u"normal", skip_children=True)<CR>')))
6388 self.menu + ActionEntry(u'Pre&vious Same Level', self.keybindings[-1])
6389
6390 # visual mode
6391 self.keybindings.append(Keybinding(u'[[', Plug(u'OrgJumpToPreviousSkipChildrenVisual', u'<Esc>:<C-u>py ORGMODE.plugins[u"Navigator"].previous(mode=u"visual", skip_children=True)<CR>', mode=MODE_VISUAL)))
6392 self.keybindings.append(Keybinding(u']]', Plug(u'OrgJumpToNextSkipChildrenVisual', u'<Esc>:<C-u>py ORGMODE.plugins[u"Navigator"].next(mode=u"visual", skip_children=True)<CR>', mode=MODE_VISUAL)))
6393
6394 # operator-pending mode
6395 self.keybindings.append(Keybinding(u'[[', Plug(u'OrgJumpToPreviousSkipChildrenOperator', u':<C-u>py ORGMODE.plugins[u"Navigator"].previous(mode=u"operator", skip_children=True)<CR>', mode=MODE_OPERATOR)))
6396 self.keybindings.append(Keybinding(u']]', Plug(u'OrgJumpToNextSkipChildrenOperator', u':<C-u>py ORGMODE.plugins[u"Navigator"].next(mode=u"operator", skip_children=True)<CR>', mode=MODE_OPERATOR)))
6397
6398# vim: set noexpandtab:
6399ftplugin/orgmode/plugins/ShowHide.py [[[1
6400173
6401# -*- coding: utf-8 -*-
6402
6403import vim
6404
6405from orgmode.liborgmode.headings import Heading
6406from orgmode._vim import ORGMODE, apply_count
6407from orgmode import settings
6408from orgmode.menu import Submenu, ActionEntry
6409from orgmode.keybinding import Keybinding, Plug, MODE_NORMAL
6410
6411
6412class ShowHide(object):
6413 u""" Show Hide plugin """
6414
6415 def __init__(self):
6416 u""" Initialize plugin """
6417 object.__init__(self)
6418 # menu entries this plugin should create
6419 self.menu = ORGMODE.orgmenu + Submenu(u'&Show Hide')
6420
6421 # key bindings for this plugin
6422 # key bindings are also registered through the menu so only additional
6423 # bindings should be put in this variable
6424 self.keybindings = []
6425
6426 @classmethod
6427 def _fold_depth(cls, h):
6428 """ Find the deepest level of open folds
6429
6430 :h: Heading
6431 :returns: Tuple (int - level of open folds, boolean - found fold) or None if h is not a Heading
6432 """
6433 if not isinstance(h, Heading):
6434 return
6435
6436 if int(vim.eval((u'foldclosed(%d)' % h.start_vim).encode(u'utf-8'))) != -1:
6437 return (h.number_of_parents, True)
6438
6439 res = [h.number_of_parents + 1]
6440 found = False
6441 for c in h.children:
6442 d, f = cls._fold_depth(c)
6443 res.append(d)
6444 found |= f
6445
6446 return (max(res), found)
6447
6448 @classmethod
6449 @apply_count
6450 def toggle_folding(cls, reverse=False):
6451 u""" Toggle folding similar to the way orgmode does
6452
6453 This is just a convenience function, don't hesitate to use the z*
6454 keybindings vim offers to deal with folding!
6455
6456 :reverse: If False open folding by one level otherwise close it by one.
6457 """
6458 d = ORGMODE.get_document()
6459 heading = d.current_heading()
6460 if not heading:
6461 vim.eval(u'feedkeys("<Tab>", "n")'.encode(u'utf-8'))
6462 return
6463
6464 cursor = vim.current.window.cursor[:]
6465
6466 if int(vim.eval((u'foldclosed(%d)' % heading.start_vim).encode(u'utf-8'))) != -1:
6467 if not reverse:
6468 # open closed fold
6469 p = heading.number_of_parents
6470 if not p:
6471 p = heading.level
6472 vim.command((u'normal! %dzo' % p).encode(u'utf-8'))
6473 else:
6474 # reverse folding opens all folds under the cursor
6475 vim.command((u'%d,%dfoldopen!' % (heading.start_vim, heading.end_of_last_child_vim)).encode(u'utf-8'))
6476 vim.current.window.cursor = cursor
6477 return heading
6478
6479 def open_fold(h):
6480 if h.number_of_parents <= open_depth:
6481 vim.command((u'normal! %dgg%dzo' % (h.start_vim, open_depth)).encode(u'utf-8'))
6482 for c in h.children:
6483 open_fold(c)
6484
6485 def close_fold(h):
6486 for c in h.children:
6487 close_fold(c)
6488 if h.number_of_parents >= open_depth - 1 and \
6489 int(vim.eval((u'foldclosed(%d)' % h.start_vim).encode(u'utf-8'))) == -1:
6490 vim.command((u'normal! %dggzc' % (h.start_vim, )).encode(u'utf-8'))
6491
6492 # find deepest fold
6493 open_depth, found_fold = cls._fold_depth(heading)
6494
6495 if not reverse:
6496 # recursively open folds
6497 if found_fold:
6498 for child in heading.children:
6499 open_fold(child)
6500 else:
6501 vim.command((u'%d,%dfoldclose!' % (heading.start_vim, heading.end_of_last_child_vim)).encode(u'utf-8'))
6502
6503 if heading.number_of_parents:
6504 # restore cursor position, it might have been changed by open_fold
6505 vim.current.window.cursor = cursor
6506
6507 p = heading.number_of_parents
6508 if not p:
6509 p = heading.level
6510 # reopen fold again beacause the former closing of the fold closed all levels, including parents!
6511 vim.command((u'normal! %dzo' % (p, )).encode(u'utf-8'))
6512 else:
6513 # close the last level of folds
6514 close_fold(heading)
6515
6516 # restore cursor position
6517 vim.current.window.cursor = cursor
6518 return heading
6519
6520 @classmethod
6521 @apply_count
6522 def global_toggle_folding(cls, reverse=False):
6523 """ Toggle folding globally
6524
6525 :reverse: If False open folding by one level otherwise close it by one.
6526 """
6527 d = ORGMODE.get_document()
6528 if reverse:
6529 foldlevel = int(vim.eval(u'&foldlevel'.encode(u'utf-8')))
6530 if foldlevel == 0:
6531 # open all folds because the user tries to close folds beyound 0
6532 vim.eval(u'feedkeys("zR", "n")'.encode(u'utf-8'))
6533 else:
6534 # vim can reduce the foldlevel on its own
6535 vim.eval(u'feedkeys("zm", "n")'.encode(u'utf-8'))
6536 else:
6537 found = False
6538 for h in d.headings:
6539 res = cls._fold_depth(h)
6540 if res:
6541 found = res[1]
6542 if found:
6543 break
6544 if not found:
6545 # no fold found and the user tries to advance the fold level
6546 # beyond maximum so close everything
6547 vim.eval(u'feedkeys("zM", "n")'.encode(u'utf-8'))
6548 else:
6549 # fold found, vim can increase the foldlevel on its own
6550 vim.eval(u'feedkeys("zr", "n")'.encode(u'utf-8'))
6551
6552 return d
6553
6554 def register(self):
6555 u"""
6556 Registration of plugin. Key bindings and other initialization should be done.
6557 """
6558 # register plug
6559
6560 self.keybindings.append(Keybinding(u'<Tab>', Plug(u'OrgToggleFoldingNormal', u':py ORGMODE.plugins[u"ShowHide"].toggle_folding()<CR>')))
6561 self.menu + ActionEntry(u'&Cycle Visibility', self.keybindings[-1])
6562
6563 self.keybindings.append(Keybinding(u'<S-Tab>', Plug(u'OrgToggleFoldingReverse', u':py ORGMODE.plugins[u"ShowHide"].toggle_folding(reverse=True)<CR>')))
6564 self.menu + ActionEntry(u'Cycle Visibility &Reverse', self.keybindings[-1])
6565
6566 self.keybindings.append(Keybinding(u'<localleader>.', Plug(u'OrgGlobalToggleFoldingNormal', u':py ORGMODE.plugins[u"ShowHide"].global_toggle_folding()<CR>')))
6567 self.menu + ActionEntry(u'Cycle Visibility &Globally', self.keybindings[-1])
6568
6569 self.keybindings.append(Keybinding(u'<localleader>,', Plug(u'OrgGlobalToggleFoldingReverse', u':py ORGMODE.plugins[u"ShowHide"].global_toggle_folding(reverse=True)<CR>')))
6570 self.menu + ActionEntry(u'Cycle Visibility Reverse G&lobally', self.keybindings[-1])
6571
6572 for i in xrange(0, 10):
6573 self.keybindings.append(Keybinding(u'<localleader>%d' % (i, ), u'zM:set fdl=%d<CR>' % i, mode=MODE_NORMAL))
6574ftplugin/orgmode/plugins/TagsProperties.py [[[1
6575206
6576# -*- coding: utf-8 -*-
6577
6578import vim
6579
6580from orgmode._vim import ORGMODE, repeat
6581from orgmode.menu import Submenu, ActionEntry
6582from orgmode.keybinding import Keybinding, Plug, Command
6583from orgmode import settings
6584
6585
6586class TagsProperties(object):
6587 u""" TagsProperties plugin """
6588
6589 def __init__(self):
6590 u""" Initialize plugin """
6591 object.__init__(self)
6592 # menu entries this plugin should create
6593 self.menu = ORGMODE.orgmenu + Submenu(u'&TAGS and Properties')
6594
6595 # key bindings for this plugin
6596 # key bindings are also registered through the menu so only additional
6597 # bindings should be put in this variable
6598 self.keybindings = []
6599
6600 # commands for this plugin
6601 self.commands = []
6602
6603 @classmethod
6604 def complete_tags(cls):
6605 u""" build a list of tags and store it in variable b:org_tag_completion
6606 """
6607 d = ORGMODE.get_document()
6608 heading = d.current_heading()
6609 if not heading:
6610 return
6611
6612 leading_portion = vim.eval(u'a:ArgLead').decode(u'utf-8')
6613 cursor = int(vim.eval(u'a:CursorPos'))
6614
6615 # extract currently completed tag
6616 idx_orig = leading_portion.rfind(u':', 0, cursor)
6617 if idx_orig == -1:
6618 idx = 0
6619 else:
6620 idx = idx_orig
6621
6622 current_tag = leading_portion[idx: cursor].lstrip(u':')
6623 head = leading_portion[:idx + 1]
6624 if idx_orig == -1:
6625 head = u''
6626 tail = leading_portion[cursor:]
6627
6628 # extract all tags of the current file
6629 all_tags = set()
6630 for h in d.all_headings():
6631 for t in h.tags:
6632 all_tags.add(t)
6633
6634 ignorecase = bool(int(settings.get(u'org_tag_completion_ignorecase', int(vim.eval(u'&ignorecase')))))
6635 possible_tags = []
6636 current_tags = heading.tags
6637 for t in all_tags:
6638 if ignorecase:
6639 if t.lower().startswith(current_tag.lower()):
6640 possible_tags.append(t)
6641 elif t.startswith(current_tag):
6642 possible_tags.append(t)
6643
6644 vim.command((u'let b:org_complete_tags = [%s]' % u', '.join([u'"%s%s:%s"' % (head, i, tail) for i in possible_tags])).encode(u'utf-8'))
6645
6646 @classmethod
6647 @repeat
6648 def set_tags(cls):
6649 u""" Set tags for current heading
6650 """
6651 d = ORGMODE.get_document()
6652 heading = d.current_heading()
6653 if not heading:
6654 return
6655
6656 # retrieve tags
6657 res = None
6658 if heading.tags:
6659 res = vim.eval(u'input("Tags: ", ":%s:", "customlist,Org_complete_tags")' % u':'.join(heading.tags))
6660 else:
6661 res = vim.eval(u'input("Tags: ", "", "customlist,Org_complete_tags")')
6662
6663 if res is None:
6664 # user pressed <Esc> abort any further processing
6665 return
6666
6667 # remove empty tags
6668 heading.tags = filter(lambda x: x.strip() != u'', res.decode(u'utf-8').strip().strip(u':').split(u':'))
6669
6670 d.write()
6671
6672 return u'OrgSetTags'
6673
6674 @classmethod
6675 def find_tags(cls):
6676 """ Find tags in current file
6677 """
6678 tags = vim.eval(u'input("Find Tags: ", "", "customlist,Org_complete_tags")')
6679 if tags is None:
6680 # user pressed <Esc> abort any further processing
6681 return
6682
6683 tags = filter(lambda x: x.strip() != u'', tags.decode(u'utf-8').strip().strip(u':').split(u':'))
6684 if tags:
6685 searchstring = u'\\('
6686 first = True
6687 for t1 in tags:
6688 if first:
6689 first = False
6690 searchstring += u'%s' % t1
6691 else:
6692 searchstring += u'\\|%s' % t1
6693
6694 for t2 in tags:
6695 if t1 == t2:
6696 continue
6697 searchstring += u'\\(:[a-zA-Z:]*\\)\?:%s' % t2
6698 searchstring += u'\\)'
6699
6700 vim.command(u'/\\zs:%s:\\ze' % searchstring)
6701 return u'OrgFindTags'
6702
6703 @classmethod
6704 def realign_tags(cls):
6705 u"""
6706 Updates tags when user finished editing a heading
6707 """
6708 d = ORGMODE.get_document(allow_dirty=True)
6709 heading = d.find_current_heading()
6710 if not heading:
6711 return
6712
6713 if vim.current.window.cursor[0] == heading.start_vim:
6714 heading.set_dirty_heading()
6715 d.write_heading(heading, including_children=False)
6716
6717 @classmethod
6718 def realign_all_tags(cls):
6719 u"""
6720 Updates tags when user finishes editing a heading
6721 """
6722 d = ORGMODE.get_document()
6723 for heading in d.all_headings():
6724 heading.set_dirty_heading()
6725
6726 d.write()
6727
6728 def register(self):
6729 u"""
6730 Registration of plugin. Key bindings and other initialization should be done.
6731 """
6732 # an Action menu entry which binds "keybinding" to action ":action"
6733 settings.set(u'org_tag_column', vim.eval(u'&textwidth'))
6734 settings.set(u'org_tag_completion_ignorecase', int(vim.eval(u'&ignorecase')))
6735
6736 cmd = Command(
6737 u'OrgSetTags',
6738 u':py ORGMODE.plugins[u"TagsProperties"].set_tags()')
6739 self.commands.append(cmd)
6740 keybinding = Keybinding(
6741 u'<localleader>st',
6742 Plug(u'OrgSetTags', cmd))
6743 self.keybindings.append(keybinding)
6744 self.menu + ActionEntry(u'Set &Tags', keybinding)
6745
6746 cmd = Command(
6747 u'OrgFindTags',
6748 u':py ORGMODE.plugins[u"TagsProperties"].find_tags()')
6749 self.commands.append(cmd)
6750 keybinding = Keybinding(
6751 u'<localleader>ft',
6752 Plug(u'OrgFindTags', cmd))
6753 self.keybindings.append(keybinding)
6754 self.menu + ActionEntry(u'&Find Tags', keybinding)
6755
6756 cmd = Command(
6757 u'OrgTagsRealign',
6758 u":py ORGMODE.plugins[u'TagsProperties'].realign_all_tags()")
6759 self.commands.append(cmd)
6760
6761 # workaround to align tags when user is leaving insert mode
6762 vim.command(u"""function Org_complete_tags(ArgLead, CmdLine, CursorPos)
6763python << EOF
6764ORGMODE.plugins[u'TagsProperties'].complete_tags()
6765EOF
6766if exists('b:org_complete_tags')
6767 let tmp = b:org_complete_tags
6768 unlet b:org_complete_tags
6769 return tmp
6770else
6771 return []
6772endif
6773endfunction""".encode(u'utf-8'))
6774
6775 # this is for all org files opened after this file
6776 vim.command(u"au orgmode FileType org :au orgmode InsertLeave <buffer> :py ORGMODE.plugins[u'TagsProperties'].realign_tags()".encode(u'utf-8'))
6777
6778 # this is for the current file
6779 vim.command(u"au orgmode InsertLeave <buffer> :py ORGMODE.plugins[u'TagsProperties'].realign_tags()".encode(u'utf-8'))
6780
6781# vim: set noexpandtab:
6782ftplugin/orgmode/plugins/Todo.py [[[1
6783351
6784# -*- coding: utf-8 -*-
6785
6786import vim
6787
6788from orgmode._vim import echom, ORGMODE, apply_count, repeat, realign_tags
6789from orgmode import settings
6790from orgmode.liborgmode.base import Direction
6791from orgmode.menu import Submenu, ActionEntry
6792from orgmode.keybinding import Keybinding, Plug
6793
6794# temporary todo states for differnent orgmode buffers
6795ORGTODOSTATES = {}
6796
6797
6798def split_access_key(t):
6799 u"""
6800 :t: todo state
6801
6802 :return: todo state and access key separated (TODO, ACCESS_KEY)
6803 """
6804 if type(t) != unicode:
6805 return (None, None)
6806
6807 idx = t.find(u'(')
6808 v, k = ((t[:idx], t[idx + 1:-1]) if t[idx + 1:-1] else (t, None)) if idx != -1 else (t, None)
6809 return (v, k)
6810
6811
6812class Todo(object):
6813 u"""
6814 Todo plugin.
6815
6816 Description taken from orgmode.org:
6817
6818 You can use TODO keywords to indicate different sequential states in the
6819 process of working on an item, for example:
6820
6821 ["TODO", "FEEDBACK", "VERIFY", "|", "DONE", "DELEGATED"]
6822
6823 The vertical bar separates the TODO keywords (states that need action) from
6824 the DONE states (which need no further action). If you don't provide the
6825 separator bar, the last state is used as the DONE state. With this setup,
6826 the command ``,d`` will cycle an entry from TODO to FEEDBACK, then to
6827 VERIFY, and finally to DONE and DELEGATED.
6828 """
6829
6830 def __init__(self):
6831 u""" Initialize plugin """
6832 object.__init__(self)
6833 # menu entries this plugin should create
6834 self.menu = ORGMODE.orgmenu + Submenu(u'&TODO Lists')
6835
6836 # key bindings for this plugin
6837 # key bindings are also registered through the menu so only additional
6838 # bindings should be put in this variable
6839 self.keybindings = []
6840
6841 @classmethod
6842 def _get_next_state(
6843 cls, current_state, all_states,
6844 direction=Direction.FORWARD, interactive=False, next_set=False):
6845 u"""
6846 WTF is going on here!!!
6847 FIXME: reimplement this in a clean way :)
6848
6849 :current_state: the current todo state
6850 :all_states: a list containing all todo states within sublists.
6851 The todo states may contain access keys
6852 :direction: direction of state or keyword set change (forward/backward)
6853 :interactive: if interactive and more than one todo sequence is
6854 specified, open a selection window
6855 :next_set: advance to the next keyword set in defined direction
6856
6857 :return: return the next state as string, or NONE if the
6858 next state is no state.
6859 """
6860 if not all_states:
6861 return
6862
6863 def find_current_todo_state(c, a, stop=0):
6864 u"""
6865 :c: current todo state
6866 :a: list of todo states
6867 :stop: internal parameter for parsing only two levels of lists
6868
6869 :return: first position of todo state in list in the form
6870 (IDX_TOPLEVEL, IDX_SECOND_LEVEL (0|1), IDX_OF_ITEM)
6871 """
6872 for i in xrange(0, len(a)):
6873 if type(a[i]) in (tuple, list) and stop < 2:
6874 r = find_current_todo_state(c, a[i], stop=stop + 1)
6875 if r:
6876 r.insert(0, i)
6877 return r
6878 # ensure that only on the second level of sublists todo states
6879 # are found
6880 if type(a[i]) == unicode and stop == 2:
6881 _i = split_access_key(a[i])[0]
6882 if c == _i:
6883 return [i]
6884
6885 ci = find_current_todo_state(current_state, all_states)
6886
6887 if not ci:
6888 if next_set and direction == Direction.BACKWARD:
6889 echom(u'Already at the first keyword set')
6890 return current_state
6891
6892 return split_access_key(all_states[0][0][0] if all_states[0][0] else all_states[0][1][0])[0] \
6893 if direction == Direction.FORWARD else \
6894 split_access_key(all_states[0][1][-1] if all_states[0][1] else all_states[0][0][-1])[0]
6895 elif next_set:
6896 if direction == Direction.FORWARD and ci[0] + 1 < len(all_states[ci[0]]):
6897 echom(u'Keyword set: %s | %s' % (u', '.join(all_states[ci[0] + 1][0]), u', '.join(all_states[ci[0] + 1][1])))
6898 return split_access_key(
6899 all_states[ci[0] + 1][0][0] if all_states[ci[0] + 1][0] else all_states[ci[0] + 1][1][0])[0]
6900 elif current_state is not None and direction == Direction.BACKWARD and ci[0] - 1 >= 0:
6901 echom(u'Keyword set: %s | %s' % (u', '.join(all_states[ci[0] - 1][0]), u', '.join(all_states[ci[0] - 1][1])))
6902 return split_access_key(
6903 all_states[ci[0] - 1][0][0] if all_states[ci[0] - 1][0] else all_states[ci[0] - 1][1][0])[0]
6904 else:
6905 echom(u'Already at the %s keyword set' % (u'first' if direction == Direction.BACKWARD else u'last'))
6906 return current_state
6907 else:
6908 next_pos = ci[2] + 1 if direction == Direction.FORWARD else ci[2] - 1
6909 if direction == Direction.FORWARD:
6910 if next_pos < len(all_states[ci[0]][ci[1]]):
6911 # select next state within done or todo states
6912 return split_access_key(all_states[ci[0]][ci[1]][next_pos])[0]
6913
6914 elif not ci[1] and next_pos - len(all_states[ci[0]][ci[1]]) < len(all_states[ci[0]][ci[1] + 1]):
6915 # finished todo states, jump to done states
6916 return split_access_key(all_states[ci[0]][ci[1] + 1][next_pos - len(all_states[ci[0]][ci[1]])])[0]
6917 else:
6918 if next_pos >= 0:
6919 # select previous state within done or todo states
6920 return split_access_key(all_states[ci[0]][ci[1]][next_pos])[0]
6921
6922 elif ci[1] and len(all_states[ci[0]][ci[1] - 1]) + next_pos < len(all_states[ci[0]][ci[1] - 1]):
6923 # finished done states, jump to todo states
6924 return split_access_key(all_states[ci[0]][ci[1] - 1][len(all_states[ci[0]][ci[1] - 1]) + next_pos])[0]
6925
6926 @classmethod
6927 @realign_tags
6928 @repeat
6929 @apply_count
6930 def toggle_todo_state(cls, direction=Direction.FORWARD, interactive=False, next_set=False):
6931 u""" Toggle state of TODO item
6932
6933 :returns: The changed heading
6934 """
6935 d = ORGMODE.get_document(allow_dirty=True)
6936
6937 # get heading
6938 heading = d.find_current_heading()
6939 if not heading:
6940 vim.eval(u'feedkeys("^", "n")')
6941 return
6942
6943 todo_states = d.get_todo_states(strip_access_key=False)
6944 # get todo states
6945 if not todo_states:
6946 echom(u'No todo keywords configured.')
6947 return
6948
6949 current_state = heading.todo
6950
6951 # get new state interactively
6952 if interactive:
6953 # determine position of the interactive prompt
6954 prompt_pos = settings.get(u'org_todo_prompt_position', u'botright')
6955 if prompt_pos not in [u'botright', u'topleft']:
6956 prompt_pos = u'botright'
6957
6958 # pass todo states to new window
6959 ORGTODOSTATES[d.bufnr] = todo_states
6960 settings.set(
6961 u'org_current_state_%d' % d.bufnr,
6962 current_state if current_state is not None else u'', overwrite=True)
6963 todo_buffer_exists = bool(int(vim.eval((
6964 u'bufexists("org:todo/%d")' % (d.bufnr, )).encode(u'utf-8'))))
6965 if todo_buffer_exists:
6966 # if the buffer already exists, reuse it
6967 vim.command((
6968 u'%s sbuffer org:todo/%d' % (prompt_pos, d.bufnr, )).encode(u'utf-8'))
6969 else:
6970 # create a new window
6971 vim.command((
6972 u'keepalt %s %dsplit org:todo/%d' % (prompt_pos, len(todo_states), d.bufnr)).encode(u'utf-8'))
6973 else:
6974 new_state = Todo._get_next_state(
6975 current_state, todo_states, direction=direction,
6976 interactive=interactive, next_set=next_set)
6977 cls.set_todo_state(new_state)
6978
6979 # plug
6980 plug = u'OrgTodoForward'
6981 if direction == Direction.BACKWARD:
6982 plug = u'OrgTodoBackward'
6983
6984 return plug
6985
6986 @classmethod
6987 def set_todo_state(cls, state):
6988 u""" Set todo state for buffer.
6989
6990 :bufnr: Number of buffer the todo state should be updated for
6991 :state: The new todo state
6992 """
6993 lineno, colno = vim.current.window.cursor
6994 d = ORGMODE.get_document(allow_dirty=True)
6995 heading = d.find_current_heading()
6996
6997 if not heading:
6998 return
6999
7000 current_state = heading.todo
7001
7002 # set new headline
7003 heading.todo = state
7004 d.write_heading(heading)
7005
7006 # move cursor along with the inserted state only when current position
7007 # is in the heading; otherwite do nothing
7008 if heading.start_vim == lineno and colno > heading.level:
7009 if current_state is not None and \
7010 colno <= heading.level + len(current_state):
7011 # the cursor is actually on the todo keyword
7012 # move it back to the beginning of the keyword in that case
7013 vim.current.window.cursor = (lineno, heading.level + 1)
7014 else:
7015 # the cursor is somewhere in the text, move it along
7016 if current_state is None and state is None:
7017 offset = 0
7018 elif current_state is None and state is not None:
7019 offset = len(state) + 1
7020 elif current_state is not None and state is None:
7021 offset = -len(current_state) - 1
7022 else:
7023 offset = len(state) - len(current_state)
7024 vim.current.window.cursor = (lineno, colno + offset)
7025
7026 @classmethod
7027 def init_org_todo(cls):
7028 u""" Initialize org todo selection window.
7029 """
7030 bufnr = int(vim.current.buffer.name.split('/')[-1])
7031 all_states = ORGTODOSTATES.get(bufnr, None)
7032
7033 # because timeoutlen can only be set globally it needs to be stored and restored later
7034 vim.command(u'let g:org_sav_timeoutlen=&timeoutlen'.encode(u'utf-8'))
7035 vim.command(u'au orgmode BufEnter <buffer> :if ! exists("g:org_sav_timeoutlen")|let g:org_sav_timeoutlen=&timeoutlen|set timeoutlen=1|endif'.encode(u'utf-8'))
7036 vim.command(u'au orgmode BufLeave <buffer> :if exists("g:org_sav_timeoutlen")|let &timeoutlen=g:org_sav_timeoutlen|unlet g:org_sav_timeoutlen|endif'.encode(u'utf-8'))
7037 # make window a scratch window and set the statusline differently
7038 vim.command(u'setlocal nolist tabstop=16 buftype=nofile timeout timeoutlen=1 winfixheight'.encode(u'utf-8'))
7039 vim.command((u'setlocal statusline=Org\\ todo\\ (%s)' % vim.eval((u'fnameescape(fnamemodify(bufname(%d), ":t"))' % bufnr).encode(u'utf-8'))).encode(u'utf-8'))
7040 vim.command((u'nnoremap <silent> <buffer> <Esc> :%sbw<CR>' % (vim.eval(u'bufnr("%")'.encode(u'utf-8')), )).encode(u'utf-8'))
7041 vim.command(u'nnoremap <silent> <buffer> <CR> :let g:org_state = fnameescape(expand("<cword>"))<Bar>bw<Bar>exec "py ORGMODE.plugins[u\'Todo\'].set_todo_state(\'".g:org_state."\')"<Bar>unlet! g:org_state<CR>'.encode(u'utf-8'))
7042
7043 if all_states is None:
7044 vim.command(u'bw'.encode(u'utf-8'))
7045 echom(u'No todo states avaiable for buffer %s' % vim.current.buffer.name)
7046
7047 for l in xrange(0, len(all_states)):
7048 res = u''
7049 for j in xrange(0, 2):
7050 if j < len(all_states[l]):
7051 for i in all_states[l][j]:
7052 if type(i) != unicode:
7053 continue
7054 v, k = split_access_key(i)
7055 if k:
7056 res += (u'\t' if res else u'') + u'[%s] %s' % (k, v)
7057 # map access keys to callback that updates current heading
7058 # map selection keys
7059 vim.command((u'nnoremap <silent> <buffer> %s :bw<Bar>py ORGMODE.plugins[u"Todo"].set_todo_state("%s".decode(u"utf-8"))<CR>' % (k, v)).encode(u'utf-8'))
7060 elif v:
7061 res += (u'\t' if res else u'') + v
7062 if res:
7063 if l == 0:
7064 # WORKAROUND: the cursor can not be positioned properly on
7065 # the first line. Another line is just inserted and it
7066 # works great
7067 vim.current.buffer[0] = u''.encode(u'utf-8')
7068 vim.current.buffer.append(res.encode(u'utf-8'))
7069
7070 # position the cursor of the current todo item
7071 vim.command(u'normal! G'.encode(u'utf-8'))
7072 current_state = settings.unset(u'org_current_state_%d' % bufnr)
7073 found = False
7074 if current_state is not None and current_state != '':
7075 for i in xrange(0, len(vim.current.buffer)):
7076 idx = vim.current.buffer[i].find(current_state)
7077 if idx != -1:
7078 vim.current.window.cursor = (i + 1, idx)
7079 found = True
7080 break
7081 if not found:
7082 vim.current.window.cursor = (2, 4)
7083
7084 # finally make buffer non modifiable
7085 vim.command(u'setfiletype orgtodo'.encode(u'utf-8'))
7086 vim.command(u'setlocal nomodifiable'.encode(u'utf-8'))
7087
7088 # remove temporary todo states for the current buffer
7089 del ORGTODOSTATES[bufnr]
7090
7091 def register(self):
7092 u"""
7093 Registration of plugin. Key bindings and other initialization should be done.
7094 """
7095 self.keybindings.append(Keybinding(u'<localleader>ct', Plug(
7096 u'OrgTodoToggleNonInteractive',
7097 u':py ORGMODE.plugins[u"Todo"].toggle_todo_state(interactive=False)<CR>')))
7098 self.menu + ActionEntry(u'&TODO/DONE/-', self.keybindings[-1])
7099
7100 self.keybindings.append(Keybinding(u'<localleader>d', Plug(
7101 u'OrgTodoToggleInteractive',
7102 u':py ORGMODE.plugins[u"Todo"].toggle_todo_state(interactive=True)<CR>')))
7103 self.menu + ActionEntry(u'&TODO/DONE/- (interactiv)', self.keybindings[-1])
7104
7105 # add submenu
7106 submenu = self.menu + Submenu(u'Select &keyword')
7107
7108 self.keybindings.append(Keybinding(u'<S-Right>', Plug(
7109 u'OrgTodoForward',
7110 u':py ORGMODE.plugins[u"Todo"].toggle_todo_state()<CR>')))
7111 submenu + ActionEntry(u'&Next keyword', self.keybindings[-1])
7112
7113 self.keybindings.append(Keybinding(u'<S-Left>', Plug(
7114 u'OrgTodoBackward',
7115 u':py ORGMODE.plugins[u"Todo"].toggle_todo_state(direction=2)<CR>')))
7116 submenu + ActionEntry(u'&Previous keyword', self.keybindings[-1])
7117
7118 self.keybindings.append(Keybinding(u'<C-S-Right>', Plug(
7119 u'OrgTodoSetForward',
7120 u':py ORGMODE.plugins[u"Todo"].toggle_todo_state(next_set=True)<CR>')))
7121 submenu + ActionEntry(u'Next keyword &set', self.keybindings[-1])
7122
7123 self.keybindings.append(Keybinding(u'<C-S-Left>', Plug(
7124 u'OrgTodoSetBackward',
7125 u':py ORGMODE.plugins[u"Todo"].toggle_todo_state(direction=2, next_set=True)<CR>')))
7126 submenu + ActionEntry(u'Previous &keyword set', self.keybindings[-1])
7127
7128 settings.set(u'org_todo_keywords', [u'TODO'.encode(u'utf-8'), u'|'.encode(u'utf-8'), u'DONE'.encode(u'utf-8')])
7129
7130 settings.set(u'org_todo_prompt_position', u'botright')
7131
7132 vim.command(u'au orgmode BufReadCmd org:todo/* :py ORGMODE.plugins[u"Todo"].init_org_todo()'.encode(u'utf-8'))
7133
7134# vim: set noexpandtab:
7135ftplugin/orgmode/plugins/__init__.py [[[1
71361
7137# -*- coding: utf-8 -*-
7138ftplugin/orgmode/plugins/Export.py [[[1
7139162
7140# -*- coding: utf-8 -*-
7141
7142import os
7143import subprocess
7144
7145import vim
7146
7147from orgmode._vim import ORGMODE, echoe, echom
7148from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu
7149from orgmode.keybinding import Keybinding, Plug, Command
7150from orgmode import settings
7151
7152
7153class Export(object):
7154 u"""
7155 Export a orgmode file using emacs orgmode.
7156
7157 This is a *very simple* wrapper of the emacs/orgmode export. emacs and
7158 orgmode need to be installed. We simply call emacs with some options to
7159 export the .org.
7160
7161 TODO: Offer export options in vim. Don't use the menu.
7162 TODO: Maybe use a native implementation.
7163 """
7164
7165 def __init__(self):
7166 u""" Initialize plugin """
7167 object.__init__(self)
7168 # menu entries this plugin should create
7169 self.menu = ORGMODE.orgmenu + Submenu(u'Export')
7170
7171 # key bindings for this plugin
7172 # key bindings are also registered through the menu so only additional
7173 # bindings should be put in this variable
7174 self.keybindings = []
7175
7176 # commands for this plugin
7177 self.commands = []
7178
7179 @classmethod
7180 def _get_init_script(cls):
7181 init_script = settings.get(u'org_export_init_script', u'')
7182 if init_script:
7183 init_script = os.path.expandvars(os.path.expanduser(init_script))
7184 if os.path.exists(init_script):
7185 return init_script
7186 else:
7187 echoe(u'Unable to find init script %s' % init_script)
7188
7189 @classmethod
7190 def _export(cls, format_):
7191 """Export current file to format_.
7192
7193 :format_: pdf or html
7194 :returns: return code
7195 """
7196 emacsbin = os.path.expandvars(os.path.expanduser(
7197 settings.get(u'org_export_emacs', u'/usr/bin/emacs')))
7198 if not os.path.exists(emacsbin):
7199 echoe(u'Unable to find emacs binary %s' % emacsbin)
7200
7201 # build the export command
7202 cmd = [
7203 emacsbin,
7204 u'-nw',
7205 u'--batch',
7206 u'--visit=%s' % vim.eval(u'expand("%:p")'),
7207 u'--funcall=%s' % format_
7208 ]
7209 # source init script as well
7210 init_script = cls._get_init_script()
7211 if init_script:
7212 cmd.extend(['--script', init_script])
7213
7214 # export
7215 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
7216 p.wait()
7217
7218 if p.returncode != 0 or settings.get(u'org_export_verbose') == 1:
7219 echom('\n'.join(p.communicate()))
7220 return p.returncode
7221
7222 @classmethod
7223 def topdf(cls):
7224 u"""Export the current buffer as pdf using emacs orgmode."""
7225 ret = cls._export(u'org-latex-export-to-pdf')
7226 if ret != 0:
7227 echoe(u'PDF export failed.')
7228 else:
7229 echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'pdf'))
7230
7231 @classmethod
7232 def tohtml(cls):
7233 u"""Export the current buffer as html using emacs orgmode."""
7234 ret = cls._export(u'org-html-export-to-html')
7235 if ret != 0:
7236 echoe(u'HTML export failed.')
7237 else:
7238 echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'html'))
7239
7240 @classmethod
7241 def tolatex(cls):
7242 u"""Export the current buffer as latex using emacs orgmode."""
7243 ret = cls._export(u'org-latex-export-to-latex')
7244 if ret != 0:
7245 echoe(u'latex export failed.')
7246 else:
7247 echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'tex'))
7248
7249 @classmethod
7250 def tomarkdown(cls):
7251 u"""Export the current buffer as markdown using emacs orgmode."""
7252 ret = cls._export(u'org-md-export-to-markdown')
7253 if ret != 0:
7254 echoe('Markdown export failed. Make sure org-md-export-to-markdown is loaded in emacs, see the manual for details.')
7255 else:
7256 echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'md'))
7257
7258 def register(self):
7259 u"""Registration and keybindings."""
7260
7261 # path to emacs executable
7262 settings.set(u'org_export_emacs', u'/usr/bin/emacs')
7263 # verbose output for export
7264 settings.set(u'org_export_verbose', 0)
7265 # allow the user to define an initialization script
7266 settings.set(u'org_export_init_script', u'')
7267
7268 # to PDF
7269 add_cmd_mapping_menu(
7270 self,
7271 name=u'OrgExportToPDF',
7272 function=u':py ORGMODE.plugins[u"Export"].topdf()<CR>',
7273 key_mapping=u'<localleader>ep',
7274 menu_desrc=u'To PDF (via Emacs)'
7275 )
7276 # to latex
7277 add_cmd_mapping_menu(
7278 self,
7279 name=u'OrgExportToLaTeX',
7280 function=u':py ORGMODE.plugins[u"Export"].tolatex()<CR>',
7281 key_mapping=u'<localleader>el',
7282 menu_desrc=u'To LaTeX (via Emacs)'
7283 )
7284 # to HTML
7285 add_cmd_mapping_menu(
7286 self,
7287 name=u'OrgExportToHTML',
7288 function=u':py ORGMODE.plugins[u"Export"].tohtml()<CR>',
7289 key_mapping=u'<localleader>eh',
7290 menu_desrc=u'To HTML (via Emacs)'
7291 )
7292 # to Markdown
7293 add_cmd_mapping_menu(
7294 self,
7295 name=u'OrgExportToMarkdown',
7296 function=u':py ORGMODE.plugins[u"Export"].tomarkdown()<CR>',
7297 key_mapping=u'<localleader>em',
7298 menu_desrc=u'To Markdown (via Emacs)'
7299 )
7300
7301# vim: set noexpandtab:
7302ftplugin/orgmode/__init__.py [[[1
73031
7304# -*- coding: utf-8 -*-
7305ftplugin/orgmode/_vim.py [[[1
7306408
7307# -*- coding: utf-8 -*-
7308
7309"""
7310 VIM ORGMODE
7311 ~~~~~~~~~~~~
7312
7313 TODO
7314"""
7315
7316import imp
7317import types
7318import re
7319
7320import vim
7321from datetime import datetime
7322
7323import orgmode.keybinding
7324import orgmode.menu
7325import orgmode.plugins
7326import orgmode.settings
7327from orgmode.exceptions import PluginError
7328from orgmode.vimbuffer import VimBuffer
7329from orgmode.liborgmode.agenda import AgendaManager
7330
7331
7332REPEAT_EXISTS = bool(int(vim.eval('exists("*repeat#set()")')))
7333TAGSPROPERTIES_EXISTS = False
7334
7335cache_heading = None
7336
7337def realign_tags(f):
7338 u"""
7339 Update tag alignment, dependency to TagsProperties plugin!
7340 """
7341 def r(*args, **kwargs):
7342 global TAGSPROPERTIES_EXISTS
7343 res = f(*args, **kwargs)
7344
7345 if not TAGSPROPERTIES_EXISTS and u'TagsProperties' in ORGMODE.plugins:
7346 TAGSPROPERTIES_EXISTS = True
7347
7348 if TAGSPROPERTIES_EXISTS:
7349 ORGMODE.plugins[u'TagsProperties'].realign_tags()
7350
7351 return res
7352 return r
7353
7354
7355def repeat(f):
7356 u"""
7357 Integrate with the repeat plugin if available
7358
7359 The decorated function must return the name of the <Plug> command to
7360 execute by the repeat plugin.
7361 """
7362 def r(*args, **kwargs):
7363 res = f(*args, **kwargs)
7364 if REPEAT_EXISTS and isinstance(res, basestring):
7365 vim.command((u'silent! call repeat#set("\\<Plug>%s")' % res)
7366 .encode(u'utf-8'))
7367 return res
7368 return r
7369
7370
7371def apply_count(f):
7372 u"""
7373 Decorator which executes function v:count or v:prevount (not implemented,
7374 yet) times. The decorated function must return a value that evaluates to
7375 True otherwise the function is not repeated.
7376 """
7377 def r(*args, **kwargs):
7378 count = 0
7379 try:
7380 count = int(vim.eval(u'v:count'.encode('utf-8')))
7381
7382 # visual count is not implemented yet
7383 #if not count:
7384 # count = int(vim.eval(u'v:prevcount'.encode(u'utf-8')))
7385 except Exception, e:
7386 pass
7387
7388 res = f(*args, **kwargs)
7389 count -= 1
7390 while res and count > 0:
7391 f(*args, **kwargs)
7392 count -= 1
7393 return res
7394 return r
7395
7396
7397def echo(message):
7398 u"""
7399 Print a regular message that will not be visible to the user when
7400 multiple lines are printed
7401 """
7402 for m in message.split(u'\n'):
7403 vim.command((u':echo "%s"' % m).encode(u'utf-8'))
7404
7405
7406def echom(message):
7407 u"""
7408 Print a regular message that will be visible to the user, even when
7409 multiple lines are printed
7410 """
7411 # probably some escaping is needed here
7412 for m in message.split(u'\n'):
7413 vim.command((u':echomsg "%s"' % m).encode(u'utf-8'))
7414
7415
7416def echoe(message):
7417 u"""
7418 Print an error message. This should only be used for serious errors!
7419 """
7420 # probably some escaping is needed here
7421 for m in message.split(u'\n'):
7422 vim.command((u':echoerr "%s"' % m).encode(u'utf-8'))
7423
7424
7425def insert_at_cursor(text, move=True, start_insertmode=False):
7426 u"""Insert text at the position of the cursor.
7427
7428 If move==True move the cursor with the inserted text.
7429 """
7430 d = ORGMODE.get_document(allow_dirty=True)
7431 line, col = vim.current.window.cursor
7432 _text = d._content[line - 1]
7433 d._content[line - 1] = _text[:col + 1] + text + _text[col + 1:]
7434 if move:
7435 vim.current.window.cursor = (line, col + len(text))
7436 if start_insertmode:
7437 vim.command(u'startinsert'.encode(u'utf-8'))
7438
7439
7440def get_user_input(message):
7441 u"""Print the message and take input from the user.
7442 Return the input or None if there is no input.
7443 """
7444 vim.command(u'call inputsave()'.encode(u'utf-8'))
7445 vim.command((u"let user_input = input('" + message + u": ')")
7446 .encode(u'utf-8'))
7447 vim.command(u'call inputrestore()'.encode(u'utf-8'))
7448 try:
7449 return vim.eval(u'user_input'.encode(u'utf-8')).decode(u'utf-8')
7450 except:
7451 return None
7452
7453
7454def get_bufnumber(bufname):
7455 """
7456 Return the number of the buffer for the given bufname if it exist;
7457 else None.
7458 """
7459 for b in vim.buffers:
7460 if b.name == bufname:
7461 return int(b.number)
7462
7463
7464def get_bufname(bufnr):
7465 """
7466 Return the name of the buffer for the given bufnr if it exist; else None.
7467 """
7468 for b in vim.buffers:
7469 if b.number == bufnr:
7470 return b.name
7471
7472
7473def indent_orgmode():
7474 u""" Set the indent value for the current line in the variable
7475 b:indent_level
7476
7477 Vim prerequisites:
7478 :setlocal indentexpr=Method-which-calls-indent_orgmode
7479
7480 :returns: None
7481 """
7482 line = int(vim.eval(u'v:lnum'.encode(u'utf-8')))
7483 d = ORGMODE.get_document()
7484 heading = d.current_heading(line - 1)
7485 if heading and line != heading.start_vim:
7486 heading.init_checkboxes()
7487 checkbox = heading.current_checkbox()
7488 level = heading.level + 1
7489 if checkbox:
7490 level = level + checkbox.number_of_parents * 6
7491 if line != checkbox.start_vim:
7492 # indent body up to the beginning of the checkbox' text
7493 # if checkbox isn't indented to the proper location, the body
7494 # won't be indented either
7495 level = checkbox.level + len(checkbox.type) + 1 + \
7496 (4 if checkbox.status else 0)
7497 vim.command((u'let b:indent_level = %d' % level).encode(u'utf-8'))
7498
7499
7500def fold_text(allow_dirty=False):
7501 u""" Set the fold text
7502 :setlocal foldtext=Method-which-calls-foldtext
7503
7504 :allow_dirty: Perform a query without (re)building the DOM if True
7505 :returns: None
7506 """
7507 line = int(vim.eval(u'v:foldstart'.encode(u'utf-8')))
7508 d = ORGMODE.get_document(allow_dirty=allow_dirty)
7509 heading = None
7510 if allow_dirty:
7511 heading = d.find_current_heading(line - 1)
7512 else:
7513 heading = d.current_heading(line - 1)
7514 if heading:
7515 str_heading = unicode(heading)
7516
7517 # expand tabs
7518 ts = int(vim.eval(u'&ts'.encode('utf-8')))
7519 idx = str_heading.find(u'\t')
7520 if idx != -1:
7521 tabs, spaces = divmod(idx, ts)
7522 str_heading = str_heading.replace(u'\t', u' ' * (ts - spaces), 1)
7523 str_heading = str_heading.replace(u'\t', u' ' * ts)
7524
7525 # Workaround for vim.command seems to break the completion menu
7526 vim.eval((u'SetOrgFoldtext("%s...")' % (re.sub(r'\[\[([^[\]]*\]\[)?([^[\]]+)\]\]', r'\2',
7527 str_heading).replace( u'\\', u'\\\\').replace(u'"', u'\\"'), )).encode(u'utf-8'))
7528
7529
7530def fold_orgmode(allow_dirty=False):
7531 u""" Set the fold expression/value for the current line in the variable
7532 b:fold_expr
7533
7534 Vim prerequisites:
7535 :setlocal foldmethod=expr
7536 :setlocal foldexpr=Method-which-calls-fold_orgmode
7537
7538 :allow_dirty: Perform a query without (re)building the DOM if True
7539 :returns: None
7540 """
7541 line = int(vim.eval(u'v:lnum'.encode(u'utf-8')))
7542 d = ORGMODE.get_document(allow_dirty=allow_dirty)
7543 heading = None
7544 if allow_dirty:
7545 heading = d.find_current_heading(line - 1)
7546 else:
7547 heading = d.current_heading(line - 1)
7548
7549 # if cache_heading != heading:
7550 # heading.init_checkboxes()
7551 # checkbox = heading.current_checkbox()
7552
7553 # cache_heading = heading
7554 if heading:
7555 # if checkbox:
7556 # vim.command((u'let b:fold_expr = ">%d"' % heading.level + checkbox.level).encode(u'utf-8'))
7557 if 0:
7558 pass
7559 elif line == heading.start_vim:
7560 vim.command((u'let b:fold_expr = ">%d"' % heading.level).encode(u'utf-8'))
7561 #elif line == heading.end_vim:
7562 # vim.command((u'let b:fold_expr = "<%d"' % heading.level).encode(u'utf-8'))
7563 # end_of_last_child_vim is a performance junky and is actually not needed
7564 #elif line == heading.end_of_last_child_vim:
7565 # vim.command((u'let b:fold_expr = "<%d"' % heading.level).encode(u'utf-8'))
7566 else:
7567 vim.command((u'let b:fold_expr = %d' % heading.level).encode(u'utf-8'))
7568
7569
7570def date_to_str(date):
7571 if isinstance(date, datetime):
7572 date = date.strftime(
7573 u'%Y-%m-%d %a %H:%M'.encode(u'utf-8')).decode(u'utf-8')
7574 else:
7575 date = date.strftime(
7576 u'%Y-%m-%d %a'.encode(u'utf-8')).decode(u'utf-8')
7577 return date
7578
7579class OrgMode(object):
7580 u""" Vim Buffer """
7581
7582 def __init__(self):
7583 object.__init__(self)
7584 self.debug = bool(int(orgmode.settings.get(u'org_debug', False)))
7585
7586 self.orgmenu = orgmode.menu.Submenu(u'&Org')
7587 self._plugins = {}
7588 # list of vim buffer objects
7589 self._documents = {}
7590
7591 # agenda manager
7592 self.agenda_manager = AgendaManager()
7593
7594 def get_document(self, bufnr=0, allow_dirty=False):
7595 """ Retrieve instance of vim buffer document. This Document should be
7596 used for manipulating the vim buffer.
7597
7598 :bufnr: Retrieve document with bufnr
7599 :allow_dirty: Allow the retrieved document to be dirty
7600
7601 :returns: vim buffer instance
7602 """
7603 if bufnr == 0:
7604 bufnr = vim.current.buffer.number
7605
7606 if bufnr in self._documents:
7607 if allow_dirty or self._documents[bufnr].is_insync:
7608 return self._documents[bufnr]
7609 self._documents[bufnr] = VimBuffer(bufnr).init_dom()
7610 return self._documents[bufnr]
7611
7612 @property
7613 def plugins(self):
7614 return self._plugins.copy()
7615
7616 @orgmode.keybinding.register_keybindings
7617 @orgmode.keybinding.register_commands
7618 @orgmode.menu.register_menu
7619 def register_plugin(self, plugin):
7620 if not isinstance(plugin, basestring):
7621 raise ValueError(u'Parameter plugin is not of type string')
7622
7623 if plugin == u'|':
7624 self.orgmenu + orgmode.menu.Separator()
7625 self.orgmenu.children[-1].create()
7626 return
7627
7628 if plugin in self._plugins:
7629 raise PluginError(u'Plugin %s has already been loaded')
7630
7631 # a python module
7632 module = None
7633
7634 # actual plugin class
7635 _class = None
7636
7637 # locate module and initialize plugin class
7638 try:
7639 module = imp.find_module(plugin, orgmode.plugins.__path__)
7640 except ImportError, e:
7641 echom(u'Plugin not found: %s' % plugin)
7642 if self.debug:
7643 raise e
7644 return
7645
7646 if not module:
7647 echom(u'Plugin not found: %s' % plugin)
7648 return
7649
7650 try:
7651 module = imp.load_module(plugin, *module)
7652 if not hasattr(module, plugin):
7653 echoe(u'Unable to find plugin: %s' % plugin)
7654 if self.debug:
7655 raise PluginError(u'Unable to find class %s' % plugin)
7656 return
7657 _class = getattr(module, plugin)
7658 self._plugins[plugin] = _class()
7659 self._plugins[plugin].register()
7660 if self.debug:
7661 echo(u'Plugin registered: %s' % plugin)
7662 return self._plugins[plugin]
7663 except Exception, e:
7664 echoe(u'Unable to activate plugin: %s' % plugin)
7665 echoe(u"%s" % e)
7666 if self.debug:
7667 import traceback
7668 echoe(traceback.format_exc())
7669
7670 def register_keybindings(self):
7671 @orgmode.keybinding.register_keybindings
7672 def dummy(plugin):
7673 return plugin
7674
7675 for p in self.plugins.itervalues():
7676 dummy(p)
7677
7678 def register_menu(self):
7679 self.orgmenu.create()
7680
7681 def unregister_menu(self):
7682 vim.command(u'silent! aunmenu Org'.encode(u'utf-8'))
7683
7684 def start(self):
7685 u""" Start orgmode and load all requested plugins
7686 """
7687 plugins = orgmode.settings.get(u"org_plugins")
7688
7689 if not plugins:
7690 echom(u'orgmode: No plugins registered.')
7691
7692 if isinstance(plugins, basestring):
7693 try:
7694 self.register_plugin(plugins)
7695 except Exception, e:
7696 import traceback
7697 traceback.print_exc()
7698 elif isinstance(plugins, types.ListType) or \
7699 isinstance(plugins, types.TupleType):
7700 for p in plugins:
7701 try:
7702 self.register_plugin(p)
7703 except Exception, e:
7704 echoe('Error in %s plugin:' % p)
7705 import traceback
7706 traceback.print_exc()
7707
7708 return plugins
7709
7710
7711ORGMODE = OrgMode()
7712
7713
7714# vim: set noexpandtab:
7715ftplugin/orgmode/exceptions.py [[[1
771623
7717# -*- coding: utf-8 -*-
7718
7719
7720class PluginError(Exception):
7721 def __init__(self, message):
7722 Exception.__init__(self, message)
7723
7724
7725class BufferNotFound(Exception):
7726 def __init__(self, message):
7727 Exception.__init__(self, message)
7728
7729
7730class BufferNotInSync(Exception):
7731 def __init__(self, message):
7732 Exception.__init__(self, message)
7733
7734
7735class HeadingDomError(Exception):
7736 def __init__(self, message):
7737 Exception.__init__(self, message)
7738
7739# vim: set noexpandtab:
7740ftplugin/orgmode/keybinding.py [[[1
7741216
7742# -*- coding: utf-8 -*-
7743
7744import vim
7745
7746MODE_ALL = u'a'
7747MODE_NORMAL = u'n'
7748MODE_VISUAL = u'v'
7749MODE_INSERT = u'i'
7750MODE_OPERATOR = u'o'
7751
7752OPTION_BUFFER_ONLY = u'<buffer>'
7753OPTION_SLIENT = u'<silent>'
7754
7755
7756def _register(f, name):
7757 def r(*args, **kwargs):
7758 p = f(*args, **kwargs)
7759 if hasattr(p, name) and isinstance(getattr(p, name), list):
7760 for i in getattr(p, name):
7761 i.create()
7762 return p
7763 return r
7764
7765
7766def register_keybindings(f):
7767 return _register(f, u'keybindings')
7768
7769
7770def register_commands(f):
7771 return _register(f, u'commands')
7772
7773
7774class Command(object):
7775 u""" A vim command """
7776
7777 def __init__(self, name, command, arguments=u'0', complete=None, overwrite_exisiting=False):
7778 u"""
7779 :name: The name of command, first character must be uppercase
7780 :command: The actual command that is executed
7781 :arguments: See :h :command-nargs, only the arguments need to be specified
7782 :complete: See :h :command-completion, only the completion arguments need to be specified
7783 """
7784 object.__init__(self)
7785
7786 self._name = name
7787 self._command = command
7788 self._arguments = arguments
7789 self._complete = complete
7790 self._overwrite_exisiting = overwrite_exisiting
7791
7792 def __unicode__(self):
7793 return u':%s<CR>' % self.name
7794
7795 def __str__(self):
7796 return self.__unicode__().encode(u'utf-8')
7797
7798 @property
7799 def name(self):
7800 return self._name
7801
7802 @property
7803 def command(self):
7804 return self._command
7805
7806 @property
7807 def arguments(self):
7808 return self._arguments
7809
7810 @property
7811 def complete(self):
7812 return self._complete
7813
7814 @property
7815 def overwrite_exisiting(self):
7816 return self._overwrite_exisiting
7817
7818 def create(self):
7819 u""" Register/create the command
7820 """
7821 vim.command((':command%(overwrite)s -nargs=%(arguments)s %(complete)s %(name)s %(command)s' %
7822 {u'overwrite': '!' if self.overwrite_exisiting else '',
7823 u'arguments': self.arguments.encode(u'utf-8'),
7824 u'complete': '-complete=%s' % self.complete.encode(u'utf-8') if self.complete else '',
7825 u'name': self.name,
7826 u'command': self.command}
7827 ).encode(u'utf-8'))
7828
7829
7830class Plug(object):
7831 u""" Represents a <Plug> to an abitrary command """
7832
7833 def __init__(self, name, command, mode=MODE_NORMAL):
7834 u"""
7835 :name: the name of the <Plug> should be ScriptnameCommandname
7836 :command: the actual command
7837 """
7838 object.__init__(self)
7839
7840 if mode not in (MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT, MODE_OPERATOR):
7841 raise ValueError(u'Parameter mode not in MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT, MODE_OPERATOR')
7842 self._mode = mode
7843
7844 self.name = name
7845 self.command = command
7846 self.created = False
7847
7848 def __unicode__(self):
7849 return u'<Plug>%s' % self.name
7850
7851 def __str__(self):
7852 return self.__unicode__().encode(u'utf-8')
7853
7854 def create(self):
7855 if not self.created:
7856 self.created = True
7857 cmd = self._mode
7858 if cmd == MODE_ALL:
7859 cmd = u''
7860 vim.command((u':%snoremap %s %s' % (cmd, str(self), self.command)).encode(u'utf-8'))
7861
7862 @property
7863 def mode(self):
7864 return self._mode
7865
7866
7867class Keybinding(object):
7868 u""" Representation of a single key binding """
7869
7870 def __init__(self, key, action, mode=None, options=None, remap=True, buffer_only=True, silent=True):
7871 u"""
7872 :key: the key(s) action is bound to
7873 :action: the action triggered by key(s)
7874 :mode: definition in which vim modes the key binding is valid. Should be one of MODE_*
7875 :option: list of other options like <silent>, <buffer> ...
7876 :repmap: allow or disallow nested mapping
7877 :buffer_only: define the key binding only for the current buffer
7878 """
7879 object.__init__(self)
7880 self._key = key
7881 self._action = action
7882
7883 # grab mode from plug if not set otherwise
7884 if isinstance(self._action, Plug) and not mode:
7885 mode = self._action.mode
7886
7887 if mode not in (MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT, MODE_OPERATOR):
7888 raise ValueError(u'Parameter mode not in MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT, MODE_OPERATOR')
7889 self._mode = mode
7890 self._options = options
7891 if self._options is None:
7892 self._options = []
7893 self._remap = remap
7894 self._buffer_only = buffer_only
7895 self._silent = silent
7896
7897 if self._buffer_only and OPTION_BUFFER_ONLY not in self._options:
7898 self._options.append(OPTION_BUFFER_ONLY)
7899
7900 if self._silent and OPTION_SLIENT not in self._options:
7901 self._options.append(OPTION_SLIENT)
7902
7903 @property
7904 def key(self):
7905 return self._key
7906
7907 @property
7908 def action(self):
7909 return str(self._action)
7910
7911 @property
7912 def mode(self):
7913 return self._mode
7914
7915 @property
7916 def options(self):
7917 return self._options[:]
7918
7919 @property
7920 def remap(self):
7921 return self._remap
7922
7923 @property
7924 def buffer_only(self):
7925 return self._buffer_only
7926
7927 @property
7928 def silent(self):
7929 return self._silent
7930
7931 def create(self):
7932 from orgmode._vim import ORGMODE, echom
7933
7934 cmd = self._mode
7935 if cmd == MODE_ALL:
7936 cmd = u''
7937 if not self._remap:
7938 cmd += u'nore'
7939 try:
7940 create_mapping = True
7941 if isinstance(self._action, Plug):
7942 # create plug
7943 self._action.create()
7944 if int(vim.eval((u'hasmapto("%s")' % (self._action, )).encode(u'utf-8'))):
7945 create_mapping = False
7946 if isinstance(self._action, Command):
7947 # create command
7948 self._action.create()
7949
7950 if create_mapping:
7951 vim.command((u':%smap %s %s %s' % (cmd, u' '.join(self._options), self._key, self._action)).encode(u'utf-8'))
7952 except Exception, e:
7953 if ORGMODE.debug:
7954 echom(u'Failed to register key binding %s %s' % (self._key, self._action))
7955
7956
7957# vim: set noexpandtab:
7958ftplugin/orgmode/menu.py [[[1
7959171
7960# -*- coding: utf-8 -*-
7961
7962import vim
7963
7964from orgmode.keybinding import Command, Plug, Keybinding
7965from orgmode.keybinding import MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT
7966
7967def register_menu(f):
7968 def r(*args, **kwargs):
7969 p = f(*args, **kwargs)
7970 def create(entry):
7971 if isinstance(entry, Submenu) or isinstance(entry, Separator) \
7972 or isinstance(entry, ActionEntry):
7973 entry.create()
7974
7975 if hasattr(p, u'menu'):
7976 if isinstance(p.menu, list) or isinstance(p.menu, tuple):
7977 for e in p.menu:
7978 create(e)
7979 else:
7980 create(p.menu)
7981 return p
7982 return r
7983
7984
7985def add_cmd_mapping_menu(plugin, name, function, key_mapping, menu_desrc):
7986 u"""A helper function to create a vim command and keybinding and add these
7987 to the menu for a given plugin.
7988
7989 :plugin: the plugin to operate on.
7990 :name: the name of the vim command (and the name of the Plug)
7991 :function: the actual python function which is called when executing the
7992 vim command.
7993 :key_mapping: the keymapping to execute the command.
7994 :menu_desrc: the text which appears in the menu.
7995 """
7996 cmd = Command(name, function)
7997 keybinding = Keybinding(key_mapping, Plug(name, cmd))
7998
7999 plugin.commands.append(cmd)
8000 plugin.keybindings.append(keybinding)
8001 plugin.menu + ActionEntry(menu_desrc, keybinding)
8002
8003
8004class Submenu(object):
8005 u""" Submenu entry """
8006
8007 def __init__(self, name, parent=None):
8008 object.__init__(self)
8009 self.name = name
8010 self.parent = parent
8011 self._children = []
8012
8013 def __add__(self, entry):
8014 if entry not in self._children:
8015 self._children.append(entry)
8016 entry.parent = self
8017 return entry
8018
8019 def __sub__(self, entry):
8020 if entry in self._children:
8021 idx = self._children.index(entry)
8022 del self._children[idx]
8023
8024 @property
8025 def children(self):
8026 return self._children[:]
8027
8028 def get_menu(self):
8029 n = self.name.replace(u' ', u'\\ ')
8030 if self.parent:
8031 return u'%s.%s' % (self.parent.get_menu(), n)
8032 return n
8033
8034 def create(self):
8035 for c in self.children:
8036 c.create()
8037
8038 def __str__(self):
8039 res = self.name
8040 for c in self.children:
8041 res += str(c)
8042 return res
8043
8044class Separator(object):
8045 u""" Menu entry for a Separator """
8046
8047 def __init__(self, parent=None):
8048 object.__init__(self)
8049 self.parent = parent
8050
8051 def __unicode__(self):
8052 return u'-----'
8053
8054 def __str__(self):
8055 return self.__unicode__().encode(u'utf-8')
8056
8057 def create(self):
8058 if self.parent:
8059 menu = self.parent.get_menu()
8060 vim.command((u'menu %s.-%s- :' % (menu, id(self))).encode(u'utf-8'))
8061
8062class ActionEntry(object):
8063 u""" ActionEntry entry """
8064
8065 def __init__(self, lname, action, rname=None, mode=MODE_NORMAL, parent=None):
8066 u"""
8067 :lname: menu title on the left hand side of the menu entry
8068 :action: could be a vim command sequence or an actual Keybinding
8069 :rname: menu title that appears on the right hand side of the menu
8070 entry. If action is a Keybinding this value ignored and is
8071 taken from the Keybinding
8072 :mode: defines when the menu entry/action is executable
8073 :parent: the parent instance of this object. The only valid parent is Submenu
8074 """
8075 object.__init__(self)
8076 self._lname = lname
8077 self._action = action
8078 self._rname = rname
8079 if mode not in (MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT):
8080 raise ValueError(u'Parameter mode not in MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT')
8081 self._mode = mode
8082 self.parent = parent
8083
8084 def __str__(self):
8085 return u'%s\t%s' % (self.lname, self.rname)
8086
8087 @property
8088 def lname(self):
8089 return self._lname.replace(u' ', u'\\ ')
8090
8091 @property
8092 def action(self):
8093 if isinstance(self._action, Keybinding):
8094 return self._action.action
8095 return self._action
8096
8097 @property
8098 def rname(self):
8099 if isinstance(self._action, Keybinding):
8100 return self._action.key.replace(u'<Tab>', u'Tab')
8101 return self._rname
8102
8103 @property
8104 def mode(self):
8105 if isinstance(self._action, Keybinding):
8106 return self._action.mode
8107 return self._mode
8108
8109 def create(self):
8110 menucmd = u':%smenu ' % self.mode
8111 menu = u''
8112 cmd = u''
8113
8114 if self.parent:
8115 menu = self.parent.get_menu()
8116 menu += u'.%s' % self.lname
8117
8118 if self.rname:
8119 cmd = u'%s %s<Tab>%s %s' % (menucmd, menu, self.rname, self.action)
8120 else:
8121 cmd = u'%s %s %s' % (menucmd, menu, self.action)
8122
8123 vim.command(cmd.encode(u'utf-8'))
8124
8125 # keybindings should be stored in the plugin.keybindings property and be registered by the appropriate keybinding registrar
8126 #if isinstance(self._action, Keybinding):
8127 # self._action.create()
8128
8129
8130# vim: set noexpandtab:
8131ftplugin/orgmode/settings.py [[[1
813294
8133# -*- coding: utf-8 -*-
8134
8135import vim
8136
8137SCOPE_ALL = 1
8138
8139# for all vim-orgmode buffers
8140SCOPE_GLOBAL = 2
8141
8142# just for the current buffer - has priority before the global settings
8143SCOPE_BUFFER = 4
8144
8145VARIABLE_LEADER = {SCOPE_GLOBAL: u'g', SCOPE_BUFFER: u'b'}
8146
8147u""" Evaluate and store settings """
8148
8149
8150def get(setting, default=None, scope=SCOPE_ALL):
8151 u""" Evaluate setting in scope of the current buffer,
8152 globally and also from the contents of the current buffer
8153
8154 WARNING: Only string values are converted to unicode. If a different value
8155 is received, e.g. a list or dict, no conversion is done.
8156
8157 :setting: name of the variable to evaluate
8158 :default: default value in case the variable is empty
8159
8160 :returns: variable value
8161 """
8162 # TODO first read setting from org file which take precedence over vim
8163 # variable settings
8164 if (scope & SCOPE_ALL | SCOPE_BUFFER) and \
8165 int(vim.eval((u'exists("b:%s")' % setting).encode(u'utf-8'))):
8166 res = vim.eval((u"b:%s" % setting).encode(u'utf-8'))
8167 if type(res) in (unicode, str):
8168 return res.decode(u'utf-8')
8169 return res
8170
8171 elif (scope & SCOPE_ALL | SCOPE_GLOBAL) and \
8172 int(vim.eval((u'exists("g:%s")' % setting).encode(u'utf-8'))):
8173 res = vim.eval((u"g:%s" % setting).encode(u'utf-8'))
8174 if type(res) in (unicode, str):
8175 return res.decode(u'utf-8')
8176 return res
8177 return default
8178
8179
8180def set(setting, value, scope=SCOPE_GLOBAL, overwrite=False):
8181 u""" Store setting in the definied scope
8182
8183 WARNING: For the return value, only string are converted to unicode. If a
8184 different value is received by vim.eval, e.g. a list or dict, no conversion
8185 is done.
8186
8187 :setting: name of the setting
8188 :value: the actual value, repr is called on the value to create a string
8189 representation
8190 :scope: the scope o the setting/variable
8191 :overwrite: overwrite existing settings (probably user definied settings)
8192
8193 :returns: the new value in case of overwrite==False the current value
8194 """
8195 if (not overwrite) and (
8196 int(vim.eval((u'exists("%s:%s")' % \
8197 (VARIABLE_LEADER[scope], setting)).encode(u'utf-8')))):
8198 res = vim.eval(
8199 (u'%s:%s' % (VARIABLE_LEADER[scope], setting)).encode(u'utf-8'))
8200 if type(res) in (unicode, str):
8201 return res.decode(u'utf-8')
8202 return res
8203 v = repr(value)
8204 if type(value) == unicode:
8205 # strip leading u of unicode string representations
8206 v = v[1:]
8207
8208 cmd = u'let %s:%s = %s' % (VARIABLE_LEADER[scope], setting, v)
8209 vim.command(cmd.encode(u'utf-8'))
8210 return value
8211
8212
8213def unset(setting, scope=SCOPE_GLOBAL):
8214 u""" Unset setting int the definied scope
8215 :setting: name of the setting
8216 :scope: the scope o the setting/variable
8217
8218 :returns: last value of setting
8219 """
8220 value = get(setting, scope=scope)
8221 cmd = u'unlet! %s:%s' % (VARIABLE_LEADER[scope], setting)
8222 vim.command(cmd.encode(u'utf-8'))
8223 return value
8224
8225
8226# vim: set noexpandtab:
8227ftplugin/orgmode/vimbuffer.py [[[1
8228480
8229# -*- coding: utf-8 -*-
8230
8231"""
8232 vimbuffer
8233 ~~~~~~~~~~
8234
8235 VimBuffer and VimBufferContent are the interface between liborgmode and
8236 vim.
8237
8238 VimBuffer extends the liborgmode.document.Document().
8239 Document() is just a general implementation for loading an org file. It
8240 has no interface to an actual file or vim buffer. This is the task of
8241 vimbuffer.VimBuffer(). It is the interfaces to vim. The main tasks for
8242 VimBuffer are to provide read and write access to a real vim buffer.
8243
8244 VimBufferContent is a helper class for VimBuffer. Basically, it hides the
8245 details of encoding - everything read from or written to VimBufferContent
8246 is UTF-8.
8247"""
8248
8249from UserList import UserList
8250
8251import vim
8252
8253from orgmode import settings
8254from orgmode.exceptions import BufferNotFound, BufferNotInSync
8255from orgmode.liborgmode.documents import Document, MultiPurposeList, Direction
8256from orgmode.liborgmode.headings import Heading
8257
8258
8259class VimBuffer(Document):
8260 def __init__(self, bufnr=0):
8261 u"""
8262 :bufnr: 0: current buffer, every other number refers to another buffer
8263 """
8264 Document.__init__(self)
8265 self._bufnr = vim.current.buffer.number if bufnr == 0 else bufnr
8266 self._changedtick = -1
8267 self._cached_heading = None
8268
8269 if self._bufnr == vim.current.buffer.number:
8270 self._content = VimBufferContent(vim.current.buffer)
8271 else:
8272 _buffer = None
8273 for b in vim.buffers:
8274 if self._bufnr == b.number:
8275 _buffer = b
8276 break
8277
8278 if not _buffer:
8279 raise BufferNotFound(u'Unable to locate buffer number #%d' % self._bufnr)
8280 self._content = VimBufferContent(_buffer)
8281
8282 self.update_changedtick()
8283 self._orig_changedtick = self._changedtick
8284
8285 @property
8286 def tabstop(self):
8287 return int(vim.eval(u'&ts'.encode(u'utf-8')))
8288
8289 @property
8290 def tag_column(self):
8291 return int(settings.get(u'org_tag_column', u'77'))
8292
8293 @property
8294 def is_insync(self):
8295 if self._changedtick == self._orig_changedtick:
8296 self.update_changedtick()
8297 return self._changedtick == self._orig_changedtick
8298
8299 @property
8300 def bufnr(self):
8301 u"""
8302 :returns: The buffer's number for the current document
8303 """
8304 return self._bufnr
8305
8306 def changedtick():
8307 u""" Number of changes in vimbuffer """
8308 def fget(self):
8309 return self._changedtick
8310 def fset(self, value):
8311 self._changedtick = value
8312 return locals()
8313 changedtick = property(**changedtick())
8314
8315 def get_todo_states(self, strip_access_key=True):
8316 u""" Returns a list containing a tuple of two lists of allowed todo
8317 states split by todo and done states. Multiple todo-done state
8318 sequences can be defined.
8319
8320 :returns: [([todo states], [done states]), ..]
8321 """
8322 states = settings.get(u'org_todo_keywords', [])
8323 if type(states) not in (list, tuple):
8324 return []
8325
8326 def parse_states(s, stop=0):
8327 res = []
8328 if not s:
8329 return res
8330 if type(s[0]) in (unicode, str):
8331 r = []
8332 for i in s:
8333 _i = i
8334 if type(_i) == str:
8335 _i = _i.decode(u'utf-8')
8336 if type(_i) == unicode and _i:
8337 if strip_access_key and u'(' in _i:
8338 _i = _i[:_i.index(u'(')]
8339 if _i:
8340 r.append(_i)
8341 else:
8342 r.append(_i)
8343 if not u'|' in r:
8344 if not stop:
8345 res.append((r[:-1], [r[-1]]))
8346 else:
8347 res = (r[:-1], [r[-1]])
8348 else:
8349 seperator_pos = r.index(u'|')
8350 if not stop:
8351 res.append((r[0:seperator_pos], r[seperator_pos + 1:]))
8352 else:
8353 res = (r[0:seperator_pos], r[seperator_pos + 1:])
8354 elif type(s) in (list, tuple) and not stop:
8355 for i in s:
8356 r = parse_states(i, stop=1)
8357 if r:
8358 res.append(r)
8359 return res
8360
8361 return parse_states(states)
8362
8363 def update_changedtick(self):
8364 if self.bufnr == vim.current.buffer.number:
8365 self._changedtick = int(vim.eval(u'b:changedtick'.encode(u'utf-8')))
8366 else:
8367 vim.command(u'unlet! g:org_changedtick | let g:org_lz = &lz | let g:org_hidden = &hidden | set lz hidden'.encode(u'utf-8'))
8368 # TODO is this likely to fail? maybe some error hangling should be added
8369 vim.command((u'keepalt buffer %d | let g:org_changedtick = b:changedtick | buffer %d' % \
8370 (self.bufnr, vim.current.buffer.number)).encode(u'utf-8'))
8371 vim.command(u'let &lz = g:org_lz | let &hidden = g:org_hidden | unlet! g:org_lz g:org_hidden | redraw'.encode(u'utf-8'))
8372 self._changedtick = int(vim.eval(u'g:org_changedtick'.encode(u'utf-8')))
8373
8374 def write(self):
8375 u""" write the changes to the vim buffer
8376
8377 :returns: True if something was written, otherwise False
8378 """
8379 if not self.is_dirty:
8380 return False
8381
8382 self.update_changedtick()
8383 if not self.is_insync:
8384 raise BufferNotInSync(u'Buffer is not in sync with vim!')
8385
8386 # write meta information
8387 if self.is_dirty_meta_information:
8388 meta_end = 0 if self._orig_meta_information_len is None else self._orig_meta_information_len
8389 self._content[:meta_end] = self.meta_information
8390 self._orig_meta_information_len = len(self.meta_information)
8391
8392 # remove deleted headings
8393 already_deleted = []
8394 for h in sorted(self._deleted_headings, cmp=lambda x, y: cmp(x._orig_start, y._orig_start), reverse=True):
8395 if h._orig_start is not None and h._orig_start not in already_deleted:
8396 # this is a heading that actually exists on the buffer and it
8397 # needs to be removed
8398 del self._content[h._orig_start:h._orig_start + h._orig_len]
8399 already_deleted.append(h._orig_start)
8400 del self._deleted_headings[:]
8401 del already_deleted
8402
8403 # update changed headings and add new headings
8404 for h in self.all_headings():
8405 if h.is_dirty:
8406 if h._orig_start is not None:
8407 # this is a heading that existed before and was changed. It
8408 # needs to be replaced
8409 if h.is_dirty_heading:
8410 self._content[h.start:h.start + 1] = [unicode(h)]
8411 if h.is_dirty_body:
8412 self._content[h.start + 1:h.start + h._orig_len] = h.body
8413 else:
8414 # this is a new heading. It needs to be inserted
8415 self._content[h.start:h.start] = [unicode(h)] + h.body
8416 h._dirty_heading = False
8417 h._dirty_body = False
8418 # for all headings the length and start offset needs to be updated
8419 h._orig_start = h.start
8420 h._orig_len = len(h)
8421
8422 self._dirty_meta_information = False
8423 self._dirty_document = False
8424
8425 self.update_changedtick()
8426 self._orig_changedtick = self._changedtick
8427 return True
8428
8429 def write_heading(self, heading, including_children=True):
8430 """ WARNING: use this function only when you know what you are doing!
8431 This function writes a heading to the vim buffer. It offers performance
8432 advantages over the regular write() function. This advantage is
8433 combined with no sanity checks! Whenever you use this function, make
8434 sure the heading you are writing contains the right offsets
8435 (Heading._orig_start, Heading._orig_len).
8436
8437 Usage example:
8438 # Retrieve a potentially dirty document
8439 d = ORGMODE.get_document(allow_dirty=True)
8440 # Don't rely on the DOM, retrieve the heading afresh
8441 h = d.find_heading(direction=Direction.FORWARD, position=100)
8442 # Update tags
8443 h.tags = ['tag1', 'tag2']
8444 # Write the heading
8445 d.write_heading(h)
8446
8447 This function can't be used to delete a heading!
8448
8449 :heading: Write this heading with to the vim buffer
8450 :including_children: Also include children in the update
8451
8452 :returns The written heading
8453 """
8454 if including_children and heading.children:
8455 for child in heading.children[::-1]:
8456 self.write_heading(child, including_children)
8457
8458 if heading.is_dirty:
8459 if heading._orig_start is not None:
8460 # this is a heading that existed before and was changed. It
8461 # needs to be replaced
8462 if heading.is_dirty_heading:
8463 self._content[heading._orig_start:heading._orig_start + 1] = [unicode(heading)]
8464 if heading.is_dirty_body:
8465 self._content[heading._orig_start + 1:heading._orig_start + heading._orig_len] = heading.body
8466 else:
8467 # this is a new heading. It needs to be inserted
8468 raise ValueError('Heading must contain the attribute _orig_start! %s' % heading)
8469 heading._dirty_heading = False
8470 heading._dirty_body = False
8471 # for all headings the length offset needs to be updated
8472 heading._orig_len = len(heading)
8473
8474 return heading
8475
8476 def write_checkbox(self, checkbox, including_children=True):
8477 if including_children and checkbox.children:
8478 for child in checkbox.children[::-1]:
8479 self.write_checkbox(child, including_children)
8480
8481 if checkbox.is_dirty:
8482 if checkbox._orig_start is not None:
8483 # this is a heading that existed before and was changed. It
8484 # needs to be replaced
8485 # print "checkbox is dirty? " + str(checkbox.is_dirty_checkbox)
8486 # print checkbox
8487 if checkbox.is_dirty_checkbox:
8488 self._content[checkbox._orig_start:checkbox._orig_start + 1] = [unicode(checkbox)]
8489 if checkbox.is_dirty_body:
8490 self._content[checkbox._orig_start + 1:checkbox._orig_start + checkbox._orig_len] = checkbox.body
8491 else:
8492 # this is a new checkbox. It needs to be inserted
8493 raise ValueError('Checkbox must contain the attribute _orig_start! %s' % checkbox)
8494 checkbox._dirty_checkbox = False
8495 checkbox._dirty_body = False
8496 # for all headings the length offset needs to be updated
8497 checkbox._orig_len = len(checkbox)
8498
8499 return checkbox
8500
8501 def write_checkboxes(self, checkboxes):
8502 pass
8503
8504 def previous_heading(self, position=None):
8505 u""" Find the next heading (search forward) and return the related object
8506 :returns: Heading object or None
8507 """
8508 h = self.current_heading(position=position)
8509 if h:
8510 return h.previous_heading
8511
8512 def current_heading(self, position=None):
8513 u""" Find the current heading (search backward) and return the related object
8514 :returns: Heading object or None
8515 """
8516 if position is None:
8517 position = vim.current.window.cursor[0] - 1
8518
8519 if not self.headings:
8520 return
8521
8522 def binaryFindInDocument():
8523 hi = len(self.headings)
8524 lo = 0
8525 while lo < hi:
8526 mid = (lo+hi)//2
8527 h = self.headings[mid]
8528 if h.end_of_last_child < position:
8529 lo = mid + 1
8530 elif h.start > position:
8531 hi = mid
8532 else:
8533 return binaryFindHeading(h)
8534
8535 def binaryFindHeading(heading):
8536 if not heading.children or heading.end >= position:
8537 return heading
8538
8539 hi = len(heading.children)
8540 lo = 0
8541 while lo < hi:
8542 mid = (lo+hi)//2
8543 h = heading.children[mid]
8544 if h.end_of_last_child < position:
8545 lo = mid + 1
8546 elif h.start > position:
8547 hi = mid
8548 else:
8549 return binaryFindHeading(h)
8550
8551 # look at the cache to find the heading
8552 h_tmp = self._cached_heading
8553 if h_tmp is not None:
8554 if h_tmp.end_of_last_child > position and \
8555 h_tmp.start < position:
8556 if h_tmp.end < position:
8557 self._cached_heading = binaryFindHeading(h_tmp)
8558 return self._cached_heading
8559
8560 self._cached_heading = binaryFindInDocument()
8561 return self._cached_heading
8562
8563 def next_heading(self, position=None):
8564 u""" Find the next heading (search forward) and return the related object
8565 :returns: Heading object or None
8566 """
8567 h = self.current_heading(position=position)
8568 if h:
8569 return h.next_heading
8570
8571 def find_current_heading(self, position=None, heading=Heading):
8572 u""" Find the next heading backwards from the position of the cursor.
8573 The difference to the function current_heading is that the returned
8574 object is not built into the DOM. In case the DOM doesn't exist or is
8575 out of sync this function is much faster in fetching the current
8576 heading.
8577
8578 :position: The position to start the search from
8579
8580 :heading: The base class for the returned heading
8581
8582 :returns: Heading object or None
8583 """
8584 return self.find_heading(vim.current.window.cursor[0] - 1 \
8585 if position is None else position, \
8586 direction=Direction.BACKWARD, heading=heading, \
8587 connect_with_document=False)
8588
8589
8590class VimBufferContent(MultiPurposeList):
8591 u""" Vim Buffer Content is a UTF-8 wrapper around a vim buffer. When
8592 retrieving or setting items in the buffer an automatic conversion is
8593 performed.
8594
8595 This ensures UTF-8 usage on the side of liborgmode and the vim plugin
8596 vim-orgmode.
8597 """
8598
8599 def __init__(self, vimbuffer, on_change=None):
8600 MultiPurposeList.__init__(self, on_change=on_change)
8601
8602 # replace data with vimbuffer to make operations change the actual
8603 # buffer
8604 self.data = vimbuffer
8605
8606 def __contains__(self, item):
8607 i = item
8608 if type(i) is unicode:
8609 i = item.encode(u'utf-8')
8610 return MultiPurposeList.__contains__(self, i)
8611
8612 def __getitem__(self, i):
8613 item = MultiPurposeList.__getitem__(self, i)
8614 if type(item) is str:
8615 return item.decode(u'utf-8')
8616 return item
8617
8618 def __getslice__(self, i, j):
8619 return [item.decode(u'utf-8') if type(item) is str else item \
8620 for item in MultiPurposeList.__getslice__(self, i, j)]
8621
8622 def __setitem__(self, i, item):
8623 _i = item
8624 if type(_i) is unicode:
8625 _i = item.encode(u'utf-8')
8626
8627 MultiPurposeList.__setitem__(self, i, _i)
8628
8629 def __setslice__(self, i, j, other):
8630 o = []
8631 o_tmp = other
8632 if type(o_tmp) not in (list, tuple) and not isinstance(o_tmp, UserList):
8633 o_tmp = list(o_tmp)
8634 for item in o_tmp:
8635 if type(item) == unicode:
8636 o.append(item.encode(u'utf-8'))
8637 else:
8638 o.append(item)
8639 MultiPurposeList.__setslice__(self, i, j, o)
8640
8641 def __add__(self, other):
8642 raise NotImplementedError()
8643 # TODO: implement me
8644 if isinstance(other, UserList):
8645 return self.__class__(self.data + other.data)
8646 elif isinstance(other, type(self.data)):
8647 return self.__class__(self.data + other)
8648 else:
8649 return self.__class__(self.data + list(other))
8650
8651 def __radd__(self, other):
8652 raise NotImplementedError()
8653 # TODO: implement me
8654 if isinstance(other, UserList):
8655 return self.__class__(other.data + self.data)
8656 elif isinstance(other, type(self.data)):
8657 return self.__class__(other + self.data)
8658 else:
8659 return self.__class__(list(other) + self.data)
8660
8661 def __iadd__(self, other):
8662 o = []
8663 o_tmp = other
8664 if type(o_tmp) not in (list, tuple) and not isinstance(o_tmp, UserList):
8665 o_tmp = list(o_tmp)
8666 for i in o_tmp:
8667 if type(i) is unicode:
8668 o.append(i.encode(u'utf-8'))
8669 else:
8670 o.append(i)
8671
8672 return MultiPurposeList.__iadd__(self, o)
8673
8674 def append(self, item):
8675 i = item
8676 if type(item) is str:
8677 i = item.encode(u'utf-8')
8678 MultiPurposeList.append(self, i)
8679
8680 def insert(self, i, item):
8681 _i = item
8682 if type(_i) is str:
8683 _i = item.encode(u'utf-8')
8684 MultiPurposeList.insert(self, i, _i)
8685
8686 def index(self, item, *args):
8687 i = item
8688 if type(i) is unicode:
8689 i = item.encode(u'utf-8')
8690 MultiPurposeList.index(self, i, *args)
8691
8692 def pop(self, i=-1):
8693 return MultiPurposeList.pop(self, i).decode(u'utf-8')
8694
8695 def extend(self, other):
8696 o = []
8697 o_tmp = other
8698 if type(o_tmp) not in (list, tuple) and not isinstance(o_tmp, UserList):
8699 o_tmp = list(o_tmp)
8700 for i in o_tmp:
8701 if type(i) is unicode:
8702 o.append(i.encode(u'utf-8'))
8703 else:
8704 o.append(i)
8705 MultiPurposeList.extend(self, o)
8706
8707
8708# vim: set noexpandtab:
8709ftplugin/org.cnf [[[1
87105
8711--langdef=org
8712--langmap=org:.org
8713--regex-org=/^(\*+)[[:space:]]+(.*)([[:space:]]+:[^\t ]*:)?$/\1 \2/s,sections/
8714--regex-org=/\[\[([^][]+)\]\]/\1/h,hyperlinks/
8715--regex-org=/\[\[[^][]+\]\[([^][]+)\]\]/\1/h,hyperlinks/
8716ftplugin/org.vim [[[1
8717140
8718" org.vim -- Text outlining and task management for Vim based on Emacs' Org-Mode
8719" @Author : Jan Christoph Ebersbach (jceb@e-jc.de)
8720" @License : AGPL3 (see http://www.gnu.org/licenses/agpl.txt)
8721" @Created : 2010-10-03
8722" @Last Modified: Tue 13. Sep 2011 20:52:57 +0200 CEST
8723" @Revision : 0.4
8724" vi: ft=vim:tw=80:sw=4:ts=4:fdm=marker
8725
8726if ! has('python') || v:version < 703
8727 echoerr "Unable to start orgmode. Orgmode depends on Vim >= 7.3 with Python support complied in."
8728 finish
8729endif
8730
8731if ! exists('b:did_ftplugin')
8732 " default emacs settings
8733 setlocal comments=fb:*,b:#,fb:-
8734 setlocal commentstring=#\ %s
8735 setlocal conceallevel=2 concealcursor="nc"
8736 " original emacs settings are: setlocal tabstop=6 shiftwidth=6, but because
8737 " of checkbox indentation the following settings are used:
8738 setlocal tabstop=6 shiftwidth=6
8739 if exists('g:org_tag_column')
8740 exe 'setlocal textwidth='.g:org_tag_column
8741 else
8742 setlocal textwidth=77
8743 endif
8744
8745 " expand tab for counting level of checkbox
8746 setlocal expandtab
8747
8748 " register keybindings if they don't have been registered before
8749 if exists("g:loaded_org")
8750 python ORGMODE.register_keybindings()
8751 endif
8752endif
8753
8754" Load orgmode just once {{{1
8755if &cp || exists("g:loaded_org")
8756 finish
8757endif
8758let g:loaded_org = 1
8759
8760" Default org plugins that will be loaded (in the given order)
8761if ! exists('g:org_plugins') && ! exists('b:org_plugins')
8762 let g:org_plugins = ['ShowHide', '|', 'Navigator', 'EditStructure', 'EditCheckbox', '|', 'Hyperlinks', '|', 'Todo', 'TagsProperties', 'Date', 'Agenda', 'Misc', '|', 'Export']
8763endif
8764
8765if ! exists('g:org_syntax_highlight_leading_stars') && ! exists('b:org_syntax_highlight_leading_stars')
8766 let g:org_syntax_highlight_leading_stars = 1
8767endif
8768
8769" Menu and document handling {{{1
8770function! <SID>OrgRegisterMenu()
8771 python ORGMODE.register_menu()
8772endfunction
8773
8774function! <SID>OrgUnregisterMenu()
8775 python ORGMODE.unregister_menu()
8776endfunction
8777
8778function! <SID>OrgDeleteUnusedDocument(bufnr)
8779python << EOF
8780b = int(vim.eval('a:bufnr'))
8781if b in ORGMODE._documents:
8782 del ORGMODE._documents[b]
8783EOF
8784endfunction
8785
8786" show and hide Org menu depending on the filetype
8787augroup orgmode
8788 au BufEnter * :if &filetype == "org" | call <SID>OrgRegisterMenu() | endif
8789 au BufLeave * :if &filetype == "org" | call <SID>OrgUnregisterMenu() | endif
8790 au BufDelete * :call <SID>OrgDeleteUnusedDocument(expand('<abuf>'))
8791augroup END
8792
8793" Start orgmode {{{1
8794" Expand our path
8795python << EOF
8796import vim, os, sys
8797
8798for p in vim.eval("&runtimepath").split(','):
8799 dname = os.path.join(p, "ftplugin")
8800 if os.path.exists(os.path.join(dname, "orgmode")):
8801 if dname not in sys.path:
8802 sys.path.append(dname)
8803 break
8804
8805from orgmode._vim import ORGMODE, insert_at_cursor, get_user_input, date_to_str
8806ORGMODE.start()
8807
8808from Date import Date
8809import datetime
8810EOF
8811
8812" 3rd Party Plugin Integration {{{1
8813" * Repeat {{{2
8814try
8815 call repeat#set()
8816catch
8817endtry
8818
8819" * Tagbar {{{2
8820let g:tagbar_type_org = {
8821 \ 'ctagstype' : 'org',
8822 \ 'kinds' : [
8823 \ 's:sections',
8824 \ 'h:hyperlinks',
8825 \ ],
8826 \ 'sort' : 0,
8827 \ 'deffile' : expand('<sfile>:p:h') . '/org.cnf'
8828 \ }
8829
8830" * Taglist {{{2
8831if exists('g:Tlist_Ctags_Cmd')
8832 " Pass parameters to taglist
8833 let g:tlist_org_settings = 'org;s:section;h:hyperlinks'
8834 let g:Tlist_Ctags_Cmd .= ' --options=' . expand('<sfile>:p:h') . '/org.cnf '
8835endif
8836
8837" * Calendar.vim {{{2
8838fun CalendarAction(day, month, year, week, dir)
8839 let g:org_timestamp = printf("%04d-%02d-%02d Fri", a:year, a:month, a:day)
8840 let datetime_date = printf("datetime.date(%d, %d, %d)", a:year, a:month, a:day)
8841 exe "py selected_date = " . datetime_date
8842 " get_user_input
8843 let msg = printf("Inserting %s | Modify date", g:org_timestamp)
8844 exe "py modifier = get_user_input('" . msg . "')"
8845 " change date according to user input
8846 exe "py print modifier"
8847 exe "py newdate = Date._modify_time(selected_date, modifier)"
8848 exe "py newdate = date_to_str(newdate)"
8849 " close Calendar
8850 exe "q"
8851 " goto previous window
8852 exe "wincmd p"
8853 exe "py timestamp = '" . g:org_timestamp_template . "' % newdate"
8854 exe "py insert_at_cursor(timestamp)"
8855 " restore calendar_action
8856 let g:calendar_action = g:org_calendar_action_backup
8857endf
8858syntax/orgagenda.vim [[[1
885979
8860" TODO do we really need a separate syntax file for the agenda?
8861" - Most of the stuff here is also in syntax.org
8862" - DRY!
8863
8864syn match org_todo_key /\[\zs[^]]*\ze\]/
8865hi def link org_todo_key Identifier
8866
8867let s:todo_headings = ''
8868let s:i = 1
8869while s:i <= g:org_heading_highlight_levels
8870 if s:todo_headings == ''
8871 let s:todo_headings = 'containedin=org_heading' . s:i
8872 else
8873 let s:todo_headings = s:todo_headings . ',org_heading' . s:i
8874 endif
8875 let s:i += 1
8876endwhile
8877unlet! s:i
8878
8879if !exists('g:loaded_orgagenda_syntax')
8880 let g:loaded_orgagenda_syntax = 1
8881 function! s:ReadTodoKeywords(keywords, todo_headings)
8882 let l:default_group = 'Todo'
8883 for l:i in a:keywords
8884 if type(l:i) == 3
8885 call s:ReadTodoKeywords(l:i, a:todo_headings)
8886 continue
8887 endif
8888 if l:i == '|'
8889 let l:default_group = 'Question'
8890 continue
8891 endif
8892 " strip access key
8893 let l:_i = substitute(l:i, "\(.*$", "", "")
8894
8895 let l:group = l:default_group
8896 for l:j in g:org_todo_keyword_faces
8897 if l:j[0] == l:_i
8898 let l:group = 'orgtodo_todo_keyword_face_' . l:_i
8899 call OrgExtendHighlightingGroup(l:default_group, l:group, OrgInterpretFaces(l:j[1]))
8900 break
8901 endif
8902 endfor
8903 exec 'syntax match orgtodo_todo_keyword_' . l:_i . ' /' . l:_i .'/ ' . a:todo_headings
8904 exec 'hi def link orgtodo_todo_keyword_' . l:_i . ' ' . l:group
8905 endfor
8906 endfunction
8907endif
8908
8909call s:ReadTodoKeywords(g:org_todo_keywords, s:todo_headings)
8910unlet! s:todo_headings
8911
8912" Timestamps
8913"<2003-09-16 Tue>
8914syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \a\a\a>\)/
8915"<2003-09-16 Tue 12:00>
8916syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \a\a\a \d\d:\d\d>\)/
8917"<2003-09-16 Tue 12:00-12:30>
8918syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \a\a\a \d\d:\d\d-\d\d:\d\d>\)/
8919"<2003-09-16 Tue>--<2003-09-16 Tue>
8920syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \a\a\a>--<\d\d\d\d-\d\d-\d\d \a\a\a>\)/
8921"<2003-09-16 Tue 12:00>--<2003-09-16 Tue 12:00>
8922syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \a\a\a \d\d:\d\d>--<\d\d\d\d-\d\d-\d\d \a\a\a \d\d:\d\d>\)/
8923syn match org_timestamp /\(<%%(diary-float.\+>\)/
8924hi def link org_timestamp PreProc
8925
8926" special words
8927syn match today /TODAY$/
8928hi def link today PreProc
8929
8930syn match week_agenda /^Week Agenda:$/
8931hi def link week_agenda PreProc
8932
8933" Hyperlinks
8934syntax match hyperlink "\[\{2}[^][]*\(\]\[[^][]*\)\?\]\{2}" contains=hyperlinkBracketsLeft,hyperlinkURL,hyperlinkBracketsRight containedin=ALL
8935syntax match hyperlinkBracketsLeft contained "\[\{2}" conceal
8936syntax match hyperlinkURL contained "[^][]*\]\[" conceal
8937syntax match hyperlinkBracketsRight contained "\]\{2}" conceal
8938hi def link hyperlink Underlined
8939syntax/orgtodo.vim [[[1
894047
8941syn match org_todo_key /\[\zs[^]]*\ze\]/
8942hi def link org_todo_key Identifier
8943
8944let s:todo_headings = ''
8945let s:i = 1
8946while s:i <= g:org_heading_highlight_levels
8947 if s:todo_headings == ''
8948 let s:todo_headings = 'containedin=org_heading' . s:i
8949 else
8950 let s:todo_headings = s:todo_headings . ',org_heading' . s:i
8951 endif
8952 let s:i += 1
8953endwhile
8954unlet! s:i
8955
8956if !exists('g:loaded_orgtodo_syntax')
8957 let g:loaded_orgtodo_syntax = 1
8958 function! s:ReadTodoKeywords(keywords, todo_headings)
8959 let l:default_group = 'Todo'
8960 for l:i in a:keywords
8961 if type(l:i) == 3
8962 call s:ReadTodoKeywords(l:i, a:todo_headings)
8963 continue
8964 endif
8965 if l:i == '|'
8966 let l:default_group = 'Question'
8967 continue
8968 endif
8969 " strip access key
8970 let l:_i = substitute(l:i, "\(.*$", "", "")
8971
8972 let l:group = l:default_group
8973 for l:j in g:org_todo_keyword_faces
8974 if l:j[0] == l:_i
8975 let l:group = 'orgtodo_todo_keyword_face_' . l:_i
8976 call OrgExtendHighlightingGroup(l:default_group, l:group, OrgInterpretFaces(l:j[1]))
8977 break
8978 endif
8979 endfor
8980 exec 'syntax match orgtodo_todo_keyword_' . l:_i . ' /' . l:_i .'/ ' . a:todo_headings
8981 exec 'hi def link orgtodo_todo_keyword_' . l:_i . ' ' . l:group
8982 endfor
8983 endfunction
8984endif
8985
8986call s:ReadTodoKeywords(g:org_todo_keywords, s:todo_headings)
8987unlet! s:todo_headings
8988syntax/org.vim [[[1
8989346
8990" Support org authoring markup as closely as possible
8991" (we're adding two markdown-like variants for =code= and blockquotes)
8992" -----------------------------------------------------------------------------
8993
8994" Inline markup
8995" *bold*, /italic/, _underline_, +strike-through+, =code=, ~verbatim~
8996" Note:
8997" - /italic/ is rendered as reverse in most terms (works fine in gVim, though)
8998" - +strike-through+ doesn't work on Vim / gVim
8999" - the non-standard `code' markup is also supported
9000" - =code= and ~verbatim~ are also supported as block-level markup, see below.
9001" Ref: http://orgmode.org/manual/Emphasis-and-monospace.html
9002"syntax match org_bold /\*[^ ]*\*/
9003
9004" FIXME: Always make org_bold syntax define before org_heading syntax
9005" to make sure that org_heading syntax got higher priority(help :syn-priority) than org_bold.
9006" If there is any other good solution, please help fix it.
9007syntax region org_bold start="\S\@<=\*\|\*\S\@=" end="\S\@<=\*\|\*\S\@=" keepend oneline
9008syntax region org_italic start="\S\@<=\/\|\/\S\@=" end="\S\@<=\/\|\/\S\@=" keepend oneline
9009syntax region org_underline start="\S\@<=_\|_\S\@=" end="\S\@<=_\|_\S\@=" keepend oneline
9010syntax region org_code start="\S\@<==\|=\S\@=" end="\S\@<==\|=\S\@=" keepend oneline
9011syntax region org_code start="\S\@<=`\|`\S\@=" end="\S\@<='\|'\S\@=" keepend oneline
9012syntax region org_verbatim start="\S\@<=\~\|\~\S\@=" end="\S\@<=\~\|\~\S\@=" keepend oneline
9013
9014hi def org_bold term=bold cterm=bold gui=bold
9015hi def org_italic term=italic cterm=italic gui=italic
9016hi def org_underline term=underline cterm=underline gui=underline
9017
9018" Headings: {{{1
9019" Load Settings: {{{2
9020if !exists('g:org_heading_highlight_colors')
9021 let g:org_heading_highlight_colors = ['Title', 'Constant', 'Identifier', 'Statement', 'PreProc', 'Type', 'Special']
9022endif
9023
9024if !exists('g:org_heading_highlight_levels')
9025 let g:org_heading_highlight_levels = len(g:org_heading_highlight_colors)
9026endif
9027
9028if !exists('g:org_heading_shade_leading_stars')
9029 let g:org_heading_shade_leading_stars = 1
9030endif
9031
9032" Enable Syntax HL: {{{2
9033unlet! s:i s:j s:contains
9034let s:i = 1
9035let s:j = len(g:org_heading_highlight_colors)
9036let s:contains = ' contains=org_timestamp,org_timestamp_inactive,org_subtask_percent,org_subtask_number,org_subtask_percent_100,org_subtask_number_all,org_list_checkbox,org_bold,org_italic,org_underline,org_code,org_verbatim'
9037if g:org_heading_shade_leading_stars == 1
9038 let s:contains = s:contains . ',org_shade_stars'
9039 syntax match org_shade_stars /^\*\{2,\}/me=e-1 contained
9040 hi def link org_shade_stars Ignore
9041else
9042 hi clear org_shade_stars
9043endif
9044
9045while s:i <= g:org_heading_highlight_levels
9046 exec 'syntax match org_heading' . s:i . ' /^\*\{' . s:i . '\}\s.*/' . s:contains
9047 exec 'hi def link org_heading' . s:i . ' ' . g:org_heading_highlight_colors[(s:i - 1) % s:j]
9048 let s:i += 1
9049endwhile
9050unlet! s:i s:j s:contains
9051
9052" Todo Keywords: {{{1
9053" Load Settings: {{{2
9054if !exists('g:org_todo_keywords')
9055 let g:org_todo_keywords = ['TODO', '|', 'DONE']
9056endif
9057
9058if !exists('g:org_todo_keyword_faces')
9059 let g:org_todo_keyword_faces = []
9060endif
9061
9062" Enable Syntax HL: {{{2
9063let s:todo_headings = ''
9064let s:i = 1
9065while s:i <= g:org_heading_highlight_levels
9066 if s:todo_headings == ''
9067 let s:todo_headings = 'containedin=org_heading' . s:i
9068 else
9069 let s:todo_headings = s:todo_headings . ',org_heading' . s:i
9070 endif
9071 let s:i += 1
9072endwhile
9073unlet! s:i
9074
9075if !exists('g:loaded_org_syntax')
9076 let g:loaded_org_syntax = 1
9077
9078 function! OrgExtendHighlightingGroup(base_group, new_group, settings)
9079 let l:base_hi = ''
9080 redir => l:base_hi
9081 silent execute 'highlight ' . a:base_group
9082 redir END
9083 let l:group_hi = substitute(split(l:base_hi, '\n')[0], '^' . a:base_group . '\s\+xxx', '', '')
9084 execute 'highlight ' . a:new_group . l:group_hi . ' ' . a:settings
9085 endfunction
9086
9087 function! OrgInterpretFaces(faces)
9088 let l:res_faces = ''
9089 if type(a:faces) == 3
9090 let l:style = []
9091 for l:f in a:faces
9092 let l:_f = [l:f]
9093 if type(l:f) == 3
9094 let l:_f = l:f
9095 endif
9096 for l:g in l:_f
9097 if type(l:g) == 1 && l:g =~ '^:'
9098 if l:g !~ '[\t ]'
9099 continue
9100 endif
9101 let l:k_v = split(l:g)
9102 if l:k_v[0] == ':foreground'
9103 let l:gui_color = ''
9104 let l:found_gui_color = 0
9105 for l:color in split(l:k_v[1], ',')
9106 if l:color =~ '^#'
9107 let l:found_gui_color = 1
9108 let l:res_faces = l:res_faces . ' guifg=' . l:color
9109 elseif l:color != ''
9110 let l:gui_color = l:color
9111 let l:res_faces = l:res_faces . ' ctermfg=' . l:color
9112 endif
9113 endfor
9114 if ! l:found_gui_color && l:gui_color != ''
9115 let l:res_faces = l:res_faces . ' guifg=' . l:gui_color
9116 endif
9117 elseif l:k_v[0] == ':background'
9118 let l:gui_color = ''
9119 let l:found_gui_color = 0
9120 for l:color in split(l:k_v[1], ',')
9121 if l:color =~ '^#'
9122 let l:found_gui_color = 1
9123 let l:res_faces = l:res_faces . ' guibg=' . l:color
9124 elseif l:color != ''
9125 let l:gui_color = l:color
9126 let l:res_faces = l:res_faces . ' ctermbg=' . l:color
9127 endif
9128 endfor
9129 if ! l:found_gui_color && l:gui_color != ''
9130 let l:res_faces = l:res_faces . ' guibg=' . l:gui_color
9131 endif
9132 elseif l:k_v[0] == ':weight' || l:k_v[0] == ':slant' || l:k_v[0] == ':decoration'
9133 if index(l:style, l:k_v[1]) == -1
9134 call add(l:style, l:k_v[1])
9135 endif
9136 endif
9137 elseif type(l:g) == 1
9138 " TODO emacs interprets the color and automatically determines
9139 " whether it should be set as foreground or background color
9140 let l:res_faces = l:res_faces . ' ctermfg=' . l:k_v[1] . ' guifg=' . l:k_v[1]
9141 endif
9142 endfor
9143 endfor
9144 let l:s = ''
9145 for l:i in l:style
9146 if l:s == ''
9147 let l:s = l:i
9148 else
9149 let l:s = l:s . ','. l:i
9150 endif
9151 endfor
9152 if l:s != ''
9153 let l:res_faces = l:res_faces . ' term=' . l:s . ' cterm=' . l:s . ' gui=' . l:s
9154 endif
9155 elseif type(a:faces) == 1
9156 " TODO emacs interprets the color and automatically determines
9157 " whether it should be set as foreground or background color
9158 let l:res_faces = l:res_faces . ' ctermfg=' . a:faces . ' guifg=' . a:faces
9159 endif
9160 return l:res_faces
9161 endfunction
9162
9163 function! s:ReadTodoKeywords(keywords, todo_headings)
9164 let l:default_group = 'Todo'
9165 for l:i in a:keywords
9166 if type(l:i) == 3
9167 call s:ReadTodoKeywords(l:i, a:todo_headings)
9168 continue
9169 endif
9170 if l:i == '|'
9171 let l:default_group = 'Question'
9172 continue
9173 endif
9174 " strip access key
9175 let l:_i = substitute(l:i, "\(.*$", "", "")
9176
9177 let l:group = l:default_group
9178 for l:j in g:org_todo_keyword_faces
9179 if l:j[0] == l:_i
9180 let l:group = 'org_todo_keyword_face_' . l:_i
9181 call OrgExtendHighlightingGroup(l:default_group, l:group, OrgInterpretFaces(l:j[1]))
9182 break
9183 endif
9184 endfor
9185 exec 'syntax match org_todo_keyword_' . l:_i . ' /\*\{1,\}\s\{1,\}\zs' . l:_i .'\(\s\|$\)/ ' . a:todo_headings
9186 exec 'hi def link org_todo_keyword_' . l:_i . ' ' . l:group
9187 endfor
9188 endfunction
9189endif
9190
9191call s:ReadTodoKeywords(g:org_todo_keywords, s:todo_headings)
9192unlet! s:todo_headings
9193
9194" Timestamps: {{{1
9195"<2003-09-16 Tue>
9196syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \a\a\a>\)/
9197"<2003-09-16 Tue 12:00>
9198syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \a\a\a \d\d:\d\d>\)/
9199"<2003-09-16 Tue 12:00-12:30>
9200syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \a\a\a \d\d:\d\d-\d\d:\d\d>\)/
9201
9202"<2003-09-16 Tue>--<2003-09-16 Tue>
9203syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \a\a\a>--<\d\d\d\d-\d\d-\d\d \a\a\a>\)/
9204"<2003-09-16 Tue 12:00>--<2003-09-16 Tue 12:00>
9205syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \a\a\a \d\d:\d\d>--<\d\d\d\d-\d\d-\d\d \a\a\a \d\d:\d\d>\)/
9206
9207syn match org_timestamp /\(<%%(diary-float.\+>\)/
9208
9209"[2003-09-16 Tue]
9210syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \a\a\a\]\)/
9211"[2003-09-16 Tue 12:00]
9212syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \a\a\a \d\d:\d\d\]\)/
9213
9214"[2003-09-16 Tue]--[2003-09-16 Tue]
9215syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \a\a\a\]--\[\d\d\d\d-\d\d-\d\d \a\a\a\]\)/
9216"[2003-09-16 Tue 12:00]--[2003-09-16 Tue 12:00]
9217syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \a\a\a \d\d:\d\d\]--\[\d\d\d\d-\d\d-\d\d \a\a\a \d\d:\d\d\]\)/
9218
9219syn match org_timestamp_inactive /\(\[%%(diary-float.\+\]\)/
9220
9221hi def link org_timestamp PreProc
9222hi def link org_timestamp_inactive Comment
9223
9224" Deadline And Schedule: {{{1
9225syn match org_deadline_scheduled /^\s*\(DEADLINE\|SCHEDULED\):/
9226hi def link org_deadline_scheduled PreProc
9227
9228" Tables: {{{1
9229syn match org_table /^\s*|.*/ contains=org_timestamp,org_timestamp_inactive,hyperlink,org_table_separator,org_table_horizontal_line
9230syn match org_table_separator /\(^\s*|[-+]\+|\?\||\)/ contained
9231hi def link org_table_separator Type
9232
9233" Hyperlinks: {{{1
9234syntax match hyperlink "\[\{2}[^][]*\(\]\[[^][]*\)\?\]\{2}" contains=hyperlinkBracketsLeft,hyperlinkURL,hyperlinkBracketsRight containedin=ALL
9235syntax match hyperlinkBracketsLeft contained "\[\{2}" conceal
9236syntax match hyperlinkURL contained "[^][]*\]\[" conceal
9237syntax match hyperlinkBracketsRight contained "\]\{2}" conceal
9238hi def link hyperlink Underlined
9239
9240" Comments: {{{1
9241syntax match org_comment /^#.*/
9242hi def link org_comment Comment
9243
9244" Bullet Lists: {{{1
9245" Ordered Lists:
9246" 1. list item
9247" 1) list item
9248" a. list item
9249" a) list item
9250syn match org_list_ordered "^\s*\(\a\|\d\+\)[.)]\(\s\|$\)" nextgroup=org_list_item
9251hi def link org_list_ordered Identifier
9252
9253" Unordered Lists:
9254" - list item
9255" * list item
9256" + list item
9257" + and - don't need a whitespace prefix
9258syn match org_list_unordered "^\(\s*[-+]\|\s\+\*\)\(\s\|$\)" nextgroup=org_list_item
9259hi def link org_list_unordered Identifier
9260
9261" Definition Lists:
9262" - Term :: expl.
9263" 1) Term :: expl.
9264syntax match org_list_def /.*\s\+::/ contained
9265hi def link org_list_def PreProc
9266
9267syntax match org_list_item /.*$/ contained contains=org_subtask_percent,org_subtask_number,org_subtask_percent_100,org_subtask_number_all,org_list_checkbox,org_bold,org_italic,org_underline,org_code,org_verbatim,org_timestamp,org_timestamp_inactive,org_list_def
9268syntax match org_list_checkbox /\[[ X-]]/ contained
9269hi def link org_list_bullet Identifier
9270hi def link org_list_checkbox PreProc
9271
9272" Block Delimiters: {{{1
9273syntax case ignore
9274syntax match org_block_delimiter /^#+BEGIN_.*/
9275syntax match org_block_delimiter /^#+END_.*/
9276syntax match org_key_identifier /^#+[^ ]*:/
9277syntax match org_title /^#+TITLE:.*/ contains=org_key_identifier
9278hi def link org_block_delimiter Comment
9279hi def link org_key_identifier Comment
9280hi def link org_title Title
9281
9282" Block Markup: {{{1
9283" we consider all BEGIN/END sections as 'verbatim' blocks (inc. 'quote', 'verse', 'center')
9284" except 'example' and 'src' which are treated as 'code' blocks.
9285" Note: the non-standard '>' prefix is supported for quotation lines.
9286" Note: the '^:.*" rule must be defined before the ':PROPERTIES:' one below.
9287" TODO: http://vim.wikia.com/wiki/Different_syntax_highlighting_within_regions_of_a_file
9288syntax match org_verbatim /^\s*>.*/
9289syntax match org_code /^\s*:.*/
9290syntax region org_verbatim start="^\s*#+BEGIN_.*" end="^\s*#+END_.*" keepend contains=org_block_delimiter
9291syntax region org_code start="^\s*#+BEGIN_SRC" end="^\s*#+END_SRC" keepend contains=org_block_delimiter
9292syntax region org_code start="^\s*#+BEGIN_EXAMPLE" end="^\s*#+END_EXAMPLE" keepend contains=org_block_delimiter
9293hi def link org_code String
9294hi def link org_verbatim String
9295
9296" Properties: {{{1
9297syn region Error matchgroup=org_properties_delimiter start=/^\s*:PROPERTIES:\s*$/ end=/^\s*:END:\s*$/ contains=org_property keepend
9298syn match org_property /^\s*:[^\t :]\+:\s\+[^\t ]/ contained contains=org_property_value
9299syn match org_property_value /:\s\zs.*/ contained
9300hi def link org_properties_delimiter PreProc
9301hi def link org_property Statement
9302hi def link org_property_value Constant
9303" Break down subtasks
9304syntax match org_subtask_number /\[\d*\/\d*]/ contained
9305syntax match org_subtask_percent /\[\d*%\]/ contained
9306syntax match org_subtask_number_all /\[\(\d\+\)\/\1\]/ contained
9307syntax match org_subtask_percent_100 /\[100%\]/ contained
9308
9309hi def link org_subtask_number String
9310hi def link org_subtask_percent String
9311hi def link org_subtask_percent_100 Identifier
9312hi def link org_subtask_number_all Identifier
9313
9314" Plugin SyntaxRange: {{{1
9315" This only works if you have SyntaxRange installed:
9316" https://github.com/vim-scripts/SyntaxRange
9317
9318" BEGIN_SRC
9319if exists('g:loaded_SyntaxRange')
9320 call SyntaxRange#Include('#+BEGIN_SRC\ vim', '#+END_SRC', 'vim', 'comment')
9321 call SyntaxRange#Include('#+BEGIN_SRC\ python', '#+END_SRC', 'python', 'comment')
9322 call SyntaxRange#Include('#+BEGIN_SRC\ c', '#+END_SRC', 'c', 'comment')
9323 " cpp must be below c, otherwise you get c syntax hl for cpp files
9324 call SyntaxRange#Include('#+BEGIN_SRC\ cpp', '#+END_SRC', 'cpp', 'comment')
9325 call SyntaxRange#Include('#+BEGIN_SRC\ ruby', '#+END_SRC', 'ruby', 'comment')
9326 " call SyntaxRange#Include('#+BEGIN_SRC\ lua', '#+END_SRC', 'lua', 'comment')
9327 " call SyntaxRange#Include('#+BEGIN_SRC\ lisp', '#+END_SRC', 'lisp', 'comment')
9328
9329 " LaTeX
9330 call SyntaxRange#Include('\\begin[.*]{.*}', '\\end{.*}', 'tex')
9331 call SyntaxRange#Include('\\begin{.*}', '\\end{.*}', 'tex')
9332 call SyntaxRange#Include('\\\[', '\\\]', 'tex')
9333endif
9334
9335" vi: ft=vim:tw=80:sw=4:ts=4:fdm=marker