diff --git a/pack/acp/README.md b/pack/acp/README.md index 282b61b..0242cd8 100644 --- a/pack/acp/README.md +++ b/pack/acp/README.md @@ -11,5 +11,7 @@ Git submodules are slow, so handle this manually. * [start/mom.vim](https://github.com/vim-scripts/mom.vim) * [start/rust.vim](https://github.com/rust-lang/rust.vim) * [start/vim-go](https://github.com/fatih/vim-go) +* [start/vim-orgmode](https://github.com/jceb/vim-orgmode) * [start/vim-ps1](https://github.com/PProvost/vim-ps1) +* [start/vim-speeddating](https://github.com/tpope/vim-speeddating) * [start/vim-surround](https://github.com/tpope/vim-surround) diff --git a/pack/acp/start/vim-orgmode/.gitignore b/pack/acp/start/vim-orgmode/.gitignore new file mode 100644 index 0000000..acf3441 --- /dev/null +++ b/pack/acp/start/vim-orgmode/.gitignore @@ -0,0 +1,6 @@ +*.pyc +*.swp +tags +.ropeproject +.cover* +cover* diff --git a/pack/acp/start/vim-orgmode/.pylintrc b/pack/acp/start/vim-orgmode/.pylintrc new file mode 100644 index 0000000..19ff0f7 --- /dev/null +++ b/pack/acp/start/vim-orgmode/.pylintrc @@ -0,0 +1,238 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +init-hook=sys.path.append(os.path.abspath('ftplugin')) + +# Profiled execution. +profile=no + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=.git + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[MESSAGES CONTROL] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). +#disable= + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html +output-format=parseable + +# Include message's id in output +include-ids=no + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (RP0004). +comment=no + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). +ignored-classes=SQLObject + +# When zope mode is activated, add a predefined set of Zope acquired attributes +# to generated-members. +zope=no + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E0201 when accessed. Python regular +# expressions are accepted. +generated-members=REQUEST,acl_users,aq_parent + + +[BASIC] + +# Required attributes for module, separated by a comma +required-attributes= + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,apply,input + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__ + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the beginning of the name of dummy variables +# (i.e. not used). +dummy-variables-rgx=_|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=800 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=\t + + +[CLASSES] + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branchs=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 diff --git a/pack/acp/start/vim-orgmode/.travis.yml b/pack/acp/start/vim-orgmode/.travis.yml new file mode 100644 index 0000000..27d8df9 --- /dev/null +++ b/pack/acp/start/vim-orgmode/.travis.yml @@ -0,0 +1,21 @@ +language: python + +before_install: + - sudo apt-get update && sudo apt-get --reinstall install -qq language-pack-pt + +python: + - "2.7" + - "3.4" + - "3.5" + - "3.6" + +install: + - pip install coverage + - pip install codecov + +script: + - cd tests + - nosetests --with-coverage . + +after_success: + - codecov diff --git a/pack/acp/start/vim-orgmode/CHANGELOG.org b/pack/acp/start/vim-orgmode/CHANGELOG.org new file mode 100644 index 0000000..9929046 --- /dev/null +++ b/pack/acp/start/vim-orgmode/CHANGELOG.org @@ -0,0 +1,214 @@ +* Changelog + All notable changes to this project will be documented in this file. + + This log is kept according to the [[http://keepachangelog.com/][Keep a CHANGELOG]] manifesto +** 0.7.0 :unreleased: +*** Added + - Subtracting when entering dates (PR #276) +*** Fixed + - =ir= text object now works with most operations (PR #284, closes #273) +** 0.6.0 <2017-11-06 Mon> :released: +*** Added + - Introduced sphinx documentation to Python modules. (PR #237) + - Add =Python3= support. (PR #231, closes #226) + - Implementing agenda overview for current buffer. (PR #229) + - =g:org_aggressive_conceal=, if value =1=, will conceal all simple format + identifying characters, default =0=. (PR #188) + - (testing on `g:org_aggressive_conceal=1' mode) Add possibility to escape + format indicating characters from leading inline markup, by escaping with + "\". + - Add alternative behavior: refrain from entering insert mode after + heading/checkbox creation through keybindings. Activate by setting + =g:org_prefer_insert_mode= to 0. (closes #211) + - Add export as LaTeX beamer slides (PR #206) + - Keybinding to create plainlist item directly. (closes #190) + - Make % jump between < and >. (PR #251, closes #250) +*** Changed + - Changed default value for =g:org_indent= from =1= to =0=. (closes #243) + - Revamped TODO keyword cycling rules. (PR #237) + - In [[syntax/org.vim][syntax/org.vim]], changed `\@<=' with computational faster `\zs' + - Using =c[n/N]= to create new plainlist item following + current plainlist item. Now these keybindings will unconditionally + create empty checkbox. (closes #190) +*** Deprecated + - Nothing +*** Removed + - Removed the requirement for TODO state keywords to be upper-case. + (PR #235) +*** Fixed + - Avoid duplicate =InsertLeave= handlers (PR #222, closes #223) + - Fix python3 compatibility issue with regexes + (PR #266, closes #263, #265) + - Fixed python3 compatible issue within =CalendarAction=. + (PR #242, closes #241) + - Tree promoting/demoting no longer destroy list and checkbox structure. + (closes #217) + - Fixed bug when promote/demote headings when it contain lists. + (PR #239, partly fixes #217) + - Silenced =W18= warning when non-ASCII coded TODO keywords are used. + (PR #236) + - Fix non-English locale support issue in OrgDate and Agenda. (PR #234, + closes #230) + - Fix =concealcursor= mis-setting. (from ="nc"= to =nc=) + - Fix duplicate =InsertLeave= autocmd for =tag_complete=. (closes #223) + - Fix utl error when =\= or white space is in the link by auto-escaping. + (closes #220) + - Fix typo vbm -> vmb (PR #219) + - Fix toggling checkboxes with plain embedded lists (PR #212, closes #209) + - Return to right window before setting todo (closes #202) + - Fix link to calendar-vim (closes #197) + - Fix =out of bound= issue when creating heading/checkbox after last + instance in document on NeoVim. (closes #213) +** 0.5.0 <2015-10-10 Sat> :released: +*** Added + - show link description in headings when folded, instead of the whole + link + - add simplified mappings to create new headings with + [|] + - improve incrementing and decrementing of list items + - moved changelog information to its own file + - add tests for the tags plugin + - copy type and indentation when creating new list items + - increase/decrease ordered list when adding new items + - add support for alphanumeric ordered lists + - add test cases for overlapping mappings + - add three dots after folded text, like in orgmode + - improve highlighting of org-mode properties (closes issue #130) + - implement global visibility as it works in Emacs org-mode (closes issue + #119) + - improve detection of speeddating plugin (closes issue #121) + - add support for high speed searching of headings that use certain tags + (closes issue #58) + - make echo, echom and echoe split messages a line ends and execute a + single vim command for each line + - add export commands OrgExportToPDF and OrgExportToHTML (closes issue + #107) + - add variables for customizing the export via Emacs: g:org_export_emacs, + g:org_export_verbose, g:org_export_init_script (closes issue #107) + - switch to subprocess.Popen for Emcas export (closes issue #107) + - add defaults and examples for all variables + - add support for inserting new checkboxes with the same keybinging as + inserting new headings (thanks to Powen Tan) + - implemented support for markdown export (issue #185) +*** Deprecated + - Nothing +*** Removed + - Nothing +*** Fixed + - allow checkbox status to be toggled when there is no indicator present + ([]) + - improve installation instructions (related to issues #111 and #176) + - optimize checkbox regex to match also just the type without status and + title + - fix broken unordered lists + - set org_tag_column to textwidth + - change commentstring to "# %s" + - fix syntax highlighting of list items + - fix indentation of first checkbox of a heading + - fix indentation of first checkbox of a heading + - disable highlighting of non-printable characters in todo state + selection window + - fix highlighting of todo keywords that are followed by additional + characters, i.e. TODOs + - omit status when entering new checkbox item if current checkbox doesn't + have one + - fix broken indentation of checkboxes (closes issue #146) + - fix CalendarAction is undefined (closes issue #142) + - correct overlapping mappings in PluginDate + - fix cache problems when inserting a new heading, together with multi + line text (closes issue #116) + - rename plug to OrgTodoToggleNonInteractive (closes issue #114) + - fix jumping to the first character within the body of a heading + - use Ignore highlighting instead of NonText for shaded stars (closes + issues #173) + - fix broken buffer number (closes issue #177) + - make exports work with emacs 24.4 (closes issue #178) + - improve comments + - fix syntax for #+BEGIN_* blocks (issue #186) +** 0.4.0-0 <2011-10-16 Sun> :released: + - fix broken repeat settings for moving a heading + - improve performance when moving a heading upward or downward (closes + issue #108) + - improve performance when changing the level of a heading (related to + issue #108) + - extend liborgmode.headings.HeadingList to allow headings to not be + tainted when moving them around + - change heading tree text object to ir/ar... because of vim's it/at text + object (closes issue #106) + - improve performance when inserting a new heading below (closes issue + #105) + - remove duplicate tags (closes issue #104) + - improve performance in insert mode (closes issue #103) + - improve performance when opening larger org files (closes issue #103) + - replace org.txt by orgguide.txt (closes issue #77) + - replace g:org_leader by (closes issue #101) + To restore the previous behavior add the following line to your vimrc: + > + let maplocalleader = ',' + < + - change normal command execution to not remap any key (related to issue + #85) + - fix regression timeout when opening folds (closes issue #100) + - vim-orgmode multistate documentation (closes issue #77) + - add support for @-signs in tags (closes issue #98) + - enable file completion for hyperlinks by default (closes issue #97) + - fix traceback when pressing while editing a link (closes issue + #96) + - implement reverse visibility cycling using (closes issue #95) + - change ,, and ,. to remap zr and zm. (closes issue #73) + - add .cnf files to the vimball archive (closes #93) + - integrate pylint code checker (closes issue #87) + - solve encoding issues in the agenda plugin (closes issue #86) + - add description for writing test cases + - add coverage report target (closes issue #74) + - add support for plain lists, thanks to Aleksandar Dimitrov (closes issue + #81) + - add agenda view, many thanks to Stefan Otte (closes issue #34) + - move cursor to the current todo state when selecting the todo state + interactively (closes issue #61) + - add parameter scope to method settings.get + - add method settings.unset + - fix cursor positioning when selecting todo states + - improve date plugin + - update vba targets to its new name vmb + - demoting a newly created second level heading doesn't cause all children + to + be deleted anymore (closes issue #65) + - add error message for missing dependencies (closes issue #59) + - rename tests directory + - change licensing of the documentation to GNU Free Documentation License + - integrate orgguide (closes issue #57) + - replace DIRECTION_* with an enum (closes issue #56 and issue #49) +** 0.3.1-0 <2011-08-14 Sun> :released: + - demoting a newly created second level heading doesn't cause all children + to be deleted anymore (closes issue #65) + - add error message for missing dependencies (closes issue #59) +** 0.3.0-0 <2011-08-09 Tue> :released: + - fix completion menu popup that disappeared because of the usage of + vim.command (closes issue #48) + - implement interactive todo state selection (closes issue #5) + - add orgmode group to au commands in TagProperties plugin (closes issue + #53) + - allow demotion of first level headings (closes issue #27) + - fix encoding issues in Date plugin + - add general support for multiple todo sequences (closes Issue #46) + - fix folded text for headings containing backslashes or double quotes + (closes issue #26) + - add Document.get_todo_states() and Document.get_all_todo_states() + - don't confuse upper case words at the beginning of a heading with a todo + state (closes issue #28) + - fix error in setting tags (issue #25) + - improve split of heading (issue #24) + - add variable g:org_improve_split_heading to enable/disable improve the + split of headings (issue #24) + - implement shortcut for moving to the partent's next sibling (g}) (issue + #22) + - fix duplication of children when inserting a new heading (issue #20) + - always start insert mode when adding a new heading (issue #21) +** 0.2.1-0 <2011-06-26 Sun> :released: + - fix encoding of todo states set by the Todo plugin (thanks to Daniel + Carl and kien for pointing out the issue) + - add documentation for remapping shortcuts + - add documentation for customizing syntax highlighting +** 0.2.0-0 <2011-06-25 Sat> :released: + - initial release diff --git a/pack/acp/start/vim-orgmode/LICENSE b/pack/acp/start/vim-orgmode/LICENSE new file mode 100644 index 0000000..e37682c --- /dev/null +++ b/pack/acp/start/vim-orgmode/LICENSE @@ -0,0 +1,60 @@ +--------------------------------------------------------------------- +All source code is licensed under the terms of the following license: +--------------------------------------------------------------------- + +Copyright (C) 2010,2011 Jan Christoph Ebersbach + +http://www.e-jc.de/ + +All rights reserved. + +The source code of this program is made available under the terms of the +GNU Affero General Public License version 3 (GNU AGPL V3) as published +by the Free Software Foundation. + +Binary versions of this program provided by Univention to you as well as +other copyrighted, protected or trademarked materials like Logos, +graphics, fonts, specific documentations and configurations, +cryptographic keys etc. are subject to a license agreement between you +and Univention and not subject to the GNU AGPL V3. + +In the case you use this program under the terms of the GNU AGPL V3, the +program is provided in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public +License for more details. + +You should have received a copy of the GNU Affero General Public License +with the Debian GNU/Linux or Univention distribution in file +/usr/share/common-licenses/AGPL-3; if not, see +. + + +-------------------------------------------------------------------- +All documentation found in the directories doc and documentation are +licensed under the terms of the following license: +-------------------------------------------------------------------- + +doc/org.txt +Copyright (C) 2010,2011 Jan Christoph Ebersbach + +doc/orgguide.txt +documentation/emacs_orgguide.org +documentation/emacs_orgguide.texi +Copyright (C) 2010 Free Software Foundation + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, with the Front-Cover texts being “A GNU Manual,” and +with the Back-Cover Texts as in (a) below. A copy of the license is +included in the section entitled “GNU Free Documentation License.” + +(a) The FSF’s Back-Cover Text is: “You have the freedom to copy and +modify this GNU manual. Buying copies from the FSF supports it in +developing GNU and promoting software freedom.” + +This document is part of a collection distributed under the GNU Free +Documentation License. If you want to distribute this document +separately from the collection, you can do so by adding a copy of the +license to the document, as described in section 6 of the license. diff --git a/pack/acp/start/vim-orgmode/Makefile b/pack/acp/start/vim-orgmode/Makefile new file mode 100644 index 0000000..219a57e --- /dev/null +++ b/pack/acp/start/vim-orgmode/Makefile @@ -0,0 +1,91 @@ +PLUGIN = orgmode +PREFIX = /usr/local +VIMDIR = $(PREFIX)/share/vim + +all: build + +build: + +# install plugin at destination +install: doc indent ftdetect ftplugin syntax + for i in doc indent ftdetect ftplugin syntax; do \ + find $$i -type f -name \*.txt -o -type f -name \*.cnf -o -type f -name \*.py -o -type f -name \*.vim | while read f; do \ + install -m 0755 -d $(DESTDIR)$(VIMDIR)/$$(dirname "$$f"); \ + install -m 0644 $$f $(DESTDIR)$(VIMDIR)/$$f; \ + done; \ + done + +# cleanup +clean: documentation + @find . -name \*.pyc -o -name \*.py,cover -exec rm {} \; + @rm -rf ${PLUGIN}.vmb ${PLUGIN}.vmb.gz tmp files + cd $< && $(MAKE) $@ + +# generate the vim ball package +${PLUGIN}.vmb: check build_vmb.vim clean + $(MAKE) DESTDIR=$(PWD)/tmp VIMDIR= install + find tmp -type f | sed -e 's/^tmp\///' > files + cp build_vmb.vim tmp + cd tmp && vim --cmd 'let g:plugin_name="${PLUGIN}"' -s build_vmb.vim + [ -e tmp/${PLUGIN}.vba ] && mv tmp/${PLUGIN}.vba tmp/$@ || true + mv tmp/$@ . + +${PLUGIN}.vmb.gz: ${PLUGIN}.vmb + @rm -f ${PLUGIN}.vmb.gz + gzip $< + +vmb: ${PLUGIN}.vmb + +vmb.gz: ${PLUGIN}.vmb.gz + +${PLUGIN}.vba: ${PLUGIN}.vmb + mv $< $@ + +${PLUGIN}.vba.gz: ${PLUGIN}.vba + @rm -f ${PLUGIN}.vba.gz + gzip $< + +vba: ${PLUGIN}.vba + +vba.gz: ${PLUGIN}.vba.gz + +# run unit tests +test: check + +check: tests/run_tests.py + cd tests && python2 run_tests.py + +# generate documentation +docs: documentation + cd $< && $(MAKE) + +# generate a test coverage report for all python files +coverage: + @echo ">>> Coverage depends on the package python-nose and python-coverage, make sure they are installed!" + cd tests && nosetests2 --with-coverage --cover-html . + +# run a static code checker +lint: + @echo ">>> Lint depends on the package pylint make sure it's installed!" + pylint --rcfile .pylintrc --disable=C0301,C0103,C0111,C0322,C0323,C0324,W0703,W0612,W0603 orgmode + +lintall: + @echo ">>> Lint depends on the package pylint make sure it's installed!" + pylint --rcfile .pylintrc orgmode + +# install vim-orgmode in the .vim/bundle directory for test purposes +VIMPLUGINDIR = $(HOME)/.vim/bundle/orgmode + +installvmb: ${PLUGIN}.vmb install_vmb.vim + rm -rvf ${VIMPLUGINDIR} + mkdir -p "${VIMPLUGINDIR}" + vim --cmd "let g:installdir='${VIMPLUGINDIR}'" -s install_vmb.vim $< + @echo "Plugin was installed in ${VIMPLUGINDIR}. Make sure you are using a plugin loader like pathegon, otherwise the ${PLUGIN} might not work properly." + +installvba: ${PLUGIN}.vba install_vba.vim + rm -rvf ${VIMPLUGINDIR} + mkdir -p "${VIMPLUGINDIR}" + vim --cmd "let g:installdir='${VIMPLUGINDIR}'" -s install_vba.vim $< + @echo "Plugin was installed in ${VIMPLUGINDIR}. Make sure you are using a plugin loader like pathegon, otherwise the ${PLUGIN} might not work properly." + +.PHONY: all build test check install clean vmb vmb.gz docs installvmb diff --git a/pack/acp/start/vim-orgmode/README.org b/pack/acp/start/vim-orgmode/README.org new file mode 100644 index 0000000..c109056 --- /dev/null +++ b/pack/acp/start/vim-orgmode/README.org @@ -0,0 +1,43 @@ +* Vim-OrgMode + + #+ATTR_HTML: title="Join the chat at https://gitter.im/jceb/vim-orgmode" + [[https://gitter.im/jceb/vim-orgmode?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge][file:https://badges.gitter.im/jceb/vim-orgmode.svg]] + [[https://travis-ci.org/jceb/vim-orgmode][file:https://travis-ci.org/jceb/vim-orgmode.svg]] + [[https://codecov.io/gh/jceb/vim-orgmode][file:https://codecov.io/gh/jceb/vim-orgmode/branch/master/graph/badge.svg]] + + Text outlining and task management for Vim based on [[http://orgmode.org/][Emacs' Org-Mode]]. + + The idea for this plugin was born by listening to the + [[http://twit.tv/floss136][Floss Weekly podcast]] introducing Emacs Org-Mode. + Org-Mode has a lot of strong features like folding, views (sparse tree) and + scheduling of tasks. These are completed by hyperlinks, tags, todo states, + priorities aso. + + vim-orgmode aims at providing the same functionality for Vim. + + [[https://github.com/jceb/vim-orgmode/blob/master/examples/mylife.org][file:examples/mylife.gif]] + +** Features + Currently vim-orgmode does not support all orgmode features but is quite + usable. Short list of the already supported features: + + - Syntax highlighting + - Cycle visibility of headings (folding) + - Navigate between headings + - Edit the structure of the document: add, move, promote, denote headings + and more + - Hyperlinks within vim-orgmode and outside (files, webpages, etc.) + - TODO list management + - Tags for headings + - Lists in alphanumeric and bullet item notation and checkbox support + - Basic date handling + - Export to other formats (via Emacs' Org-Mode) + +* Installation and Usage + Installation and usage instructions are found in the file [[doc/orgguide.txt][doc/orgguide.txt]]. + +* License + Information about the license is found in file [[LICENSE]]. + +* Changelog + All changes are found in file [[https://github.com/jceb/vim-orgmode/blob/master/CHANGELOG.org][CHANGELOG.org]] diff --git a/pack/acp/start/vim-orgmode/build_vmb.vim b/pack/acp/start/vim-orgmode/build_vmb.vim new file mode 100644 index 0000000..fd5fe05 --- /dev/null +++ b/pack/acp/start/vim-orgmode/build_vmb.vim @@ -0,0 +1,4 @@ +:let g:vimball_home = "." +:e ../files +:execute '%MkVimball!' . g:plugin_name +:q! diff --git a/pack/acp/start/vim-orgmode/debian/changelog b/pack/acp/start/vim-orgmode/debian/changelog new file mode 100644 index 0000000..c673ddc --- /dev/null +++ b/pack/acp/start/vim-orgmode/debian/changelog @@ -0,0 +1,869 @@ +vim-orgmode (0.3.0-2) unstable; urgency=low + + * update documentation + + -- Jan Christoph Ebersbach Tue, 09 Aug 2011 21:13:40 +0200 + +vim-orgmode (0.3.0-1) unstable; urgency=low + + * update documentation + + -- Jan Christoph Ebersbach Tue, 09 Aug 2011 08:37:25 +0200 + +vim-orgmode (0.3.0-0) unstable; urgency=low + + * fix completion menu popup that disappeared because of the usage of + vim.command + * closes issue #48 + + -- Jan Christoph Ebersbach Tue, 09 Aug 2011 08:25:03 +0200 + +vim-orgmode (0.2.1-25) unstable; urgency=low + + * playing around with ftdetect vs. setfiletype + + -- Jan Christoph Ebersbach Mon, 08 Aug 2011 08:11:23 +0200 + +vim-orgmode (0.2.1-24) unstable; urgency=low + + * improve implementation of todo state selection (issue #5) + + -- Jan Christoph Ebersbach Sun, 07 Aug 2011 19:55:06 +0200 + +vim-orgmode (0.2.1-23) unstable; urgency=low + + * more precise regex for dates - pull request by sotte + * closes issue #52 + + -- Roman Asendorf Wed, 03 Aug 2011 22:40:48 +0200 + +vim-orgmode (0.2.1-22) unstable; urgency=low + + * implement interactive todo state selection + * closes issue #5 + + -- Jan Christoph Ebersbach Wed, 03 Aug 2011 22:23:14 +0200 + +vim-orgmode (0.2.1-21) unstable; urgency=low + + * added emacs as suggested package - pull request by sotte + * closes issue #54 + + -- Roman Asendorf Wed, 03 Aug 2011 21:52:15 +0200 + +vim-orgmode (0.2.1-20) unstable; urgency=low + + * simple export via emacs for pdf and html - pull request by sotte + * closes issue #54 + + -- Roman Asendorf Wed, 03 Aug 2011 21:43:15 +0200 + +vim-orgmode (0.2.1-19) unstable; urgency=low + + * add orgmode group to au commands in TagProperties plugin + * closes issue #53 + + -- Jan Christoph Ebersbach Wed, 03 Aug 2011 20:02:08 +0200 + +vim-orgmode (0.2.1-18) unstable; urgency=low + + * allow demotion of first level headings + * closes issue #27 + + -- Jan Christoph Ebersbach Mon, 01 Aug 2011 21:59:32 +0200 + +vim-orgmode (0.2.1-17) unstable; urgency=low + + * fix encoding issues in Date plugin + + -- Jan Christoph Ebersbach Tue, 12 Jul 2011 17:59:41 +0200 + +vim-orgmode (0.2.1-16) unstable; urgency=low + + * update installvba target to also work with older vim version + + -- Jan Christoph Ebersbach Mon, 11 Jul 2011 08:23:18 +0200 + +vim-orgmode (0.2.1-15) unstable; urgency=low + + * make switching to the next todo sequence more convenient + * fix issue in offset calculation when old and new state are None + + -- Jan Christoph Ebersbach Sat, 09 Jul 2011 17:54:46 +0200 + +vim-orgmode (0.2.1-14) unstable; urgency=low + + * make switching to the next todo sequence more consistent + + -- Jan Christoph Ebersbach Sat, 09 Jul 2011 17:38:18 +0200 + +vim-orgmode (0.2.1-13) unstable; urgency=low + + * fix minor issues related to switching to the next todo sequence + + -- Jan Christoph Ebersbach Sat, 09 Jul 2011 17:33:56 +0200 + +vim-orgmode (0.2.1-12) unstable; urgency=low + + * add general support for multiple todo sequences + * closes issue #46 + + -- Jan Christoph Ebersbach Sat, 09 Jul 2011 16:39:49 +0200 + +vim-orgmode (0.2.1-11) unstable; urgency=low + + * update documentation + * update clean target + + -- Jan Christoph Ebersbach Sat, 09 Jul 2011 11:29:57 +0200 + +vim-orgmode (0.2.1-10) unstable; urgency=low + + * fix todo tests broken by version 0.2.1-8 + * fix DONE result of VimBuffer.get_todo_states.parse_states + + -- Jan Christoph Ebersbach Sat, 09 Jul 2011 09:49:07 +0200 + +vim-orgmode (0.2.1-9) unstable; urgency=low + + * fix folded text for headings containing backslashes or double quotes + * closes issue #26 + + -- Jan Christoph Ebersbach Fri, 08 Jul 2011 23:21:07 +0200 + +vim-orgmode (0.2.1-8) unstable; urgency=low + + * add Document.get_todo_states() and Document.get_all_todo_states() + * don't confuse upper case words at the beginning of a heading with a todo + state + * closes issue #28 + + -- Jan Christoph Ebersbach Fri, 08 Jul 2011 22:30:29 +0200 + +vim-orgmode (0.2.1-7) unstable; urgency=low + + * fix error in setting tags + * closes issue #25 + + -- Jan Christoph Ebersbach Mon, 04 Jul 2011 12:56:27 +0200 + +vim-orgmode (0.2.1-6) unstable; urgency=low + + * improve split of heading + * add variable g:org_improve_split_heading to enable/disable improve + the split of headings + * closes issue #24 + * change Makefile to be more generic + + -- Jan Christoph Ebersbach Mon, 04 Jul 2011 07:54:17 +0200 + +vim-orgmode (0.2.1-5) unstable; urgency=low + + * implement shortcut for moving to the partent's next sibling + * closes issue #22 + + -- Jan Christoph Ebersbach Sun, 03 Jul 2011 13:21:04 +0200 + +vim-orgmode (0.2.1-4) unstable; urgency=low + + * fix duplication of children when inserting a new heading + * closes issue #20 + + -- Jan Christoph Ebersbach Sun, 03 Jul 2011 12:43:32 +0200 + +vim-orgmode (0.2.1-3) unstable; urgency=low + + * add support for new vimball file extension .vmb + + -- Jan Christoph Ebersbach Sun, 03 Jul 2011 12:27:07 +0200 + +vim-orgmode (0.2.1-2) unstable; urgency=low + + * always start insert mode when adding a new heading + * closes issue #21 + + -- Jan Christoph Ebersbach Sun, 03 Jul 2011 12:25:20 +0200 + +vim-orgmode (0.2.1-1) unstable; urgency=low + + * make target installvba create the VIMPLUGINDIR before attempting the + installation + + -- Jan Christoph Ebersbach Sun, 26 Jun 2011 21:07:54 +0200 + +vim-orgmode (0.2.1-0) unstable; urgency=low + + * add documentation for customizing syntax highlighting + + -- Jan Christoph Ebersbach Sun, 26 Jun 2011 19:08:59 +0200 + +vim-orgmode (0.2.0-6) unstable; urgency=low + + * add dependency to clean target to orgmode.vba + + -- Jan Christoph Ebersbach Sun, 26 Jun 2011 14:03:18 +0200 + +vim-orgmode (0.2.0-5) unstable; urgency=low + + * add documentation for remapping shortcuts + + -- Jan Christoph Ebersbach Sun, 26 Jun 2011 13:47:30 +0200 + +vim-orgmode (0.2.0-4) unstable; urgency=low + + * fix names for Todo plugin + + -- Jan Christoph Ebersbach Sun, 26 Jun 2011 13:24:07 +0200 + +vim-orgmode (0.2.0-3) unstable; urgency=low + + * fix encoding of todo states set by the Todo plugin (thanks to Daniel Carl + for pointing out the issue) + * add target installvba to locally install orgmode.vba + * cleanup Makefile + + -- Jan Christoph Ebersbach Sun, 26 Jun 2011 13:15:34 +0200 + +vim-orgmode (0.2.0-2) unstable; urgency=low + + * add hint for updateing vim-orgmode from a previous version + * move all documentation related to installation and usage to doc/org.txt + + -- Jan Christoph Ebersbach Sun, 26 Jun 2011 12:46:55 +0200 + +vim-orgmode (0.2.0-1) unstable; urgency=low + + * minor changes to the build files + + -- Jan Christoph Ebersbach Sat, 25 Jun 2011 17:43:48 +0200 + +vim-orgmode (0.2.0-0) unstable; urgency=low + + * remove unused echo command in Makefile + * add vim help file doc/org.txt + * first release + + -- Jan Christoph Ebersbach Sat, 25 Jun 2011 17:35:31 +0200 + +vim-orgmode (0.1.0-89) unstable; urgency=low + + * add separate target to generate the vba.gz archive + + -- Jan Christoph Ebersbach Sat, 25 Jun 2011 16:47:23 +0200 + +vim-orgmode (0.1.0-88) unstable; urgency=low + + * improve error message in case ditaa is not available + + -- Jan Christoph Ebersbach Sat, 25 Jun 2011 16:42:36 +0200 + +vim-orgmode (0.1.0-87) unstable; urgency=low + + * update README and diagram + + -- Jan Christoph Ebersbach Sat, 25 Jun 2011 16:38:31 +0200 + +vim-orgmode (0.1.0-86) unstable; urgency=low + + * fix issue in EditStructure._move_heading that caused the current heading + not to be removed from the list of heading + * improve error messages for non-allowed characters in tags or todo states + * allow \t and space in tags because of an automatic replacement by _ later + on + * fix issue when creating the debian package an not all path components + exist + + -- Jan Christoph Ebersbach Sat, 25 Jun 2011 16:36:39 +0200 + +vim-orgmode (0.1.0-85) unstable; urgency=low + + * add support and tests for splitting a headings title in insert mode + + -- Jan Christoph Ebersbach Sat, 25 Jun 2011 15:55:16 +0200 + +vim-orgmode (0.1.0-84) unstable; urgency=low + + * correct test cases for Navigator plugin + + -- Jan Christoph Ebersbach Sat, 25 Jun 2011 14:05:17 +0200 + +vim-orgmode (0.1.0-83) unstable; urgency=low + + * remove LoggingWork from loaded plugins + * fix a problem when positioning the cursor on a newly created heading + * fix a problem when positioning the cursor on the first character of a + non-heading line + * fix a problem when indenting non-heading lines in insert mode + + -- Jan Christoph Ebersbach Sat, 25 Jun 2011 13:13:41 +0200 + +vim-orgmode (0.1.0-82) unstable; urgency=low + + * change global function insert_at_cursor to append text after cursor + * add support for insert mode to insert_at_cursor + * fix traceback in Date plugin caused by conversion to unicode + * change insert date mappings to ,si and ,sa + * add submenu Change Date to Date plugin + + -- Jan Christoph Ebersbach Sat, 25 Jun 2011 13:00:54 +0200 + +vim-orgmode (0.1.0-81) unstable; urgency=low + + * fix error in TagsProperties.set_tags when None type object is returned + * change TagsProperties.realign_tags to use VimBuffer.write_heading instead + of VimBuffer.write method + * add convenience function VimBuffer.find_current_heading + * remove trailing spaces when a heading contains a single uppercase word + (pseudo TODO state) + * change Heading.copy to create a completely detached heading even without a + connection to a document + * change Heading.start to reture Heading._orig_start when the heading has no + connection to a document + * add parameter connect_with_document to Document.find_heading + * convert plugins and global functions to use VimBuffer.find_current_heading + + -- Jan Christoph Ebersbach Tue, 21 Jun 2011 23:26:25 +0200 + +vim-orgmode (0.1.0-80) unstable; urgency=low + + * update README + + -- Jan Christoph Ebersbach Tue, 21 Jun 2011 07:46:04 +0200 + +vim-orgmode (0.1.0-79) unstable; urgency=low + + * implement VimBuffer.write_heading to just update a single heading + + -- Jan Christoph Ebersbach Sun, 19 Jun 2011 22:19:00 +0200 + +vim-orgmode (0.1.0-78) unstable; urgency=low + + * fix assignment to vim.current.buffer in test cases + + -- Jan Christoph Ebersbach Sun, 19 Jun 2011 15:21:39 +0200 + +vim-orgmode (0.1.0-77) unstable; urgency=low + + * improve memory usage by removing unused document objects + + -- Jan Christoph Ebersbach Sun, 19 Jun 2011 14:33:10 +0200 + +vim-orgmode (0.1.0-76) unstable; urgency=low + + * improve speed by implementing a better reuse of document objects + + -- Jan Christoph Ebersbach Sun, 19 Jun 2011 14:18:26 +0200 + +vim-orgmode (0.1.0-75) unstable; urgency=low + + * fix issue with tag recognition in headings that contain zero or just one + word + + -- Jan Christoph Ebersbach Sun, 19 Jun 2011 14:07:29 +0200 + +vim-orgmode (0.1.0-74) unstable; urgency=low + + * change EditStructure mappings back to using the shift key + + -- Jan Christoph Ebersbach Sun, 19 Jun 2011 13:29:50 +0200 + +vim-orgmode (0.1.0-73) unstable; urgency=low + + * fix regression introduced in version 0.1.0-71 + + -- Jan Christoph Ebersbach Sun, 19 Jun 2011 13:28:43 +0200 + +vim-orgmode (0.1.0-72) unstable; urgency=low + + * change default value of g:org_tag_completion_ignorecase to &ignorecase + + -- Jan Christoph Ebersbach Sun, 19 Jun 2011 13:20:55 +0200 + +vim-orgmode (0.1.0-71) unstable; urgency=low + + * convert vim.eval return values to unicode + + -- Jan Christoph Ebersbach Sun, 19 Jun 2011 13:15:51 +0200 + +vim-orgmode (0.1.0-70) unstable; urgency=low + + * update README + + -- Jan Christoph Ebersbach Sat, 18 Jun 2011 19:21:04 +0200 + +vim-orgmode (0.1.0-69) unstable; urgency=low + + * add support for tagbar plugin + * move ctags support to file ftplugin/org.cnf + + -- Jan Christoph Ebersbach Sat, 18 Jun 2011 19:15:13 +0200 + +vim-orgmode (0.1.0-68) unstable; urgency=low + + * fix encoding error for plugins that directly access vim.current.buffer + + -- Jan Christoph Ebersbach Thu, 16 Jun 2011 12:44:06 +0200 + +vim-orgmode (0.1.0-67) unstable; urgency=low + + * change regex strings to raw strings + + -- Jan Christoph Ebersbach Fri, 17 Jun 2011 08:09:23 +0200 + +vim-orgmode (0.1.0-66) unstable; urgency=low + + * remove highlighting group Question from title colors + * fix utf-8 encoding for vim.command calls + + -- Jan Christoph Ebersbach Thu, 16 Jun 2011 12:39:46 +0200 + +vim-orgmode (0.1.0-65) unstable; urgency=low + + * fix error when aktivating lazyredraw and hidden for the current buffer + * set commentstring to "# %s" + * fix error when switching buffers to get current changetick + * fix error when running get_document with bufnr 0 that opened to the first + document that was queried instead of return the current document + + -- Jan Christoph Ebersbach Wed, 15 Jun 2011 12:42:03 +0200 + +vim-orgmode (0.1.0-64) unstable; urgency=low + + * remove parameter mode from function change_visual_selection + * add more test cases for Navigator plugin, current this plugin is somehow + broken + + -- Jan Christoph Ebersbach Tue, 14 Jun 2011 20:30:45 +0200 + +vim-orgmode (0.1.0-63) unstable; urgency=low + + * convert LoggingWork plugin to unicode + + -- Jan Christoph Ebersbach Tue, 14 Jun 2011 18:47:52 +0200 + +vim-orgmode (0.1.0-62) unstable; urgency=low + + * move initialization of speeddating commands to __init__ method + + -- Jan Christoph Ebersbach Tue, 14 Jun 2011 18:45:16 +0200 + +vim-orgmode (0.1.0-61) unstable; urgency=low + + * convert Date plugin to unicode + * correct a typo in mapping for May + + -- Jan Christoph Ebersbach Tue, 14 Jun 2011 18:36:18 +0200 + +vim-orgmode (0.1.0-60) unstable; urgency=low + + * add missing counter to test_edit_structure.py + + -- Jan Christoph Ebersbach Tue, 14 Jun 2011 18:34:51 +0200 + +vim-orgmode (0.1.0-59) unstable; urgency=low + + * convert Todo plugin to unicode + + -- Jan Christoph Ebersbach Tue, 14 Jun 2011 18:32:01 +0200 + +vim-orgmode (0.1.0-58) unstable; urgency=low + + * fix indentation of docstring in VimBuffer + + -- Jan Christoph Ebersbach Mon, 13 Jun 2011 21:51:16 +0200 + +vim-orgmode (0.1.0-57) unstable; urgency=low + + * convert TagsProperties to liborgmode + + -- Jan Christoph Ebersbach Mon, 13 Jun 2011 21:48:07 +0200 + +vim-orgmode (0.1.0-56) unstable; urgency=low + + * add missing % when doing string formatting in EditStructure plugin + + -- Jan Christoph Ebersbach Mon, 13 Jun 2011 19:27:38 +0200 + +vim-orgmode (0.1.0-55) unstable; urgency=low + + * fix encoding error in keybinding.py when evaluation self.overwrite_exisiting + * convert Hyperlink plugin to unicode + * add hint to current plugin when a traceback occures during initialization + + -- Jan Christoph Ebersbach Mon, 13 Jun 2011 19:23:13 +0200 + +vim-orgmode (0.1.0-54) unstable; urgency=low + + * add copy method to Heading class to make a copy of the object + * track orig_start in Document.write method instead of the heading objects + * convert EditStructure plugin to liborgmode + * swap promote/demote meaning + + -- Jan Christoph Ebersbach Mon, 13 Jun 2011 18:59:21 +0200 + +vim-orgmode (0.1.0-53) unstable; urgency=low + + * fix performance issue when identing/folding text + * rename method load to init_dom + + -- Jan Christoph Ebersbach Sat, 28 May 2011 15:21:58 +0200 + +vim-orgmode (0.1.0-52) unstable; urgency=low + + * fix performance issue by introducing static computation of start value + + -- Jan Christoph Ebersbach Sat, 28 May 2011 14:51:38 +0200 + +vim-orgmode (0.1.0-51) unstable; urgency=low + + * add support for multiple color definitions for :foreground and :background + + -- Jan Christoph Ebersbach Fri, 20 May 2011 08:29:59 +0200 + +vim-orgmode (0.1.0-50) unstable; urgency=low + + * add support for org-todo-keyword-faces + * add support for nested org-todo-keyword lists + * add descriptions and examples for variables + + -- Jan Christoph Ebersbach Sun, 15 May 2011 12:00:41 +0200 + +vim-orgmode (0.1.0-49) unstable; urgency=low + + * general cleanup in syntax file + * add keyword default to all highlighting statements + * add syntax support for comments + * clear highlighting for org_shade_stars group if + g:org_heading_shade_leading_stars is not set + + -- Jan Christoph Ebersbach Sat, 14 May 2011 23:33:50 +0200 + +vim-orgmode (0.1.0-48) unstable; urgency=low + + * add syntax support for tables + + -- Jan Christoph Ebersbach Sat, 14 May 2011 23:04:46 +0200 + +vim-orgmode (0.1.0-47) unstable; urgency=low + + * add syntax support for DEADLINE and SCHEDULED + + -- Jan Christoph Ebersbach Sat, 14 May 2011 21:27:07 +0200 + +vim-orgmode (0.1.0-46) unstable; urgency=low + + * add syntax support for timestamps + + -- Jan Christoph Ebersbach Sat, 14 May 2011 21:11:13 +0200 + +vim-orgmode (0.1.0-45) unstable; urgency=low + + * add syntax support for properties + + -- Jan Christoph Ebersbach Sat, 14 May 2011 20:52:29 +0200 + +vim-orgmode (0.1.0-44) unstable; urgency=low + + * convert tests to unicode + + -- Jan Christoph Ebersbach Fri, 13 May 2011 23:39:38 +0200 + +vim-orgmode (0.1.0-43) unstable; urgency=low + + * change comparison of None to "is" instead of "==" + * add function flatten_list to prevent sublists + * add convenience methods get_index_in_parent_list and get_parent_list to Heading class + * add __unicode__ and __str__ methods to Document + * started porting of EditStructure plugin + + -- Jan Christoph Ebersbach Fri, 13 May 2011 22:59:41 +0200 + +vim-orgmode (0.1.0-42) unstable; urgency=low + + * fix syntax error in syntax/org.vim + + -- Jan Christoph Ebersbach Fri, 13 May 2011 19:30:44 +0200 + +vim-orgmode (0.1.0-41) unstable; urgency=low + + * replace hbsitz's org syntax file + * change highlighting to integrate much better with colorschemes + * change license to AGPL 3 + + -- Jan Christoph Ebersbach Thu, 12 May 2011 18:46:13 +0200 + +vim-orgmode (0.1.0-40) unstable; urgency=low + + * add tag_column and tabstop implementation to VimBuffer + * rename setting org_tags_column to org_tag_column + * rename setting org_tags_completion_ignorecase to + org_tag_completion_ignorecase + * fix unicode settings in test cases + + -- Jan Christoph Ebersbach Tue, 10 May 2011 13:16:35 +0900 + +vim-orgmode (0.1.0-39) unstable; urgency=low + + * fix bug in echo function - use echo command + + -- Jan Christoph Ebersbach Mon, 09 May 2011 21:17:50 +0900 + +vim-orgmode (0.1.0-38) unstable; urgency=low + + * convert Navigator plugin to unicode and liborgmode + + -- Jan Christoph Ebersbach Mon, 09 May 2011 21:17:17 +0900 + +vim-orgmode (0.1.0-37) unstable; urgency=low + + * change echo function to use vim echo function + * add missing unicode parameter to string in ShowHide keybinding + * enable Misc plugin + + -- Jan Christoph Ebersbach Mon, 09 May 2011 20:32:22 +0900 + +vim-orgmode (0.1.0-36) unstable; urgency=low + + * convert Misc plugin to unicode and liborgmode + + -- Jan Christoph Ebersbach Mon, 09 May 2011 20:14:04 +0900 + +vim-orgmode (0.1.0-35) unstable; urgency=low + + * fix a bug in ShowHide.toggle_folding when folding the first heading in + document when it doesn't have a child heading + + -- Jan Christoph Ebersbach Mon, 09 May 2011 19:55:52 +0900 + +vim-orgmode (0.1.0-34) unstable; urgency=low + + * enable tests for ShowHide plugin + + -- Jan Christoph Ebersbach Mon, 09 May 2011 18:40:11 +0900 + +vim-orgmode (0.1.0-33) unstable; urgency=low + + * optimize update_changedtick to not run through all but just the wanted + buffer + + -- Jan Christoph Ebersbach Mon, 09 May 2011 18:33:14 +0900 + +vim-orgmode (0.1.0-32) unstable; urgency=low + + * remove debug infomation from liborgmode.py + * add __str__ method for for Heading class + * port base functionality of orgmode to unicode + * echo a message instead of an error when no plugins are definied + * add heading parameter to VimBuffer methods + * add functionality to reuse VimBuffer objects and recognise user changes + * add BufNotInSync exception + * disable all but ShowHide plugin + * port ShowHide plugin to unicode and liborgmode + * port PluginExample to unicode and liborgmode + + -- Jan Christoph Ebersbach Mon, 09 May 2011 18:20:59 +0900 + +vim-orgmode (0.1.0-31) unstable; urgency=low + + * add vim swap files to gitignore list + + -- Jan Christoph Ebersbach Mon, 09 May 2011 14:29:41 +0900 + +vim-orgmode (0.1.0-30) unstable; urgency=low + + * add support for unicode + + -- Jan Christoph Ebersbach Mon, 09 May 2011 14:27:37 +0900 + +vim-orgmode (0.1.0-29) unstable; urgency=low + + * remove unsued import from liborgmode + * add heading parameter to VimBuffer.loag function + * update tests to use renamed liborgmode/document files + + -- Jan Christoph Ebersbach Mon, 09 May 2011 11:49:25 +0900 + +vim-orgmode (0.1.0-28) unstable; urgency=low + + * rename ftplugin/orgmode/liborgmode.py to ftplugin/liborgmode.py + * rename ftplugin/orgmode/vimbuffer.py to ftplugin/orgmode/document.py + + -- Jan Christoph Ebersbach Mon, 09 May 2011 11:46:39 +0900 + +vim-orgmode (0.1.0-27) unstable; urgency=low + + * update README + + -- Jan Christoph Ebersbach Fri, 06 May 2011 21:40:48 +0900 + +vim-orgmode (0.1.0-26) unstable; urgency=low + + * add load method to documents for initializing the DOM + + -- Jan Christoph Ebersbach Fri, 06 May 2011 21:21:39 +0900 + +vim-orgmode (0.1.0-25) unstable; urgency=low + + * fix heading initialization (Heading.parse_heading_from_data) when orig_start == 0 + * fix comparing of deleted headings + * fix overwriting a single item in a heading + + -- Jan Christoph Ebersbach Fri, 06 May 2011 21:12:49 +0900 + +vim-orgmode (0.1.0-24) unstable; urgency=low + + * add support for tags and todo + * externalize heading create to a separate classmethod + * add all relevant parameters to the heading constructor + + -- Jan Christoph Ebersbach Fri, 06 May 2011 20:20:37 +0900 + +vim-orgmode (0.1.0-23) unstable; urgency=low + + * make Document.is_dirty function test all headings not just the first two + levels + * add more tests for replacing headings + + -- Jan Christoph Ebersbach Thu, 05 May 2011 21:37:27 +0900 + +vim-orgmode (0.1.0-22) unstable; urgency=low + + * distinguish between heading and body changes + * make writing the VimBuffer more efficient + + -- Jan Christoph Ebersbach Thu, 05 May 2011 21:03:28 +0900 + +vim-orgmode (0.1.0-21) unstable; urgency=low + + * use functionality of UserList in MultiPurposeList + + -- Jan Christoph Ebersbach Thu, 05 May 2011 20:13:26 +0900 + +vim-orgmode (0.1.0-20) unstable; urgency=low + + * update README + + -- Jan Christoph Ebersbach Thu, 05 May 2011 10:51:12 +0900 + +vim-orgmode (0.1.0-19) unstable; urgency=low + + * major refactoring of liborgmode + * tests and plugins are currently broken + + -- Jan Christoph Ebersbach Thu, 05 May 2011 10:48:44 +0900 + +vim-orgmode (0.1.0-18) unstable; urgency=low + + * install -D doesn't work on BSD based systems; migrate commands to install -d + + -- Jan Christoph Ebersbach Mon, 02 May 2011 15:41:17 +0900 + +vim-orgmode (0.1.0-17) unstable; urgency=low + + * merge date-plugin from sotte + + -- Jan Christoph Ebersbach Mon, 02 May 2011 15:40:40 +0900 + +vim-orgmode (0.1.0-16) unstable; urgency=low + + * rename heading.py to liborgmode.py + + -- Jan Christoph Ebersbach Tue, 26 Apr 2011 21:55:17 +0900 + +vim-orgmode (0.1.0-15) unstable; urgency=low + + * add variable g:org_syntax_highlight_leading_stars to customize + highlighting of leading stars + + -- Jan Christoph Ebersbach Tue, 26 Apr 2011 21:30:31 +0900 + +vim-orgmode (0.1.0-14) unstable; urgency=low + + * merge todo_refactoring from sotte + + -- Jan Christoph Ebersbach Tue, 26 Apr 2011 21:16:19 +0900 + +vim-orgmode (0.1.0-13) unstable; urgency=low + + * add documentation in form of a diagram describing the functionality of + vim-orgmode, liborgmode and orgcmd + + -- Jan Christoph Ebersbach Tue, 26 Apr 2011 20:41:20 +0900 + +vim-orgmode (0.1.0-12) unstable; urgency=low + + * update todos + + -- Jan Christoph Ebersbach Mon, 28 Mar 2011 00:07:37 +0200 + +vim-orgmode (0.1.0-11) unstable; urgency=low + + * move initialization to start method + + -- Jan Christoph Ebersbach Thu, 24 Mar 2011 20:39:49 +0100 + +vim-orgmode (0.1.0-10) unstable; urgency=low + + * fix problem with the order of decorators (@classmethod must be first) + * remove silence from + + -- Jan Christoph Ebersbach Thu, 24 Mar 2011 17:36:55 +0100 + +vim-orgmode (0.1.0-9) unstable; urgency=low + + * rename update_tag_alignment + + -- Jan Christoph Ebersbach Wed, 23 Mar 2011 19:40:44 +0100 + +vim-orgmode (0.1.0-8) unstable; urgency=low + + * make edit tasks update tags alignment + + -- Jan Christoph Ebersbach Tue, 22 Mar 2011 22:28:59 +0100 + +vim-orgmode (0.1.0-7) unstable; urgency=low + + * clean up of unused imports + * change a bunch of methods into classmethods + + -- Jan Christoph Ebersbach Tue, 22 Mar 2011 22:17:44 +0100 + +vim-orgmode (0.1.0-6) unstable; urgency=low + + * add vim as build dependency + * update Installation and Building information in README + * update todos + + -- Jan Christoph Ebersbach Tue, 22 Mar 2011 21:35:02 +0100 + +vim-orgmode (0.1.0-5) unstable; urgency=low + + * fix passing of function arguments + * fix empty links creation + * update todos for Hyperlinks plugin + + -- Jan Christoph Ebersbach Mon, 21 Mar 2011 12:35:14 +0100 + +vim-orgmode (0.1.0-4) unstable; urgency=low + + * implement Hyperlinks plugin (closes: #11) + + -- Jan Christoph Ebersbach Sun, 20 Mar 2011 00:07:04 +0100 + +vim-orgmode (0.1.0-3) unstable; urgency=low + + * fix syntax issue in PluginExample.py + + -- Jan Christoph Ebersbach Fri, 11 Mar 2011 08:47:34 +0100 + +vim-orgmode (0.1.0-2) unstable; urgency=low + + * correct filename of PluginExample.py (closes: #3) + + -- Jan Christoph Ebersbach Wed, 09 Mar 2011 22:02:49 +0100 + +vim-orgmode (0.1.0-1) unstable; urgency=low + + * Initial release. + + -- Jan Christoph Ebersbach Tue, 22 Feb 2011 22:14:01 +0100 diff --git a/pack/acp/start/vim-orgmode/debian/compat b/pack/acp/start/vim-orgmode/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/pack/acp/start/vim-orgmode/debian/compat @@ -0,0 +1 @@ +5 diff --git a/pack/acp/start/vim-orgmode/debian/control b/pack/acp/start/vim-orgmode/debian/control new file mode 100644 index 0000000..c490273 --- /dev/null +++ b/pack/acp/start/vim-orgmode/debian/control @@ -0,0 +1,14 @@ +Source: vim-orgmode +Priority: extra +Section: editors +Maintainer: Jan Christoph Ebersbach +Build-Depends: debhelper (>= 7), vim +Standards-Version: 3.7.3 +Homepage: http://www.e-jc.de/ + +Package: vim-orgmode +Architecture: all +Depends: python, vim-common, vim +Suggests: emacs +Description: Clone of Org-mode for Vim + Clone of Org-mode for Vim diff --git a/pack/acp/start/vim-orgmode/debian/copyright b/pack/acp/start/vim-orgmode/debian/copyright new file mode 100644 index 0000000..7825a05 --- /dev/null +++ b/pack/acp/start/vim-orgmode/debian/copyright @@ -0,0 +1,28 @@ +Copyright (C) 2010,2011 Jan Christoph Ebersbach + +http://www.e-jc.de/ + +All rights reserved. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 3 as +published by the Free Software Foundation. + +Binary versions of this file provided by Univention to you as +well as other copyrighted, protected or trademarked materials like +Logos, graphics, fonts, specific documentations and configurations, +cryptographic keys etc. are subject to a license agreement between +you and Univention. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Except file syntax/org.vim which was copied from Herbert Sitz and +changed later on by Jan Christoph Ebersbach. diff --git a/pack/acp/start/vim-orgmode/debian/dirs b/pack/acp/start/vim-orgmode/debian/dirs new file mode 100644 index 0000000..68f4190 --- /dev/null +++ b/pack/acp/start/vim-orgmode/debian/dirs @@ -0,0 +1 @@ +usr/share/vim/addons diff --git a/pack/acp/start/vim-orgmode/debian/docs b/pack/acp/start/vim-orgmode/debian/docs new file mode 100644 index 0000000..e9f1307 --- /dev/null +++ b/pack/acp/start/vim-orgmode/debian/docs @@ -0,0 +1,2 @@ +README.org +LICENSE diff --git a/pack/acp/start/vim-orgmode/debian/examples b/pack/acp/start/vim-orgmode/debian/examples new file mode 100644 index 0000000..401fb6c --- /dev/null +++ b/pack/acp/start/vim-orgmode/debian/examples @@ -0,0 +1 @@ +examples/plugins/PluginExample.py diff --git a/pack/acp/start/vim-orgmode/debian/rules b/pack/acp/start/vim-orgmode/debian/rules new file mode 100755 index 0000000..4142a9d --- /dev/null +++ b/pack/acp/start/vim-orgmode/debian/rules @@ -0,0 +1,11 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +DESTDIR=$(CURDIR)/debian/vim-orgmode +ADDONSDIR=/usr/share/vim/addons + +override_dh_auto_install: + dh_auto_install -- DESTDIR=$(DESTDIR) VIMDIR=$(ADDONSDIR) + +%: + dh $@ diff --git a/pack/acp/start/vim-orgmode/doc/orgguide.txt b/pack/acp/start/vim-orgmode/doc/orgguide.txt new file mode 100644 index 0000000..e8fe382 --- /dev/null +++ b/pack/acp/start/vim-orgmode/doc/orgguide.txt @@ -0,0 +1,1553 @@ +*orgguide.txt* For Vim version 7.3 Last change: 2014 December 26 + + _ _ ____ __ __ _____ ____ ___ __ __ _____ ____ ____ + ( \/ )(_ _)( \/ ) ( _ )( _ \ / __)( \/ )( _ )( _ \( ___) + \ / _)(_ ) ( )(_)( ) /( (_-. ) ( )(_)( )(_) ))__) + \/ (____)(_/\/\_) (_____)(_)\_) \___/(_/\/\_)(_____)(____/(____) + + +============================================================================== +TABLE OF CONTENTS *org* *org-toc* *orgguide* *orgguide-toc* + + 1. About vim-orgmode guide |orgguide-about| + 2. Introduction |orgguide-introduction| + 3. Installation |orgguide-installation| + 4. Document structure |orgguide-docstructure| + 5. Tables |orgguide-tables| + 6. Hyperlinks |orgguide-hyperlinks| + 7. Todo items |orgguide-todo| + 8. Tags |orgguide-tags| + 9. Properties |orgguide-properties| + 10. Dates and Times |orgguide-dates| + 11. Capture - Refile - Archive |orgguide-capture| + 12. Agenda views |orgguide-agenda| + 13. Export/Markup for rich export |orgguide-export| + 14. Publishing |orgguide-publishing| + 15. Working with source code |orgguide-source| + 16. Miscellaneous |orgguide-misc| + 17. MobileOrg |orgguide-mobileorg| + 18. Customization |orgguide-customization| + 19. Development |orgguide-development| + 20. License vim-orgmode |orgguide-license| + 21. Contributors |orgguide-contributors| + 22. Changelog |orgguide-changelog| + 23. Links |orgguide-links| + 24. Development |orgguide-development| + +============================================================================== +ORG MODE GUIDE *orgguide-about* + +Copyright © 2010 Free Software Foundation + + Permission is granted to copy, distribute and/or modify this document under + the terms of the GNU Free Documentation License, Version 1.3 or any later + version published by the Free Software Foundation; with no Invariant + Sections, with the Front-Cover texts being “A GNU Manual,” and with the + Back-Cover Texts as in (a) below. A copy of the license is included in the + section entitled “GNU Free Documentation License.” + + (a) The FSF’s Back-Cover Text is: “You have the freedom to copy and modify + this GNU manual. Buying copies from the FSF supports it in developing GNU + and promoting software freedom.” + + This document is part of a collection distributed under the GNU Free + Documentation License. If you want to distribute this document separately + from the collection, you can do so by adding a copy of the license to the + document, as described in section 6 of the license. + +============================================================================== +INTRODUCTION *vim-orgmode* *orgguide-introduction* + +Vim-orgmode: Text outlining and task management for Vim based on Emacs' +Org-Mode. + +The idea for this plugin was born by listening to the Floss Weekly podcast +introducing Emacs' Org-Mode (http://twit.tv/floss136). Org-Mode has a lot of +strong features like folding, views (sparse tree) and scheduling of tasks. +These are completed by hyperlinks, tags, todo states, priorities aso. + +Vim-orgmode aims at providing the same functionality for Vim and for command +line tools*. + +* WAITING for command line tools and other programs a library liborgmode is + provided. It encapsulates all functionality for parsing and modifying org + files. + +------------------------------------------------------------------------------ +Preface~ + vim-orgmode is a file type plugin for keeping notes, maintaining TODO + lists, and doing project planning with a fast and effective plain-text + system. It is also an authoring and publishing system. + + This document is a copy of the orgmode-guide for emacs + (http://orgmode.org/) with modifications for vim. It contains all basic + features and commands, along with important hints for customization. + + To start create a new file with the extension ".org". + +------------------------------------------------------------------------------ +Features~ + Currently vim-orgmode does not support all orgmode features but is quite + usable. Short list of the already supported features: + + - Syntax highlighting + - Cycle visibility of headings (folding) + - Navigate between headings + - Edit the structure of the document: add, move, promote, denote headings + and more + - Hyperlinks within vim-orgmode and outside (files, webpages, etc.) + - TODO list management + - Tags for headings + - Lists in alphanumeric and bullet item notation and checkbox support + - Basic date handling + - Export to other formats (via emacs) + +------------------------------------------------------------------------------ +Default mappings~ + *org-mappings* +Here is a short overview of the default mappings. They also can be invoked +via the 'Org' menu. Most are only usable in command mode. + + Show/Hide:~ + - Cycle Visibility + + Editing Structure:~ + In GVIM:~ + - insert heading above + - insert heading below, taking over children + - insert heading below, taking over children + - insert heading above, after children + In general Vim Versions:~ + hN - insert heading above + hh - insert heading below, taking over children + hn - insert heading above, after children + + m} - move heading down + m{ - move heading up + m]] - move subtree down + m[[ - move subtree up + + yah - yank heading + dah - delete heading + yar - yank subtree + dar - delete subtree + p - paste subtree + + >> or >ah - demote heading + << or ar - demote subtree + d - select keyword + - previous keyword + - next keyword + - previous keyword set + - next keyword set + + Plain List:~ + cl or - insert plainlist item below + cL or - insert plainlist item above + + Checkboxes:~ + cc - toggle status + cn or - insert checkbox below + cN or - insert checkbox above + + TAGS and properties:~ + st - set tags + + Dates:~ + sa - insert date + si - insert inactive date + pa - insert date by using calendar selection + pi - insert inactive date by using calendar selection + + Agenda:~ + caa - agenda for the week + cat - agenda of all TODOs + caA - agenda for the week for current buffer + caT - agenda of all TODOs for current buffer + + Not yet implemented in vim-orgmode~ + caL - timeline of current buffer + + Export:~ + ep - export as PDF + eb - export as Beamer PDF + eh - export as HTML + el - export as LaTeX + +------------------------------------------------------------------------------ +Inline markup~ + + We support org authoring markup as closely as possible + (we're adding two markdown-like variants for =code= and blockquotes). + + Inline markup: +> + *bold* + /italic/ + _underline_ + +strike-through+ + =code= + ~verbatim~ +< + + Note: + - /italic/ is rendered as reverse in most terms (works fine in gVim, though) + - +strike-through+ doesn't work on Vim / GVim + - the non-standard `code' markup is also supported + - =code= and ~verbatim~ are also supported as block-level markup, see below. + + Ref: http://orgmode.org/manual/Emphasis-and-monospace.html + +------------------------------------------------------------------------------ +INSTALLATION AND UPGRADE *orgguide-installation* + + Download the latest stable release at + http://www.vim.org/scripts/script.php?script_id=3642 + + Open the vimball archive in vim and source it. + + $ vim orgmode.vba +> + :so % +< + ATTENTION: All .pyc files of former versions of vim-orgmode need to be + deleted beforehand! + + Add the following line to your .vimrc file to ensure that filetype plugins + are loaded properly: +> + filetype plugin indent on +< + Installation can also be done with plugin managers that automatically pull + dependencies i.e. vim-plug (https://github.com/junegunn/vim-plug), dein.vim + (https://github.com/Shougo/dein.vim). + + NOTE: For some functionality vim-orgmode relies on external plugins which + are mentioned in suggested plugins. + +------------------------------------------------------------------------------ +Suggested plugins~ + + Universal Text Linking~ + (http://www.vim.org/scripts/script.php?script_id=293) general support for + text linking. The hyperlinks feature of vim-orgmode depends on this + plugin. + + repeat~ + (http://www.vim.org/scripts/script.php?script_id=2136) + Repeat actions that would not be repeatable otherwise. This plugin is + needed when you want to repeat the previous orgmode action. + + taglist~ + ([http://www.vim.org/scripts/script.php?script_id=273) + Display tags for the currently edited file. Vim-orgmode ships with support + for displaying the heading structure and hyperlinks in the taglist plugin. + + tagbar~ + (http://www.vim.org/scripts/script.php?script_id=3465) + A new approach to displaying tags for the currently edited file. + Vim-orgmode ships with support for displaying the heading structure and + hyperlinks in the tagbar plugin. + + speeddating~ + (http://www.vim.org/scripts/script.php?script_id=2120) + In-/decrease dates the vim way: C-a and C-x. Dates and times in the + orgmode format can be in-/decreased if this plugins is installed. + + Narrow Region~ + (http://www.vim.org/scripts/script.php?script_id=3075) + Emulation of Emacs' Narrow Region feature. It might be useful when dealing + with large orgmode files. + + pathogen~ + (http://www.vim.org/scripts/script.php?script_id=2332) + Easy management of multiple vim plugins. + + calendar~ + (https://github.com/mattn/calendar-vim) + This plugin will create a calendar window for timestamp insertion. + + SyntaxRange~ + (http://www.vim.org/scripts/script.php?script_id=4168) + Use proper syntax highlighting for code blocks such as: +> + #+BEGIN_SRC cpp + int i = 1; + #+END_SRC +< + +------------------------------------------------------------------------------ +Feedback~ + If you find problems with vim-orgmode, or if you have questions, remarks, + or ideas about it, please create a ticket on + https://github.com/jceb/vim-orgmode + +============================================================================== +DOCUMENT STRUCTURE *orgguide-docstructure* + +------------------------------------------------------------------------------ +Outlines~ + Outlines allow a document to be organized in a hierarchical structure, which + (at least for me) is the best representation of notes and thoughts. An + overview of this structure is achieved by folding (hiding) large parts of + the document to show only the general document structure and the parts + currently being worked on. vim-orgmode greatly simplifies the use of + outlines by compressing the entire show/hide functionality into a single + command, OrgToggleFolding, which is bound to the key. + +------------------------------------------------------------------------------ +Headlines~ + + Headlines define the structure of an outline tree. The headlines in + vim-orgmode start with one or more stars, on the left margin. For example: +> + * Top level headline + ** Second level + *** 3rd level + some text + *** 3rd level + more text + + * Another top level headline +< + + Some people find the many stars too noisy and would prefer an outline + that has whitespace followed by a single star as headline starters. + |g:org_heading_shade_leading_stars| describes a setup to realize this. + + Body text under headings is not indented by default, but you can control + this with the |g:org_indent| variable. + +------------------------------------------------------------------------------ +Text objects~ + + Vim offers a mighty feature called |text-objects|. A text object is bound to + a certain character sequence that can be used in combination with all kinds + of editing and selection tasks. + + vim-orgmode implements a number of text objects to make editing org files + easier: + + ih inner heading, referring to the current heading + excluding the heading level characters (*) + ah a heading, referring to the current heading including + everything + ir inner subtree, starting with the current heading + ar a subtree, starting with the current heading + Oh inner outer heading, referring to the parent + Or inner outer heading, including subtree, referring to + the parent + OH an outer heading + OT an outer subtree + + Motions can be used like text objects as well. See |orgguide-motion|. + +------------------------------------------------------------------------------ +Visibility cycling~ + Outlines make it possible to hide parts of the text in the buffer. + vim-orgmode uses just two commands, bound to and to change the + visibility in the buffer. + + or *orgguide-Tab* or *orgguide-S-Tab* + Subtree cycling: Rotate current subtree among the + states +> + ,-> FOLDED -> CHILDREN -> SUBTREE --. + '-----------------------------------' +< + + When called with the shift key, global cycling is invoked. + + , or *orgguide-,* or *orgguide-.* + . Global cycling: Rotate the entire buffer among the + states. The same can be achieved by using the + keybindings zm and zr. +> + ,-> OVERVIEW -> CONTENTS -> SHOW ALL --. + '--------------------------------------' +< + + Vim-orgmode doesn't implement the following functionality, yet.~ + When Emacs first visits an org file, the global state is set to + OVERVIEW, i.e. only the top level headlines are visible. This can be + configured through the variable =org-startup-folded=, or on a per-file + basis by adding a startup keyword =overview=, =content=, =showall=, like + this: +> + #+STARTUP: content +< +------------------------------------------------------------------------------ +Motion~ + *orgguide-motion* + The following commands jump to other headlines in the buffer. + + } Next heading. + + { Previous heading. + + ]] Next heading same level. + + [[ Previous heading same level. + + g{ Backward to higher level heading. + + g} Forward to higher level heading. + +------------------------------------------------------------------------------ +Structure editing~ + + *orgguide-S-CR* + Insert new heading with same level as current. If the + cursor is in a plain list item, a new item is created + (see section [[#Plain-lists][Plain lists]]). When this + command is used in the middle of a line, the line is + split and the rest of the line becomes the new + headline. + + Not yet implemented in vim-orgmode~ + M-S- Insert new TODO entry with same level as current + heading. + + or + In a new entry with no text yet, and + will cycle through reasonable levels. + + << or *orgguide-<<* or *orgguide-CTRL-d* + (insert mode) Promote current heading by one level. + + >> or *orgguide->>* or *orgguide-CTRL-t* + (insert mode) Demote current heading by one level. + + *orgguide-<[[* + <[[ Promote the current subtree by one level. + + *orgguide->]]* + >]] Demote the current subtree by one level. + + *orgguide-m{* + m{ Move heading up (swap with previous/next subtree of + same level). + + *orgguide-m}* + m} Move heading down (swap with previous/next subtree of + same level). + + *orgguide-m[[* + m[[ Move subtree up (swap with previous/next subtree of + same level). + + *orgguide-m]]* + m]] Move subtree down (swap with previous/next subtree of + same level). + + Not yet implemented in vim-orgmode~ + C-c C-w Refile entry or region to a different location. See + section [[#Refiling-notes][Refiling notes]]. + + *orgguide-nr* + nr Narrow buffer to current subtree / widen it again + (only if NarrowRegion plugin is installed) + + When there is an active region (Transient Mark mode), promotion and demotion + work on all headlines in the region. + +------------------------------------------------------------------------------ +Sparse trees~ + Not yet implemented in vim-orgmode~ + +------------------------------------------------------------------------------ +Plain lists~ + *orgguide-plain-list* + Within an entry of the outline tree, hand-formatted lists can provide + additional structure. + + They also provide a way to create lists of checkboxes (see section + |orgguide-checkboxes|). + + vim-orgmode supports editing such lists, and the exporter (see section + |orgguide-export|) parses and formats them. + + vim-orgmode knows ordered lists, unordered lists, and description lists: + - 'Unordered' list items start with ‘-’, ‘+’, or ‘*’ as bullets. + - 'Ordered' list items start with ‘1.’ or ‘1)’. + - 'Description' list use ‘ :: ’ to separate the 'term' from the + description. + + Items belonging to the same list must have the same indentation on the + first line. An item ends before the next line that is indented like its + bullet/number, or less. A list ends when all items are closed, or before + two blank lines. An example: +> + ** Lord of the Rings + My favorite scenes are (in this order) + 1. The attack of the Rohirrim + 2. Eowyn's fight with the witch king + + this was already my favorite scene in the book + + I really like Miranda Otto. + Important actors in this film are: + - Elijah Wood :: He plays Frodo + - Sean Austin :: He plays Sam, Frodo's friend. +< + + The following commands act on items when the cursor is in the first line + of an item (the line with the bullet or number). + + Not yet implemented in vim-orgmode~ + The following commands act on items when the cursor is in the first line of + an item (the line with the bullet or number). + +------------------------------------------------------------------------------ +Footnotes~ + Not yet implemented in vim-orgmode~ + +============================================================================== +TABLES *orgguide-tables* + Not yet implemented in vim-orgmode~ + +============================================================================== +HYPERLINKS *orgguide-hyperlinks* + +NOTE: The |utl| plugin is used for this feature and needs to be installed. + http://www.vim.org/scripts/script.php?script_id=293 + +Like HTML, vim-orgmode provides links inside a file, external links to other +files, Usenet articles, emails, and much more. + +------------------------------------------------------------------------------ +Link format~ + *orgguide-linkformat* + vim-orgmode will recognize plain URL-like links and activate them as links. + The general link format, however, looks like this: +> + [[link][description]] or alternatively [[link]] +< + + If vim was compiled with |+conceal|, vim-orgmode will shorten this format to + just display 'description' or 'link' once the link was completely entered + (that is, if all brackets are present) and you've left insert mode or + you're editing another line. + To edit the invisible ‘link’ part, go into insert mode, or call the + 'Insert/edit Link' command by pressing 'gil'. + +------------------------------------------------------------------------------ +Internal links~ + Not yet implemented in vim-orgmode~ + +------------------------------------------------------------------------------ +External links~ + + |utl| supports links to files and websites. Others can be added by extending + utl (see |utl-smartSamples|). External links are URL-like locators. They + start with a short identifying string followed by a colon. There can be no + space after the colon. Here are some examples: +> + http://www.astro.uva.nl/~dominik on the web + file:/home/dominik/images/jupiter.jpg file, absolute path + /home/dominik/images/jupiter.jpg same as above +< + + A link should be enclosed in double brackets and may contain a descriptive + text to be displayed instead of the URL (see section |orgguide-linkformat|), + for example: +> + [[http://www.vim.org/][VIM]] +< + +------------------------------------------------------------------------------ +Handling links~ + + vim-orgmode provides methods to create a link in the correct syntax, to + insert it into an org file, and to follow the link. + + Not yet implemented in vim-orgmode~ + C-c l Store a link to the current location. This is a + /global/ command (you must create the key binding + yourself) which can be used in any buffer to create a + link. The link will be stored for later insertion into + an org buffer (see below). + + *orgguide-gil* + gil Insert a link. This prompts for a link to be inserted + into the buffer. You can just type a link, or use + history keys and to access stored links. + You will be prompted for the description part of the + link. File name completion is enabled to link to a + local file. In addition vim-orgmode provides the + command :OrgHyperlinkInsert to insert a link from + command line. + + gil When the cursor is on an existing link, gil allows you + to edit the link and description parts of the link. + + Not yet implemented in vim-orgmode~ + C-c C-o or mouse-1 or mouse-2 Open link at point. + + Not yet implemented in vim-orgmode~ + C-c & Jump back to a recorded position. A position is + recorded by the commands following internal links, and + by C-c %. Using this command several times in direct + succession moves through a ring of previously recorded + positions. + +------------------------------------------------------------------------------ +Targeted links~ + Not yet implemented in vim-orgmode~ + +============================================================================== +TODO ITEMS *orgguide-todo* + +vim-orgmode does not maintain TODO lists as separate documents. Instead, TODO +items are an integral part of the notes file, because TODO items usually come +up while taking notes! With vim-orgmode, simply mark any entry in a tree as +being a TODO item. In this way, information is not duplicated, and the entire +context from which the TODO item emerged is always present. + +Of course, this technique for managing TODO items scatters them throughout +your notes file. vim-orgmode compensates for this by providing methods to give +you an overview of all the things that you have to do. + +------------------------------------------------------------------------------ +Using TODO states~ + + Any headline becomes a TODO item when it starts with the word ‘TODO’, + for example: +> + *** TODO Write letter to Sam Fortune +< + + The most important commands to work with TODO entries are: + + ct Rotate the TODO state of the current item among. See + |orgguide-tags-settings|for more information. +> + ,-> (unmarked) -> TODO -> DONE --. + '--------------------------------' +< + + Not yet implemented in vim-orgmode~ + The same rotation can also be done “remotely” from the timeline and + agenda buffers with the t command key (see section + |orgguide-agenda-commands|). + + or Select the following/preceding TODO state, similar to + cycling. + + Not yet implemented in vim-orgmode~ + C-c / t View TODO items in a /sparse tree/ (see section + [[#Sparse-trees][Sparse trees]]). Folds the buffer, + but shows all TODO items and the headings hierarchy + above them. + + cat Show the global TODO list. This collects the TODO + items from all agenda files (see section + |orgguide-agenda-views|) into a single buffer. + + Not yet implemented in vim-orgmode~ + S-M- Insert a new TODO entry below the current one. + +------------------------------------------------------------------------------ +Multi-state workflows~ + + You can use TODO keywords to indicate different 'sequential' states in + the process of working on an item, for example: +> + :let g:org_todo_keywords=['TODO', 'FEEDBACK', 'VERIFY', '|', 'DONE', 'DELEGATED'] +< + + The vertical bar separates the TODO keywords (states that 'need action') + from the DONE states (which need 'no further action'). If you don’t + provide the separator bar, the last state is used as the DONE state. + With this setup, the command will cycle an entry from TODO to + FEEDBACK, then to VERIFY, and finally to DONE and DELEGATED. + + Sometimes you may want to use different sets of TODO keywords in + parallel. For example, you may want to have the basic TODO/DONE, but + also a workflow for bug fixing, and a separate state indicating that an + item has been canceled (so it is not DONE, but also does not require + action). Your setup would then look like this: +> + :let g:org_todo_keywords = [['TODO(t)', '|', 'DONE(d)'], + \ ['REPORT(r)', 'BUG(b)', 'KNOWNCAUSE(k)', '|', 'FIXED(f)'], + \ ['CANCELED(c)']] +< + The keywords should all be different, this helps vim-orgmode to keep track + of which subsequence should be used for a given entry. The example also + shows how to define keys for fast access of a particular state, by + adding a letter in parenthesis after each keyword - you will be prompted + for the key after pressing d. + + *orgguide-d* + d prompt for fast access of a todo state + + Not yet implemented in vim-orgmode~ + To define TODO keywords that are valid only in a single file, use the + following text anywhere in the file. + +> + #+BEGIN_EXAMPLE + #+TODO: TODO(t) | DONE(d) + #+TODO: REPORT(r) BUG(b) KNOWNCAUSE(k) | FIXED(f) + #+TODO: | CANCELED(c) + #+END_EXAMPLE +< + + After changing one of these lines, use C-c C-c with the cursor still in + the line to make the changes known to vim-orgmode. + +------------------------------------------------------------------------------ +Progress logging~ + Not yet implemented in vim-orgmode~ + +------------------------------------------------------------------------------ +Priorities~ + Not yet implemented in vim-orgmode~ + +------------------------------------------------------------------------------ +Breaking tasks down into subtasks~ + Not fully implemented in vim-orgmode~ + +It is often advisable to break down large tasks into smaller, manageable +subtasks. You can do this by creating an outline tree below a TODO item, +with detailed subtasks on the tree. To keep the overview over the +fraction of subtasks that are already completed, insert either ‘[/]’ or +‘[%]’ anywhere in the headline. These cookies will be updated each time +the TODO status of a child changes, or when pressing C-c C-c on the +cookie. For example: + +> + * Organize Party [33%] + ** TODO Call people [1/2] + *** TODO Peter + *** DONE Sarah + ** TODO Buy food + ** DONE Talk to neighbor +< + +c# Update the checkboxes status of current heading. It + also update the heading status too. + +------------------------------------------------------------------------------ +Checkboxes~ + *orgguide-checkboxes* + +Every item in a plain list (see section |orgguide-plain-list|) +can be made into a checkbox by starting it with the string ‘[ ]’. +Checkboxes are not included into the global TODO list, so they are often +great to split a task into a number of simple steps. Here is an example +of a checkbox list. + +> + * TODO Organize party [1/3] + - [-] call people [1/2] + - [ ] Peter + - [X] Sarah + - [X] order food + - [ ] think about what music to play +< + +Checkboxes work hierarchically, so if a checkbox item has children that +are checkboxes, toggling one of the children checkboxes will make the +parent checkbox reflect if none, some, or all of the children are +checked. + +The following commands work with checkboxes: + +cc Toggle checkbox status or (with prefix arg) checkbox + presence at point. + +cn or + or Insert a new checkbox below current line. + +cN or + Insert a new checkbox above current line. + +============================================================================== +TAGS *orgguide-tags* + +An excellent way to implement labels and contexts for cross-correlating +information is to assign 'tags' to headlines. vim-orgmode has extensive +support for tags. + +Every headline can contain a list of tags; they occur at the end of the +headline. Tags are normal words containing letters, numbers, ‘_’, and +‘@’. Tags must be preceded and followed by a single colon, e.g., +‘:work:’. Several tags can be specified, as in ‘:work:urgent:’. Tags +will by default be in bold face with the same color as the headline. + +------------------------------------------------------------------------------ +Tag inheritance~ + *orgguide-tags-inheritance* + Not yet implemented in vim-orgmode~ + +------------------------------------------------------------------------------ +Setting tags~ + *orgguide-tags-settings* + Tags can simply be typed into the buffer at the end of a headline. After + a colon, offers completion on tags. There is also a special + command for inserting tags: + + *orgguide-st* + st Enter new tags for the current headline. vim-orgmode + will either offer completion or a special single-key + interface for setting tags, see below. After pressing + , the tags will be inserted and aligned to + 'org-tags-column'. + + *orgguide-ft* + ft Find tags in the current file. + + vim-orgmode will support tag insertion based on a 'list of tags'. By default + this list is constructed dynamically, containing all tags currently used + in the buffer. + +------------------------------------------------------------------------------ +Tag searches~ + *orgguide-tags-search* + Not yet implemented in vim-orgmode~ + +============================================================================== +PROPERTIES *orgguide-properties* + + Not yet implemented in vim-orgmode~ + +============================================================================== +DATES AND TIMES *orgguide-dates* + +To assist project planning, TODO items can be labeled with a date and/or +a time. The specially formatted string carrying the date and time +information is called a 'timestamp' in vim-orgmode. + +------------------------------------------------------------------------------ +Timestamps~ + + A timestamp is a specification of a date (possibly with a time or a range of + times) in a special format, either <2003-09-16 Tue> or <2003-09-16 Tue + 09:39> or <2003-09-16 Tue 12:00-12:30>. A timestamp can appear anywhere in + the headline or body of an org tree entry. Its presence causes entries to + be shown on specific dates in the agenda (see section |orgguide-agenda|). We + distinguish: + + Plain timestamp; Event; Appointment ~ + A simple timestamp just assigns a date/time to an item. This is just like + writing down an appointment or event in a paper agenda. +> + * Meet Peter at the movies <2006-11-01 Wed 19:15> + * Discussion on climate change <2006-11-02 Thu 20:00-22:00> +< + Timestamp with repeater interval ~ + Not yet implemented in vim-orgmode~ + + Diary-style sexp entries ~ + Not yet implemented in vim-orgmode~ + + Time/Date range~ + Two timestamps connected by ‘--’ denote a range. +> + ** Meeting in Amsterdam + <2004-08-23 Mon>--<2004-08-26 Thu> +< + Inactive timestamp~ + Just like a plain timestamp, but with square brackets instead of angular + ones. These timestamps are inactive in the sense that they do 'not' + trigger an entry to show up in the agenda. +> + * Gillian comes late for the fifth time [2006-11-01 Wed] +< +------------------------------------------------------------------------------ +Creating timestamps~ + + For vim-orgmode to recognize timestamps, they need to be in the specific + format. All commands listed below produce timestamps in the correct format. + + *orgmode--sa* + sa Prompt for a date and insert a corresponding + timestamp. + + Not yet implemented in vim-orgmode~ + When the cursor is at an existing timestamp in the + buffer, the command is used to modify this timestamp + instead of inserting a new one. + + Not yet implemented in vim-orgmode~ + When this command is used twice in succession, a time + range is inserted. With a prefix, also add the current + time. + + *orgmode-si* + si Like |orgmode--sa|, but insert an inactive + timestamp that will not cause an agenda entry. + + *orgmode-ctrl-a* or *orgmode-ctrl-x* + CTRL-A or CTRL-X Change the item under the cursor in a timestamp. + The cursor can be on a year, month, day, hour or + minute. NOTE: The plugin 'speeddating' should be + installed for this feature. + + Not yet implemented in vim-orgmode~ + When the timestamp contains a time range like + ‘15:30-16:30’, modifying the first time will also + shift the second, shifting the time block with + constant length. To change the length, modify the + second time. + + When vim-orgmode prompts for a date/time, it will accept any string + containing some date and/or time information, and intelligently interpret + the string, deriving defaults for unspecified information from the current + date and time. + Example~ + If the current date is <2016-06-14 Tue>, entering +3 at the prompt will + insert the date <2016-06-17 Fri>, entering sat will insert date + <2016-06-18 Sat> + + You can also select a date in the pop-up calendar. + NOTE: The plugin 'calendar' should be installed for this feature. + + *orgmode-pa* + pa Open a calendar and prompt a user selected date, then + insert a corresponding timestamp. + + *orgmode-pi* + pi Like |orgmode--pa|, but insert an inactive + timestamp that will not cause an agenda entry. + +------------------------------------------------------------------------------ +Deadlines and scheduling~ + Not yet implemented in vim-orgmode~ + +------------------------------------------------------------------------------ +Clocking work time~ + Not yet implemented in vim-orgmode~ + +============================================================================== +CAPTURE - REFILE - ARCHIVE *orgguide-capture* + + Not yet implemented in vim-orgmode~ + +============================================================================== +AGENDA VIEWS *orgguide-agenda* + +Due to the way vim-orgmode works, TODO items, time-stamped items, and tagged +headlines can be scattered throughout a file or even a number of files. To get +an overview of open action items, or of events that are important for a +particular date, this information must be collected, sorted and displayed in +an organized way. There are several different views, see below. + +The extracted information is displayed in a special agenda buffer. This +buffer is read-only. + +Not yet implemented in vim-orgmode~ +... but provides commands to visit the corresponding locations in the original +org files, and even to edit these files remotely. Remote editing from the +agenda buffer means, for example, that you can change the dates of deadlines +and appointments from the agenda buffer. The commands available in the Agenda +buffer are listed in |orgguide-agenda-commands|. + +- |orgguide-agenda-files| Files being searched for agenda information +- |orgguide-agenda-dispatcher| Keyboard access to agenda views +- |orgguide-agenda-views| What is available out of the box? +- |orgguide-agenda-commands| Remote editing of org trees +- |orgguide-agenda-custom| Defining special searches and views + +------------------------------------------------------------------------------ +Agenda files~ + *g:org_agenda_files* *orgguide-agenda-files* + Default: [] + The information to be shown is normally collected from all 'agendafiles', + the files listed in the variable g:org_agenda_files. + + You can change the list of agenda files like this: +> + let g:org_agenda_files = ['~/org/index.org', ~/org/project.org'] +< + + Also globbing is allowed. This makes it easy to use ALL *.org files in a + folder. Using all *.org files in ~/org/ is done like this: +> + let g:org_agenda_files = ['~/org/*.org'] +< + + WARNING: This might be slow if you have a lot of org files. + +------------------------------------------------------------------------------ +The agenda dispatcher ~ + *orgguide-agenda-dispatcher* + Not yet implemented in vim-orgmode~ + +------------------------------------------------------------------------------ +The built-in agenda views ~ + *orgguide-agenda-views* + + The weekly/daily agenda~ + The purpose of the weekly/daily 'agenda' is to act like a page of a + paper agenda, showing all the tasks for the current week or day. + + *orgguide-caa* + caa Compile an agenda for the current week from a list of + org files. The agenda shows the entries for each day. + + The global TODO list~ + The global TODO list contains all unfinished TODO items formatted and + collected into a single place. + + Not yet implemented in vim-orgmode~ + Remote editing of TODO items lets you change the state of a TODO entry + with a single key press. The commands available in the TODO list are + described in |agenda-commands| + + *orgguide-cat* + cat Show the global TODO list. This collects the TODO + items from all agenda files into a single buffer. + + Not yet implemented in vim-orgmode~ + *orgguide-caT* + caT Like the above, but allows selection of a specific + TODO keyword. + + Matching tags and properties~ + Not yet implemented in vim-orgmode~ + + Timeline for a single file~ + The timeline summarizes all time-stamped items from a single vim-orgmode + file in a /time-sorted view/. The main purpose of this command is to + give an overview over events in a project. + + *orgguide-caL* + caL Show a time-sorted view of the vim-orgmode, with all + time-stamped items. + + Search view~ + Not yet implemented in vim-orgmode~ + +------------------------------------------------------------------------------ +Commands in the agenda buffer~ + *orgguide-agenda-commands* + Entries in the agenda buffer are linked back to the org file where they + originate. Commands are provided to show and jump to the + original entry location, and to edit the org files “remotely” from the + agenda buffer. + + Not yet implemented in vim-orgmode~ + only partly implemented + + Motion~ + Not yet implemented in vim-orgmode~ + + View/Go to org file~ + *orgguide-agenda-Tab* + Go to the original location of the item in an + alternative window. + + *orgguide-agenda-CR* + Go to the original location of the item and stay in + the same/the agenda window. + + *orgguide-agenda-S-CR* + Go to the original location of the item in a new split + window. + + Not yet implemented in vim-orgmode~ + + Change display~ + Not yet implemented in vim-orgmode~ + +------------------------------------------------------------------------------ +Custom agenda views~ + *orgguide-agenda-custom* + Not yet implemented in vim-orgmode~ + +============================================================================== +EXPORTING *orgguide-export* + +NOTE: vim-orgmode relies on Emacs for this feature. Emacs _and_ Emacs' + org-mode need to be installed! For PDF export a Latex environment + is needed as well! + +vim-orgmode documents can be exported into a variety of other formats: +ASCII export for inclusion into emails, HTML to publish on the web, +LaTeX/PDF for beautiful printed documents and DocBook to enter the world +of many other formats using DocBook tools. There is also export to +iCalendar format so that planning information can be incorporated into +desktop calendars. + +Currently, the export to pdf, html, latex and markdown is supported via the +following commands and the 'export' menu: +> + :OrgExportToPDF + :OrgExportToBeamerPDF + :OrgExportToHTML + :OrgExportToLaTeX + :OrgExportToMarkdown +< + +Make sure that you have configured your emacs accordingly, as for instance +the markdown exporter is not loaded by default. To load it, add + +> + (eval-after-load "org" + '(require 'ox-md nil t)) +< + +to your init.el. Make also sure to specify your path by using the +|g:org_export_init_script| option. + + *g:org_export_emacs* +Default: "/usr/bin/emacs" +Path to Emacs executable. Example: +> + :let g:org_export_emacs="~/bin/emcas" +< + + *g:org_export_verbose* +Default: 0 +If set, Emacs' export output is displayed. +> + :let g:org_export_verbose=1 +< + + *g:org_export_init_script* +Default: "" +For the export via Emacs a separate configuration file can be sourced to +determine Emacs' export behavior. Examples: + +Source the ~/.emacs configuration file: +> + :let g:org_export_init_script="~/.emacs" +< + +Or source a different file: +> + :let g:org_export_init_script="~/.emacs_org_init" +< + +============================================================================== +PUBLISHING *orgguide-publishing* + + Not yet implemented in vim-orgmode~ + +============================================================================== +WORKING WITH SOURCE CODE *orgguide-source* + + Not yet implemented in vim-orgmode~ + +============================================================================== +MISCELLANEOUS *orgguide-misc* + + Not yet implemented in vim-orgmode~ + +============================================================================== +MOBILEORG *orgguide-mobileorg* + + Not yet implemented in vim-orgmode~ + +============================================================================== +CUSTOMIZATION *orgguide-customization* + +------------------------------------------------------------------------------ +Remapping shortcuts~ + vim-orgmode provides an easy way for remapping the default keyboard + shortcuts. For this task it relies on vim's mappings. All shortcuts + of vim-orgmode are accessible by s. + + To change a keyboard shortcut the name of the related is needed. + First we need to look up the current mapping in the Org menu. The following + command reveals the 's name: +> + :map +< + + The result should look something like this: +> + :map ,t + n ,t @OrgSetTags +< + + Now we can create an alternate mapping: +> + nmap +< + + To change the mapping for editing tags to t the vimrc entry would + look like this: +> + nmap t @OrgSetTags +< + +------------------------------------------------------------------------------ +Alternate behavior~ + vim-orgmode provides some variables for users to customize certain behaviors + of their orgmode if so desired. + + *g:org_prefer_insert_mode* + Default: 1 + Defines if vim-orgmode will automatically jump into Insert Mode after a new + heading/checkbox/plainlist instance is created through keyboard bindings. If + value is set to 0, orgmode will retain it's original mode. + Example: +> + let org_prefer_insert_mode = 1 +< + +------------------------------------------------------------------------------ +syntax highlighting and indentation~ + Syntax highlighting is customizable to fit nicely with the user's + colorscheme. + + *g:org_aggressive_conceal* + Default: 0 + Defines if format indicating characters for inline markups(bold, italic, + inline code, verbatims, in-file hyper-link, etc.) are displayed. Format + indicating characters will be concealed if value is `1`, rendering a much + cleaner view. However, since this feature is newly introduced(<2016-04-08>) + and still need further testing. It is inactive by default. Example: +> + let g:org_aggressive_conceal = 0 +< + + *g:org_heading_highlight_colors* + Default: ['Title', 'Constant', 'Identifier', 'Statement', 'PreProc', 'Type', + \ 'Special'] + Define the highlighting colors/group names for headings. Example: +> + let g:org_heading_highlight_colors = ['Title', 'Constant', 'Identifier', + \ 'Statement', 'PreProc', 'Type', 'Special'] +< + + *g:org_heading_highlight_levels* + Default: len(g:org_heading_highlight_colors) + Define the number of levels of highlighting. If this number is bigger than + the list of colors defined in of g:org_heading_highlight_colors the colors + of g:org_heading_highlight_colors get repeated. Example: +> + let g:org_heading_highlight_levels = len(g:org_heading_highlight_colors) +< + + *g:org_heading_shade_leading_stars* + Default: 1 + Defines if leading stars are displayed in the color of the heading or if a + special NonText highlighting is used that hides them from user. Example: +> + let g:org_heading_shade_leading_stars = 1 +< + + *g:org_todo_keywords* + Default: ['TODO', '|', 'DONE'] + Defines the keywords that are highlighted in headings. For more information + about this variable, please consult the org-mode documentation + (http://orgmode.org/org.html#index-org_002dtodo_002dkeywords-511). Example: +> + let g:org_todo_keywords = ['TODO', '|', 'DONE'] +< + + *g:org_todo_keyword_faces* + Default: [] + Defines special faces (styles) for displaying g:org_todo_keywords. Please + refer to vim documentation (topic |attr-list|) for allowed values for + :weight, :slant, :decoration. Muliple colors can be separated by comma for + :foreground and :background faces to provide different colors for GUI and + terminal mode. Example: +> + let g:org_todo_keyword_faces = [] +< + + *g:org_indent* + Default: 0 + Defines if body text is indented. By default, text is not indented according + to heading level (heading.level + 1). You can enable it by setting: +> + let g:org_indent = 1 +< + + Syntax Highlighting Examples~ + Define an additionaly keyword 'WAITING' and set the foreground color to + 'cyan'. Define another keyword 'CANCELED' and set the foreground color to + red, background to black and the weight to normal, slant to italc and + decoration to underline: + +> + let g:org_todo_keywords = [['TODO', 'WAITING', '|', 'DONE'], + \ ['|', 'CANCELED']] + let g:org_todo_keyword_faces = [['WAITING', 'cyan'], ['CANCELED', + \ [':foreground red', ':background black', ':weight bold', + \ ':slant italic', ':decoration underline']]] +< + +============================================================================== +DEVELOPMENT *orgguide-development* + +The development of vim-orgmode is coordinated via github: + https://github.com/jceb/vim-orgmode + +If you like this project, have questions, suggestions or problems, simply drop +us a line and open an issue. Patches are very welcome! + +Here is a quick start about the vim-orgmode development. + +------------------------------------------------------------------------------ +Structure and Source Code~ + The majority of the source code is stored in folder ftplugin/orgmode. This + is where the actual functionality of the plugin is located. + + I choose to implement vim-orgmode mainly in Python. I hope this will ease + the implementation especially with the functionality of the Python standard + library at hand. + + Right below the directory ftplugin/orgmode the basic implementation of + vim-orgmode is found. This basic functionality provides everything for + higher level implementations that modify the buffer, provide a menu and + keybindings to the user and everything else that is needed. + + Below the directory ftplugin/orgmode/plugins the plugins are located. Every + plugin must provide a class equal to its filename with the .py-extension. + An example for a plugin can be found in file + ftplugin/orgmode/plugins/Example.py. + + *g:org_plugins* + Default: ['ShowHide', '|', 'Navigator', 'EditStructure', '|', 'Hyperlinks', + \ '|', 'Todo', 'TagsProperties', 'Date', 'Agenda', 'Misc', '|', + \ 'Export'] + Every plugin must be enabled by the user by setting the g:org_plugins + variable. By default all shipped plugins are enabled. Example: +> + let g:org_plugins = ['ShowHide', '|', 'Navigator', 'EditStructure'] +< + + Files and folders~ + . + ├── debian - files needed for building a Debian package + ├── doc - vim documentation + ├── documentation - development documentation + ├── examples - example of aplugin + ├── ftdetect - Filetype detection for orgmode files + ├── ftplugin - Home of the main part of vim-orgmode + │ └── orgmode - Home for all Python code + │ ├── liborgmode - vim unrelated part of vim-orgmde. Contains + │ │ basic data structures and algorithms to + │ │ parse and edit orgfiles. + │ └── plugins - Home for all orgmode plugins + ├── indent - Indentation for orgmode files + ├── syntax - Syntax highlighting + ├── tests - Tests to verify the consistency and + │ correctness of orgmode and the plugins + ├── build_vmb.vim - Build file for creating a Vimball + ├── install-vmb.vim - Local installation of vmb via make target + ├── LICENSE - License Information + ├── README.org - README :) + └── Makefile - make commands + +------------------------------------------------------------------------------ +Writing a plugin~ + To write a plugin: + 1. copy file ftplugin/orgmode/plugins/Example.py to + ftplugin/orgmode/plugins/YourPlugin.py + 2. Change class name to "YourPlugin" + 3. Set the menu name, it doesn't need to match the filename anymore, e.g. + "Your Plugin" + 4. Prepare keybindings in function register by defining a proper action and + a key this action should be mapped to. For further information refer to + section Keybindings. + 5. Register your plugin: +> + let g:org_plugins = ['ShowHide', '|', 'Navigator', 'EditStructure', + \ 'YourPlugin'] +< + + 6. Write unittests and implement YourPlugin. + +------------------------------------------------------------------------------ +Keybindings~ + Keybindings alias mappings are described very well in the vim + documentation, see |map-modes|. vim-orgmode tries to make it easy for the + developer to register new keybindings, make them customizable and provide + menu entries so that the user can access the functionality like in original + orgmode. + + This is done by providing three classes: Keybinding, Plug and ActionEntry + + Keybinding~ + This is the basic class that encapsulates a single keybinding consisting + of a key/mapping and an action. Several options can be set when creating + the object to specify the mode and all kinds of other things. + + If a Plug is given instead of an action string the Plug is bound to the + key. All relevant data is read from the Plug, e.g. name, mode aso. + + Example~ + Map g{ to moving to parent heading in normal mode: +> + Keybinding('g{', \ + ':py ORGMODE.plugins["Navigator"].parent(mode="normal")', \ + mode=MODE_NORMAL) + + vim -> :nmap g{ + \ :py ORGMODE.plugins["Navigator"].parent(mode="normal") +< + + Map g{ to moving to parent heading in normal mode by using a Plug: +> + Keybinding('g{', Plug('OrgJumpToParentNormal', \ + ':py ORGMODE.plugins["Navigator"].parent(mode="normal")')) + + vim -> :nnoremap OrgJumpToParentNormal :py + \ ORGMODE.plugins["Navigator"].parent(mode="normal") + vim -> :nmap g{ OrgJumpToParentNormal +< + + Plug~ + A Plug is a unique keybinding that can not be executed by pressing + any key. This makes it a special Keybinding that takes a name and + an action to create an object. A plug normally goes together with a + regular Keybinding to bind the Plug to a key. + + This special behavior is needed to ensure that keybindings are + customizable by the user. If the user creates a keybinding to a + Plug the Keybinding object makes sure that the users keybinding is + used and the keybinding specified by the plugin is not used. + + Example~ + Map g{ to moving to parent heading in normal mode by using a Plug: +> + Keybinding('g{', Plug('OrgJumpToParentNormal', \ + ':py ORGMODE.plugins["Navigator"].parent(mode="normal")')) + + vim -> :nnoremap OrgJumpToParentNormal + \ :py ORGMODE.plugins["Navigator"].parent(mode="normal") + vim -> :nmap g{ OrgJumpToParentNormal +< + + ActionEntry~ + An ActionEntry makes Keybindings accessible by the vim menu. It takes a + description and a Keybinding object and builds a menu entry from this. The + resulting object can be added to a Submenu object by using the + operator. + + Example~ + Map g{ to moving to parent heading in normal mode by using a Plug: +> + k = Keybinding('g{', Plug('OrgJumpToParentNormal', \ + ':py ORGMODE.plugins["Navigator"].parent(mode="normal")')) + + vim -> :nnoremap OrgJumpToParentNormal + \ :py ORGMODE.plugins["Navigator"].parent(mode="normal") + vim -> :nmap g{ OrgJumpToParentNormal + + menu + ActionEntry('&Up', k) + vim -> :nmenu &Org.&Naviagte Headings.&Upg{ + \ OrgJumpToParentNormal +> + +------------------------------------------------------------------------------ +Building a Vimball~ + Vimball is an archive format for vim plugins. It's of use when you want to + install vim-orgmode for a single user. To build a Vimball just run the + following command in the root folder of this plugin. Please make sure that + vim is installed on your computer: +> + make vmb +< + + For installing the plugin form the resulting orgmode.vmb.gz file, please + refer to the Installation section. + +------------------------------------------------------------------------------ +Building a Debian Package~ + A Debian package is of use when you want to make vim-orgmode available to + all users on your computer. Make sure you've debhelper and vim installed, + than run the following command from the root directory of this plugin to + build the debian package: +> + dpkg-buildpackage -us -uc +< + + For installing the plugin form the resulting vim-orgmode_X.X.X-X.deb file, + please refer to the Installation section. + +------------------------------------------------------------------------------ +Creating Tests Cases~ + For every plugin it's important to write automated test cases. This is + important to ensure that little changes don't break things at the other end + of the project. + + vim-orgmode relies on Pyunit (http://docs.python.org/library/unittest.html). + All tests are located in the tests directory. Run +> + make test +< + + to run all tests. To create a new test the test should be added to the + corresponding test file. + + In case a new plugin is created a new test file needs to be created as well. + The test needs to be added to the test suite located in the file + tests/run_tests.py. + + Finally the +> + make coverage +< + + should be run. The result shows the test coverage of all project files. One + hundred percent (100%) is of course the goal :-) + +============================================================================== +LINKS *orgguide-links* + +- Original org-mode for Emacs (http://orgmode.org) + +- VimOrganizer, another vim port of Emacs org-mode + (http://www.vim.org/scripts/script.php?script_id=3342) + +============================================================================== +CHANGELOG *orgguide-changelog* + +Is found in file CHANGELOG.org + +============================================================================== +CONTRIBUTORS *orgguide-contributors* + +Thanks to all how contributed to vim-orgmode. All contributors are name here +in alphabetic order: + +- Stefan Otte +- Aleksandar Dimitrov + +============================================================================== +LICENSE VIM-ORGMODE *orgguide-license* + +Copyright (C) 2010, 2011 Jan Christoph Ebersbach + +http://www.e-jc.de/ + +All rights reserved. + +The source code of this program is made available under the terms of the GNU +Affero General Public License version 3 (GNU AGPL V3) as published by the Free +Software Foundation. + +Binary versions of this program provided by Univention to you as well as other +copyrighted, protected or trademarked materials like Logos, graphics, fonts, +specific documentations and configurations, cryptographic keys etc. are +subject to a license agreement between you and Univention and not subject to +the GNU AGPL V3. + +In the case you use this program under the terms of the GNU AGPL V3, the +program is provided in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +details. + +You should have received a copy of the GNU Affero General Public License with +the Debian GNU/Linux or Univention distribution in file +/usr/share/common-licenses/AGPL-3; if not, see . + +vim:tw=78:ts=2:sw=2:expandtab:ft=help:norl: diff --git a/pack/acp/start/vim-orgmode/documentation/Makefile b/pack/acp/start/vim-orgmode/documentation/Makefile new file mode 100644 index 0000000..2e3a821 --- /dev/null +++ b/pack/acp/start/vim-orgmode/documentation/Makefile @@ -0,0 +1,9 @@ +diagram: diagram.png + +diagram.png: diagram.txt + if [ -n "$(shell which ditaa)" ]; then ditaa $^ $@; elif [ -f ditaa0_9.jar ]; then java -jar ditaa0_9.jar $^ $@; else echo "Unable to find ditaa, please install ditaa or download ditaa0_9.jar (http://ditaa.sf.net/)and place it in folder documentation"; exit 1 ; fi + +clean: + @rm -f diagram.png + +.PHONY: diagram clean diff --git a/pack/acp/start/vim-orgmode/documentation/diagram.txt b/pack/acp/start/vim-orgmode/documentation/diagram.txt new file mode 100644 index 0000000..be71629 --- /dev/null +++ b/pack/acp/start/vim-orgmode/documentation/diagram.txt @@ -0,0 +1,75 @@ + /------------------------------\ +-----------------------------------+ + | Legend | | vim-orgmode | + | cCCC | | cBLU | + | | | o support for plugins | + | | | o plugins implement orgmode | + +--------------+---------------+ | functionality in vim | + | vim-orgmode | liborgmode | | o mainly plugins implement | + | cBLU | cYEL | | keybindings for interactively | + | | | | changing org-mode files | + | | | | | + +--------------+---------------+ | | + | orgcmd | doesn't exist | +---+-------------------------------+ + | cRED | yet | |1 + | | | | + | | | | + \--------------+-=-------------/ |x + v + +-----------------------------------+ + | Plugins | + | cBLU | + | o manipulate headings | + | o change tags, todo states, lists | + | o integrate with orgcmd | + | o reusable functionality doesn't | + | belong here but into liborgmode!| + | o timer, time tracking | + | | + | | + +---+-------------------------------+ +------------+ + | | VimBuffer | + | | {d} | + | /---------+ cBLU | + | | | | + v | +------------+ + +-----------------------------------+ +---------------++ + 1 | Document | | Extend Document| +-=------------+ + /-----+ cYEL |<-------+ cYEL | |+------------+| + | | o represents an org-mode document | | o abstraction | || file || + | | o contains links to other | | of data/file | || {d} || + | x | documents | | access +-------+| cYEL || + \---->| o contains meta information | | o read | || || + | o contains headings | | o write | |+------------+| + | | | | +--------------+ + | | | | + | | +---------------++ +-=------------+ + +----+------------------------------+ | |+------------+| + |1 ^ | || stdin/ || + | | \--------+| stdout || + | | || {d} || + | \------------\ || cRED || + |x | |+------------+| + v | +--------------+ + +-----------------------------------+ +-+=--------------------------+ + 1 | Heading | |+---------------------------+| + /-----+ cYEL | || orgcmd || + | | o represents a single heading | || cRED || + | | o contains links to other headings| || o implement command line || + | x | o parent heading | || tool for processing org || + \---->| o siblings | || files || + | o children | || o provide output filter || + | | || o convert org-mode docs || + | o title | || to other formats || + | o level | |+---------------------------+| + | o body | +-----------------------------+ + | o tags | + | o todo state | + | o closing date | + | o scheduled date | + | o priority | + | o item lists (class hierarchy) | + | | + | | + | | + +-----------------------------------+ + diff --git a/pack/acp/start/vim-orgmode/documentation/emacs_orgguide.org b/pack/acp/start/vim-orgmode/documentation/emacs_orgguide.org new file mode 100644 index 0000000..66d7171 --- /dev/null +++ b/pack/acp/start/vim-orgmode/documentation/emacs_orgguide.org @@ -0,0 +1,3087 @@ +| [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* Org Mode Guide + +Copyright © 2010 Free Software Foundation + +#+BEGIN_QUOTE + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 or + any later version published by the Free Software Foundation; with no + Invariant Sections, with the Front-Cover texts being “A GNU Manual,” + and with the Back-Cover Texts as in (a) below. A copy of the license + is included in the section entitled “GNU Free Documentation License.” + + (a) The FSF’s Back-Cover Text is: “You have the freedom to copy and + modify this GNU manual. Buying copies from the FSF supports it in + developing GNU and promoting software freedom.” + + This document is part of a collection distributed under the GNU Free + Documentation License. If you want to distribute this document + separately from the collection, you can do so by adding a copy of the + license to the document, as described in section 6 of the license. +#+END_QUOTE + +[[#Introduction][1. Introduction]] + +Getting started + +[[#Document-Structure][2. Document Structure]] + +A tree works like your brain + +[[#Tables][3. Tables]] + +Pure magic for quick formatting + +[[#Hyperlinks][4. Hyperlinks]] + +Notes in context + +[[#TODO-Items][5. TODO Items]] + +Every tree branch can be a TODO item + +[[#Tags][6. Tags]] + +Tagging headlines and matching sets of tags + +[[#Properties][7. Properties]] + +[[#Dates-and-Times][8. Dates and Times]] + +Making items useful for planning + +[[#Capture-_002d-Refile-_002d-Archive][9. Capture - Refile - Archive]] + +The ins and outs for projects + +[[#Agenda-Views][10. Agenda Views]] + +Collecting information into views + +[[#Markup][11. Markup for rich export]] + +Prepare text for rich export + +[[#Exporting][12. Exporting]] + +Sharing and publishing of notes + +[[#Publishing][13. Publishing]] + +Create a web site of linked Org files + +[[#Working-With-Source-Code][14. Working with source code]] + +Source code snippets embedded in Org + +[[#Miscellaneous][15. Miscellaneous]] + +All the rest which did not fit elsewhere + +#+BEGIN_EXAMPLE + — The Detailed Node Listing — + + Introduction +#+END_EXAMPLE + +[[#Preface][1.1 Preface]] + +Welcome + +[[#Installation][1.2 Installation]] + +How to install a downloaded version of Org + +[[#Activation][1.3 Activation]] + +How to activate Org for certain buffers + +[[#Feedback][1.4 Feedback]] + +Bug reports, ideas, patches etc. + +#+BEGIN_EXAMPLE + Document Structure +#+END_EXAMPLE + +[[#Outlines][2.1 Outlines]] + +Org is based on Outline mode + +[[#Headlines][2.2 Headlines]] + +How to typeset Org tree headlines + +[[#Visibility-cycling][2.3 Visibility cycling]] + +Show and hide, much simplified + +[[#Motion][2.4 Motion]] + +Jumping to other headlines + +[[#Structure-editing][2.5 Structure editing]] + +Changing sequence and level of headlines + +[[#Sparse-trees][2.6 Sparse trees]] + +Matches embedded in context + +[[#Plain-lists][2.7 Plain lists]] + +Additional structure within an entry + +[[#Footnotes][2.8 Footnotes]] + +How footnotes are defined in Org’s syntax + +#+BEGIN_EXAMPLE + Hyperlinks +#+END_EXAMPLE + +[[#Link-format][4.1 Link format]] + +How links in Org are formatted + +[[#Internal-links][4.2 Internal links]] + +Links to other places in the current file + +[[#External-links][4.3 External links]] + +URL-like links to the world + +[[#Handling-links][4.4 Handling links]] + +Creating, inserting and following + +[[#Targeted-links][4.5 Targeted links]] + +Point at a location in a file + +#+BEGIN_EXAMPLE + TODO Items +#+END_EXAMPLE + +[[#Using-TODO-states][5.1 Using TODO states]] + +Setting and switching states + +[[#Multi_002dstate-workflows][5.2 Multi-state workflows]] + +More than just on/off + +[[#Progress-logging][5.3 Progress logging]] + +Dates and notes for progress + +[[#Priorities][5.4 Priorities]] + +Some things are more important than others + +[[#Breaking-down-tasks][5.5 Breaking tasks down into subtasks]] + +Splitting a task into manageable pieces + +[[#Checkboxes][5.6 Checkboxes]] + +Tick-off lists + +#+BEGIN_EXAMPLE + Progress logging +#+END_EXAMPLE + +[[#Closing-items][Closing items]] + +When was this entry marked DONE? + +[[#Tracking-TODO-state-changes][Tracking TODO state changes]] + +When did the status change? + +#+BEGIN_EXAMPLE + Tags +#+END_EXAMPLE + +[[#Tag-inheritance][6.1 Tag inheritance]] + +Tags use the tree structure of the outline + +[[#Setting-tags][6.2 Setting tags]] + +How to assign tags to a headline + +[[#Tag-searches][6.3 Tag searches]] + +Searching for combinations of tags + +#+BEGIN_EXAMPLE + Dates and Times +#+END_EXAMPLE + +[[#Timestamps][8.1 Timestamps]] + +Assigning a time to a tree entry + +[[#Creating-timestamps][8.2 Creating timestamps]] + +Commands which insert timestamps + +[[#Deadlines-and-scheduling][8.3 Deadlines and scheduling]] + +Planning your work + +[[#Clocking-work-time][8.4 Clocking work time]] + +Tracking how long you spend on a task + +#+BEGIN_EXAMPLE + Capture - Refile - Archive +#+END_EXAMPLE + +[[#Capture][9.1 Capture]] + +[[#Refiling-notes][9.2 Refiling notes]] + +Moving a tree from one place to another + +[[#Archiving][9.3 Archiving]] + +What to do with finished projects + +#+BEGIN_EXAMPLE + Capture +#+END_EXAMPLE + +[[#Setting-up-a-capture-location][Setting up a capture location]] + +Where notes will be stored + +[[#Using-capture][Using capture]] + +Commands to invoke and terminate capture + +[[#Capture-templates][Capture templates]] + +Define the outline of different note types + +#+BEGIN_EXAMPLE + Agenda Views +#+END_EXAMPLE + +[[#Agenda-files][10.1 Agenda files]] + +Files being searched for agenda information + +[[#Agenda-dispatcher][10.2 The agenda dispatcher]] + +Keyboard access to agenda views + +[[#Built_002din-agenda-views][10.3 The built-in agenda views]] + +What is available out of the box? + +[[#Agenda-commands][10.4 Commands in the agenda buffer]] + +Remote editing of Org trees + +[[#Custom-agenda-views][10.5 Custom agenda views]] + +Defining special searches and views + +#+BEGIN_EXAMPLE + The built-in agenda views +#+END_EXAMPLE + +[[#Weekly_002fdaily-agenda][10.3.1 The weekly/daily agenda]] + +The calendar page with current tasks + +[[#Global-TODO-list][10.3.2 The global TODO list]] + +All unfinished action items + +[[#Matching-tags-and-properties][10.3.3 Matching tags and properties]] + +Structured information with fine-tuned search + +[[#Timeline][10.3.4 Timeline for a single file]] + +Time-sorted view for single file + +[[#Search-view][10.3.5 Search view]] + +Find entries by searching for text + +#+BEGIN_EXAMPLE + Markup for rich export +#+END_EXAMPLE + +[[#Structural-markup-elements][11.1 Structural markup elements]] + +The basic structure as seen by the exporter + +[[#Images-and-tables][11.2 Images and Tables]] + +Tables and Images will be included + +[[#Literal-examples][11.3 Literal examples]] + +Source code examples with special formatting + +[[#Include-files][11.4 Include files]] + +Include additional files into a document + +[[#Embedded-LaTeX][11.5 Embedded LaTeX]] + +LaTeX can be freely used inside Org documents + +#+BEGIN_EXAMPLE + Structural markup elements +#+END_EXAMPLE + +[[#Document-title][• Document title]] + +Where the title is taken from + +[[#Headings-and-sections][• Headings and sections]] + +The document structure as seen by the exporter + +[[#Table-of-contents][• Table of contents]] + +The if and where of the table of contents + +[[#Paragraphs][• Paragraphs]] + +[[#Emphasis-and-monospace][• Emphasis and monospace]] + +Bold, italic, etc. + +[[#Comment-lines][• Comment lines]] + +What will *not* be exported + +#+BEGIN_EXAMPLE + Exporting +#+END_EXAMPLE + +[[#Export-options][12.1 Export options]] + +Per-file export settings + +[[#The-export-dispatcher][12.2 The export dispatcher]] + +How to access exporter commands + +[[#ASCII_002fLatin_002d1_002fUTF_002d8-export][12.3 ASCII/Latin-1/UTF-8 +export]] + +Exporting to flat files with encoding + +[[#HTML-export][12.4 HTML export]] + +Exporting to HTML + +[[#LaTeX-and-PDF-export][12.5 LaTeX and PDF export]] + +Exporting to LaTeX, and processing to PDF + +[[#DocBook-export][12.6 DocBook export]] + +Exporting to DocBook + +[[#iCalendar-export][12.7 iCalendar export]] + +#+BEGIN_EXAMPLE + Miscellaneous +#+END_EXAMPLE + +[[#Completion][15.1 Completion]] + +M-TAB knows what you need + +[[#Clean-view][15.2 A cleaner outline view]] + +Getting rid of leading stars in the outline + +[[#MobileOrg][15.3 MobileOrg]] + +Org-mode on the iPhone + +#+BEGIN_EXAMPLE +#+END_EXAMPLE + +-------------- + +| [[[#Top][<]]] | [[[#Preface][>]]] | | [[[#Top][<<]]] | [[[#Top][Up]]] | [[[#Document-Structure][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 1. Introduction + +| [[#Preface][1.1 Preface]] | | Welcome | +| [[#Installation][1.2 Installation]] | | How to install a downloaded version of Org | +| [[#Activation][1.3 Activation]] | | How to activate Org for certain buffers | +| [[#Feedback][1.4 Feedback]] | | Bug reports, ideas, patches etc. | + +-------------- + +| [[[#Introduction][<]]] | [[[#Installation][>]]] | | [[[#Introduction][<<]]] | [[[#Introduction][Up]]] | [[[#Document-Structure][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 1.1 Preface + +Org is a mode for keeping notes, maintaining TODO lists, and doing +project planning with a fast and effective plain-text system. It is also +an authoring and publishing system. + +/This document is a much compressed derivative of the +[[http://orgmode.org/index.html#sec-4_1][comprehensive Org-mode +manual]]. It contains all basic features and commands, along with +important hints for customization. It is intended for beginners who +would shy back from a 200 page manual because of sheer size./ + +-------------- + +| [[[#Preface][<]]] | [[[#Activation][>]]] | | [[[#Introduction][<<]]] | [[[#Introduction][Up]]] | [[[#Document-Structure][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 1.2 Installation + +*Important:* /If you are using a version of Org that is part of the +Emacs distribution or an XEmacs package, please skip this section and go +directly to [[#Activation][Activation]]./ + +If you have downloaded Org from the Web, either as a distribution +‘=.zip=’ or ‘=.tar=’ file, or as a Git archive, it is best to run it +directly from the distribution directory. You need to add the ‘=lisp=’ +subdirectories to the Emacs load path. To do this, add the following +line to ‘=.emacs=’: + +#+BEGIN_EXAMPLE + (setq load-path (cons "~/path/to/orgdir/lisp" load-path)) + (setq load-path (cons "~/path/to/orgdir/contrib/lisp" load-path)) +#+END_EXAMPLE + +For speed you should byte-compile the Lisp files with the shell command: + +#+BEGIN_EXAMPLE + make +#+END_EXAMPLE + +Then add the following line to ‘=.emacs=’. It is needed so that Emacs +can autoload functions that are located in files not immediately loaded +when Org-mode starts. + +#+BEGIN_EXAMPLE + (require 'org-install) +#+END_EXAMPLE + +-------------- + +| [[[#Installation][<]]] | [[[#Feedback][>]]] | | [[[#Introduction][<<]]] | [[[#Introduction][Up]]] | [[[#Document-Structure][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 1.3 Activation + +Add the following lines to your ‘=.emacs=’ file. The last three lines +define /global/ keys for some commands — please choose suitable keys +yourself. + +#+BEGIN_EXAMPLE + ;; The following lines are always needed. Choose your own keys. + (add-to-list 'auto-mode-alist '("\\.org\\'" . org-mode)) + (add-hook 'org-mode-hook 'turn-on-font-lock) ; not needed when global-font-lock-mode is on + (global-set-key "\C-cl" 'org-store-link) + (global-set-key "\C-ca" 'org-agenda) + (global-set-key "\C-cb" 'org-iswitchb) +#+END_EXAMPLE + +With this setup, all files with extension ‘.org’ will be put into Org +mode. + +-------------- + +| [[[#Activation][<]]] | [[[#Document-Structure][>]]] | | [[[#Introduction][<<]]] | [[[#Introduction][Up]]] | [[[#Document-Structure][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 1.4 Feedback + +If you find problems with Org, or if you have questions, remarks, or +ideas about it, please mail to the Org mailing list +[[mailto:emacs-orgmode@gnu.org][emacs-orgmode@gnu.org]]. For information +on how to submit bug reports, see the main manual. + +-------------- + +| [[[#Feedback][<]]] | [[[#Outlines][>]]] | | [[[#Introduction][<<]]] | [[[#Top][Up]]] | [[[#Tables][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 2. Document Structure + +Org is based on Outline mode and provides flexible commands to edit the +structure of the document. + +| [[#Outlines][2.1 Outlines]] | | Org is based on Outline mode | +| [[#Headlines][2.2 Headlines]] | | How to typeset Org tree headlines | +| [[#Visibility-cycling][2.3 Visibility cycling]] | | Show and hide, much simplified | +| [[#Motion][2.4 Motion]] | | Jumping to other headlines | +| [[#Structure-editing][2.5 Structure editing]] | | Changing sequence and level of headlines | +| [[#Sparse-trees][2.6 Sparse trees]] | | Matches embedded in context | +| [[#Plain-lists][2.7 Plain lists]] | | Additional structure within an entry | +| [[#Footnotes][2.8 Footnotes]] | | How footnotes are defined in Org’s syntax | + +-------------- + +| [[[#Document-Structure][<]]] | [[[#Headlines][>]]] | | [[[#Document-Structure][<<]]] | [[[#Document-Structure][Up]]] | [[[#Tables][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 2.1 Outlines + +Org is implemented on top of Outline mode. Outlines allow a document to +be organized in a hierarchical structure, which (at least for me) is the +best representation of notes and thoughts. An overview of this structure +is achieved by folding (hiding) large parts of the document to show only +the general document structure and the parts currently being worked on. +Org greatly simplifies the use of outlines by compressing the entire +show/hide functionality into a single command, =org-cycle=, which is +bound to the key. + +-------------- + +| [[[#Outlines][<]]] | [[[#Visibility-cycling][>]]] | | [[[#Document-Structure][<<]]] | [[[#Document-Structure][Up]]] | [[[#Tables][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 2.2 Headlines + +Headlines define the structure of an outline tree. The headlines in Org +start with one or more stars, on the left margin[[#FOOT1][(1)]]. For +example: + +#+BEGIN_EXAMPLE + * Top level headline + ** Second level + *** 3rd level + some text + *** 3rd level + more text + + * Another top level headline +#+END_EXAMPLE + +Some people find the many stars too noisy and would prefer an outline +that has whitespace followed by a single star as headline starters. +[[#Clean-view][A cleaner outline view]], describes a setup to realize +this. + +-------------- + +| [[[#Headlines][<]]] | [[[#Motion][>]]] | | [[[#Document-Structure][<<]]] | [[[#Document-Structure][Up]]] | [[[#Tables][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 2.3 Visibility cycling + +Outlines make it possible to hide parts of the text in the buffer. Org +uses just two commands, bound to and S- to change the +visibility in the buffer. + +- :: /Subtree cycling/: Rotate current subtree among the states + + #+BEGIN_EXAMPLE + ,-> FOLDED -> CHILDREN -> SUBTREE --. + '-----------------------------------' + #+END_EXAMPLE + + When called with a prefix argument (C-u ) or with the shift key, + global cycling is invoked. + +- S- and C-u :: /Global cycling/: Rotate the entire buffer + among the states + + #+BEGIN_EXAMPLE + ,-> OVERVIEW -> CONTENTS -> SHOW ALL --. + '--------------------------------------' + #+END_EXAMPLE + +- C-u C-u C-u :: Show all, including drawers. + +When Emacs first visits an Org file, the global state is set to +OVERVIEW, i.e. only the top level headlines are visible. This can be +configured through the variable =org-startup-folded=, or on a per-file +basis by adding a startup keyword =overview=, =content=, =showall=, like +this: + +#+BEGIN_EXAMPLE + #+STARTUP: content +#+END_EXAMPLE + +-------------- + +| [[[#Visibility-cycling][<]]] | [[[#Structure-editing][>]]] | | [[[#Document-Structure][<<]]] | [[[#Document-Structure][Up]]] | [[[#Tables][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 2.4 Motion + +The following commands jump to other headlines in the buffer. + +- C-c C-n :: Next heading. + +- C-c C-p :: Previous heading. + +- C-c C-f :: Next heading same level. + +- C-c C-b :: Previous heading same level. + +- C-c C-u :: Backward to higher level heading. + +-------------- + +| [[[#Motion][<]]] | [[[#Sparse-trees][>]]] | | [[[#Document-Structure][<<]]] | [[[#Document-Structure][Up]]] | [[[#Tables][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 2.5 Structure editing + +- M- :: Insert new heading with same level as current. If the + cursor is in a plain list item, a new item is created (see section + [[#Plain-lists][Plain lists]]). When this command is used in the + middle of a line, the line is split and the rest of the line becomes + the new headline[[#FOOT2][(2)]]. + +- M-S- :: Insert new TODO entry with same level as current + heading. + +- in new, empty entry :: In a new entry with no text yet, + will cycle through reasonable levels. + +- M-/ :: Promote/demote current heading by one level. + +- M-S-/ :: Promote/demote the current subtree by one + level. + +- M-S-/ :: Move subtree up/down (swap with previous/next + subtree of same level). + +- C-c C-w :: Refile entry or region to a different location. See + section [[#Refiling-notes][Refiling notes]]. + +- C-x n s/w :: Narrow buffer to current subtree / widen it again + +When there is an active region (Transient Mark mode), promotion and +demotion work on all headlines in the region. + +-------------- + +| [[[#Structure-editing][<]]] | [[[#Plain-lists][>]]] | | [[[#Document-Structure][<<]]] | [[[#Document-Structure][Up]]] | [[[#Tables][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 2.6 Sparse trees + +An important feature of Org mode is the ability to construct /sparse +trees/ for selected information in an outline tree, so that the entire +document is folded as much as possible, but the selected information is +made visible along with the headline structure above it[[#FOOT3][(3)]]. +Just try it out and you will see immediately how it works. + +Org mode contains several commands creating such trees, all these +commands can be accessed through a dispatcher: + +- C-c / :: This prompts for an extra key to select a sparse-tree + creating command. + +- C-c / r :: Occur. Prompts for a regexp and shows a sparse tree with + all matches. Each match is also highlighted; the highlights disappear + by pressing C-c C-c. + +The other sparse tree commands select headings based on TODO keywords, +tags, or properties and will be discussed later in this manual. + +-------------- + +| [[[#Sparse-trees][<]]] | [[[#Footnotes][>]]] | | [[[#Document-Structure][<<]]] | [[[#Document-Structure][Up]]] | [[[#Tables][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 2.7 Plain lists + +Within an entry of the outline tree, hand-formatted lists can provide +additional structure. They also provide a way to create lists of +checkboxes (see section [[#Checkboxes][Checkboxes]]). Org supports +editing such lists, and the HTML exporter (see section +[[#Exporting][Exporting]]) parses and formats them. + +Org knows ordered lists, unordered lists, and description lists. + +- /Unordered/ list items start with ‘-’, ‘+’, or ‘*’ as bullets. +- /Ordered/ list items start with ‘1.’ or ‘1)’. +- /Description/ list use ‘ :: ’ to separate the /term/ from the + description. + +Items belonging to the same list must have the same indentation on the +first line. An item ends before the next line that is indented like its +bullet/number, or less. A list ends when all items are closed, or before +two blank lines. An example: + +#+BEGIN_EXAMPLE + ** Lord of the Rings + My favorite scenes are (in this order) + 1. The attack of the Rohirrim + 2. Eowyn's fight with the witch king + + this was already my favorite scene in the book + + I really like Miranda Otto. + Important actors in this film are: + - Elijah Wood :: He plays Frodo + - Sean Austin :: He plays Sam, Frodo's friend. +#+END_EXAMPLE + +The following commands act on items when the cursor is in the first line +of an item (the line with the bullet or number). + +- :: Items can be folded just like headline levels. + +- M- :: Insert new item at current level. With a prefix argument, + force a new heading (see section [[#Structure-editing][Structure + editing]]). + +- M-S- :: Insert a new item with a checkbox (see section + [[#Checkboxes][Checkboxes]]). + +- M-S-/ :: Move the item including subitems up/down (swap + with previous/next item of same indentation). If the list is ordered, + renumbering is automatic. + +- M-/M- :: Decrease/increase the indentation of an item, + leaving children alone. + +- M-S-/ :: Decrease/increase the indentation of the item, + including subitems. + +- C-c C-c :: If there is a checkbox (see section + [[#Checkboxes][Checkboxes]]) in the item line, toggle the state of + the checkbox. Also verify bullets and indentation consistency in the + whole list. + +- C-c - :: Cycle the entire list level through the different + itemize/enumerate bullets (‘-’, ‘+’, ‘*’, ‘1.’, ‘1)’). + +-------------- + +| [[[#Plain-lists][<]]] | [[[#Tables][>]]] | | [[[#Document-Structure][<<]]] | [[[#Document-Structure][Up]]] | [[[#Tables][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 2.8 Footnotes + +A footnote is defined in a paragraph that is started by a footnote +marker in square brackets in column 0, no indentation allowed. The +footnote reference is simply the marker in square brackets, inside text. +For example: + +#+BEGIN_EXAMPLE + The Org homepage[fn:1] now looks a lot better than it used to. + ... + [fn:1] The link is: http://orgmode.org +#+END_EXAMPLE + +The following commands handle footnotes: + +- C-c C-x f :: The footnote action command. When the cursor is on a + footnote reference, jump to the definition. When it is at a + definition, jump to the (first) reference. Otherwise, create a new + footnote. When this command is called with a prefix argument, a menu + of additional options including renumbering is offered. + +- C-c C-c :: Jump between definition and reference. + +*Further reading* +[[http://orgmode.org/manual/Document-Structure.html#Document-Structure][Chapter +2 of the manual]] + [[http://sachachua.com/wp/2008/01/outlining-your-notes-with-org/][Sacha +Chua’s tutorial]] + +-------------- + +| [[[#Footnotes][<]]] | [[[#Hyperlinks][>]]] | | [[[#Document-Structure][<<]]] | [[[#Top][Up]]] | [[[#Hyperlinks][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 3. Tables + +Org comes with a fast and intuitive table editor. Spreadsheet-like +calculations are supported in connection with the Emacs ‘=calc=’ package +(see the Emacs Calculator manual for more information about the Emacs +calculator). + +Org makes it easy to format tables in plain ASCII. Any line with ‘|’ as +the first non-whitespace character is considered part of a table. ‘|’ is +also the column separator. A table might look like this: + +#+BEGIN_EXAMPLE + | Name | Phone | Age | + |-------+-------+-----| + | Peter | 1234 | 17 | + | Anna | 4321 | 25 | +#+END_EXAMPLE + +A table is re-aligned automatically each time you press or +or C-c C-c inside the table. also moves to the next field ( +to the next row) and creates new table rows at the end of the table or +before horizontal lines. The indentation of the table is set by the +first line. Any line starting with ‘|-’ is considered as a horizontal +separator line and will be expanded on the next re-align to span the +whole table width. So, to create the above table, you would only type + +#+BEGIN_EXAMPLE + |Name|Phone|Age| + |- +#+END_EXAMPLE + +and then press to align the table and start filling in fields. +Even faster would be to type =|Name|Phone|Age= followed by C-c . + +When typing text into a field, Org treats , , and all +character keys in a special way, so that inserting and deleting avoids +shifting other fields. Also, when typing /immediately after the cursor +was moved into a new field with , S- or /, the field is +automatically made blank. + +- *Creation and conversion* + C-c | :: Convert the active region to table. If every line contains + at least one TAB character, the function assumes that the material is + tab separated. If every line contains a comma, comma-separated values + (CSV) are assumed. If not, lines are split at whitespace into fields. + If there is no active region, this command creates an empty Org + table. But it’s easier just to start typing, like |Name|Phone|Age C-c + . + +- *Re-aligning and field motion* + C-c C-c :: Re-align the table without moving the cursor. + +- :: Re-align the table, move to the next field. Creates a new + row if necessary. + +- S- :: Re-align, move to previous field. + +- :: Re-align the table and move down to next row. Creates a new + row if necessary. + +- *Column and row editing* + M- + M- :: Move the current column left/right. + +- M-S- :: Kill the current column. + +- M-S- :: Insert a new column to the left of the cursor + position. + +- M- + M- :: Move the current row up/down. + +- M-S- :: Kill the current row or horizontal line. + +- M-S- :: Insert a new row above the current row. With a prefix + argument, the line is created below the current one. + +- C-c - :: Insert a horizontal line below current row. With a prefix + argument, the line is created above the current line. + +- C-c :: Insert a horizontal line below current row, and move + the cursor into the row below that line. + +- C-c \^ :: Sort the table lines in the region. The position of point + indicates the column to be used for sorting, and the range of lines + is the range between the nearest horizontal separator lines, or the + entire table. + +*Further reading* +[[http://orgmode.org/manual/Tables.html#Tables][Chapter 3 of the +manual]] + [[http://orgmode.org/worg/org-tutorials/tables.php][Bastien’s table +tutorial]] + +[[http://orgmode.org/worg/org-tutorials/org-spreadsheet-intro.php][Bastien’s +spreadsheet tutorial]] + [[http://orgmode.org/worg/org-tutorials/org-plot.php][Eric’s plotting +tutorial]] + +-------------- + +| [[[#Tables][<]]] | [[[#Link-format][>]]] | | [[[#Tables][<<]]] | [[[#Top][Up]]] | [[[#TODO-Items][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 4. Hyperlinks + +Like HTML, Org provides links inside a file, external links to other +files, Usenet articles, emails, and much more. + +| [[#Link-format][4.1 Link format]] | | How links in Org are formatted | +| [[#Internal-links][4.2 Internal links]] | | Links to other places in the current file | +| [[#External-links][4.3 External links]] | | URL-like links to the world | +| [[#Handling-links][4.4 Handling links]] | | Creating, inserting and following | +| [[#Targeted-links][4.5 Targeted links]] | | Point at a location in a file | + +-------------- + +| [[[#Hyperlinks][<]]] | [[[#Internal-links][>]]] | | [[[#Hyperlinks][<<]]] | [[[#Hyperlinks][Up]]] | [[[#TODO-Items][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 4.1 Link format + +Org will recognize plain URL-like links and activate them as clickable +links. The general link format, however, looks like this: + +#+BEGIN_EXAMPLE + [[link][description]] or alternatively [[link]] +#+END_EXAMPLE + +Once a link in the buffer is complete (all brackets present), Org will +change the display so that ‘description’ is displayed instead of +‘[[link][description]]’ and ‘link’ is displayed instead of ‘[[link]]’. +To edit the invisible ‘link’ part, use C-c C-l with the cursor on the +link. + +-------------- + +| [[[#Link-format][<]]] | [[[#External-links][>]]] | | [[[#Hyperlinks][<<]]] | [[[#Hyperlinks][Up]]] | [[[#TODO-Items][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 4.2 Internal links + +If the link does not look like a URL, it is considered to be internal in +the current file. The most important case is a link like +‘[[#my-custom-id]]’ which will link to the entry with the =CUSTOM_ID= +property ‘my-custom-id’. + +Links such as ‘[[My Target]]’ or ‘[[My Target][Find my target]]’ lead to +a text search in the current file for the corresponding target which +looks like ‘<>’. + +-------------- + +| [[[#Internal-links][<]]] | [[[#Handling-links][>]]] | | [[[#Hyperlinks][<<]]] | [[[#Hyperlinks][Up]]] | [[[#TODO-Items][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 4.3 External links + +Org supports links to files, websites, Usenet and email messages, BBDB +database entries and links to both IRC conversations and their logs. +External links are URL-like locators. They start with a short +identifying string followed by a colon. There can be no space after the +colon. Here are some examples: + +#+BEGIN_EXAMPLE + http://www.astro.uva.nl/~dominik on the web + file:/home/dominik/images/jupiter.jpg file, absolute path + /home/dominik/images/jupiter.jpg same as above + file:papers/last.pdf file, relative path + file:projects.org another Org file + docview:papers/last.pdf::NNN open file in doc-view mode at page NNN + id:B7423F4D-2E8A-471B-8810-C40F074717E9 Link to heading by ID + news:comp.emacs Usenet link + mailto:adent@galaxy.net Mail link + vm:folder VM folder link + vm:folder#id VM message link + wl:folder#id WANDERLUST message link + mhe:folder#id MH-E message link + rmail:folder#id RMAIL message link + gnus:group#id Gnus article link + bbdb:R.*Stallman BBDB link (with regexp) + irc:/irc.com/#emacs/bob IRC link + info:org:External%20links Info node link (with encoded space) +#+END_EXAMPLE + +A link should be enclosed in double brackets and may contain a +descriptive text to be displayed instead of the URL (see section +[[#Link-format][Link format]]), for example: + +#+BEGIN_EXAMPLE + [[http://www.gnu.org/software/emacs/][GNU Emacs]] +#+END_EXAMPLE + +If the description is a file name or URL that points to an image, HTML +export (see section [[#HTML-export][HTML export]]) will inline the image +as a clickable button. If there is no description at all and the link +points to an image, that image will be inlined into the exported HTML +file. + +-------------- + +| [[[#External-links][<]]] | [[[#Targeted-links][>]]] | | [[[#Hyperlinks][<<]]] | [[[#Hyperlinks][Up]]] | [[[#TODO-Items][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 4.4 Handling links + +Org provides methods to create a link in the correct syntax, to insert +it into an Org file, and to follow the link. + +- C-c l :: Store a link to the current location. This is a /global/ + command (you must create the key binding yourself) which can be used + in any buffer to create a link. The link will be stored for later + insertion into an Org buffer (see below). + +- C-c C-l :: Insert a link. This prompts for a link to be inserted + into the buffer. You can just type a link, or use history keys + and to access stored links. You will be prompted for the + description part of the link. When called with a C-u prefix argument, + file name completion is used to link to a file. + +- C-c C-l (with cursor on existing link) :: When the cursor is on an + existing link, C-c C-l allows you to edit the link and description + parts of the link. + +- C-c C-o or mouse-1 or mouse-2 :: Open link at point. + +- C-c & :: Jump back to a recorded position. A position is recorded by + the commands following internal links, and by C-c %. Using this + command several times in direct succession moves through a ring of + previously recorded positions. + +-------------- + +| [[[#Handling-links][<]]] | [[[#TODO-Items][>]]] | | [[[#Hyperlinks][<<]]] | [[[#Hyperlinks][Up]]] | [[[#TODO-Items][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 4.5 Targeted links + +File links can contain additional information to make Emacs jump to a +particular location in the file when following a link. This can be a +line number or a search option after a double colon. + +Here is the syntax of the different ways to attach a search to a file +link, together with an explanation: + +#+BEGIN_EXAMPLE + [[file:~/code/main.c::255]] Find line 255 + [[file:~/xx.org::My Target]] Find ‘<>’ + [[file:~/xx.org::#my-custom-id]] Find entry with custom id +#+END_EXAMPLE + +*Further reading* +[[http://orgmode.org/manual/Hyperlinks.html#Hyperlinks][Chapter 4 of the +manual]] + +-------------- + +| [[[#Targeted-links][<]]] | [[[#Using-TODO-states][>]]] | | [[[#Hyperlinks][<<]]] | [[[#Top][Up]]] | [[[#Tags][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 5. TODO Items + +Org mode does not maintain TODO lists as separate +documents[[#FOOT4][(4)]]. Instead, TODO items are an integral part of +the notes file, because TODO items usually come up while taking notes! +With Org mode, simply mark any entry in a tree as being a TODO item. In +this way, information is not duplicated, and the entire context from +which the TODO item emerged is always present. + +Of course, this technique for managing TODO items scatters them +throughout your notes file. Org mode compensates for this by providing +methods to give you an overview of all the things that you have to do. + +| [[#Using-TODO-states][5.1 Using TODO states]] | | Setting and switching states | +| [[#Multi_002dstate-workflows][5.2 Multi-state workflows]] | | More than just on/off | +| [[#Progress-logging][5.3 Progress logging]] | | Dates and notes for progress | +| [[#Priorities][5.4 Priorities]] | | Some things are more important than others | +| [[#Breaking-down-tasks][5.5 Breaking tasks down into subtasks]] | | Splitting a task into manageable pieces | +| [[#Checkboxes][5.6 Checkboxes]] | | Tick-off lists | + +-------------- + +| [[[#TODO-Items][<]]] | [[[#Multi_002dstate-workflows][>]]] | | [[[#TODO-Items][<<]]] | [[[#TODO-Items][Up]]] | [[[#Tags][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 5.1 Using TODO states + +Any headline becomes a TODO item when it starts with the word ‘TODO’, +for example: + +#+BEGIN_EXAMPLE + *** TODO Write letter to Sam Fortune +#+END_EXAMPLE + +The most important commands to work with TODO entries are: + +- C-c C-t :: Rotate the TODO state of the current item among + + #+BEGIN_EXAMPLE + ,-> (unmarked) -> TODO -> DONE --. + '--------------------------------' + #+END_EXAMPLE + + The same rotation can also be done “remotely” from the timeline and + agenda buffers with the t command key (see section + [[#Agenda-commands][Commands in the agenda buffer]]). + +- S-/ :: Select the following/preceding TODO state, + similar to cycling. + +- C-c / t :: View TODO items in a /sparse tree/ (see section + [[#Sparse-trees][Sparse trees]]). Folds the buffer, but shows all + TODO items and the headings hierarchy above them. + +- C-c a t :: Show the global TODO list. Collects the TODO items from + all agenda files (see section [[#Agenda-Views][Agenda Views]]) into a + single buffer. See section [[#Global-TODO-list][The global TODO + list]], for more information. + +- S-M- :: Insert a new TODO entry below the current one. + +Changing a TODO state can also trigger tag changes. See the docstring of +the option =org-todo-state-tags-triggers= for details. + +-------------- + +| [[[#Using-TODO-states][<]]] | [[[#Progress-logging][>]]] | | [[[#TODO-Items][<<]]] | [[[#TODO-Items][Up]]] | [[[#Tags][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 5.2 Multi-state workflows + +You can use TODO keywords to indicate different /sequential/ states in +the process of working on an item, for example: + +#+BEGIN_EXAMPLE + (setq org-todo-keywords + '((sequence "TODO" "FEEDBACK" "VERIFY" "|" "DONE" "DELEGATED"))) +#+END_EXAMPLE + +The vertical bar separates the TODO keywords (states that /need action/) +from the DONE states (which need /no further action/). If you don’t +provide the separator bar, the last state is used as the DONE state. +With this setup, the command C-c C-t will cycle an entry from TODO to +FEEDBACK, then to VERIFY, and finally to DONE and DELEGATED. + +Sometimes you may want to use different sets of TODO keywords in +parallel. For example, you may want to have the basic =TODO=/=DONE=, but +also a workflow for bug fixing, and a separate state indicating that an +item has been canceled (so it is not DONE, but also does not require +action). Your setup would then look like this: + +#+BEGIN_EXAMPLE + (setq org-todo-keywords + '((sequence "TODO(t)" "|" "DONE(d)") + (sequence "REPORT(r)" "BUG(b)" "KNOWNCAUSE(k)" "|" "FIXED(f)") + (sequence "|" "CANCELED(c)"))) +#+END_EXAMPLE + +The keywords should all be different, this helps Org mode to keep track +of which subsequence should be used for a given entry. The example also +shows how to define keys for fast access of a particular state, by +adding a letter in parenthesis after each keyword - you will be prompted +for the key after C-c C-t. + +To define TODO keywords that are valid only in a single file, use the +following text anywhere in the file. + +#+BEGIN_EXAMPLE + #+TODO: TODO(t) | DONE(d) + #+TODO: REPORT(r) BUG(b) KNOWNCAUSE(k) | FIXED(f) + #+TODO: | CANCELED(c) +#+END_EXAMPLE + +After changing one of these lines, use C-c C-c with the cursor still in +the line to make the changes known to Org mode. + +-------------- + +| [[[#Multi_002dstate-workflows][<]]] | [[[#Closing-items][>]]] | | [[[#TODO-Items][<<]]] | [[[#TODO-Items][Up]]] | [[[#Tags][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 5.3 Progress logging + +Org mode can automatically record a timestamp and possibly a note when +you mark a TODO item as DONE, or even each time you change the state of +a TODO item. This system is highly configurable, settings can be on a +per-keyword basis and can be localized to a file or even a subtree. For +information on how to clock working time for a task, see +[[#Clocking-work-time][Clocking work time]]. + +| [[#Closing-items][Closing items]] | | When was this entry marked DONE? | +| [[#Tracking-TODO-state-changes][Tracking TODO state changes]] | | When did the status change? | + +-------------- + +| [[[#Progress-logging][<]]] | [[[#Tracking-TODO-state-changes][>]]] | | [[[#TODO-Items][<<]]] | [[[#Progress-logging][Up]]] | [[[#Tags][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +*** Closing items + +The most basic logging is to keep track of /when/ a certain TODO item +was finished. This is achieved with[[#FOOT5][(5)]]. + +#+BEGIN_EXAMPLE + (setq org-log-done 'time) +#+END_EXAMPLE + +Then each time you turn an entry from a TODO (not-done) state into any +of the DONE states, a line ‘CLOSED: [timestamp]’ will be inserted just +after the headline. If you want to record a note along with the +timestamp, use[[#FOOT6][(6)]] + +#+BEGIN_EXAMPLE + (setq org-log-done 'note) +#+END_EXAMPLE + +You will then be prompted for a note, and that note will be stored below +the entry with a ‘Closing Note’ heading. + +-------------- + +| [[[#Closing-items][<]]] | [[[#Priorities][>]]] | | [[[#TODO-Items][<<]]] | [[[#Progress-logging][Up]]] | [[[#Tags][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +*** Tracking TODO state changes + +You might want to keep track of TODO state changes. You can either +record just a timestamp, or a time-stamped note for a change. These +records will be inserted after the headline as an itemized list. When +taking a lot of notes, you might want to get the notes out of the way +into a drawer. Customize the variable =org-log-into-drawer= to get this +behavior. + +For state logging, Org mode expects configuration on a per-keyword +basis. This is achieved by adding special markers ‘!’ (for a timestamp) +and ‘@’ (for a note) in parentheses after each keyword. For example: + +#+BEGIN_EXAMPLE + #+TODO: TODO(t) WAIT(w@/!) | DONE(d!) CANCELED(c@) +#+END_EXAMPLE + +will define TODO keywords and fast access keys, and also request that a +time is recorded when the entry is set to DONE, and that a note is +recorded when switching to WAIT or CANCELED. The same syntax works also +when setting =org-todo-keywords=. + +-------------- + +| [[[#Tracking-TODO-state-changes][<]]] | [[[#Breaking-down-tasks][>]]] | | [[[#TODO-Items][<<]]] | [[[#TODO-Items][Up]]] | [[[#Tags][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 5.4 Priorities + +If you use Org mode extensively, you may end up with enough TODO items +that it starts to make sense to prioritize them. Prioritizing can be +done by placing a /priority cookie/ into the headline of a TODO item, +like this + +#+BEGIN_EXAMPLE + *** TODO [#A] Write letter to Sam Fortune +#+END_EXAMPLE + +Org mode supports three priorities: ‘A’, ‘B’, and ‘C’. ‘A’ is the +highest, ‘B’ the default if none is given. Priorities make a difference +only in the agenda. + +- C-c , :: Set the priority of the current headline. Press ‘A’, ‘B’ or + ‘C’ to select a priority, or to remove the cookie. + +- S- + S- :: Increase/decrease priority of current headline + +-------------- + +| [[[#Priorities][<]]] | [[[#Checkboxes][>]]] | | [[[#TODO-Items][<<]]] | [[[#TODO-Items][Up]]] | [[[#Tags][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 5.5 Breaking tasks down into subtasks + +It is often advisable to break down large tasks into smaller, manageable +subtasks. You can do this by creating an outline tree below a TODO item, +with detailed subtasks on the tree. To keep the overview over the +fraction of subtasks that are already completed, insert either ‘[/]’ or +‘[%]’ anywhere in the headline. These cookies will be updated each time +the TODO status of a child changes, or when pressing C-c C-c on the +cookie. For example: + +#+BEGIN_EXAMPLE + * Organize Party [33%] + ** TODO Call people [1/2] + *** TODO Peter + *** DONE Sarah + ** TODO Buy food + ** DONE Talk to neighbor +#+END_EXAMPLE + +-------------- + +| [[[#Breaking-down-tasks][<]]] | [[[#Tags][>]]] | | [[[#TODO-Items][<<]]] | [[[#TODO-Items][Up]]] | [[[#Tags][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 5.6 Checkboxes + +Every item in a plain list (see section [[#Plain-lists][Plain lists]]) +can be made into a checkbox by starting it with the string ‘[ ]’. +Checkboxes are not included into the global TODO list, so they are often +great to split a task into a number of simple steps. Here is an example +of a checkbox list. + +#+BEGIN_EXAMPLE + * TODO Organize party [1/3] + - [-] call people [1/2] + - [ ] Peter + - [X] Sarah + - [X] order food + - [ ] think about what music to play +#+END_EXAMPLE + +Checkboxes work hierarchically, so if a checkbox item has children that +are checkboxes, toggling one of the children checkboxes will make the +parent checkbox reflect if none, some, or all of the children are +checked. + +The following commands work with checkboxes: + +- C-c C-c :: Toggle checkbox status or (with prefix arg) checkbox + presence at point. + +- M-S- :: Insert a new item with a checkbox. This works only if + the cursor is already in a plain list item (see section + [[#Plain-lists][Plain lists]]). + +*Further reading* +[[http://orgmode.org/manual/TODO-Items.html#TODO-Items][Chapter 5 of the +manual]] + [[http://orgmode.org/worg/org-tutorials/orgtutorial_dto.php][David +O’Toole’s introductory tutorial]] + +[[http://members.optusnet.com.au/~charles57/GTD/gtd_workflow.html][Charles +Cave’s GTD setup]] + +-------------- + +| [[[#Checkboxes][<]]] | [[[#Tag-inheritance][>]]] | | [[[#TODO-Items][<<]]] | [[[#Top][Up]]] | [[[#Properties][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 6. Tags + +An excellent way to implement labels and contexts for cross-correlating +information is to assign /tags/ to headlines. Org mode has extensive +support for tags. + +Every headline can contain a list of tags; they occur at the end of the +headline. Tags are normal words containing letters, numbers, ‘\_’, and +‘@’. Tags must be preceded and followed by a single colon, e.g., +‘:work:’. Several tags can be specified, as in ‘:work:urgent:’. Tags +will by default be in bold face with the same color as the headline. + +| [[#Tag-inheritance][6.1 Tag inheritance]] | | Tags use the tree structure of the outline | +| [[#Setting-tags][6.2 Setting tags]] | | How to assign tags to a headline | +| [[#Tag-searches][6.3 Tag searches]] | | Searching for combinations of tags | + +-------------- + +| [[[#Tags][<]]] | [[[#Setting-tags][>]]] | | [[[#Tags][<<]]] | [[[#Tags][Up]]] | [[[#Properties][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 6.1 Tag inheritance + +/Tags/ make use of the hierarchical structure of outline trees. If a +heading has a certain tag, all subheadings will inherit the tag as well. +For example, in the list + +#+BEGIN_EXAMPLE + * Meeting with the French group :work: + ** Summary by Frank :boss:notes: + *** TODO Prepare slides for him :action: +#+END_EXAMPLE + +the final heading will have the tags ‘:work:’, ‘:boss:’, ‘:notes:’, and +‘:action:’ even though the final heading is not explicitly marked with +those tags. You can also set tags that all entries in a file should +inherit just as if these tags were defined in a hypothetical level zero +that surrounds the entire file. Use a line like this[[#FOOT7][(7)]]: + +#+BEGIN_EXAMPLE + #+FILETAGS: :Peter:Boss:Secret: +#+END_EXAMPLE + +-------------- + +| [[[#Tag-inheritance][<]]] | [[[#Tag-searches][>]]] | | [[[#Tags][<<]]] | [[[#Tags][Up]]] | [[[#Properties][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 6.2 Setting tags + +Tags can simply be typed into the buffer at the end of a headline. After +a colon, M- offers completion on tags. There is also a special +command for inserting tags: + +- C-c C-q :: Enter new tags for the current headline. Org mode will + either offer completion or a special single-key interface for setting + tags, see below. After pressing , the tags will be inserted and + aligned to =org-tags-column=. When called with a C-u prefix, all tags + in the current buffer will be aligned to that column, just to make + things look nice. + +- C-c C-c :: When the cursor is in a headline, this does the same as + C-c C-q. + +Org will support tag insertion based on a /list of tags/. By default +this list is constructed dynamically, containing all tags currently used +in the buffer. You may also globally specify a hard list of tags with +the variable =org-tag-alist=. Finally you can set the default tags for a +given file with lines like + +#+BEGIN_EXAMPLE + #+TAGS: @work @home @tennisclub + #+TAGS: laptop car pc sailboat +#+END_EXAMPLE + +By default Org mode uses the standard minibuffer completion facilities +for entering tags. However, it also implements another, quicker, tag +selection method called /fast tag selection/. This allows you to select +and deselect tags with just a single key press. For this to work well +you should assign unique letters to most of your commonly used tags. You +can do this globally by configuring the variable =org-tag-alist= in your +‘=.emacs=’ file. For example, you may find the need to tag many items in +different files with ‘:@home:’. In this case you can set something like: + +#+BEGIN_EXAMPLE + (setq org-tag-alist '(("@work" . ?w) ("@home" . ?h) ("laptop" . ?l))) +#+END_EXAMPLE + +If the tag is only relevant to the file you are working on, then you can +instead set the TAGS option line as: + +#+BEGIN_EXAMPLE + #+TAGS: @work(w) @home(h) @tennisclub(t) laptop(l) pc(p) +#+END_EXAMPLE + +-------------- + +| [[[#Setting-tags][<]]] | [[[#Properties][>]]] | | [[[#Tags][<<]]] | [[[#Tags][Up]]] | [[[#Properties][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 6.3 Tag searches + +Once a system of tags has been set up, it can be used to collect related +information into special lists. + +- C-c \ + C-c / m :: Create a sparse tree with all headlines matching a tags + search. With a C-u prefix argument, ignore headlines that are not a + TODO line. + +- C-c a m :: Create a global list of tag matches from all agenda + files. See section [[#Matching-tags-and-properties][Matching tags and + properties]]. + +- C-c a M :: Create a global list of tag matches from all agenda + files, but check only TODO items and force checking subitems (see + variable =org-tags-match-list-sublevels=). + +These commands all prompt for a match string which allows basic Boolean +logic like ‘+boss+urgent-project1’, to find entries with tags ‘boss’ and +‘urgent’, but not ‘project1’, or ‘Kathy|Sally’ to find entries which are +tagged, like ‘Kathy’ or ‘Sally’. The full syntax of the search string is +rich and allows also matching against TODO keywords, entry levels and +properties. For a complete description with many examples, see +[[#Matching-tags-and-properties][Matching tags and properties]]. + +*Further reading* +[[http://orgmode.org/manual/Tags.html#Tags][Chapter 6 of the manual]] + +[[http://sachachua.com/wp/2008/01/tagging-in-org-plus-bonus-code-for-timeclocks-and-tags/][Sacha +Chua’s article about tagging in Org-mode]] + +-------------- + +| [[[#Tag-searches][<]]] | [[[#Dates-and-Times][>]]] | | [[[#Tags][<<]]] | [[[#Top][Up]]] | [[[#Dates-and-Times][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 7. Properties + +Properties are key-value pairs associates with and entry. They live in a +special drawer with the name =PROPERTIES=. Each property is specified on +a single line, with the key (surrounded by colons) first, and the value +after it: + +#+BEGIN_EXAMPLE + * CD collection + ** Classic + *** Goldberg Variations + :PROPERTIES: + :Title: Goldberg Variations + :Composer: J.S. Bach + :Publisher: Deutsche Grammophon + :NDisks: 1 + :END: +#+END_EXAMPLE + +You may define the allowed values for a particular property ‘:Xyz:’ by +setting a property ‘:Xyz\_ALL:’. This special property is /inherited/, +so if you set it in a level 1 entry, it will apply to the entire tree. +When allowed values are defined, setting the corresponding property +becomes easier and is less prone to typing errors. For the example with +the CD collection, we can predefine publishers and the number of disks +in a box like this: + +#+BEGIN_EXAMPLE + * CD collection + :PROPERTIES: + :NDisks_ALL: 1 2 3 4 + :Publisher_ALL: "Deutsche Grammophon" Philips EMI + :END: +#+END_EXAMPLE + +or globally using =org-global-properties=, or file-wide like this: + +#+BEGIN_EXAMPLE + #+PROPERTY: NDisks_ALL 1 2 3 4 +#+END_EXAMPLE + +- C-c C-x p :: Set a property. This prompts for a property name and a + value. + +- C-c C-c d :: Remove a property from the current entry. + +To create sparse trees and special lists with selection based on +properties, the same commands are used as for tag searches (see section +[[#Tag-searches][Tag searches]]). The syntax for the search string is +described in [[#Matching-tags-and-properties][Matching tags and +properties]]. + +*Further reading* +[[http://orgmode.org/manual/Properties-and-Columns.html#Properties-and-Columns][Chapter +7 of the manual]] + +[[http://orgmode.org/worg/org-tutorials/org-column-view-tutorial.php][Bastien +Guerry’s column view tutorial]] + +-------------- + +| [[[#Properties][<]]] | [[[#Timestamps][>]]] | | [[[#Properties][<<]]] | [[[#Top][Up]]] | [[[#Capture-_002d-Refile-_002d-Archive][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 8. Dates and Times + +To assist project planning, TODO items can be labeled with a date and/or +a time. The specially formatted string carrying the date and time +information is called a /timestamp/ in Org mode. + +| [[#Timestamps][8.1 Timestamps]] | | Assigning a time to a tree entry | +| [[#Creating-timestamps][8.2 Creating timestamps]] | | Commands which insert timestamps | +| [[#Deadlines-and-scheduling][8.3 Deadlines and scheduling]] | | Planning your work | +| [[#Clocking-work-time][8.4 Clocking work time]] | | Tracking how long you spend on a task | + +-------------- + +| [[[#Dates-and-Times][<]]] | [[[#Creating-timestamps][>]]] | | [[[#Dates-and-Times][<<]]] | [[[#Dates-and-Times][Up]]] | [[[#Capture-_002d-Refile-_002d-Archive][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 8.1 Timestamps + +A timestamp is a specification of a date (possibly with a time or a +range of times) in a special format, either ‘<2003-09-16 Tue>’ or +‘<2003-09-16 Tue 09:39>’ or ‘<2003-09-16 Tue 12:00-12:30>’. A timestamp +can appear anywhere in the headline or body of an Org tree entry. Its +presence causes entries to be shown on specific dates in the agenda (see +section [[#Weekly_002fdaily-agenda][The weekly/daily agenda]]). We +distinguish: + +*Plain timestamp; Event; Appointment* + A simple timestamp just assigns a date/time to an item. This is just +like writing down an appointment or event in a paper agenda. + +#+BEGIN_EXAMPLE + * Meet Peter at the movies <2006-11-01 Wed 19:15> + * Discussion on climate change <2006-11-02 Thu 20:00-22:00> +#+END_EXAMPLE + +*Timestamp with repeater interval* + A timestamp may contain a /repeater interval/, indicating that it +applies not only on the given date, but again and again after a certain +interval of N days (d), weeks (w), months (m), or years (y). The +following will show up in the agenda every Wednesday: + +#+BEGIN_EXAMPLE + * Pick up Sam at school <2007-05-16 Wed 12:30 +1w> +#+END_EXAMPLE + +*Diary-style sexp entries* + For more complex date specifications, Org mode supports using the +special sexp diary entries implemented in the Emacs calendar/diary +package. For example + +#+BEGIN_EXAMPLE + * The nerd meeting on every 2nd Thursday of the month + <%%(diary-float t 4 2)> +#+END_EXAMPLE + +*Time/Date range* + Two timestamps connected by ‘--’ denote a range. + +#+BEGIN_EXAMPLE + ** Meeting in Amsterdam + <2004-08-23 Mon>--<2004-08-26 Thu> +#+END_EXAMPLE + +*Inactive timestamp* + Just like a plain timestamp, but with square brackets instead of +angular ones. These timestamps are inactive in the sense that they do +/not/ trigger an entry to show up in the agenda. + +#+BEGIN_EXAMPLE + * Gillian comes late for the fifth time [2006-11-01 Wed] +#+END_EXAMPLE + +-------------- + +| [[[#Timestamps][<]]] | [[[#Deadlines-and-scheduling][>]]] | | [[[#Dates-and-Times][<<]]] | [[[#Dates-and-Times][Up]]] | [[[#Capture-_002d-Refile-_002d-Archive][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 8.2 Creating timestamps + +For Org mode to recognize timestamps, they need to be in the specific +format. All commands listed below produce timestamps in the correct +format. + +- C-c . :: Prompt for a date and insert a corresponding timestamp. + When the cursor is at an existing timestamp in the buffer, the + command is used to modify this timestamp instead of inserting a new + one. When this command is used twice in succession, a time range is + inserted. With a prefix, also add the current time. + +- C-c ! :: Like C-c ., but insert an inactive timestamp that will not + cause an agenda entry. + +- S-/ :: Change date at cursor by one day. + +- S-/ :: Change the item under the cursor in a timestamp. + The cursor can be on a year, month, day, hour or minute. When the + timestamp contains a time range like ‘15:30-16:30’, modifying the + first time will also shift the second, shifting the time block with + constant length. To change the length, modify the second time. + +When Org mode prompts for a date/time, it will accept any string +containing some date and/or time information, and intelligently +interpret the string, deriving defaults for unspecified information from +the current date and time. You can also select a date in the pop-up +calendar. See the manual for more information on how exactly the +date/time prompt works. + +-------------- + +| [[[#Creating-timestamps][<]]] | [[[#Clocking-work-time][>]]] | | [[[#Dates-and-Times][<<]]] | [[[#Dates-and-Times][Up]]] | [[[#Capture-_002d-Refile-_002d-Archive][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 8.3 Deadlines and scheduling + +A timestamp may be preceded by special keywords to facilitate planning: + +*DEADLINE* + Meaning: the task (most likely a TODO item, though not necessarily) is +supposed to be finished on that date. + +- C-c C-d :: Insert ‘DEADLINE’ keyword along with a stamp, in the line + following the headline. + +On the deadline date, the task will be listed in the agenda. In +addition, the agenda for /today/ will carry a warning about the +approaching or missed deadline, starting =org-deadline-warning-days= +before the due date, and continuing until the entry is marked DONE. An +example: + +#+BEGIN_EXAMPLE + *** TODO write article about the Earth for the Guide + The editor in charge is [[bbdb:Ford Prefect]] + DEADLINE: <2004-02-29 Sun> +#+END_EXAMPLE + +*SCHEDULED* + Meaning: you are /planning to start working/ on that task on the given +date[[#FOOT8][(8)]]. + +- C-c C-s :: Insert ‘SCHEDULED’ keyword along with a stamp, in the + line following the headline. + +The headline will be listed under the given date[[#FOOT9][(9)]]. In +addition, a reminder that the scheduled date has passed will be present +in the compilation for /today/, until the entry is marked DONE. I.e. the +task will automatically be forwarded until completed. + +#+BEGIN_EXAMPLE + *** TODO Call Trillian for a date on New Years Eve. + SCHEDULED: <2004-12-25 Sat> +#+END_EXAMPLE + +Some tasks need to be repeated again and again. Org mode helps to +organize such tasks using a so-called repeater in a DEADLINE, SCHEDULED, +or plain timestamp. In the following example + +#+BEGIN_EXAMPLE + ** TODO Pay the rent + DEADLINE: <2005-10-01 Sat +1m> +#+END_EXAMPLE + +the =+1m= is a repeater; the intended interpretation is that the task +has a deadline on <2005-10-01> and repeats itself every (one) month +starting from that time. + +-------------- + +| [[[#Deadlines-and-scheduling][<]]] | [[[#Capture-_002d-Refile-_002d-Archive][>]]] | | [[[#Dates-and-Times][<<]]] | [[[#Dates-and-Times][Up]]] | [[[#Capture-_002d-Refile-_002d-Archive][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 8.4 Clocking work time + +Org mode allows you to clock the time you spend on specific tasks in a +project. + +- C-c C-x C-i :: Start the clock on the current item (clock-in). This + inserts the CLOCK keyword together with a timestamp. When called with + a C-u prefix argument, select the task from a list of recently + clocked tasks. + +- C-c C-x C-o :: Stop the clock (clock-out). This inserts another + timestamp at the same location where the clock was last started. It + also directly computes the resulting time in inserts it after the + time range as ‘=> HH:MM’. + +- C-c C-x C-e :: Update the effort estimate for the current clock + task. + +- C-c C-x C-x :: Cancel the current clock. This is useful if a clock + was started by mistake, or if you ended up working on something else. + +- C-c C-x C-j :: Jump to the entry that contains the currently running + clock. With a C-u prefix arg, select the target task from a list of + recently clocked tasks. + +- C-c C-x C-r :: Insert a dynamic block containing a clock report as + an Org-mode table into the current file. When the cursor is at an + existing clock table, just update it. + + #+BEGIN_EXAMPLE + #+BEGIN: clocktable :maxlevel 2 :emphasize nil :scope file + #+END: clocktable + #+END_EXAMPLE + + For details about how to customize this view, see + [[http://orgmode.org/manual/Clocking-work-time.html#Clocking-work-time][the + manual]]. + +- C-c C-c :: Update dynamic block at point. The cursor needs to be in + the =#+BEGIN= line of the dynamic block. + +The l key may be used in the timeline (see section [[#Timeline][Timeline +for a single file]]) and in the agenda (see section +[[#Weekly_002fdaily-agenda][The weekly/daily agenda]]) to show which +tasks have been worked on or closed during a day. + +*Further reading* +[[http://orgmode.org/manual/Dates-and-Times.html#Dates-and-Times][Chapter +8 of the manual]] + [[http://members.optusnet.com.au/~charles57/GTD/org_dates/][Charles +Cave’s Date and Time tutorial]] + [[http://doc.norang.ca/org-mode.html#Clocking][Bernt Hansen’s clocking +workflow]] + +-------------- + +| [[[#Clocking-work-time][<]]] | [[[#Capture][>]]] | | [[[#Dates-and-Times][<<]]] | [[[#Top][Up]]] | [[[#Agenda-Views][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 9. Capture - Refile - Archive + +An important part of any organization system is the ability to quickly +capture new ideas and tasks, and to associate reference material with +them. Org defines a capture process to create tasks. It stores files +related to a task (/attachments/) in a special directory. Once in the +system, tasks and projects need to be moved around. Moving completed +project trees to an archive file keeps the system compact and fast. + +| [[#Capture][9.1 Capture]] | | | +| [[#Refiling-notes][9.2 Refiling notes]] | | Moving a tree from one place to another | +| [[#Archiving][9.3 Archiving]] | | What to do with finished projects | + +-------------- + +| [[[#Capture-_002d-Refile-_002d-Archive][<]]] | [[[#Setting-up-a-capture-location][>]]] | | [[[#Capture-_002d-Refile-_002d-Archive][<<]]] | [[[#Capture-_002d-Refile-_002d-Archive][Up]]] | [[[#Agenda-Views][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 9.1 Capture + +Org’s method for capturing new items is heavily inspired by John Wiegley +excellent remember package. It lets you store quick notes with little +interruption of your work flow. Org lets you define templates for new +entries and associate them with different targets for storing notes. + +| [[#Setting-up-a-capture-location][Setting up a capture location]] | | Where notes will be stored | +| [[#Using-capture][Using capture]] | | Commands to invoke and terminate capture | +| [[#Capture-templates][Capture templates]] | | Define the outline of different note types | + +-------------- + +| [[[#Capture][<]]] | [[[#Using-capture][>]]] | | [[[#Capture-_002d-Refile-_002d-Archive][<<]]] | [[[#Capture][Up]]] | [[[#Agenda-Views][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +*** Setting up a capture location + +The following customization sets a default target[[#FOOT10][(10)]] file +for notes, and defines a global key[[#FOOT11][(11)]] for capturing new +stuff. + +#+BEGIN_EXAMPLE + (setq org-default-notes-file (concat org-directory "/notes.org")) + (define-key global-map "\C-cc" 'org-capture) +#+END_EXAMPLE + +-------------- + +| [[[#Setting-up-a-capture-location][<]]] | [[[#Capture-templates][>]]] | | [[[#Capture-_002d-Refile-_002d-Archive][<<]]] | [[[#Capture][Up]]] | [[[#Agenda-Views][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +*** Using capture + +- C-c c :: Start a capture process. You will be placed into a narrowed + indirect buffer to edit the item. + +- C-c C-c :: Once you are done entering information into the capture + buffer, C-c C-c will return you to the window configuration before + the capture process, so that you can resume your work without further + distraction. + +- C-c C-w :: Finalize by moving the entry to a refile location (see + section [[#Refiling-notes][Refiling notes]]). + +- C-c C-k :: Abort the capture process and return to the previous + state. + +-------------- + +| [[[#Using-capture][<]]] | [[[#Refiling-notes][>]]] | | [[[#Capture-_002d-Refile-_002d-Archive][<<]]] | [[[#Capture][Up]]] | [[[#Agenda-Views][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +*** Capture templates + +You can use templates to generate different types of capture notes, and +to store them in different places. For example, if you would like to +store new tasks under a heading ‘Tasks’ in file ‘=TODO.org=’, and +journal entries in a date tree in ‘=journal.org=’ you could use: + +#+BEGIN_EXAMPLE + (setq org-capture-templates + '(("t" "Todo" entry (file+headline "~/org/gtd.org" "Tasks") + "* TODO %?\n %i\n %a") + ("j" "Journal" entry (file+datetree "~/org/journal.org") + "* %?\nEntered on %U\n %i\n %a"))) +#+END_EXAMPLE + +In these entries, the first string is the key to reach the template, the +second is a short description. Then follows the type of the entry and a +definition of the target location for storing the note. Finally, the +template itself, a string with %-escapes to fill in information based on +time and context. + +When you call M-x org-capture, Org will prompt for a key to select the +template (if you have more than one template) and then prepare the +buffer like + +#+BEGIN_EXAMPLE + * TODO + [[file:link to where you were when initiating capture]] +#+END_EXAMPLE + +During expansion of the template, special %-escapes[[#FOOT12][(12)]] +allow dynamic insertion of content. Here is a small selection of the +possibilities, consult the manual for more. + +#+BEGIN_EXAMPLE + %a annotation, normally the link created with org-store-link + %i initial content, the region when remember is called with C-u. + %t timestamp, date only + %T timestamp with date and time + %u, %U like the above, but inactive timestamps +#+END_EXAMPLE + +-------------- + +| [[[#Capture-templates][<]]] | [[[#Archiving][>]]] | | [[[#Capture-_002d-Refile-_002d-Archive][<<]]] | [[[#Capture-_002d-Refile-_002d-Archive][Up]]] | [[[#Agenda-Views][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 9.2 Refiling notes + +When reviewing the captured data, you may want to refile some of the +entries into a different list, for example into a project. Cutting, +finding the right location, and then pasting the note is cumbersome. To +simplify this process, you can use the following special command: + +- C-c C-w :: Refile the entry or region at point. This command offers + possible locations for refiling the entry and lets you select one + with completion. The item (or all items in the region) is filed below + the target heading as a subitem. + By default, all level 1 headlines in the current buffer are + considered to be targets, but you can have more complex definitions + across a number of files. See the variable =org-refile-targets= for + details. + +- C-u C-c C-w :: Use the refile interface to jump to a heading. + +- C-u C-u C-c C-w :: Jump to the location where =org-refile= last + moved a tree to. + +-------------- + +| [[[#Refiling-notes][<]]] | [[[#Agenda-Views][>]]] | | [[[#Capture-_002d-Refile-_002d-Archive][<<]]] | [[[#Capture-_002d-Refile-_002d-Archive][Up]]] | [[[#Agenda-Views][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 9.3 Archiving + +When a project represented by a (sub)tree is finished, you may want to +move the tree out of the way and to stop it from contributing to the +agenda. Archiving is important to keep your working files compact and +global searches like the construction of agenda views fast. The most +common archiving action is to move a project tree to another file, the +archive file. + +- C-c C-x C-a :: Archive the current entry using the command specified + in the variable =org-archive-default-command=. + +- C-c C-x C-s or short C-c $ :: Archive the subtree starting at the + cursor position to the location given by =org-archive-location=. + +The default archive location is a file in the same directory as the +current file, with the name derived by appending ‘=_archive=’ to the +current file name. For information and examples on how to change this, +see the documentation string of the variable =org-archive-location=. +There is also an in-buffer option for setting this variable, for example + +#+BEGIN_EXAMPLE + #+ARCHIVE: %s_done:: +#+END_EXAMPLE + +*Further reading* +[[http://orgmode.org/manual/Capture-_002d-Refile-_002d-Archive.html#Capture-_002d-Refile-_002d-Archive][Chapter +9 of the manual]] + [[http://members.optusnet.com.au/~charles57/GTD/remember.html][Charles +Cave’s remember tutorial]] + +[[http://orgmode.org/worg/org-tutorials/org-protocol-custom-handler.php][Sebastian +Rose’s tutorial for capturing from a web browser]] + +-------------- + +| [[[#Archiving][<]]] | [[[#Agenda-files][>]]] | | [[[#Capture-_002d-Refile-_002d-Archive][<<]]] | [[[#Top][Up]]] | [[[#Markup][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 10. Agenda Views + +Due to the way Org works, TODO items, time-stamped items, and tagged +headlines can be scattered throughout a file or even a number of files. +To get an overview of open action items, or of events that are important +for a particular date, this information must be collected, sorted and +displayed in an organized way. There are several different views, see +below. + +The extracted information is displayed in a special /agenda buffer/. +This buffer is read-only, but provides commands to visit the +corresponding locations in the original Org files, and even to edit +these files remotely. Remote editing from the agenda buffer means, for +example, that you can change the dates of deadlines and appointments +from the agenda buffer. The commands available in the Agenda buffer are +listed in [[#Agenda-commands][Commands in the agenda buffer]]. + +| [[#Agenda-files][10.1 Agenda files]] | | Files being searched for agenda information | +| [[#Agenda-dispatcher][10.2 The agenda dispatcher]] | | Keyboard access to agenda views | +| [[#Built_002din-agenda-views][10.3 The built-in agenda views]] | | What is available out of the box? | +| [[#Agenda-commands][10.4 Commands in the agenda buffer]] | | Remote editing of Org trees | +| [[#Custom-agenda-views][10.5 Custom agenda views]] | | Defining special searches and views | + +-------------- + +| [[[#Agenda-Views][<]]] | [[[#Agenda-dispatcher][>]]] | | [[[#Agenda-Views][<<]]] | [[[#Agenda-Views][Up]]] | [[[#Markup][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 10.1 Agenda files + +The information to be shown is normally collected from all /agenda +files/, the files listed in the variable =org-agenda-files=. + +- C-c [ :: Add current file to the list of agenda files. The file is + added to the front of the list. If it was already in the list, it is + moved to the front. With a prefix argument, file is added/moved to + the end. + +- C-c ] :: Remove current file from the list of agenda files. + +- C-, :: Cycle through agenda file list, visiting one file after the + other. + +-------------- + +| [[[#Agenda-files][<]]] | [[[#Built_002din-agenda-views][>]]] | | [[[#Agenda-Views][<<]]] | [[[#Agenda-Views][Up]]] | [[[#Markup][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 10.2 The agenda dispatcher + +The views are created through a dispatcher, which should be bound to a +global key—for example C-c a (see section +[[#Installation][Installation]]). After pressing C-c a, an additional +letter is required to execute a command: + +- a :: The calendar-like agenda (see section + [[#Weekly_002fdaily-agenda][The weekly/daily agenda]]). + +- t / T :: A list of all TODO items (see section + [[#Global-TODO-list][The global TODO list]]). + +- m / M :: A list of headlines matching a TAGS expression (see section + [[#Matching-tags-and-properties][Matching tags and properties]]). + +- L :: The timeline view for the current buffer (see section + [[#Timeline][Timeline for a single file]]). + +- s :: A list of entries selected by a boolean expression of keywords + and/or regular expressions that must or must not occur in the entry. + +-------------- + +| [[[#Agenda-dispatcher][<]]] | [[[#Weekly_002fdaily-agenda][>]]] | | [[[#Agenda-Views][<<]]] | [[[#Agenda-Views][Up]]] | [[[#Markup][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 10.3 The built-in agenda views + +| [[#Weekly_002fdaily-agenda][10.3.1 The weekly/daily agenda]] | | The calendar page with current tasks | +| [[#Global-TODO-list][10.3.2 The global TODO list]] | | All unfinished action items | +| [[#Matching-tags-and-properties][10.3.3 Matching tags and properties]] | | Structured information with fine-tuned search | +| [[#Timeline][10.3.4 Timeline for a single file]] | | Time-sorted view for single file | +| [[#Search-view][10.3.5 Search view]] | | Find entries by searching for text | + +-------------- + +| [[[#Built_002din-agenda-views][<]]] | [[[#Global-TODO-list][>]]] | | [[[#Agenda-Views][<<]]] | [[[#Built_002din-agenda-views][Up]]] | [[[#Markup][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +*** 10.3.1 The weekly/daily agenda + +The purpose of the weekly/daily /agenda/ is to act like a page of a +paper agenda, showing all the tasks for the current week or day. + +- C-c a a :: Compile an agenda for the current week from a list of Org + files. The agenda shows the entries for each day. + +Emacs contains the calendar and diary by Edward M. Reingold. Org-mode +understands the syntax of the diary and allows you to use diary sexp +entries directly in Org files: + +#+BEGIN_EXAMPLE + * Birthdays and similar stuff + #+CATEGORY: Holiday + %%(org-calendar-holiday) ; special function for holiday names + #+CATEGORY: Ann + %%(diary-anniversary 5 14 1956)(13) Arthur Dent is %d years old + %%(diary-anniversary 10 2 1869) Mahatma Gandhi would be %d years old +#+END_EXAMPLE + +Org can interact with Emacs appointments notification facility. To add +all the appointments of your agenda files, use the command +=org-agenda-to-appt=. See the docstring for details. + +-------------- + +| [[[#Weekly_002fdaily-agenda][<]]] | [[[#Matching-tags-and-properties][>]]] | | [[[#Agenda-Views][<<]]] | [[[#Built_002din-agenda-views][Up]]] | [[[#Markup][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +*** 10.3.2 The global TODO list + +The global TODO list contains all unfinished TODO items formatted and +collected into a single place. Remote editing of TODO items lets you can +change the state of a TODO entry with a single key press. The commands +available in the TODO list are described in [[#Agenda-commands][Commands +in the agenda buffer]]. + +- C-c a t :: Show the global TODO list. This collects the TODO items + from all agenda files (see section [[#Agenda-Views][Agenda Views]]) + into a single buffer. + +- C-c a T :: Like the above, but allows selection of a specific TODO + keyword. + +-------------- + +| [[[#Global-TODO-list][<]]] | [[[#Timeline][>]]] | | [[[#Agenda-Views][<<]]] | [[[#Built_002din-agenda-views][Up]]] | [[[#Markup][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +*** 10.3.3 Matching tags and properties + +If headlines in the agenda files are marked with /tags/ (see section +[[#Tags][Tags]]), or have properties (see section +[[#Properties][Properties]]), you can select headlines based on this +metadata and collect them into an agenda buffer. The match syntax +described here also applies when creating sparse trees with C-c / m. The +commands available in the tags list are described in +[[#Agenda-commands][Commands in the agenda buffer]]. + +- C-c a m :: Produce a list of all headlines that match a given set of + tags. The command prompts for a selection criterion, which is a + boolean logic expression with tags, like ‘+work+urgent-withboss’ or + ‘work|home’ (see section [[#Tags][Tags]]). If you often need a + specific search, define a custom command for it (see section + [[#Agenda-dispatcher][The agenda dispatcher]]). + +- C-c a M :: Like C-c a m, but only select headlines that are also + TODO items. + +**** Match syntax + +A search string can use Boolean operators ‘&’ for AND and ‘|’ for OR. +‘&’ binds more strongly than ‘|’. Parentheses are currently not +implemented. Each element in the search is either a tag, a regular +expression matching tags, or an expression like +=PROPERTY OPERATOR VALUE= with a comparison operator, accessing a +property value. Each element may be preceded by ‘-’, to select against +it, and ‘+’ is syntactic sugar for positive selection. The AND operator +‘&’ is optional when ‘+’ or ‘-’ is present. Here are some examples, +using only tags. + +- ‘+work-boss’ :: Select headlines tagged ‘:work:’, but discard those + also tagged ‘:boss:’. + +- ‘work|laptop’ :: Selects lines tagged ‘:work:’ or ‘:laptop:’. + +- ‘work|laptop+night’ :: Like before, but require the ‘:laptop:’ lines + to be tagged also ‘:night:’. + +You may also test for properties at the same time as matching tags, see +the manual for more information. + +-------------- + +| [[[#Matching-tags-and-properties][<]]] | [[[#Search-view][>]]] | | [[[#Agenda-Views][<<]]] | [[[#Built_002din-agenda-views][Up]]] | [[[#Markup][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +*** 10.3.4 Timeline for a single file + +The timeline summarizes all time-stamped items from a single Org mode +file in a /time-sorted view/. The main purpose of this command is to +give an overview over events in a project. + +- C-c a L :: Show a time-sorted view of the Org file, with all + time-stamped items. When called with a C-u prefix, all unfinished + TODO entries (scheduled or not) are also listed under the current + date. + +-------------- + +| [[[#Timeline][<]]] | [[[#Agenda-commands][>]]] | | [[[#Agenda-Views][<<]]] | [[[#Built_002din-agenda-views][Up]]] | [[[#Markup][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +*** 10.3.5 Search view + +This agenda view is a general text search facility for Org mode entries. +It is particularly useful to find notes. + +- C-c a s :: This is a special search that lets you select entries by + matching a substring or specific words using a boolean logic. + +For example, the search string ‘computer equipment’ will find entries +that contain ‘computer equipment’ as a substring. Search view can also +search for specific keywords in the entry, using Boolean logic. The +search string ‘+computer +wifi -ethernet -{8\.11[bg]}’ will search for +note entries that contain the keywords =computer= and =wifi=, but not +the keyword =ethernet=, and which are also not matched by the regular +expression =8\.11[bg]=, meaning to exclude both 8.11b and 8.11g. + +Note that in addition to the agenda files, this command will also search +the files listed in =org-agenda-text-search-extra-files=. + +-------------- + +| [[[#Search-view][<]]] | [[[#Custom-agenda-views][>]]] | | [[[#Agenda-Views][<<]]] | [[[#Agenda-Views][Up]]] | [[[#Markup][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 10.4 Commands in the agenda buffer + +Entries in the agenda buffer are linked back to the Org file or diary +file where they originate. Commands are provided to show and jump to the +original entry location, and to edit the Org files “remotely” from the +agenda buffer. This is just a selection of the many commands, explore +the =Agenda= menu and the manual for a complete list. + +- *Motion* + n :: Next line (same as and C-p). + +- p :: Previous line (same as and C-n). + +- *View/Go to Org file* + mouse-3 + :: Display the original location of the item in another + window. With prefix arg, make sure that the entire entry is made + visible in the outline, not only the heading. + +- :: Go to the original location of the item in another window. + Under Emacs 22, mouse-1 will also works for this. + +- :: Go to the original location of the item and delete other + windows. + +- *Change display* + o :: Delete other windows. + +- d / w :: Switch to day/week view. + +- f and b :: Go forward/backward in time to display the following + =org-agenda-current-span= days. For example, if the display covers a + week, switch to the following/previous week. + +- . :: Go to today. + +- j :: Prompt for a date and go there. + +- v l or short l :: Toggle Logbook mode. In Logbook mode, entries that + were marked DONE while logging was on (variable =org-log-done=) are + shown in the agenda, as are entries that have been clocked on that + day. When called with a C-u prefix, show all possible logbook + entries, including state changes. + +- r or g :: Recreate the agenda buffer, to reflect the changes. + +- s :: Save all Org buffers in the current Emacs session, and also the + locations of IDs. + +- *Secondary filtering and query editing* + / :: Filter the current agenda view with respect to a tag. You are + prompted for a letter to select a tag. Press ‘-’ first to select + against the tag. + +- \ :: Narrow the current agenda filter by an additional condition. + +- *Remote editing (see the manual for many more commands)* + 0-9 :: Digit argument. + +- t :: Change the TODO state of the item, in the agenda and in the org + file. + +- C-k :: Delete the current agenda item along with the entire subtree + belonging to it in the original Org file. + +- C-c C-w :: Refile the entry at point. + +- C-c C-x C-a or short a :: Archive the subtree corresponding to the + entry at point using the default archiving command set in + =org-archive-default-command=. + +- C-c C-x C-s or short $ :: Archive the subtree corresponding to the + current headline. + +- C-c C-s :: Schedule this item, with prefix arg remove the scheduling + timestamp + +- C-c C-d :: Set a deadline for this item, with prefix arg remove the + deadline. + +- S- and S- :: Change the timestamp associated with the + current line by one day. + +- I :: Start the clock on the current item. + +- O / X :: Stop/cancel the previously started clock. + +- J :: Jump to the running clock in another window. + +-------------- + +| [[[#Agenda-commands][<]]] | [[[#Markup][>]]] | | [[[#Agenda-Views][<<]]] | [[[#Agenda-Views][Up]]] | [[[#Markup][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 10.5 Custom agenda views + +The main application of custom searches is the definition of keyboard +shortcuts for frequently used searches, either creating an agenda +buffer, or a sparse tree (the latter covering of course only the current +buffer). Custom commands are configured in the variable +=org-agenda-custom-commands=. You can customize this variable, for +example by pressing C-c a C. You can also directly set it with Emacs +Lisp in ‘=.emacs=’. The following example contains all valid search +types: + +#+BEGIN_EXAMPLE + (setq org-agenda-custom-commands + '(("w" todo "WAITING") + ("u" tags "+boss-urgent") + ("v" tags-todo "+boss-urgent"))) +#+END_EXAMPLE + +The initial string in each entry defines the keys you have to press +after the dispatcher command C-c a in order to access the command. +Usually this will be just a single character. The second parameter is +the search type, followed by the string or regular expression to be used +for the matching. The example above will therefore define: + +- C-c a w :: as a global search for TODO entries with ‘WAITING’ as the + TODO keyword + +- C-c a u :: as a global tags search for headlines marked ‘:boss:’ but + not ‘:urgent:’ + +- C-c a v :: as the same search as C-c a u, but limiting the search to + headlines that are also TODO items + +*Further reading* +[[http://orgmode.org/manual/Agenda-Views.html#Agenda-Views][Chapter 10 +of the manual]] + +[[http://orgmode.org/worg/org-tutorials/org-custom-agenda-commands.php][Mat +Lundin’s tutorial about custom agenda commands]] + +[[http://www.newartisans.com/2007/08/using-org-mode-as-a-day-planner.html][John +Wiegley’s setup]] + +-------------- + +| [[[#Custom-agenda-views][<]]] | [[[#Structural-markup-elements][>]]] | | [[[#Agenda-Views][<<]]] | [[[#Top][Up]]] | [[[#Exporting][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 11. Markup for rich export + +When exporting Org-mode documents, the exporter tries to reflect the +structure of the document as accurately as possible in the backend. +Since export targets like HTML, LaTeX, or DocBook allow much richer +formatting, Org mode has rules on how to prepare text for rich export. +This section summarizes the markup rules used in an Org-mode buffer. + +| [[#Structural-markup-elements][11.1 Structural markup elements]] | | The basic structure as seen by the exporter | +| [[#Images-and-tables][11.2 Images and Tables]] | | Tables and Images will be included | +| [[#Literal-examples][11.3 Literal examples]] | | Source code examples with special formatting | +| [[#Include-files][11.4 Include files]] | | Include additional files into a document | +| [[#Embedded-LaTeX][11.5 Embedded LaTeX]] | | LaTeX can be freely used inside Org documents | + +-------------- + +| [[[#Markup][<]]] | [[[#Images-and-tables][>]]] | | [[[#Markup][<<]]] | [[[#Markup][Up]]] | [[[#Exporting][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 11.1 Structural markup elements + +| [[#Document-title][• Document title]] | | Where the title is taken from | +| [[#Headings-and-sections][• Headings and sections]] | | The document structure as seen by the exporter | +| [[#Table-of-contents][• Table of contents]] | | The if and where of the table of contents | +| [[#Paragraphs][• Paragraphs]] | | | +| [[#Emphasis-and-monospace][• Emphasis and monospace]] | | Bold, italic, etc. | +| [[#Comment-lines][• Comment lines]] | | What will *not* be exported | + +*** Document title + +The title of the exported document is taken from the special line + +#+BEGIN_EXAMPLE + #+TITLE: This is the title of the document +#+END_EXAMPLE + +*** Headings and sections + +The outline structure of the document as described in +[[#Document-Structure][Document Structure]], forms the basis for +defining sections of the exported document. However, since the outline +structure is also used for (for example) lists of tasks, only the first +three outline levels will be used as headings. Deeper levels will become +itemized lists. You can change the location of this switch globally by +setting the variable =org-export-headline-levels=, or on a per-file +basis with a line + +#+BEGIN_EXAMPLE + #+OPTIONS: H:4 +#+END_EXAMPLE + +*** Table of contents + +The table of contents is normally inserted directly before the first +headline of the file. + +#+BEGIN_EXAMPLE + #+OPTIONS: toc:2 (only to two levels in TOC) + #+OPTIONS: toc:nil (no TOC at all) +#+END_EXAMPLE + +*** Paragraphs, line breaks, and quoting + +Paragraphs are separated by at least one empty line. If you need to +enforce a line break within a paragraph, use ‘\\’ at the end of a line. + +To keep the line breaks in a region, but otherwise use normal +formatting, you can use this construct, which can also be used to format +poetry. + +#+BEGIN_EXAMPLE + #+BEGIN_VERSE + Great clouds overhead + Tiny black birds rise and fall + Snow covers Emacs + + -- AlexSchroeder + #+END_VERSE +#+END_EXAMPLE + +When quoting a passage from another document, it is customary to format +this as a paragraph that is indented on both the left and the right +margin. You can include quotations in Org-mode documents like this: + +#+BEGIN_EXAMPLE + #+BEGIN_QUOTE + Everything should be made as simple as possible, + but not any simpler -- Albert Einstein + #+END_QUOTE +#+END_EXAMPLE + +If you would like to center some text, do it like this: + +#+BEGIN_EXAMPLE + #+BEGIN_CENTER + Everything should be made as simple as possible, \\ + but not any simpler + #+END_CENTER +#+END_EXAMPLE + +*** Emphasis and monospace + +You can make words **bold**, //italic//, \_underlined\_, ==code== and +=~verbatim~=, and, if you must, ‘+strike-through+’. Text in the code and +verbatim string is not processed for Org-mode specific syntax, it is +exported verbatim. To insert a horizontal rules, use a line consisting +of only dashes, and at least 5 of them. + +*** Comment lines + +Lines starting with ‘#’ in column zero are treated as comments and will +never be exported. If you want an indented line to be treated as a +comment, start it with ‘#+ ’. Also entire subtrees starting with the +word ‘COMMENT’ will never be exported. Finally, regions surrounded by +‘#+BEGIN\_COMMENT’ ... ‘#+END\_COMMENT’ will not be exported. + +- C-c ; :: Toggle the COMMENT keyword at the beginning of an entry. + +-------------- + +| [[[#Structural-markup-elements][<]]] | [[[#Literal-examples][>]]] | | [[[#Markup][<<]]] | [[[#Markup][Up]]] | [[[#Exporting][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 11.2 Images and Tables + +For Org mode tables, the lines before the first horizontal separator +line will become table header lines. You can use the following lines +somewhere before the table to assign a caption and a label for cross +references, and in the text you can refer to the object with +=\ref{tab:basic-data}=: + +#+BEGIN_EXAMPLE + #+CAPTION: This is the caption for the next table (or link) + #+LABEL: tbl:basic-data + | ... | ...| + |-----|----| +#+END_EXAMPLE + +Some backends (HTML, LaTeX, and DocBook) allow you to directly include +images into the exported document. Org does this, if a link to an image +files does not have a description part, for example =[[./img/a.jpg]]=. +If you wish to define a caption for the image and maybe a label for +internal cross references, you sure that the link is on a line by itself +precede it with: + +#+BEGIN_EXAMPLE + #+CAPTION: This is the caption for the next figure link (or table) + #+LABEL: fig:SED-HR4049 + [[./img/a.jpg]] +#+END_EXAMPLE + +You may also define additional attributes for the figure. As this is +backend-specific, see the sections about the individual backends for +more information. + +-------------- + +| [[[#Images-and-tables][<]]] | [[[#Include-files][>]]] | | [[[#Markup][<<]]] | [[[#Markup][Up]]] | [[[#Exporting][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 11.3 Literal examples + +You can include literal examples that should not be subjected to markup. +Such examples will be typeset in monospace, so this is well suited for +source code and similar examples. + +#+BEGIN_EXAMPLE + #+BEGIN_EXAMPLE + Some example from a text file. + #+END_EXAMPLE +#+END_EXAMPLE + +For simplicity when using small examples, you can also start the example +lines with a colon followed by a space. There may also be additional +whitespace before the colon: + +#+BEGIN_EXAMPLE + Here is an example + : Some example from a text file. +#+END_EXAMPLE + +For source code from a programming language, or any other text that can +be marked up by font-lock in Emacs, you can ask for it to look like the +fontified Emacs buffer + +#+BEGIN_EXAMPLE + #+BEGIN_SRC emacs-lisp + (defun org-xor (a b) + "Exclusive or." + (if a (not b) b)) + #+END_SRC +#+END_EXAMPLE + +To edit the example in a special buffer supporting this language, use +C-c ' to both enter and leave the editing buffer. + +-------------- + +| [[[#Literal-examples][<]]] | [[[#Embedded-LaTeX][>]]] | | [[[#Markup][<<]]] | [[[#Markup][Up]]] | [[[#Exporting][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 11.4 Include files + +During export, you can include the content of another file. For example, +to include your ‘=.emacs=’ file, you could use: + +#+BEGIN_EXAMPLE + #+INCLUDE: "~/.emacs" src emacs-lisp +#+END_EXAMPLE + +The optional second and third parameter are the markup (e.g. ‘quote’, +‘example’, or ‘src’), and, if the markup is ‘src’, the language for +formatting the contents. The markup is optional, if it is not given, the +text will be assumed to be in Org mode format and will be processed +normally. C-c ' will visit the included file. + +-------------- + +| [[[#Include-files][<]]] | [[[#Exporting][>]]] | | [[[#Markup][<<]]] | [[[#Markup][Up]]] | [[[#Exporting][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 11.5 Embedded LaTeX + +For scientific notes which need to be able to contain mathematical +symbols and the occasional formula, Org-mode supports embedding LaTeX +code into its files. You can directly use TeX-like macros for special +symbols, enter formulas and entire LaTeX environments. + +#+BEGIN_EXAMPLE + Angles are written as Greek letters \alpha, \beta and \gamma. The mass if + the sun is M_sun = 1.989 x 10^30 kg. The radius of the sun is R_{sun} = + 6.96 x 10^8 m. If $a^2=b$ and $b=2$, then the solution must be either + $a=+\sqrt{2}$ or $a=-\sqrt{2}$. + + \begin{equation} + x=\sqrt{b} + \end{equation} +#+END_EXAMPLE + +With +[[http://orgmode.org/manual/LaTeX-fragments.html#LaTeX-fragments][special +setup]], LaTeX snippets will be included as images when exporting to +HTML. + +*Further reading* +[[http://orgmode.org/manual/Markup.html#Markup][Chapter 11 of the +manual]] + +-------------- + +| [[[#Embedded-LaTeX][<]]] | [[[#Export-options][>]]] | | [[[#Markup][<<]]] | [[[#Top][Up]]] | [[[#Publishing][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 12. Exporting + +Org-mode documents can be exported into a variety of other formats: +ASCII export for inclusion into emails, HTML to publish on the web, +LaTeX/PDF for beautiful printed documents and DocBook to enter the world +of many other formats using DocBook tools. There is also export to +iCalendar format so that planning information can be incorporated into +desktop calendars. + +| [[#Export-options][12.1 Export options]] | | Per-file export settings | +| [[#The-export-dispatcher][12.2 The export dispatcher]] | | How to access exporter commands | +| [[#ASCII_002fLatin_002d1_002fUTF_002d8-export][12.3 ASCII/Latin-1/UTF-8 export]] | | Exporting to flat files with encoding | +| [[#HTML-export][12.4 HTML export]] | | Exporting to HTML | +| [[#LaTeX-and-PDF-export][12.5 LaTeX and PDF export]] | | Exporting to LaTeX, and processing to PDF | +| [[#DocBook-export][12.6 DocBook export]] | | Exporting to DocBook | +| [[#iCalendar-export][12.7 iCalendar export]] | | | + +-------------- + +| [[[#Exporting][<]]] | [[[#The-export-dispatcher][>]]] | | [[[#Exporting][<<]]] | [[[#Exporting][Up]]] | [[[#Publishing][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 12.1 Export options + +The exporter recognizes special lines in the buffer which provide +additional information. These lines may be put anywhere in the file. The +whole set of lines can be inserted into the buffer with C-c C-e t. + +- C-c C-e t :: Insert template with export options, see example below. + +#+BEGIN_EXAMPLE + #+TITLE: the title to be shown (default is the buffer name) + #+AUTHOR: the author (default taken from user-full-name) + #+DATE: a date, fixed, of a format string for format-time-string + #+EMAIL: his/her email address (default from user-mail-address) + #+DESCRIPTION: the page description, e.g. for the XHTML meta tag + #+KEYWORDS: the page keywords, e.g. for the XHTML meta tag + #+LANGUAGE: language for HTML, e.g. ‘en’ (org-export-default-language) + #+TEXT: Some descriptive text to be inserted at the beginning. + #+TEXT: Several lines may be given. + #+OPTIONS: H:2 num:t toc:t \n:nil @:t ::t |:t ^:t f:t TeX:t ... + #+LINK_UP: the ``up'' link of an exported page + #+LINK_HOME: the ``home'' link of an exported page + #+LATEX_HEADER: extra line(s) for the LaTeX header, like \usepackage{xyz} +#+END_EXAMPLE + +-------------- + +| [[[#Export-options][<]]] | [[[#ASCII_002fLatin_002d1_002fUTF_002d8-export][>]]] | | [[[#Exporting][<<]]] | [[[#Exporting][Up]]] | [[[#Publishing][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 12.2 The export dispatcher + +All export commands can be reached using the export dispatcher, which is +a prefix key that prompts for an additional key specifying the command. +Normally the entire file is exported, but if there is an active region +that contains one outline tree, the first heading is used as document +title and the subtrees are exported. + +- C-c C-e :: Dispatcher for export and publishing commands. + +-------------- + +| [[[#The-export-dispatcher][<]]] | [[[#HTML-export][>]]] | | [[[#Exporting][<<]]] | [[[#Exporting][Up]]] | [[[#Publishing][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 12.3 ASCII/Latin-1/UTF-8 export + +ASCII export produces a simple and very readable version of an Org-mode +file, containing only plain ASCII. Latin-1 and UTF-8 export augment the +file with special characters and symbols available in these encodings. + +- C-c C-e a :: Export as ASCII file. + +- C-c C-e n and C-c C-e N :: Like the above commands, but use Latin-1 + encoding. + +- C-c C-e u and C-c C-e U :: Like the above commands, but use UTF-8 + encoding. + +-------------- + +| [[[#ASCII_002fLatin_002d1_002fUTF_002d8-export][<]]] | [[[#LaTeX-and-PDF-export][>]]] | | [[[#Exporting][<<]]] | [[[#Exporting][Up]]] | [[[#Publishing][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 12.4 HTML export + +- C-c C-e h :: Export as HTML file ‘=myfile.html=’. + +- C-c C-e b :: Export as HTML file and immediately open it with a + browser. + +To insert HTML that should be copied verbatim to the exported file use +either + +#+BEGIN_EXAMPLE + #+HTML: Literal HTML code for export +#+END_EXAMPLE + +or + +#+BEGIN_EXAMPLE + #+BEGIN_HTML + All lines between these markers are exported literally + #+END_HTML +#+END_EXAMPLE + +-------------- + +| [[[#HTML-export][<]]] | [[[#DocBook-export][>]]] | | [[[#Exporting][<<]]] | [[[#Exporting][Up]]] | [[[#Publishing][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 12.5 LaTeX and PDF export + +- C-c C-e l :: Export as LaTeX file ‘=myfile.tex=’. + +- C-c C-e p :: Export as LaTeX and then process to PDF. + +- C-c C-e d :: Export as LaTeX and then process to PDF, then open the + resulting PDF file. + +By default, the LaTeX output uses the class =article=. You can change +this by adding an option like =#+LaTeX_CLASS: myclass= in your file. The +class must be listed in =org-export-latex-classes=. + +Embedded LaTeX as described in [[#Embedded-LaTeX][Embedded LaTeX]], will +be correctly inserted into the LaTeX file. Similarly to the HTML +exporter, you can use =#+LaTeX:= and =#+BEGIN_LaTeX ... #+END_LaTeX= +construct to add verbatim LaTeX code. + +-------------- + +| [[[#LaTeX-and-PDF-export][<]]] | [[[#iCalendar-export][>]]] | | [[[#Exporting][<<]]] | [[[#Exporting][Up]]] | [[[#Publishing][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 12.6 DocBook export + +- C-c C-e D :: Export as DocBook file. + +Similarly to the HTML exporter, you can use =#+DocBook:= and +=#+BEGIN_DocBook ... #+END_DocBook= construct to add verbatim LaTeX +code. + +-------------- + +| [[[#DocBook-export][<]]] | [[[#Publishing][>]]] | | [[[#Exporting][<<]]] | [[[#Exporting][Up]]] | [[[#Publishing][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 12.7 iCalendar export + +- C-c C-e i :: Create iCalendar entries for the current file in a + ‘=.ics=’ file. + +- C-c C-e c :: Create a single large iCalendar file from all files in + =org-agenda-files= and write it to the file given by + =org-combined-agenda-icalendar-file=. + +*Further reading* +[[http://orgmode.org/manual/Exporting.html#Exporting][Chapter 12 of the +manual]] + +[[http://orgmode.org/worg/org-tutorials/images-and-xhtml-export.php][Sebastian +Rose’s image handling tutorial]] + [[http://orgmode.org/worg/org-tutorials/org-latex-export.php][Thomas +Dye’s LaTeX export tutorial]] +[[http://orgmode.org/worg/org-tutorials/org-beamer/tutorial.php][Eric +Fraga’s BEAMER presentation tutorial]] + +-------------- + +| [[[#iCalendar-export][<]]] | [[[#Working-With-Source-Code][>]]] | | [[[#Exporting][<<]]] | [[[#Top][Up]]] | [[[#Working-With-Source-Code][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 13. Publishing + +Org includes a publishing management system that allows you to configure +automatic HTML conversion of /projects/ composed of interlinked org +files. You can also configure Org to automatically upload your exported +HTML pages and related attachments, such as images and source code +files, to a web server. For detailed instructions about setup, see the +manual. + +Here is an example: + +#+BEGIN_EXAMPLE + (setq org-publish-project-alist + '(("org" + :base-directory "~/org/" + :publishing-directory "~/public_html" + :section-numbers nil + :table-of-contents nil + :style ""))) +#+END_EXAMPLE + +- C-c C-e C :: Prompt for a specific project and publish all files + that belong to it. + +- C-c C-e P :: Publish the project containing the current file. + +- C-c C-e F :: Publish only the current file. + +- C-c C-e E :: Publish every project. + +Org uses timestamps to track when a file has changed. The above +functions normally only publish changed files. You can override this and +force publishing of all files by giving a prefix argument to any of the +commands above. + +*Further reading* +[[http://orgmode.org/manual/Publishing.html#Publishing][Chapter 13 of +the manual]] + +[[http://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.php][Sebastian +Rose’s publishing tutorial]] + [[http://orgmode.org/worg/org-tutorials/org-jekyll.php][Ian Barton’s +Jekyll/blogging setup]] + +-------------- + +| [[[#Publishing][<]]] | [[[#Miscellaneous][>]]] | | [[[#Publishing][<<]]] | [[[#Top][Up]]] | [[[#Miscellaneous][>>]]] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 14. Working with source code + +Org-mode provides a number of features for working with source code, +including editing of code blocks in their native major-mode, evaluation +of code blocks, tangling of code blocks, and exporting code blocks and +their results in several formats. + +*** Structure of Code Blocks + +The structure of code blocks is as follows: + +#+BEGIN_EXAMPLE + #+srcname: + #+begin_src
+ + #+end_src +#+END_EXAMPLE + +Where == is a string used to name the code block, == +specifies the language of the code block (e.g. =emacs-lisp=, =shell=, +=R=, =python=, etc...), == can be used to control export of +the code block, =
= can be used to control many aspects +of code block behavior as demonstrated below, and == contains the +actual source code. + +*** Editing source code + +Use C-c ' to edit the current code block. This brings up a language +major-mode edit buffer containing the body of the code block. Saving +this buffer will write the new contents back to the Org buffer. Use C-c +' again to exit the edit buffer. + +*** Evaluating code blocks + +Use C-c C-c to evaluate the current code block and insert its results in +the Org-mode buffer. By default, evaluation is only turned on for +=emacs-lisp= code blocks, however support exists for evaluating blocks +in many languages. For a complete list of supported languages see the +manual. The following shows a code block and its results. + +#+BEGIN_EXAMPLE + #+begin_src emacs-lisp + (+ 1 2 3 4) + #+end_src + + #+results: + : 10 +#+END_EXAMPLE + +*** Extracting source code + +Use C-c C-v t to create pure source code files by extracting code from +source blocks in the current buffer. This is referred to as “tangling”—a +term adopted from the literate programming community. During “tangling” +of code blocks their bodies are expanded using +=org-babel-expand-src-block= which can expand both variable and “noweb” +style references. In order to tangle a code block it must have a +=:tangle= header argument, see the manual for details. + +*** Library of Babel + +Use C-c C-v l to load the code blocks from an Org-mode files into the +“Library of Babel”, these blocks can then be evaluated from any Org-mode +buffer. A collection of generally useful code blocks is distributed with +Org-mode in =contrib/library-of-babel.org=. + +*** Header Arguments + +Many aspects of the evaluation and export of code blocks are controlled +through header arguments. These can be specified globally, at the file +level, at the outline subtree level, and at the individual code block +level. The following describes some of the header arguments. + +- =:var= :: The =:var= header argument is used to pass arguments to + code blocks. The values passed to arguments can be literal values, + values from org-mode tables and literal example blocks, or the + results of other named code blocks. + +- =:results= :: The =:results= header argument controls the + /collection/, /type/, and /handling/ of code block results. Values of + =output= or =value= (the default) specify how results are collected + from a code block’s evaluation. Values of =vector=, =scalar= =file= + =raw= =html= =latex= and =code= specify the type of the results of + the code block which dictates how they will be incorporated into the + Org-mode buffer. Values of =silent=, =replace=, =prepend=, and + =append= specify handling of code block results, specifically if and + how the results should be inserted into the Org-mode buffer. + +- =:session= :: A header argument of =:session= will cause the code + block to be evaluated in a persistent interactive inferior process in + Emacs. This allows for persisting state between code block + evaluations, and for manual inspection of the results of evaluation. + +- =:exports= :: Any combination of the /code/ or the /results/ of a + block can be retained on export, this is specified by setting the + =:results= header argument to =code= =results= =none= or =both=. + +- =:tangle= :: A header argument of =:tangle yes= will cause a code + block’s contents to be tangled to a file named after the filename of + the Org-mode buffer. An alternate file name can be specified with + =:tangle filename=. + +- =:cache= :: A header argument of =:cache yes= will cause associate a + hash of the expanded code block with the results, ensuring that code + blocks are only re-run when their inputs have changed. + +- =:noweb= :: A header argument of =:noweb yes= will expand “noweb” + style references on evaluation and tangling. + +- =:file= :: Code blocks which output results to files (e.g. graphs, + diagrams and figures) can accept a =:file filename= header argument + in which case the results are saved to the named file, and a link to + the file is inserted into the Org-mode buffer. + +*Further reading* +[[http://orgmode.org/manual/Literal-examples.html#Literal-examples][Chapter +11.3 of the manual]] + [[http://orgmode.org/worg/org-contrib/babel/index.php][The Babel site +on Worg]] + +-------------- + +| [[[#Working-With-Source-Code][<]]] | [[[#Completion][>]]] | | [[[#Working-With-Source-Code][<<]]] | [[[#Top][Up]]] | [ >> ] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* 15. Miscellaneous + +| [[#Completion][15.1 Completion]] | | M-TAB knows what you need | +| [[#Clean-view][15.2 A cleaner outline view]] | | Getting rid of leading stars in the outline | +| [[#MobileOrg][15.3 MobileOrg]] | | Org-mode on the iPhone | + +-------------- + +| [[[#Miscellaneous][<]]] | [[[#Clean-view][>]]] | | [[[#Miscellaneous][<<]]] | [[[#Miscellaneous][Up]]] | [ >> ] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 15.1 Completion + +Org supports in-buffer completion with M-. This type of completion +does not make use of the minibuffer. You simply type a few letters into +the buffer and use the key to complete text right there. For example, +this command will complete TeX symbols after ‘\’, TODO keywords at the +beginning of a headline, and tags after ‘:’ in a headline. + +-------------- + +| [[[#Completion][<]]] | [[[#MobileOrg][>]]] | | [[[#Miscellaneous][<<]]] | [[[#Miscellaneous][Up]]] | [ >> ] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 15.2 A cleaner outline view + +Some people find it noisy and distracting that the Org headlines start +with a potentially large number of stars, and that text below the +headlines is not indented. While this is no problem when writing a +/book-like/ document where the outline headings are really section +headings, in a more /list-oriented/ outline, indented structure is a lot +cleaner: + +#+BEGIN_EXAMPLE + * Top level headline | * Top level headline + ** Second level | * Second level + *** 3rd level | * 3rd level + some text | some text + *** 3rd level | * 3rd level + more text | more text + * Another top level headline | * Another top level headline +#+END_EXAMPLE + +If you are using at least Emacs 23.1.50.3 and version 6.29 of Org, this +kind of view can be achieved dynamically at display time using +=org-indent-mode=, which will prepend intangible space to each line. You +can turn on =org-indent-mode= for all files by customizing the variable +=org-startup-indented=, or you can turn it on for individual files using + +#+BEGIN_EXAMPLE + #+STARTUP: indent +#+END_EXAMPLE + +If you want a similar effect in earlier version of Emacs and/or Org, or +if you want the indentation to be hard space characters so that the +plain text file looks as similar as possible to the Emacs display, Org +supports you by helping to indent (with ) text below each headline, +by hiding leading stars, and by only using levels 1, 3, etc to get two +characters indentation for each level. To get this support in a file, +use + +#+BEGIN_EXAMPLE + #+STARTUP: hidestars odd +#+END_EXAMPLE + +-------------- + +| [[[#Clean-view][<]]] | [ > ] | | [[[#Miscellaneous][<<]]] | [[[#Miscellaneous][Up]]] | [ >> ] | | | | | [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +** 15.3 MobileOrg + +/MobileOrg/ is an application originally developed for the /iPhone/iPod +Touch/ series of devices, developed by Richard Moreland. There is also +an independent implementation for Android devices, by Matt Jones. For +details, see the Org-mode manual. + +*Further reading* +[[http://orgmode.org/manual/Miscellaneous.html#Miscellaneous][Chapter 15 +of the manual]] + [[http://orgmode.org/manual/MobileOrg.html#MobileOrg][Appendix B of the +manual]] + [[http://orgmode.org/orgcard.pdf][Key reference card]] + +-------------- + +| [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* Footnotes + +*** [[#DOCF1][(1)]] + +See the variable =org-special-ctrl-a/e= to configure special behavior of +C-a and C-e in headlines. + +*** [[#DOCF2][(2)]] + +If you do not want the line to be split, customize the variable +=org-M-RET-may-split-line=. + +*** [[#DOCF3][(3)]] + +See also the variables =org-show-hierarchy-above=, +=org-show-following-heading=, =org-show-siblings=, and +=org-show-entry-below= for detailed control on how much context is shown +around each match. + +*** [[#DOCF4][(4)]] + +Of course, you can make a document that contains only long lists of TODO +items, but this is not required. + +*** [[#DOCF5][(5)]] + +The corresponding in-buffer setting is: =#+STARTUP: logdone= + +*** [[#DOCF6][(6)]] + +The corresponding in-buffer setting is: =#+STARTUP: lognotedone= + +*** [[#DOCF7][(7)]] + +As with all these in-buffer settings, pressing C-c C-c activates any +changes in the line. + +*** [[#DOCF8][(8)]] + +This is quite different from what is normally understood by /scheduling +a meeting/, which is done in Org-mode by just inserting a time stamp +without keyword. + +*** [[#DOCF9][(9)]] + +It will still be listed on that date after it has been marked DONE. If +you don’t like this, set the variable +=org-agenda-skip-scheduled-if-done=. + +*** [[#DOCF10][(10)]] + +Using capture templates, you can define more fine-grained capture +locations, see [[#Capture-templates][Capture templates]]. + +*** [[#DOCF11][(11)]] + +Please select your own key, C-c c is only a suggestion. + +*** [[#DOCF12][(12)]] + +If you need one of these sequences literally, escape the % with a +backslash. + +*** [[#DOCF13][(13)]] + +Note that the order of the arguments (month, day, year) depends on the +setting of =calendar-date-style=. + +-------------- + +| [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* Short Table of Contents + +- [[#Introduction][1. Introduction]] +- [[#Document-Structure][2. Document Structure]] +- [[#Tables][3. Tables]] +- [[#Hyperlinks][4. Hyperlinks]] +- [[#TODO-Items][5. TODO Items]] +- [[#Tags][6. Tags]] +- [[#Properties][7. Properties]] +- [[#Dates-and-Times][8. Dates and Times]] +- [[#Capture-_002d-Refile-_002d-Archive][9. Capture - Refile - + Archive]] +- [[#Agenda-Views][10. Agenda Views]] +- [[#Markup][11. Markup for rich export]] +- [[#Exporting][12. Exporting]] +- [[#Publishing][13. Publishing]] +- [[#Working-With-Source-Code][14. Working with source code]] +- [[#Miscellaneous][15. Miscellaneous]] + +-------------- + +| [[[#Top][Top]]] | [Contents] | [Index] | [[[#SEC_About][?]]] | + +* About This Document + +This document was generated by /Stefan Otte/ on /August 4, 2011/ using +[[http://www.nongnu.org/texi2html/][/texi2html 1.82/]]. + +The buttons in the navigation panels have the following meaning: + +| Button | Name | Go to | From 1.2.3 go to | +|--------------+---------------+-------------------------------------------------+--------------------| +| [ < ] | Back | Previous section in reading order | 1.2.2 | +| [ > ] | Forward | Next section in reading order | 1.2.4 | +| [ << ] | FastBack | Beginning of this chapter or previous chapter | 1 | +| [ Up ] | Up | Up section | 1.2 | +| [ >> ] | FastForward | Next chapter | 2 | +| [Top] | Top | Cover (top) of document | | +| [Contents] | Contents | Table of contents | | +| [Index] | Index | Index | | +| [ ? ] | About | About (help) | | + +where the *Example* assumes that the current position is at +*Subsubsection One-Two-Three* of a document of the following structure: + +- 1. Section One + + - 1.1 Subsection One-One + + - ... + + - 1.2 Subsection One-Two + + - 1.2.1 Subsubsection One-Two-One + - 1.2.2 Subsubsection One-Two-Two + - 1.2.3 Subsubsection One-Two-Three *<== Current Position* + - 1.2.4 Subsubsection One-Two-Four + + - 1.3 Subsection One-Three + + - ... + + - 1.4 Subsection One-Four + +-------------- + +This document was generated by /Stefan Otte/ on /August 4, 2011/ using +[[http://www.nongnu.org/texi2html/][/texi2html 1.82/]]. + diff --git a/pack/acp/start/vim-orgmode/documentation/emacs_orgguide.texi b/pack/acp/start/vim-orgmode/documentation/emacs_orgguide.texi new file mode 100644 index 0000000..61045ef --- /dev/null +++ b/pack/acp/start/vim-orgmode/documentation/emacs_orgguide.texi @@ -0,0 +1,2689 @@ +\input texinfo +@c %**start of header +@setfilename ../../info/orgguide +@settitle The compact Org-mode Guide + +@set VERSION 7.7 +@set DATE July 2011 + +@c Use proper quote and backtick for code sections in PDF output +@c Cf. Texinfo manual 14.2 +@set txicodequoteundirected +@set txicodequotebacktick + +@c Version and Contact Info +@set MAINTAINERSITE @uref{http://orgmode.org,maintainers webpage} +@set AUTHOR Carsten Dominik +@set MAINTAINER Carsten Dominik +@set MAINTAINEREMAIL @email{carsten at orgmode dot org} +@set MAINTAINERCONTACT @uref{mailto:carsten at orgmode dot org,contact the maintainer} +@c %**end of header +@finalout + +@c Macro definitions +@iftex +@c @hyphenation{time-stamp time-stamps time-stamp-ing time-stamp-ed} +@end iftex + +@c Subheadings inside a table. +@macro tsubheading{text} +@ifinfo +@subsubheading \text\ +@end ifinfo +@ifnotinfo +@item @b{\text\} +@end ifnotinfo +@end macro + +@macro seealso{text} +@noindent @b{Further reading}@*@noindent \text\ +@end macro +@copying + +Copyright @copyright{} 2010 Free Software Foundation + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, with the Front-Cover texts being ``A GNU Manual,'' +and with the Back-Cover Texts as in (a) below. A copy of the license +is included in the section entitled ``GNU Free Documentation License.'' + +(a) The FSF's Back-Cover Text is: ``You have the freedom to copy and +modify this GNU manual. Buying copies from the FSF supports it in +developing GNU and promoting software freedom.'' + +This document is part of a collection distributed under the GNU Free +Documentation License. If you want to distribute this document +separately from the collection, you can do so by adding a copy of the +license to the document, as described in section 6 of the license. +@end quotation +@end copying + +@dircategory Emacs +@direntry +* Org Mode Guide: (orgguide). Abbreviated Org-mode Manual +@end direntry + +@titlepage +@title The compact Org-mode Guide + +@subtitle Release @value{VERSION} +@author by Carsten Dominik + +@c The following two commands start the copyright page. +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@c Output the table of contents at the beginning. +@shortcontents + +@ifnottex +@node Top, Introduction, (dir), (dir) +@top Org Mode Guide + +@insertcopying +@end ifnottex + +@menu +* Introduction:: Getting started +* Document Structure:: A tree works like your brain +* Tables:: Pure magic for quick formatting +* Hyperlinks:: Notes in context +* TODO Items:: Every tree branch can be a TODO item +* Tags:: Tagging headlines and matching sets of tags +* Properties:: Properties +* Dates and Times:: Making items useful for planning +* Capture - Refile - Archive:: The ins and outs for projects +* Agenda Views:: Collecting information into views +* Markup:: Prepare text for rich export +* Exporting:: Sharing and publishing of notes +* Publishing:: Create a web site of linked Org files +* Working With Source Code:: Source code snippets embedded in Org +* Miscellaneous:: All the rest which did not fit elsewhere + +@detailmenu + --- The Detailed Node Listing --- + +Introduction + +* Preface:: Welcome +* Installation:: How to install a downloaded version of Org +* Activation:: How to activate Org for certain buffers +* Feedback:: Bug reports, ideas, patches etc. + +Document Structure + +* Outlines:: Org is based on Outline mode +* Headlines:: How to typeset Org tree headlines +* Visibility cycling:: Show and hide, much simplified +* Motion:: Jumping to other headlines +* Structure editing:: Changing sequence and level of headlines +* Sparse trees:: Matches embedded in context +* Plain lists:: Additional structure within an entry +* Footnotes:: How footnotes are defined in Org's syntax + +Hyperlinks + +* Link format:: How links in Org are formatted +* Internal links:: Links to other places in the current file +* External links:: URL-like links to the world +* Handling links:: Creating, inserting and following +* Targeted links:: Point at a location in a file + +TODO Items + +* Using TODO states:: Setting and switching states +* Multi-state workflows:: More than just on/off +* Progress logging:: Dates and notes for progress +* Priorities:: Some things are more important than others +* Breaking down tasks:: Splitting a task into manageable pieces +* Checkboxes:: Tick-off lists + +Progress logging + +* Closing items:: When was this entry marked DONE? +* Tracking TODO state changes:: When did the status change? + +Tags + +* Tag inheritance:: Tags use the tree structure of the outline +* Setting tags:: How to assign tags to a headline +* Tag searches:: Searching for combinations of tags + +Dates and Times + +* Timestamps:: Assigning a time to a tree entry +* Creating timestamps:: Commands which insert timestamps +* Deadlines and scheduling:: Planning your work +* Clocking work time:: Tracking how long you spend on a task + +Capture - Refile - Archive + +* Capture:: +* Refiling notes:: Moving a tree from one place to another +* Archiving:: What to do with finished projects + +Capture + +* Setting up a capture location:: Where notes will be stored +* Using capture:: Commands to invoke and terminate capture +* Capture templates:: Define the outline of different note types + +Agenda Views + +* Agenda files:: Files being searched for agenda information +* Agenda dispatcher:: Keyboard access to agenda views +* Built-in agenda views:: What is available out of the box? +* Agenda commands:: Remote editing of Org trees +* Custom agenda views:: Defining special searches and views + +The built-in agenda views + +* Weekly/daily agenda:: The calendar page with current tasks +* Global TODO list:: All unfinished action items +* Matching tags and properties:: Structured information with fine-tuned search +* Timeline:: Time-sorted view for single file +* Search view:: Find entries by searching for text + +Markup for rich export + +* Structural markup elements:: The basic structure as seen by the exporter +* Images and tables:: Tables and Images will be included +* Literal examples:: Source code examples with special formatting +* Include files:: Include additional files into a document +* Embedded LaTeX:: LaTeX can be freely used inside Org documents + +Structural markup elements + +* Document title:: Where the title is taken from +* Headings and sections:: The document structure as seen by the exporter +* Table of contents:: The if and where of the table of contents +* Paragraphs:: Paragraphs +* Emphasis and monospace:: Bold, italic, etc. +* Comment lines:: What will *not* be exported + +Exporting + +* Export options:: Per-file export settings +* The export dispatcher:: How to access exporter commands +* ASCII/Latin-1/UTF-8 export:: Exporting to flat files with encoding +* HTML export:: Exporting to HTML +* LaTeX and PDF export:: Exporting to La@TeX{}, and processing to PDF +* DocBook export:: Exporting to DocBook +* iCalendar export:: + +Miscellaneous + +* Completion:: M-TAB knows what you need +* Clean view:: Getting rid of leading stars in the outline +* MobileOrg:: Org-mode on the iPhone + +@end detailmenu +@end menu + +@node Introduction, Document Structure, Top, Top +@chapter Introduction + +@menu +* Preface:: Welcome +* Installation:: How to install a downloaded version of Org +* Activation:: How to activate Org for certain buffers +* Feedback:: Bug reports, ideas, patches etc. +@end menu + +@node Preface, Installation, Introduction, Introduction +@section Preface + +Org is a mode for keeping notes, maintaining TODO lists, and doing project +planning with a fast and effective plain-text system. It is also an +authoring and publishing system. + +@i{This document is a much compressed derivative of the +@uref{http://orgmode.org/index.html#sec-4_1, comprehensive Org-mode manual}. +It contains all basic features and commands, along with important hints for +customization. It is intended for beginners who would shy back from a 200 +page manual because of sheer size.} + +@node Installation, Activation, Preface, Introduction +@section Installation + +@b{Important:} @i{If you are using a version of Org that is part of the Emacs +distribution or an XEmacs package, please skip this section and go directly +to @ref{Activation}.} + +If you have downloaded Org from the Web, either as a distribution @file{.zip} +or @file{.tar} file, or as a Git archive, it is best to run it directly from +the distribution directory. You need to add the @file{lisp} subdirectories +to the Emacs load path. To do this, add the following line to @file{.emacs}: + +@smallexample +(setq load-path (cons "~/path/to/orgdir/lisp" load-path)) +(setq load-path (cons "~/path/to/orgdir/contrib/lisp" load-path)) +@end smallexample + +@noindent For speed you should byte-compile the Lisp files with the shell +command: + +@smallexample +make +@end smallexample + +Then add the following line to @file{.emacs}. It is needed so that +Emacs can autoload functions that are located in files not immediately loaded +when Org-mode starts. +@smalllisp +(require 'org-install) +@end smalllisp + +@node Activation, Feedback, Installation, Introduction +@section Activation + +Add the following lines to your @file{.emacs} file. The last three lines +define @emph{global} keys for some commands --- please choose suitable keys +yourself. + +@smalllisp +;; The following lines are always needed. Choose your own keys. +(add-to-list 'auto-mode-alist '("\\.org\\'" . org-mode)) +(add-hook 'org-mode-hook 'turn-on-font-lock) ; not needed when global-font-lock-mode is on +(global-set-key "\C-cl" 'org-store-link) +(global-set-key "\C-ca" 'org-agenda) +(global-set-key "\C-cb" 'org-iswitchb) +@end smalllisp + +With this setup, all files with extension @samp{.org} will be put +into Org mode. + +@node Feedback, , Activation, Introduction +@section Feedback + +If you find problems with Org, or if you have questions, remarks, or ideas +about it, please mail to the Org mailing list @email{emacs-orgmode@@gnu.org}. +For information on how to submit bug reports, see the main manual. + +@node Document Structure, Tables, Introduction, Top +@chapter Document Structure + +Org is based on Outline mode and provides flexible commands to +edit the structure of the document. + +@menu +* Outlines:: Org is based on Outline mode +* Headlines:: How to typeset Org tree headlines +* Visibility cycling:: Show and hide, much simplified +* Motion:: Jumping to other headlines +* Structure editing:: Changing sequence and level of headlines +* Sparse trees:: Matches embedded in context +* Plain lists:: Additional structure within an entry +* Footnotes:: How footnotes are defined in Org's syntax +@end menu + +@node Outlines, Headlines, Document Structure, Document Structure +@section Outlines + +Org is implemented on top of Outline mode. Outlines allow a +document to be organized in a hierarchical structure, which (at least +for me) is the best representation of notes and thoughts. An overview +of this structure is achieved by folding (hiding) large parts of the +document to show only the general document structure and the parts +currently being worked on. Org greatly simplifies the use of +outlines by compressing the entire show/hide functionality into a single +command, @command{org-cycle}, which is bound to the @key{TAB} key. + +@node Headlines, Visibility cycling, Outlines, Document Structure +@section Headlines + +Headlines define the structure of an outline tree. The headlines in +Org start with one or more stars, on the left margin@footnote{See +the variable @code{org-special-ctrl-a/e} to configure special behavior +of @kbd{C-a} and @kbd{C-e} in headlines.}. For example: + +@smallexample +* Top level headline +** Second level +*** 3rd level + some text +*** 3rd level + more text + +* Another top level headline +@end smallexample + +@noindent Some people find the many stars too noisy and would prefer an +outline that has whitespace followed by a single star as headline +starters. @ref{Clean view}, describes a setup to realize this. + +@node Visibility cycling, Motion, Headlines, Document Structure +@section Visibility cycling + +Outlines make it possible to hide parts of the text in the buffer. +Org uses just two commands, bound to @key{TAB} and +@kbd{S-@key{TAB}} to change the visibility in the buffer. + +@table @kbd +@item @key{TAB} +@emph{Subtree cycling}: Rotate current subtree among the states + +@smallexample +,-> FOLDED -> CHILDREN -> SUBTREE --. +'-----------------------------------' +@end smallexample + +When called with a prefix argument (@kbd{C-u @key{TAB}}) or with the shift +key, global cycling is invoked. + +@item S-@key{TAB} @r{and} C-u @key{TAB} +@emph{Global cycling}: Rotate the entire buffer among the states + +@smallexample +,-> OVERVIEW -> CONTENTS -> SHOW ALL --. +'--------------------------------------' +@end smallexample + +@item C-u C-u C-u @key{TAB} +Show all, including drawers. +@end table + +When Emacs first visits an Org file, the global state is set to +OVERVIEW, i.e.@: only the top level headlines are visible. This can be +configured through the variable @code{org-startup-folded}, or on a +per-file basis by adding a startup keyword @code{overview}, @code{content}, +@code{showall}, like this: + +@smallexample +#+STARTUP: content +@end smallexample + + +@node Motion, Structure editing, Visibility cycling, Document Structure +@section Motion +The following commands jump to other headlines in the buffer. + +@table @kbd +@item C-c C-n +Next heading. +@item C-c C-p +Previous heading. +@item C-c C-f +Next heading same level. +@item C-c C-b +Previous heading same level. +@item C-c C-u +Backward to higher level heading. +@end table + +@node Structure editing, Sparse trees, Motion, Document Structure +@section Structure editing + +@table @kbd +@item M-@key{RET} +Insert new heading with same level as current. If the cursor is in a plain +list item, a new item is created (@pxref{Plain lists}). When this command is +used in the middle of a line, the line is split and the rest of the line +becomes the new headline@footnote{If you do not want the line to be split, +customize the variable @code{org-M-RET-may-split-line}.}. +@item M-S-@key{RET} +Insert new TODO entry with same level as current heading. +@item @key{TAB} @r{in new, empty entry} +In a new entry with no text yet, @key{TAB} will cycle through reasonable +levels. +@item M-@key{left}@r{/}@key{right} +Promote/demote current heading by one level. +@item M-S-@key{left}@r{/}@key{right} +Promote/demote the current subtree by one level. +@item M-S-@key{up}@r{/}@key{down} +Move subtree up/down (swap with previous/next subtree of same +level). +@item C-c C-w +Refile entry or region to a different location. @xref{Refiling notes}. +@item C-x n s/w +Narrow buffer to current subtree / widen it again +@end table + +When there is an active region (Transient Mark mode), promotion and +demotion work on all headlines in the region. + +@node Sparse trees, Plain lists, Structure editing, Document Structure +@section Sparse trees + +An important feature of Org mode is the ability to construct @emph{sparse +trees} for selected information in an outline tree, so that the entire +document is folded as much as possible, but the selected information is made +visible along with the headline structure above it@footnote{See also the +variables @code{org-show-hierarchy-above}, @code{org-show-following-heading}, +@code{org-show-siblings}, and @code{org-show-entry-below} for detailed +control on how much context is shown around each match.}. Just try it out +and you will see immediately how it works. + +Org mode contains several commands creating such trees, all these +commands can be accessed through a dispatcher: + +@table @kbd +@item C-c / +This prompts for an extra key to select a sparse-tree creating command. +@item C-c / r +Occur. Prompts for a regexp and shows a sparse tree with all matches. Each +match is also highlighted; the highlights disappear by pressing @kbd{C-c C-c}. +@end table + +The other sparse tree commands select headings based on TODO keywords, +tags, or properties and will be discussed later in this manual. + +@node Plain lists, Footnotes, Sparse trees, Document Structure +@section Plain lists + +Within an entry of the outline tree, hand-formatted lists can provide +additional structure. They also provide a way to create lists of +checkboxes (@pxref{Checkboxes}). Org supports editing such lists, +and the HTML exporter (@pxref{Exporting}) parses and formats them. + +Org knows ordered lists, unordered lists, and description lists. +@itemize @bullet +@item +@emph{Unordered} list items start with @samp{-}, @samp{+}, or +@samp{*} as bullets. +@item +@emph{Ordered} list items start with @samp{1.} or @samp{1)}. +@item +@emph{Description} list use @samp{ :: } to separate the @emph{term} from the +description. +@end itemize + +Items belonging to the same list must have the same indentation on the first +line. An item ends before the next line that is indented like its +bullet/number, or less. A list ends when all items are closed, or before two +blank lines. An example: + +@smallexample +@group +** Lord of the Rings + My favorite scenes are (in this order) + 1. The attack of the Rohirrim + 2. Eowyn's fight with the witch king + + this was already my favorite scene in the book + + I really like Miranda Otto. + Important actors in this film are: + - @b{Elijah Wood} :: He plays Frodo + - @b{Sean Austin} :: He plays Sam, Frodo's friend. +@end group +@end smallexample + +The following commands act on items when the cursor is in the first line of +an item (the line with the bullet or number). + +@table @kbd +@item @key{TAB} +Items can be folded just like headline levels. +@item M-@key{RET} +Insert new item at current level. With a prefix argument, force a new +heading (@pxref{Structure editing}). +@item M-S-@key{RET} +Insert a new item with a checkbox (@pxref{Checkboxes}). +@item M-S-@key{up}@r{/}@key{down} +Move the item including subitems up/down (swap with previous/next item +of same indentation). If the list is ordered, renumbering is +automatic. +@item M-@key{left}@r{/}M-@key{right} +Decrease/increase the indentation of an item, leaving children alone. +@item M-S-@key{left}@r{/}@key{right} +Decrease/increase the indentation of the item, including subitems. +@item C-c C-c +If there is a checkbox (@pxref{Checkboxes}) in the item line, toggle the +state of the checkbox. Also verify bullets and indentation consistency in +the whole list. +@item C-c - +Cycle the entire list level through the different itemize/enumerate bullets +(@samp{-}, @samp{+}, @samp{*}, @samp{1.}, @samp{1)}). +@end table + +@node Footnotes, , Plain lists, Document Structure +@section Footnotes + +A footnote is defined in a paragraph that is started by a footnote marker in +square brackets in column 0, no indentation allowed. The footnote reference +is simply the marker in square brackets, inside text. For example: + +@smallexample +The Org homepage[fn:1] now looks a lot better than it used to. +... +[fn:1] The link is: http://orgmode.org +@end smallexample + +@noindent The following commands handle footnotes: + +@table @kbd +@item C-c C-x f +The footnote action command. When the cursor is on a footnote reference, +jump to the definition. When it is at a definition, jump to the (first) +reference. Otherwise, create a new footnote. When this command is called +with a prefix argument, a menu of additional options including renumbering is +offered. + +@item C-c C-c +Jump between definition and reference. +@end table + +@seealso{ +@uref{http://orgmode.org/manual/Document-Structure.html#Document-Structure, +Chapter 2 of the manual}@* +@uref{http://sachachua.com/wp/2008/01/outlining-your-notes-with-org/, +Sacha Chua's tutorial}} + + +@node Tables, Hyperlinks, Document Structure, Top +@chapter Tables + +Org comes with a fast and intuitive table editor. Spreadsheet-like +calculations are supported in connection with the Emacs @file{calc} +package +@ifinfo +(@pxref{Top,Calc,,Calc,Gnu Emacs Calculator Manual}). +@end ifinfo +@ifnotinfo +(see the Emacs Calculator manual for more information about the Emacs +calculator). +@end ifnotinfo + +Org makes it easy to format tables in plain ASCII. Any line with +@samp{|} as the first non-whitespace character is considered part of a +table. @samp{|} is also the column separator. A table might look like +this: + +@smallexample +| Name | Phone | Age | +|-------+-------+-----| +| Peter | 1234 | 17 | +| Anna | 4321 | 25 | +@end smallexample + +A table is re-aligned automatically each time you press @key{TAB} or +@key{RET} or @kbd{C-c C-c} inside the table. @key{TAB} also moves to +the next field (@key{RET} to the next row) and creates new table rows +at the end of the table or before horizontal lines. The indentation +of the table is set by the first line. Any line starting with +@samp{|-} is considered as a horizontal separator line and will be +expanded on the next re-align to span the whole table width. So, to +create the above table, you would only type + +@smallexample +|Name|Phone|Age| +|- +@end smallexample + +@noindent and then press @key{TAB} to align the table and start filling in +fields. Even faster would be to type @code{|Name|Phone|Age} followed by +@kbd{C-c @key{RET}}. + +When typing text into a field, Org treats @key{DEL}, +@key{Backspace}, and all character keys in a special way, so that +inserting and deleting avoids shifting other fields. Also, when +typing @emph{immediately after the cursor was moved into a new field +with @kbd{@key{TAB}}, @kbd{S-@key{TAB}} or @kbd{@key{RET}}}, the +field is automatically made blank. + +@table @kbd +@tsubheading{Creation and conversion} +@item C-c | +Convert the active region to table. If every line contains at least one TAB +character, the function assumes that the material is tab separated. If every +line contains a comma, comma-separated values (CSV) are assumed. If not, +lines are split at whitespace into fields. +@* +If there is no active region, this command creates an empty Org +table. But it's easier just to start typing, like +@kbd{|Name|Phone|Age C-c @key{RET}}. + +@tsubheading{Re-aligning and field motion} +@item C-c C-c +Re-align the table without moving the cursor. +@c +@item @key{TAB} +Re-align the table, move to the next field. Creates a new row if +necessary. +@c +@item S-@key{TAB} +Re-align, move to previous field. +@c +@item @key{RET} +Re-align the table and move down to next row. Creates a new row if +necessary. + +@tsubheading{Column and row editing} +@item M-@key{left} +@itemx M-@key{right} +Move the current column left/right. +@c +@item M-S-@key{left} +Kill the current column. +@c +@item M-S-@key{right} +Insert a new column to the left of the cursor position. +@c +@item M-@key{up} +@itemx M-@key{down} +Move the current row up/down. +@c +@item M-S-@key{up} +Kill the current row or horizontal line. +@c +@item M-S-@key{down} +Insert a new row above the current row. With a prefix argument, the line is +created below the current one. +@c +@item C-c - +Insert a horizontal line below current row. With a prefix argument, the line +is created above the current line. +@c +@item C-c @key{RET} +Insert a horizontal line below current row, and move the cursor into the row +below that line. +@c +@item C-c ^ +Sort the table lines in the region. The position of point indicates the +column to be used for sorting, and the range of lines is the range +between the nearest horizontal separator lines, or the entire table. + +@end table + +@seealso{ +@uref{http://orgmode.org/manual/Tables.html#Tables, Chapter 3 of the +manual}@* +@uref{http://orgmode.org/worg/org-tutorials/tables.php, Bastien's +table tutorial}@* +@uref{http://orgmode.org/worg/org-tutorials/org-spreadsheet-intro.php, +Bastien's spreadsheet tutorial}@* +@uref{http://orgmode.org/worg/org-tutorials/org-plot.php, Eric's plotting tutorial}} + +@node Hyperlinks, TODO Items, Tables, Top +@chapter Hyperlinks + +Like HTML, Org provides links inside a file, external links to +other files, Usenet articles, emails, and much more. + +@menu +* Link format:: How links in Org are formatted +* Internal links:: Links to other places in the current file +* External links:: URL-like links to the world +* Handling links:: Creating, inserting and following +* Targeted links:: Point at a location in a file +@end menu + +@node Link format, Internal links, Hyperlinks, Hyperlinks +@section Link format + +Org will recognize plain URL-like links and activate them as +clickable links. The general link format, however, looks like this: + +@smallexample +[[link][description]] @r{or alternatively} [[link]] +@end smallexample + +@noindent +Once a link in the buffer is complete (all brackets present), Org will change +the display so that @samp{description} is displayed instead of +@samp{[[link][description]]} and @samp{link} is displayed instead of +@samp{[[link]]}. To edit the invisible @samp{link} part, use @kbd{C-c +C-l} with the cursor on the link. + +@node Internal links, External links, Link format, Hyperlinks +@section Internal links + +If the link does not look like a URL, it is considered to be internal in the +current file. The most important case is a link like +@samp{[[#my-custom-id]]} which will link to the entry with the +@code{CUSTOM_ID} property @samp{my-custom-id}. + +Links such as @samp{[[My Target]]} or @samp{[[My Target][Find my target]]} +lead to a text search in the current file for the corresponding target which +looks like @samp{<>}. + +@node External links, Handling links, Internal links, Hyperlinks +@section External links + +Org supports links to files, websites, Usenet and email messages, +BBDB database entries and links to both IRC conversations and their +logs. External links are URL-like locators. They start with a short +identifying string followed by a colon. There can be no space after +the colon. Here are some examples: + +@smallexample +http://www.astro.uva.nl/~dominik @r{on the web} +file:/home/dominik/images/jupiter.jpg @r{file, absolute path} +/home/dominik/images/jupiter.jpg @r{same as above} +file:papers/last.pdf @r{file, relative path} +file:projects.org @r{another Org file} +docview:papers/last.pdf::NNN @r{open file in doc-view mode at page NNN} +id:B7423F4D-2E8A-471B-8810-C40F074717E9 @r{Link to heading by ID} +news:comp.emacs @r{Usenet link} +mailto:adent@@galaxy.net @r{Mail link} +vm:folder @r{VM folder link} +vm:folder#id @r{VM message link} +wl:folder#id @r{WANDERLUST message link} +mhe:folder#id @r{MH-E message link} +rmail:folder#id @r{RMAIL message link} +gnus:group#id @r{Gnus article link} +bbdb:R.*Stallman @r{BBDB link (with regexp)} +irc:/irc.com/#emacs/bob @r{IRC link} +info:org:External%20links @r{Info node link (with encoded space)} +@end smallexample + +A link should be enclosed in double brackets and may contain a +descriptive text to be displayed instead of the URL (@pxref{Link +format}), for example: + +@smallexample +[[http://www.gnu.org/software/emacs/][GNU Emacs]] +@end smallexample + +@noindent +If the description is a file name or URL that points to an image, HTML export +(@pxref{HTML export}) will inline the image as a clickable button. If there +is no description at all and the link points to an image, that image will be +inlined into the exported HTML file. + +@node Handling links, Targeted links, External links, Hyperlinks +@section Handling links + +Org provides methods to create a link in the correct syntax, to +insert it into an Org file, and to follow the link. + +@table @kbd +@item C-c l +Store a link to the current location. This is a @emph{global} command (you +must create the key binding yourself) which can be used in any buffer to +create a link. The link will be stored for later insertion into an Org +buffer (see below). +@c +@item C-c C-l +Insert a link. This prompts for a link to be inserted into the buffer. You +can just type a link, or use history keys @key{up} and @key{down} to access +stored links. You will be prompted for the description part of the link. +When called with a @kbd{C-u} prefix argument, file name completion is used to +link to a file. +@c +@item C-c C-l @r{(with cursor on existing link)} +When the cursor is on an existing link, @kbd{C-c C-l} allows you to edit the +link and description parts of the link. +@c +@item C-c C-o @r{or} mouse-1 @r{or} mouse-2 +Open link at point. +@item C-c & +Jump back to a recorded position. A position is recorded by the +commands following internal links, and by @kbd{C-c %}. Using this +command several times in direct succession moves through a ring of +previously recorded positions. +@c +@end table + +@node Targeted links, , Handling links, Hyperlinks +@section Targeted links + +File links can contain additional information to make Emacs jump to a +particular location in the file when following a link. This can be a +line number or a search option after a double colon. + +Here is the syntax of the different ways to attach a search to a file +link, together with an explanation: + +@smallexample +[[file:~/code/main.c::255]] @r{Find line 255} +[[file:~/xx.org::My Target]] @r{Find @samp{<>}} +[[file:~/xx.org::#my-custom-id]] @r{Find entry with custom id} +@end smallexample + +@seealso{ +@uref{http://orgmode.org/manual/Hyperlinks.html#Hyperlinks, Chapter 4 of the +manual}} + +@node TODO Items, Tags, Hyperlinks, Top +@chapter TODO Items + +Org mode does not maintain TODO lists as separate documents@footnote{Of +course, you can make a document that contains only long lists of TODO items, +but this is not required.}. Instead, TODO items are an integral part of the +notes file, because TODO items usually come up while taking notes! With Org +mode, simply mark any entry in a tree as being a TODO item. In this way, +information is not duplicated, and the entire context from which the TODO +item emerged is always present. + +Of course, this technique for managing TODO items scatters them +throughout your notes file. Org mode compensates for this by providing +methods to give you an overview of all the things that you have to do. + +@menu +* Using TODO states:: Setting and switching states +* Multi-state workflows:: More than just on/off +* Progress logging:: Dates and notes for progress +* Priorities:: Some things are more important than others +* Breaking down tasks:: Splitting a task into manageable pieces +* Checkboxes:: Tick-off lists +@end menu + +@node Using TODO states, Multi-state workflows, TODO Items, TODO Items +@section Using TODO states + +Any headline becomes a TODO item when it starts with the word +@samp{TODO}, for example: + +@smallexample +*** TODO Write letter to Sam Fortune +@end smallexample + +@noindent +The most important commands to work with TODO entries are: + +@table @kbd +@item C-c C-t +Rotate the TODO state of the current item among + +@smallexample +,-> (unmarked) -> TODO -> DONE --. +'--------------------------------' +@end smallexample + +The same rotation can also be done ``remotely'' from the timeline and +agenda buffers with the @kbd{t} command key (@pxref{Agenda commands}). + +@item S-@key{right}@r{/}@key{left} +Select the following/preceding TODO state, similar to cycling. +@item C-c / t +View TODO items in a @emph{sparse tree} (@pxref{Sparse trees}). Folds the +buffer, but shows all TODO items and the headings hierarchy above +them. +@item C-c a t +Show the global TODO list. Collects the TODO items from all agenda files +(@pxref{Agenda Views}) into a single buffer. @xref{Global TODO list}, for +more information. +@item S-M-@key{RET} +Insert a new TODO entry below the current one. +@end table + +@noindent +Changing a TODO state can also trigger tag changes. See the docstring of the +option @code{org-todo-state-tags-triggers} for details. + +@node Multi-state workflows, Progress logging, Using TODO states, TODO Items +@section Multi-state workflows + +You can use TODO keywords to indicate different @emph{sequential} states +in the process of working on an item, for example: + +@smalllisp +(setq org-todo-keywords + '((sequence "TODO" "FEEDBACK" "VERIFY" "|" "DONE" "DELEGATED"))) +@end smalllisp + +The vertical bar separates the TODO keywords (states that @emph{need +action}) from the DONE states (which need @emph{no further action}). If +you don't provide the separator bar, the last state is used as the DONE +state. +With this setup, the command @kbd{C-c C-t} will cycle an entry from TODO +to FEEDBACK, then to VERIFY, and finally to DONE and DELEGATED. + +Sometimes you may want to use different sets of TODO keywords in +parallel. For example, you may want to have the basic +@code{TODO}/@code{DONE}, but also a workflow for bug fixing, and a +separate state indicating that an item has been canceled (so it is not +DONE, but also does not require action). Your setup would then look +like this: + +@smalllisp +(setq org-todo-keywords + '((sequence "TODO(t)" "|" "DONE(d)") + (sequence "REPORT(r)" "BUG(b)" "KNOWNCAUSE(k)" "|" "FIXED(f)") + (sequence "|" "CANCELED(c)"))) +@end smalllisp + +The keywords should all be different, this helps Org mode to keep track of +which subsequence should be used for a given entry. The example also shows +how to define keys for fast access of a particular state, by adding a letter +in parenthesis after each keyword - you will be prompted for the key after +@kbd{C-c C-t}. + +To define TODO keywords that are valid only in a single file, use the +following text anywhere in the file. + +@smallexample +#+TODO: TODO(t) | DONE(d) +#+TODO: REPORT(r) BUG(b) KNOWNCAUSE(k) | FIXED(f) +#+TODO: | CANCELED(c) +@end smallexample + +After changing one of these lines, use @kbd{C-c C-c} with the cursor still in +the line to make the changes known to Org mode. + +@node Progress logging, Priorities, Multi-state workflows, TODO Items +@section Progress logging + +Org mode can automatically record a timestamp and possibly a note when +you mark a TODO item as DONE, or even each time you change the state of +a TODO item. This system is highly configurable, settings can be on a +per-keyword basis and can be localized to a file or even a subtree. For +information on how to clock working time for a task, see @ref{Clocking +work time}. + +@menu +* Closing items:: When was this entry marked DONE? +* Tracking TODO state changes:: When did the status change? +@end menu + +@node Closing items, Tracking TODO state changes, Progress logging, Progress logging +@unnumberedsubsec Closing items + +The most basic logging is to keep track of @emph{when} a certain TODO +item was finished. This is achieved with@footnote{The corresponding +in-buffer setting is: @code{#+STARTUP: logdone}}. + +@smalllisp +(setq org-log-done 'time) +@end smalllisp + +@noindent +Then each time you turn an entry from a TODO (not-done) state into any of the +DONE states, a line @samp{CLOSED: [timestamp]} will be inserted just after +the headline. If you want to record a note along with the timestamp, +use@footnote{The corresponding in-buffer setting is: @code{#+STARTUP: +lognotedone}} + +@smalllisp +(setq org-log-done 'note) +@end smalllisp + +@noindent +You will then be prompted for a note, and that note will be stored below +the entry with a @samp{Closing Note} heading. + +@node Tracking TODO state changes, , Closing items, Progress logging +@unnumberedsubsec Tracking TODO state changes + +You might want to keep track of TODO state changes. You can either record +just a timestamp, or a time-stamped note for a change. These records will be +inserted after the headline as an itemized list. When taking a lot of notes, +you might want to get the notes out of the way into a drawer. Customize the +variable @code{org-log-into-drawer} to get this behavior. + +For state logging, Org mode expects configuration on a per-keyword basis. +This is achieved by adding special markers @samp{!} (for a timestamp) and +@samp{@@} (for a note) in parentheses after each keyword. For example: +@smallexample +#+TODO: TODO(t) WAIT(w@@/!) | DONE(d!) CANCELED(c@@) +@end smallexample +@noindent +will define TODO keywords and fast access keys, and also request that a time +is recorded when the entry is set to DONE, and that a note is recorded when +switching to WAIT or CANCELED. The same syntax works also when setting +@code{org-todo-keywords}. + +@node Priorities, Breaking down tasks, Progress logging, TODO Items +@section Priorities + +If you use Org mode extensively, you may end up with enough TODO items that +it starts to make sense to prioritize them. Prioritizing can be done by +placing a @emph{priority cookie} into the headline of a TODO item, like this + +@smallexample +*** TODO [#A] Write letter to Sam Fortune +@end smallexample + +@noindent +Org mode supports three priorities: @samp{A}, @samp{B}, and @samp{C}. +@samp{A} is the highest, @samp{B} the default if none is given. Priorities +make a difference only in the agenda. + +@table @kbd +@item @kbd{C-c ,} +Set the priority of the current headline. Press @samp{A}, @samp{B} or +@samp{C} to select a priority, or @key{SPC} to remove the cookie. +@c +@item S-@key{up} +@itemx S-@key{down} +Increase/decrease priority of current headline +@end table + +@node Breaking down tasks, Checkboxes, Priorities, TODO Items +@section Breaking tasks down into subtasks + +It is often advisable to break down large tasks into smaller, manageable +subtasks. You can do this by creating an outline tree below a TODO item, +with detailed subtasks on the tree. To keep the overview over the fraction +of subtasks that are already completed, insert either @samp{[/]} or +@samp{[%]} anywhere in the headline. These cookies will be updated each time +the TODO status of a child changes, or when pressing @kbd{C-c C-c} on the +cookie. For example: + +@smallexample +* Organize Party [33%] +** TODO Call people [1/2] +*** TODO Peter +*** DONE Sarah +** TODO Buy food +** DONE Talk to neighbor +@end smallexample + +@node Checkboxes, , Breaking down tasks, TODO Items +@section Checkboxes + +Every item in a plain list (@pxref{Plain lists}) can be made into a checkbox +by starting it with the string @samp{[ ]}. Checkboxes are not included into +the global TODO list, so they are often great to split a task into a number +of simple steps. +Here is an example of a checkbox list. + +@smallexample +* TODO Organize party [1/3] + - [-] call people [1/2] + - [ ] Peter + - [X] Sarah + - [X] order food + - [ ] think about what music to play +@end smallexample + +Checkboxes work hierarchically, so if a checkbox item has children that +are checkboxes, toggling one of the children checkboxes will make the +parent checkbox reflect if none, some, or all of the children are +checked. + +@noindent The following commands work with checkboxes: + +@table @kbd +@item C-c C-c +Toggle checkbox status or (with prefix arg) checkbox presence at point. +@item M-S-@key{RET} +Insert a new item with a checkbox. +This works only if the cursor is already in a plain list item +(@pxref{Plain lists}). +@end table + +@seealso{ +@uref{http://orgmode.org/manual/TODO-Items.html#TODO-Items, Chapter 5 of the manual}@* +@uref{http://orgmode.org/worg/org-tutorials/orgtutorial_dto.php, David +O'Toole's introductory tutorial}@* +@uref{http://members.optusnet.com.au/~charles57/GTD/gtd_workflow.html, +Charles Cave's GTD setup}} + +@node Tags, Properties, TODO Items, Top +@chapter Tags + +An excellent way to implement labels and contexts for cross-correlating +information is to assign @i{tags} to headlines. Org mode has extensive +support for tags. + +Every headline can contain a list of tags; they occur at the end of the +headline. Tags are normal words containing letters, numbers, @samp{_}, and +@samp{@@}. Tags must be preceded and followed by a single colon, e.g., +@samp{:work:}. Several tags can be specified, as in @samp{:work:urgent:}. +Tags will by default be in bold face with the same color as the headline. + +@menu +* Tag inheritance:: Tags use the tree structure of the outline +* Setting tags:: How to assign tags to a headline +* Tag searches:: Searching for combinations of tags +@end menu + +@node Tag inheritance, Setting tags, Tags, Tags +@section Tag inheritance + +@i{Tags} make use of the hierarchical structure of outline trees. If a +heading has a certain tag, all subheadings will inherit the tag as +well. For example, in the list + +@smallexample +* Meeting with the French group :work: +** Summary by Frank :boss:notes: +*** TODO Prepare slides for him :action: +@end smallexample + +@noindent +the final heading will have the tags @samp{:work:}, @samp{:boss:}, +@samp{:notes:}, and @samp{:action:} even though the final heading is not +explicitly marked with those tags. You can also set tags that all entries in +a file should inherit just as if these tags were defined in a hypothetical +level zero that surrounds the entire file. Use a line like this@footnote{As +with all these in-buffer settings, pressing @kbd{C-c C-c} activates any +changes in the line.}: + +@smallexample +#+FILETAGS: :Peter:Boss:Secret: +@end smallexample + +@node Setting tags, Tag searches, Tag inheritance, Tags +@section Setting tags + +Tags can simply be typed into the buffer at the end of a headline. +After a colon, @kbd{M-@key{TAB}} offers completion on tags. There is +also a special command for inserting tags: + +@table @kbd +@item C-c C-q +Enter new tags for the current headline. Org mode will either offer +completion or a special single-key interface for setting tags, see +below. After pressing @key{RET}, the tags will be inserted and aligned +to @code{org-tags-column}. When called with a @kbd{C-u} prefix, all +tags in the current buffer will be aligned to that column, just to make +things look nice. +@item C-c C-c +When the cursor is in a headline, this does the same as @kbd{C-c C-q}. +@end table + +Org will support tag insertion based on a @emph{list of tags}. By +default this list is constructed dynamically, containing all tags +currently used in the buffer. You may also globally specify a hard list +of tags with the variable @code{org-tag-alist}. Finally you can set +the default tags for a given file with lines like + +@smallexample +#+TAGS: @@work @@home @@tennisclub +#+TAGS: laptop car pc sailboat +@end smallexample + +By default Org mode uses the standard minibuffer completion facilities for +entering tags. However, it also implements another, quicker, tag selection +method called @emph{fast tag selection}. This allows you to select and +deselect tags with just a single key press. For this to work well you should +assign unique letters to most of your commonly used tags. You can do this +globally by configuring the variable @code{org-tag-alist} in your +@file{.emacs} file. For example, you may find the need to tag many items in +different files with @samp{:@@home:}. In this case you can set something +like: + +@smalllisp +(setq org-tag-alist '(("@@work" . ?w) ("@@home" . ?h) ("laptop" . ?l))) +@end smalllisp + +@noindent If the tag is only relevant to the file you are working on, then you +can instead set the TAGS option line as: + +@smallexample +#+TAGS: @@work(w) @@home(h) @@tennisclub(t) laptop(l) pc(p) +@end smallexample + +@node Tag searches, , Setting tags, Tags +@section Tag searches + +Once a system of tags has been set up, it can be used to collect related +information into special lists. + +@table @kbd +@item C-c \ +@itemx C-c / m +Create a sparse tree with all headlines matching a tags search. With a +@kbd{C-u} prefix argument, ignore headlines that are not a TODO line. +@item C-c a m +Create a global list of tag matches from all agenda files. +@xref{Matching tags and properties}. +@item C-c a M +Create a global list of tag matches from all agenda files, but check +only TODO items and force checking subitems (see variable +@code{org-tags-match-list-sublevels}). +@end table + +These commands all prompt for a match string which allows basic Boolean logic +like @samp{+boss+urgent-project1}, to find entries with tags @samp{boss} and +@samp{urgent}, but not @samp{project1}, or @samp{Kathy|Sally} to find entries +which are tagged, like @samp{Kathy} or @samp{Sally}. The full syntax of the +search string is rich and allows also matching against TODO keywords, entry +levels and properties. For a complete description with many examples, see +@ref{Matching tags and properties}. + +@seealso{ +@uref{http://orgmode.org/manual/Tags.html#Tags, Chapter 6 of the manual}@* +@uref{http://sachachua.com/wp/2008/01/tagging-in-org-plus-bonus-code-for-timeclocks-and-tags/, +Sacha Chua's article about tagging in Org-mode}} + +@node Properties, Dates and Times, Tags, Top +@chapter Properties + +Properties are key-value pairs associates with and entry. They live in a +special drawer with the name @code{PROPERTIES}. Each +property is specified on a single line, with the key (surrounded by colons) +first, and the value after it: + +@smallexample +* CD collection +** Classic +*** Goldberg Variations + :PROPERTIES: + :Title: Goldberg Variations + :Composer: J.S. Bach + :Publisher: Deutsche Grammophon + :NDisks: 1 + :END: +@end smallexample + +You may define the allowed values for a particular property @samp{:Xyz:} +by setting a property @samp{:Xyz_ALL:}. This special property is +@emph{inherited}, so if you set it in a level 1 entry, it will apply to +the entire tree. When allowed values are defined, setting the +corresponding property becomes easier and is less prone to typing +errors. For the example with the CD collection, we can predefine +publishers and the number of disks in a box like this: + +@smallexample +* CD collection + :PROPERTIES: + :NDisks_ALL: 1 2 3 4 + :Publisher_ALL: "Deutsche Grammophon" Philips EMI + :END: +@end smallexample +or globally using @code{org-global-properties}, or file-wide like this: +@smallexample +#+PROPERTY: NDisks_ALL 1 2 3 4 +@end smallexample + +@table @kbd +@item C-c C-x p +Set a property. This prompts for a property name and a value. +@item C-c C-c d +Remove a property from the current entry. +@end table + +To create sparse trees and special lists with selection based on properties, +the same commands are used as for tag searches (@pxref{Tag searches}). The +syntax for the search string is described in @ref{Matching tags and +properties}. + +@table @kbd +@end table + +@seealso{ +@uref{http://orgmode.org/manual/Properties-and-Columns.html#Properties-and-Columns, +Chapter 7 of the manual}@* +@uref{http://orgmode.org/worg/org-tutorials/org-column-view-tutorial.php,Bastien +Guerry's column view tutorial}} + +@node Dates and Times, Capture - Refile - Archive, Properties, Top +@chapter Dates and Times + +To assist project planning, TODO items can be labeled with a date and/or +a time. The specially formatted string carrying the date and time +information is called a @emph{timestamp} in Org mode. + +@menu +* Timestamps:: Assigning a time to a tree entry +* Creating timestamps:: Commands which insert timestamps +* Deadlines and scheduling:: Planning your work +* Clocking work time:: Tracking how long you spend on a task +@end menu + + +@node Timestamps, Creating timestamps, Dates and Times, Dates and Times +@section Timestamps + +A timestamp is a specification of a date (possibly with a time or a range of +times) in a special format, either @samp{<2003-09-16 Tue>} or +@samp{<2003-09-16 Tue 09:39>} or @samp{<2003-09-16 Tue 12:00-12:30>}. A +timestamp can appear anywhere in the headline or body of an Org tree entry. +Its presence causes entries to be shown on specific dates in the agenda +(@pxref{Weekly/daily agenda}). We distinguish: + +@noindent @b{Plain timestamp; Event; Appointment}@* +A simple timestamp just assigns a date/time to an item. This is just +like writing down an appointment or event in a paper agenda. + +@smallexample +* Meet Peter at the movies <2006-11-01 Wed 19:15> +* Discussion on climate change <2006-11-02 Thu 20:00-22:00> +@end smallexample + +@noindent @b{Timestamp with repeater interval}@* +A timestamp may contain a @emph{repeater interval}, indicating that it +applies not only on the given date, but again and again after a certain +interval of N days (d), weeks (w), months (m), or years (y). The +following will show up in the agenda every Wednesday: +@smallexample +* Pick up Sam at school <2007-05-16 Wed 12:30 +1w> +@end smallexample + +@noindent @b{Diary-style sexp entries}@* +For more complex date specifications, Org mode supports using the +special sexp diary entries implemented in the Emacs calendar/diary +package. For example +@smallexample +* The nerd meeting on every 2nd Thursday of the month + <%%(diary-float t 4 2)> +@end smallexample + +@noindent @b{Time/Date range}@* +Two timestamps connected by @samp{--} denote a range. +@smallexample +** Meeting in Amsterdam + <2004-08-23 Mon>--<2004-08-26 Thu> +@end smallexample + +@noindent @b{Inactive timestamp}@* +Just like a plain timestamp, but with square brackets instead of +angular ones. These timestamps are inactive in the sense that they do +@emph{not} trigger an entry to show up in the agenda. + +@smallexample +* Gillian comes late for the fifth time [2006-11-01 Wed] +@end smallexample + + +@node Creating timestamps, Deadlines and scheduling, Timestamps, Dates and Times +@section Creating timestamps + +For Org mode to recognize timestamps, they need to be in the specific +format. All commands listed below produce timestamps in the correct +format. + +@table @kbd +@item C-c . +Prompt for a date and insert a corresponding timestamp. When the cursor is +at an existing timestamp in the buffer, the command is used to modify this +timestamp instead of inserting a new one. When this command is used twice in +succession, a time range is inserted. With a prefix, also add the current +time. +@c +@item C-c ! +Like @kbd{C-c .}, but insert an inactive timestamp that will not cause +an agenda entry. +@c +@item S-@key{left}@r{/}@key{right} +Change date at cursor by one day. +@c +@item S-@key{up}@r{/}@key{down} +Change the item under the cursor in a timestamp. The cursor can be on a +year, month, day, hour or minute. When the timestamp contains a time range +like @samp{15:30-16:30}, modifying the first time will also shift the second, +shifting the time block with constant length. To change the length, modify +the second time. +@end table + +When Org mode prompts for a date/time, it will accept any string containing +some date and/or time information, and intelligently interpret the string, +deriving defaults for unspecified information from the current date and time. +You can also select a date in the pop-up calendar. See the manual for more +information on how exactly the date/time prompt works. + +@node Deadlines and scheduling, Clocking work time, Creating timestamps, Dates and Times +@section Deadlines and scheduling + +A timestamp may be preceded by special keywords to facilitate planning: + +@noindent @b{DEADLINE}@* +Meaning: the task (most likely a TODO item, though not necessarily) is supposed +to be finished on that date. +@table @kbd +@item C-c C-d +Insert @samp{DEADLINE} keyword along with a stamp, in the line following the +headline. +@end table + +On the deadline date, the task will be listed in the agenda. In +addition, the agenda for @emph{today} will carry a warning about the +approaching or missed deadline, starting +@code{org-deadline-warning-days} before the due date, and continuing +until the entry is marked DONE. An example: + +@smallexample +*** TODO write article about the Earth for the Guide + The editor in charge is [[bbdb:Ford Prefect]] + DEADLINE: <2004-02-29 Sun> +@end smallexample + + +@noindent @b{SCHEDULED}@* +Meaning: you are @i{planning to start working} on that task on the given +date@footnote{This is quite different from what is normally understood by +@i{scheduling a meeting}, which is done in Org-mode by just inserting a time +stamp without keyword.}. + +@table @kbd +@item C-c C-s +Insert @samp{SCHEDULED} keyword along with a stamp, in the line following the +headline. +@end table + +The headline will be listed under the given date@footnote{It will still +be listed on that date after it has been marked DONE. If you don't like +this, set the variable @code{org-agenda-skip-scheduled-if-done}.}. In +addition, a reminder that the scheduled date has passed will be present +in the compilation for @emph{today}, until the entry is marked DONE. +I.e.@: the task will automatically be forwarded until completed. + +@smallexample +*** TODO Call Trillian for a date on New Years Eve. + SCHEDULED: <2004-12-25 Sat> +@end smallexample + +Some tasks need to be repeated again and again. Org mode helps to +organize such tasks using a so-called repeater in a DEADLINE, SCHEDULED, +or plain timestamp. In the following example +@smallexample +** TODO Pay the rent + DEADLINE: <2005-10-01 Sat +1m> +@end smallexample +@noindent +the @code{+1m} is a repeater; the intended interpretation is that the task +has a deadline on <2005-10-01> and repeats itself every (one) month starting +from that time. + +@node Clocking work time, , Deadlines and scheduling, Dates and Times +@section Clocking work time + +Org mode allows you to clock the time you spend on specific tasks in a +project. + +@table @kbd +@item C-c C-x C-i +Start the clock on the current item (clock-in). This inserts the CLOCK +keyword together with a timestamp. When called with a @kbd{C-u} prefix +argument, select the task from a list of recently clocked tasks. +@c +@item C-c C-x C-o +Stop the clock (clock-out). This inserts another timestamp at the same +location where the clock was last started. It also directly computes +the resulting time in inserts it after the time range as @samp{=> +HH:MM}. +@item C-c C-x C-e +Update the effort estimate for the current clock task. +@item C-c C-x C-x +Cancel the current clock. This is useful if a clock was started by +mistake, or if you ended up working on something else. +@item C-c C-x C-j +Jump to the entry that contains the currently running clock. With a +@kbd{C-u} prefix arg, select the target task from a list of recently clocked +tasks. +@item C-c C-x C-r +Insert a dynamic block containing a clock +report as an Org-mode table into the current file. When the cursor is +at an existing clock table, just update it. +@smallexample +#+BEGIN: clocktable :maxlevel 2 :emphasize nil :scope file +#+END: clocktable +@end smallexample +@noindent +For details about how to customize this view, see @uref{http://orgmode.org/manual/Clocking-work-time.html#Clocking-work-time,the manual}. +@item C-c C-c +Update dynamic block at point. The cursor needs to be in the +@code{#+BEGIN} line of the dynamic block. +@end table + +The @kbd{l} key may be used in the timeline (@pxref{Timeline}) and in +the agenda (@pxref{Weekly/daily agenda}) to show which tasks have been +worked on or closed during a day. + +@seealso{ +@uref{http://orgmode.org/manual/Dates-and-Times.html#Dates-and-Times, +Chapter 8 of the manual}@* +@uref{http://members.optusnet.com.au/~charles57/GTD/org_dates/, Charles +Cave's Date and Time tutorial}@* +@uref{http://doc.norang.ca/org-mode.html#Clocking, Bernt Hansen's clocking workflow}} + +@node Capture - Refile - Archive, Agenda Views, Dates and Times, Top +@chapter Capture - Refile - Archive + +An important part of any organization system is the ability to quickly +capture new ideas and tasks, and to associate reference material with them. +Org defines a capture process to create tasks. It stores files related to a +task (@i{attachments}) in a special directory. Once in the system, tasks and +projects need to be moved around. Moving completed project trees to an +archive file keeps the system compact and fast. + +@menu +* Capture:: +* Refiling notes:: Moving a tree from one place to another +* Archiving:: What to do with finished projects +@end menu + +@node Capture, Refiling notes, Capture - Refile - Archive, Capture - Refile - Archive +@section Capture + +Org's method for capturing new items is heavily inspired by John Wiegley +excellent remember package. It lets you store quick notes with little +interruption of your work flow. Org lets you define templates for new +entries and associate them with different targets for storing notes. + +@menu +* Setting up a capture location:: Where notes will be stored +* Using capture:: Commands to invoke and terminate capture +* Capture templates:: Define the outline of different note types +@end menu + +@node Setting up a capture location, Using capture, Capture, Capture +@unnumberedsubsec Setting up a capture location + +The following customization sets a default target@footnote{Using capture +templates, you can define more fine-grained capture locations, see +@ref{Capture templates}.} file for notes, and defines a global +key@footnote{Please select your own key, @kbd{C-c c} is only a suggestion.} +for capturing new stuff. + +@example +(setq org-default-notes-file (concat org-directory "/notes.org")) +(define-key global-map "\C-cc" 'org-capture) +@end example + +@node Using capture, Capture templates, Setting up a capture location, Capture +@unnumberedsubsec Using capture + +@table @kbd +@item C-c c +Start a capture process. You will be placed into a narrowed indirect buffer +to edit the item. +@item C-c C-c +Once you are done entering information into the capture buffer, +@kbd{C-c C-c} will return you to the window configuration before the capture +process, so that you can resume your work without further distraction. +@item C-c C-w +Finalize by moving the entry to a refile location (@pxref{Refiling notes}). +@item C-c C-k +Abort the capture process and return to the previous state. +@end table + +@node Capture templates, , Using capture, Capture +@unnumberedsubsec Capture templates + +You can use templates to generate different types of capture notes, and to +store them in different places. For example, if you would like +to store new tasks under a heading @samp{Tasks} in file @file{TODO.org}, and +journal entries in a date tree in @file{journal.org} you could +use: + +@smallexample +(setq org-capture-templates + '(("t" "Todo" entry (file+headline "~/org/gtd.org" "Tasks") + "* TODO %?\n %i\n %a") + ("j" "Journal" entry (file+datetree "~/org/journal.org") + "* %?\nEntered on %U\n %i\n %a"))) +@end smallexample + +@noindent In these entries, the first string is the key to reach the +template, the second is a short description. Then follows the type of the +entry and a definition of the target location for storing the note. Finally, +the template itself, a string with %-escapes to fill in information based on +time and context. + +When you call @kbd{M-x org-capture}, Org will prompt for a key to select the +template (if you have more than one template) and then prepare the buffer like +@smallexample +* TODO + [[file:@var{link to where you were when initiating capture}]] +@end smallexample + +@noindent +During expansion of the template, special @kbd{%}-escapes@footnote{If you +need one of these sequences literally, escape the @kbd{%} with a backslash.} +allow dynamic insertion of content. Here is a small selection of the +possibilities, consult the manual for more. +@smallexample +%a @r{annotation, normally the link created with @code{org-store-link}} +%i @r{initial content, the region when remember is called with C-u.} +%t @r{timestamp, date only} +%T @r{timestamp with date and time} +%u, %U @r{like the above, but inactive timestamps} +@end smallexample + +@node Refiling notes, Archiving, Capture, Capture - Refile - Archive +@section Refiling notes + +When reviewing the captured data, you may want to refile some of the entries +into a different list, for example into a project. Cutting, finding the +right location, and then pasting the note is cumbersome. To simplify this +process, you can use the following special command: + +@table @kbd +@item C-c C-w +Refile the entry or region at point. This command offers possible locations +for refiling the entry and lets you select one with completion. The item (or +all items in the region) is filed below the target heading as a subitem.@* +By default, all level 1 headlines in the current buffer are considered to be +targets, but you can have more complex definitions across a number of files. +See the variable @code{org-refile-targets} for details. +@item C-u C-c C-w +Use the refile interface to jump to a heading. +@item C-u C-u C-c C-w +Jump to the location where @code{org-refile} last moved a tree to. +@end table + +@node Archiving, , Refiling notes, Capture - Refile - Archive +@section Archiving + +When a project represented by a (sub)tree is finished, you may want +to move the tree out of the way and to stop it from contributing to the +agenda. Archiving is important to keep your working files compact and global +searches like the construction of agenda views fast. +The most common archiving action is to move a project tree to another file, +the archive file. + +@table @kbd +@item C-c C-x C-a +Archive the current entry using the command specified in the variable +@code{org-archive-default-command}. +@item C-c C-x C-s@ @r{or short} @ C-c $ +Archive the subtree starting at the cursor position to the location +given by @code{org-archive-location}. +@end table + +The default archive location is a file in the same directory as the +current file, with the name derived by appending @file{_archive} to the +current file name. For information and examples on how to change this, +see the documentation string of the variable +@code{org-archive-location}. There is also an in-buffer option for +setting this variable, for example + +@smallexample +#+ARCHIVE: %s_done:: +@end smallexample + +@seealso{ +@uref{http://orgmode.org/manual/Capture-_002d-Refile-_002d-Archive.html#Capture-_002d-Refile-_002d-Archive, +Chapter 9 of the manual}@* +@uref{http://members.optusnet.com.au/~charles57/GTD/remember.html, Charles +Cave's remember tutorial}@* +@uref{http://orgmode.org/worg/org-tutorials/org-protocol-custom-handler.php, +Sebastian Rose's tutorial for capturing from a web browser}}@uref{}@* + +@node Agenda Views, Markup, Capture - Refile - Archive, Top +@chapter Agenda Views + +Due to the way Org works, TODO items, time-stamped items, and tagged +headlines can be scattered throughout a file or even a number of files. To +get an overview of open action items, or of events that are important for a +particular date, this information must be collected, sorted and displayed in +an organized way. There are several different views, see below. + +The extracted information is displayed in a special @emph{agenda buffer}. +This buffer is read-only, but provides commands to visit the corresponding +locations in the original Org files, and even to edit these files remotely. +Remote editing from the agenda buffer means, for example, that you can +change the dates of deadlines and appointments from the agenda buffer. +The commands available in the Agenda buffer are listed in @ref{Agenda +commands}. + +@menu +* Agenda files:: Files being searched for agenda information +* Agenda dispatcher:: Keyboard access to agenda views +* Built-in agenda views:: What is available out of the box? +* Agenda commands:: Remote editing of Org trees +* Custom agenda views:: Defining special searches and views +@end menu + +@node Agenda files, Agenda dispatcher, Agenda Views, Agenda Views +@section Agenda files + +The information to be shown is normally collected from all @emph{agenda +files}, the files listed in the variable +@code{org-agenda-files}. + +@table @kbd +@item C-c [ +Add current file to the list of agenda files. The file is added to +the front of the list. If it was already in the list, it is moved to +the front. With a prefix argument, file is added/moved to the end. +@item C-c ] +Remove current file from the list of agenda files. +@item C-, +Cycle through agenda file list, visiting one file after the other. +@end table + +@node Agenda dispatcher, Built-in agenda views, Agenda files, Agenda Views +@section The agenda dispatcher +The views are created through a dispatcher, which should be bound to a +global key---for example @kbd{C-c a} (@pxref{Installation}). After +pressing @kbd{C-c a}, an additional letter is required to execute a +command: +@table @kbd +@item a +The calendar-like agenda (@pxref{Weekly/daily agenda}). +@item t @r{/} T +A list of all TODO items (@pxref{Global TODO list}). +@item m @r{/} M +A list of headlines matching a TAGS expression (@pxref{Matching +tags and properties}). +@item L +The timeline view for the current buffer (@pxref{Timeline}). +@item s +A list of entries selected by a boolean expression of keywords +and/or regular expressions that must or must not occur in the entry. +@end table + +@node Built-in agenda views, Agenda commands, Agenda dispatcher, Agenda Views +@section The built-in agenda views + +@menu +* Weekly/daily agenda:: The calendar page with current tasks +* Global TODO list:: All unfinished action items +* Matching tags and properties:: Structured information with fine-tuned search +* Timeline:: Time-sorted view for single file +* Search view:: Find entries by searching for text +@end menu + +@node Weekly/daily agenda, Global TODO list, Built-in agenda views, Built-in agenda views +@subsection The weekly/daily agenda + +The purpose of the weekly/daily @emph{agenda} is to act like a page of a +paper agenda, showing all the tasks for the current week or day. + +@table @kbd +@item C-c a a +Compile an agenda for the current week from a list of Org files. The agenda +shows the entries for each day. +@end table + +Emacs contains the calendar and diary by Edward M. Reingold. Org-mode +understands the syntax of the diary and allows you to use diary sexp entries +directly in Org files: + +@smallexample +* Birthdays and similar stuff +#+CATEGORY: Holiday +%%(org-calendar-holiday) ; special function for holiday names +#+CATEGORY: Ann +%%(diary-anniversary 5 14 1956)@footnote{Note that the order of the arguments (month, day, year) depends on the setting of @code{calendar-date-style}.} Arthur Dent is %d years old +%%(diary-anniversary 10 2 1869) Mahatma Gandhi would be %d years old +@end smallexample + +Org can interact with Emacs appointments notification facility. To add all +the appointments of your agenda files, use the command +@code{org-agenda-to-appt}. See the docstring for details. + +@node Global TODO list, Matching tags and properties, Weekly/daily agenda, Built-in agenda views +@subsection The global TODO list + +The global TODO list contains all unfinished TODO items formatted and +collected into a single place. Remote editing of TODO items lets you +can change the state of a TODO entry with a single key press. The commands +available in the TODO list are described in @ref{Agenda commands}. + +@table @kbd +@item C-c a t +Show the global TODO list. This collects the TODO items from all +agenda files (@pxref{Agenda Views}) into a single buffer. +@item C-c a T +Like the above, but allows selection of a specific TODO keyword. +@end table + +@node Matching tags and properties, Timeline, Global TODO list, Built-in agenda views +@subsection Matching tags and properties + +If headlines in the agenda files are marked with @emph{tags} (@pxref{Tags}), +or have properties (@pxref{Properties}), you can select headlines +based on this metadata and collect them into an agenda buffer. The match +syntax described here also applies when creating sparse trees with @kbd{C-c / +m}. The commands available in the tags list are described in @ref{Agenda +commands}. + +@table @kbd +@item C-c a m +Produce a list of all headlines that match a given set of tags. The +command prompts for a selection criterion, which is a boolean logic +expression with tags, like @samp{+work+urgent-withboss} or +@samp{work|home} (@pxref{Tags}). If you often need a specific search, +define a custom command for it (@pxref{Agenda dispatcher}). +@item C-c a M +Like @kbd{C-c a m}, but only select headlines that are also TODO items. +@end table + +@subsubheading Match syntax + +A search string can use Boolean operators @samp{&} for AND and @samp{|} for +OR. @samp{&} binds more strongly than @samp{|}. Parentheses are currently +not implemented. Each element in the search is either a tag, a regular +expression matching tags, or an expression like @code{PROPERTY OPERATOR +VALUE} with a comparison operator, accessing a property value. Each element +may be preceded by @samp{-}, to select against it, and @samp{+} is syntactic +sugar for positive selection. The AND operator @samp{&} is optional when +@samp{+} or @samp{-} is present. Here are some examples, using only tags. + +@table @samp +@item +work-boss +Select headlines tagged @samp{:work:}, but discard those also tagged +@samp{:boss:}. +@item work|laptop +Selects lines tagged @samp{:work:} or @samp{:laptop:}. +@item work|laptop+night +Like before, but require the @samp{:laptop:} lines to be tagged also +@samp{:night:}. +@end table + +You may also test for properties at the same +time as matching tags, see the manual for more information. + +@node Timeline, Search view, Matching tags and properties, Built-in agenda views +@subsection Timeline for a single file + +The timeline summarizes all time-stamped items from a single Org mode +file in a @emph{time-sorted view}. The main purpose of this command is +to give an overview over events in a project. + +@table @kbd +@item C-c a L +Show a time-sorted view of the Org file, with all time-stamped items. +When called with a @kbd{C-u} prefix, all unfinished TODO entries +(scheduled or not) are also listed under the current date. +@end table + +@node Search view, , Timeline, Built-in agenda views +@subsection Search view + +This agenda view is a general text search facility for Org mode entries. +It is particularly useful to find notes. + +@table @kbd +@item C-c a s +This is a special search that lets you select entries by matching a substring +or specific words using a boolean logic. +@end table +For example, the search string @samp{computer equipment} will find entries +that contain @samp{computer equipment} as a substring. +Search view can also search for specific keywords in the entry, using Boolean +logic. The search string @samp{+computer +wifi -ethernet -@{8\.11[bg]@}} +will search for note entries that contain the keywords @code{computer} +and @code{wifi}, but not the keyword @code{ethernet}, and which are also +not matched by the regular expression @code{8\.11[bg]}, meaning to +exclude both 8.11b and 8.11g. + +Note that in addition to the agenda files, this command will also search +the files listed in @code{org-agenda-text-search-extra-files}. + +@node Agenda commands, Custom agenda views, Built-in agenda views, Agenda Views +@section Commands in the agenda buffer + +Entries in the agenda buffer are linked back to the Org file or diary +file where they originate. Commands are provided to show and jump to the +original entry location, and to edit the Org files ``remotely'' from +the agenda buffer. This is just a selection of the many commands, explore +the @code{Agenda} menu and the manual for a complete list. + +@table @kbd +@tsubheading{Motion} +@item n +Next line (same as @key{up} and @kbd{C-p}). +@item p +Previous line (same as @key{down} and @kbd{C-n}). +@tsubheading{View/Go to Org file} +@item mouse-3 +@itemx @key{SPC} +Display the original location of the item in another window. +With prefix arg, make sure that the entire entry is made visible in the +outline, not only the heading. +@c +@itemx @key{TAB} +Go to the original location of the item in another window. Under Emacs +22, @kbd{mouse-1} will also works for this. +@c +@itemx @key{RET} +Go to the original location of the item and delete other windows. +@c + +@tsubheading{Change display} +@item o +Delete other windows. +@c +@item d @r{/} w +Switch to day/week view. +@c +@item f @r{and} b +Go forward/backward in time to display the following +@code{org-agenda-current-span} days. For example, if the display covers a +week, switch to the following/previous week. +@c +@item . +Go to today. +@c +@item j +Prompt for a date and go there. +@c +@item v l @ @r{or short} @ l +Toggle Logbook mode. In Logbook mode, entries that were marked DONE while +logging was on (variable @code{org-log-done}) are shown in the agenda, as are +entries that have been clocked on that day. When called with a @kbd{C-u} +prefix, show all possible logbook entries, including state changes. +@c +@item r @r{or} g +Recreate the agenda buffer, to reflect the changes. +@item s +Save all Org buffers in the current Emacs session, and also the locations of +IDs. + +@tsubheading{Secondary filtering and query editing} + +@item / +Filter the current agenda view with respect to a tag. You are prompted for a +letter to select a tag. Press @samp{-} first to select against the tag. + +@item \ +Narrow the current agenda filter by an additional condition. + +@tsubheading{Remote editing (see the manual for many more commands)} + +@item 0-9 +Digit argument. +@c +@item t +Change the TODO state of the item, in the agenda and in the +org file. +@c +@item C-k +Delete the current agenda item along with the entire subtree belonging +to it in the original Org file. +@c +@item C-c C-w +Refile the entry at point. +@c +@item C-c C-x C-a @ @r{or short} @ a +Archive the subtree corresponding to the entry at point using the default +archiving command set in @code{org-archive-default-command}. +@c +@item C-c C-x C-s @ @r{or short} @ $ +Archive the subtree corresponding to the current headline. +@c +@item C-c C-s +Schedule this item, with prefix arg remove the scheduling timestamp +@c +@item C-c C-d +Set a deadline for this item, with prefix arg remove the deadline. +@c +@item S-@key{right} @r{and} S-@key{left} +Change the timestamp associated with the current line by one day. +@c +@item I +Start the clock on the current item. +@c +@item O / X +Stop/cancel the previously started clock. + +@item J +Jump to the running clock in another window. +@end table + +@node Custom agenda views, , Agenda commands, Agenda Views +@section Custom agenda views + +The main application of custom searches is the definition of keyboard +shortcuts for frequently used searches, either creating an agenda +buffer, or a sparse tree (the latter covering of course only the current +buffer). +Custom commands are configured in the variable +@code{org-agenda-custom-commands}. You can customize this variable, for +example by pressing @kbd{C-c a C}. You can also directly set it with +Emacs Lisp in @file{.emacs}. The following example contains all valid +search types: + +@smalllisp +@group +(setq org-agenda-custom-commands + '(("w" todo "WAITING") + ("u" tags "+boss-urgent") + ("v" tags-todo "+boss-urgent"))) +@end group +@end smalllisp + +@noindent +The initial string in each entry defines the keys you have to press after the +dispatcher command @kbd{C-c a} in order to access the command. Usually this +will be just a single character. The second parameter is the search type, +followed by the string or regular expression to be used for the matching. +The example above will therefore define: + +@table @kbd +@item C-c a w +as a global search for TODO entries with @samp{WAITING} as the TODO +keyword +@item C-c a u +as a global tags search for headlines marked @samp{:boss:} but not +@samp{:urgent:} +@item C-c a v +as the same search as @kbd{C-c a u}, but limiting the search to +headlines that are also TODO items +@end table + +@seealso{ +@uref{http://orgmode.org/manual/Agenda-Views.html#Agenda-Views, Chapter 10 of +the manual}@* +@uref{http://orgmode.org/worg/org-tutorials/org-custom-agenda-commands.php, +Mat Lundin's tutorial about custom agenda commands}@* +@uref{http://www.newartisans.com/2007/08/using-org-mode-as-a-day-planner.html, +John Wiegley's setup}} + +@node Markup, Exporting, Agenda Views, Top +@chapter Markup for rich export + +When exporting Org-mode documents, the exporter tries to reflect the +structure of the document as accurately as possible in the backend. Since +export targets like HTML, La@TeX{}, or DocBook allow much richer formatting, +Org mode has rules on how to prepare text for rich export. This section +summarizes the markup rules used in an Org-mode buffer. + +@menu +* Structural markup elements:: The basic structure as seen by the exporter +* Images and tables:: Tables and Images will be included +* Literal examples:: Source code examples with special formatting +* Include files:: Include additional files into a document +* Embedded LaTeX:: LaTeX can be freely used inside Org documents +@end menu + +@node Structural markup elements, Images and tables, Markup, Markup +@section Structural markup elements + +@menu +* Document title:: Where the title is taken from +* Headings and sections:: The document structure as seen by the exporter +* Table of contents:: The if and where of the table of contents +* Paragraphs:: Paragraphs +* Emphasis and monospace:: Bold, italic, etc. +* Comment lines:: What will *not* be exported +@end menu + +@node Document title, Headings and sections, Structural markup elements, Structural markup elements +@subheading Document title + +@noindent +The title of the exported document is taken from the special line + +@smallexample +#+TITLE: This is the title of the document +@end smallexample + +@node Headings and sections, Table of contents, Document title, Structural markup elements +@subheading Headings and sections + +The outline structure of the document as described in @ref{Document +Structure}, forms the basis for defining sections of the exported document. +However, since the outline structure is also used for (for example) lists of +tasks, only the first three outline levels will be used as headings. Deeper +levels will become itemized lists. You can change the location of this +switch globally by setting the variable @code{org-export-headline-levels}, or on a +per-file basis with a line + +@smallexample +#+OPTIONS: H:4 +@end smallexample + +@node Table of contents, Paragraphs, Headings and sections, Structural markup elements +@subheading Table of contents + +The table of contents is normally inserted directly before the first headline +of the file. + +@smallexample +#+OPTIONS: toc:2 (only to two levels in TOC) +#+OPTIONS: toc:nil (no TOC at all) +@end smallexample + +@node Paragraphs, Emphasis and monospace, Table of contents, Structural markup elements +@subheading Paragraphs, line breaks, and quoting + +Paragraphs are separated by at least one empty line. If you need to enforce +a line break within a paragraph, use @samp{\\} at the end of a line. + +To keep the line breaks in a region, but otherwise use normal formatting, you +can use this construct, which can also be used to format poetry. + +@smallexample +#+BEGIN_VERSE + Great clouds overhead + Tiny black birds rise and fall + Snow covers Emacs + + -- AlexSchroeder +#+END_VERSE +@end smallexample + +When quoting a passage from another document, it is customary to format this +as a paragraph that is indented on both the left and the right margin. You +can include quotations in Org-mode documents like this: + +@smallexample +#+BEGIN_QUOTE +Everything should be made as simple as possible, +but not any simpler -- Albert Einstein +#+END_QUOTE +@end smallexample + +If you would like to center some text, do it like this: +@smallexample +#+BEGIN_CENTER +Everything should be made as simple as possible, \\ +but not any simpler +#+END_CENTER +@end smallexample + +@node Emphasis and monospace, Comment lines, Paragraphs, Structural markup elements +@subheading Emphasis and monospace + +You can make words @b{*bold*}, @i{/italic/}, _underlined_, @code{=code=} +and @code{~verbatim~}, and, if you must, @samp{+strike-through+}. Text +in the code and verbatim string is not processed for Org-mode specific +syntax, it is exported verbatim. To insert a horizontal rules, use a line +consisting of only dashes, and at least 5 of them. + +@node Comment lines, , Emphasis and monospace, Structural markup elements +@subheading Comment lines + +Lines starting with @samp{#} in column zero are treated as comments and will +never be exported. If you want an indented line to be treated as a comment, +start it with @samp{#+ }. Also entire subtrees starting with the word +@samp{COMMENT} will never be exported. Finally, regions surrounded by +@samp{#+BEGIN_COMMENT} ... @samp{#+END_COMMENT} will not be exported. + +@table @kbd +@item C-c ; +Toggle the COMMENT keyword at the beginning of an entry. +@end table + +@node Images and tables, Literal examples, Structural markup elements, Markup +@section Images and Tables + +For Org mode tables, the lines before the first horizontal separator line +will become table header lines. You can use the following lines somewhere +before the table to assign a caption and a label for cross references, and in +the text you can refer to the object with @code{\ref@{tab:basic-data@}}: + +@smallexample +#+CAPTION: This is the caption for the next table (or link) +#+LABEL: tbl:basic-data + | ... | ...| + |-----|----| +@end smallexample + +Some backends (HTML, La@TeX{}, and DocBook) allow you to directly include +images into the exported document. Org does this, if a link to an image +files does not have a description part, for example @code{[[./img/a.jpg]]}. +If you wish to define a caption for the image and maybe a label for internal +cross references, you sure that the link is on a line by itself precede it +with: + +@smallexample +#+CAPTION: This is the caption for the next figure link (or table) +#+LABEL: fig:SED-HR4049 +[[./img/a.jpg]] +@end smallexample + +You may also define additional attributes for the figure. As this is +backend-specific, see the sections about the individual backends for more +information. + + +@node Literal examples, Include files, Images and tables, Markup +@section Literal examples + +You can include literal examples that should not be subjected to +markup. Such examples will be typeset in monospace, so this is well suited +for source code and similar examples. + +@smallexample +#+BEGIN_EXAMPLE +Some example from a text file. +#+END_EXAMPLE +@end smallexample + +For simplicity when using small examples, you can also start the example +lines with a colon followed by a space. There may also be additional +whitespace before the colon: + +@smallexample +Here is an example + : Some example from a text file. +@end smallexample + +For source code from a programming language, or any other text +that can be marked up by font-lock in Emacs, you can ask for it to +look like the fontified Emacs buffer + +@smallexample +#+BEGIN_SRC emacs-lisp +(defun org-xor (a b) + "Exclusive or." + (if a (not b) b)) +#+END_SRC +@end smallexample + +To edit the example in a special buffer supporting this language, use +@kbd{C-c '} to both enter and leave the editing buffer. + +@node Include files, Embedded LaTeX, Literal examples, Markup +@section Include files + +During export, you can include the content of another file. For example, to +include your @file{.emacs} file, you could use: + +@smallexample +#+INCLUDE: "~/.emacs" src emacs-lisp +@end smallexample +@noindent +The optional second and third parameter are the markup (e.g.@: @samp{quote}, +@samp{example}, or @samp{src}), and, if the markup is @samp{src}, the +language for formatting the contents. The markup is optional, if it is not +given, the text will be assumed to be in Org mode format and will be +processed normally. @kbd{C-c '} will visit the included file. + +@node Embedded LaTeX, , Include files, Markup +@section Embedded La@TeX{} + +For scientific notes which need to be able to contain mathematical symbols +and the occasional formula, Org-mode supports embedding La@TeX{} code into +its files. You can directly use TeX-like macros for special symbols, enter +formulas and entire LaTeX environments. + +@smallexample +Angles are written as Greek letters \alpha, \beta and \gamma. The mass if +the sun is M_sun = 1.989 x 10^30 kg. The radius of the sun is R_@{sun@} = +6.96 x 10^8 m. If $a^2=b$ and $b=2$, then the solution must be either +$a=+\sqrt@{2@}$ or $a=-\sqrt@{2@}$. + +\begin@{equation@} +x=\sqrt@{b@} +\end@{equation@} +@end smallexample +@noindent With +@uref{http://orgmode.org/manual/LaTeX-fragments.html#LaTeX-fragments,special +setup}, LaTeX snippets will be included as images when exporting to HTML. + +@seealso{ +@uref{http://orgmode.org/manual/Markup.html#Markup, Chapter 11 of the manual}} + +@node Exporting, Publishing, Markup, Top +@chapter Exporting + +Org-mode documents can be exported into a variety of other formats: ASCII +export for inclusion into emails, HTML to publish on the web, La@TeX{}/PDF +for beautiful printed documents and DocBook to enter the world of many other +formats using DocBook tools. There is also export to iCalendar format so +that planning information can be incorporated into desktop calendars. + +@menu +* Export options:: Per-file export settings +* The export dispatcher:: How to access exporter commands +* ASCII/Latin-1/UTF-8 export:: Exporting to flat files with encoding +* HTML export:: Exporting to HTML +* LaTeX and PDF export:: Exporting to La@TeX{}, and processing to PDF +* DocBook export:: Exporting to DocBook +* iCalendar export:: +@end menu + +@node Export options, The export dispatcher, Exporting, Exporting +@section Export options + +The exporter recognizes special lines in the buffer which provide +additional information. These lines may be put anywhere in the file. +The whole set of lines can be inserted into the buffer with @kbd{C-c +C-e t}. + +@table @kbd +@item C-c C-e t +Insert template with export options, see example below. +@end table + +@smallexample +#+TITLE: the title to be shown (default is the buffer name) +#+AUTHOR: the author (default taken from @code{user-full-name}) +#+DATE: a date, fixed, of a format string for @code{format-time-string} +#+EMAIL: his/her email address (default from @code{user-mail-address}) +#+DESCRIPTION: the page description, e.g.@: for the XHTML meta tag +#+KEYWORDS: the page keywords, e.g.@: for the XHTML meta tag +#+LANGUAGE: language for HTML, e.g.@: @samp{en} (@code{org-export-default-language}) +#+TEXT: Some descriptive text to be inserted at the beginning. +#+TEXT: Several lines may be given. +#+OPTIONS: H:2 num:t toc:t \n:nil @@:t ::t |:t ^:t f:t TeX:t ... +#+LINK_UP: the ``up'' link of an exported page +#+LINK_HOME: the ``home'' link of an exported page +#+LATEX_HEADER: extra line(s) for the LaTeX header, like \usepackage@{xyz@} +@end smallexample + +@node The export dispatcher, ASCII/Latin-1/UTF-8 export, Export options, Exporting +@section The export dispatcher + +All export commands can be reached using the export dispatcher, which is a +prefix key that prompts for an additional key specifying the command. +Normally the entire file is exported, but if there is an active region that +contains one outline tree, the first heading is used as document title and +the subtrees are exported. + +@table @kbd +@item C-c C-e +Dispatcher for export and publishing commands. +@end table + +@node ASCII/Latin-1/UTF-8 export, HTML export, The export dispatcher, Exporting +@section ASCII/Latin-1/UTF-8 export + +ASCII export produces a simple and very readable version of an Org-mode +file, containing only plain ASCII. Latin-1 and UTF-8 export augment the file +with special characters and symbols available in these encodings. + +@table @kbd +@item C-c C-e a +Export as ASCII file. +@item C-c C-e n @ @ @r{and} @ @ C-c C-e N +Like the above commands, but use Latin-1 encoding. +@item C-c C-e u @ @ @r{and} @ @ C-c C-e U +Like the above commands, but use UTF-8 encoding. +@end table + +@node HTML export, LaTeX and PDF export, ASCII/Latin-1/UTF-8 export, Exporting +@section HTML export + +@table @kbd +@item C-c C-e h +Export as HTML file @file{myfile.html}. +@item C-c C-e b +Export as HTML file and immediately open it with a browser. +@end table + +To insert HTML that should be copied verbatim to +the exported file use either + +@smallexample +#+HTML: Literal HTML code for export +@end smallexample +@noindent or +@smallexample +#+BEGIN_HTML +All lines between these markers are exported literally +#+END_HTML +@end smallexample + +@node LaTeX and PDF export, DocBook export, HTML export, Exporting +@section La@TeX{} and PDF export + +@table @kbd +@item C-c C-e l +Export as La@TeX{} file @file{myfile.tex}. +@item C-c C-e p +Export as La@TeX{} and then process to PDF. +@item C-c C-e d +Export as La@TeX{} and then process to PDF, then open the resulting PDF file. +@end table + +By default, the La@TeX{} output uses the class @code{article}. You can +change this by adding an option like @code{#+LaTeX_CLASS: myclass} in your +file. The class must be listed in @code{org-export-latex-classes}. + +Embedded La@TeX{} as described in @ref{Embedded LaTeX}, will be correctly +inserted into the La@TeX{} file. Similarly to the HTML exporter, you can use +@code{#+LaTeX:} and @code{#+BEGIN_LaTeX ... #+END_LaTeX} construct to add +verbatim LaTeX code. + +@node DocBook export, iCalendar export, LaTeX and PDF export, Exporting +@section DocBook export + +@table @kbd +@item C-c C-e D +Export as DocBook file. +@end table + +Similarly to the HTML exporter, you can use @code{#+DocBook:} and +@code{#+BEGIN_DocBook ... #+END_DocBook} construct to add verbatim LaTeX +code. + +@node iCalendar export, , DocBook export, Exporting +@section iCalendar export + +@table @kbd +@item C-c C-e i +Create iCalendar entries for the current file in a @file{.ics} file. +@item C-c C-e c +Create a single large iCalendar file from all files in +@code{org-agenda-files} and write it to the file given by +@code{org-combined-agenda-icalendar-file}. +@end table + +@seealso{ +@uref{http://orgmode.org/manual/Exporting.html#Exporting, Chapter 12 of the manual}@* +@uref{http://orgmode.org/worg/org-tutorials/images-and-xhtml-export.php, +Sebastian Rose's image handling tutorial}@* +@uref{http://orgmode.org/worg/org-tutorials/org-latex-export.php, Thomas +Dye's LaTeX export tutorial} +@uref{http://orgmode.org/worg/org-tutorials/org-beamer/tutorial.php, Eric +Fraga's BEAMER presentation tutorial}} + +@node Publishing, Working With Source Code, Exporting, Top +@chapter Publishing + +Org includes a publishing management system that allows you to configure +automatic HTML conversion of @emph{projects} composed of interlinked org +files. You can also configure Org to automatically upload your exported HTML +pages and related attachments, such as images and source code files, to a web +server. For detailed instructions about setup, see the manual. + +Here is an example: + +@smalllisp +(setq org-publish-project-alist + '(("org" + :base-directory "~/org/" + :publishing-directory "~/public_html" + :section-numbers nil + :table-of-contents nil + :style ""))) +@end smalllisp + +@table @kbd +@item C-c C-e C +Prompt for a specific project and publish all files that belong to it. +@item C-c C-e P +Publish the project containing the current file. +@item C-c C-e F +Publish only the current file. +@item C-c C-e E +Publish every project. +@end table + +Org uses timestamps to track when a file has changed. The above functions +normally only publish changed files. You can override this and force +publishing of all files by giving a prefix argument to any of the commands +above. + +@seealso{ +@uref{http://orgmode.org/manual/Publishing.html#Publishing, Chapter 13 of the +manual}@* +@uref{http://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.php, +Sebastian Rose's publishing tutorial}@* +@uref{http://orgmode.org/worg/org-tutorials/org-jekyll.php, Ian Barton's +Jekyll/blogging setup}} + +@node Working With Source Code, Miscellaneous, Publishing, Top +@chapter Working with source code +Org-mode provides a number of features for working with source code, +including editing of code blocks in their native major-mode, evaluation of +code blocks, tangling of code blocks, and exporting code blocks and their +results in several formats. + +@subheading Structure of Code Blocks +The structure of code blocks is as follows: + +@example +#+srcname: +#+begin_src
+ +#+end_src +@end example + +Where @code{} is a string used to name the code block, +@code{} specifies the language of the code block +(e.g.@: @code{emacs-lisp}, @code{shell}, @code{R}, @code{python}, etc...), +@code{} can be used to control export of the code block, +@code{
} can be used to control many aspects of code block +behavior as demonstrated below, and @code{} contains the actual source +code. + +@subheading Editing source code +Use @kbd{C-c '} to edit the current code block. This brings up a language +major-mode edit buffer containing the body of the code block. Saving this +buffer will write the new contents back to the Org buffer. Use @kbd{C-c '} +again to exit the edit buffer. + +@subheading Evaluating code blocks +Use @kbd{C-c C-c} to evaluate the current code block and insert its results +in the Org-mode buffer. By default, evaluation is only turned on for +@code{emacs-lisp} code blocks, however support exists for evaluating blocks +in many languages. For a complete list of supported languages see the +manual. The following shows a code block and its results. + +@example +#+begin_src emacs-lisp + (+ 1 2 3 4) +#+end_src + +#+results: +: 10 +@end example + +@subheading Extracting source code +Use @kbd{C-c C-v t} to create pure source code files by extracting code from +source blocks in the current buffer. This is referred to as ``tangling''---a +term adopted from the literate programming community. During ``tangling'' of +code blocks their bodies are expanded using @code{org-babel-expand-src-block} +which can expand both variable and ``noweb'' style references. In order to +tangle a code block it must have a @code{:tangle} header argument, see the +manual for details. + +@subheading Library of Babel +Use @kbd{C-c C-v l} to load the code blocks from an Org-mode files into the +``Library of Babel'', these blocks can then be evaluated from any Org-mode +buffer. A collection of generally useful code blocks is distributed with +Org-mode in @code{contrib/library-of-babel.org}. + +@subheading Header Arguments +Many aspects of the evaluation and export of code blocks are controlled +through header arguments. These can be specified globally, at the file +level, at the outline subtree level, and at the individual code block level. +The following describes some of the header arguments. +@table @code +@item :var +The @code{:var} header argument is used to pass arguments to code blocks. +The values passed to arguments can be literal values, values from org-mode +tables and literal example blocks, or the results of other named code blocks. +@item :results +The @code{:results} header argument controls the @emph{collection}, +@emph{type}, and @emph{handling} of code block results. Values of +@code{output} or @code{value} (the default) specify how results are collected +from a code block's evaluation. Values of @code{vector}, @code{scalar} +@code{file} @code{raw} @code{html} @code{latex} and @code{code} specify the +type of the results of the code block which dictates how they will be +incorporated into the Org-mode buffer. Values of @code{silent}, +@code{replace}, @code{prepend}, and @code{append} specify handling of code +block results, specifically if and how the results should be inserted into +the Org-mode buffer. +@item :session +A header argument of @code{:session} will cause the code block to be +evaluated in a persistent interactive inferior process in Emacs. This allows +for persisting state between code block evaluations, and for manual +inspection of the results of evaluation. +@item :exports +Any combination of the @emph{code} or the @emph{results} of a block can be +retained on export, this is specified by setting the @code{:results} header +argument to @code{code} @code{results} @code{none} or @code{both}. +@item :tangle +A header argument of @code{:tangle yes} will cause a code block's contents to +be tangled to a file named after the filename of the Org-mode buffer. An +alternate file name can be specified with @code{:tangle filename}. +@item :cache +A header argument of @code{:cache yes} will cause associate a hash of the +expanded code block with the results, ensuring that code blocks are only +re-run when their inputs have changed. +@item :noweb +A header argument of @code{:noweb yes} will expand ``noweb'' style references +on evaluation and tangling. +@item :file +Code blocks which output results to files (e.g.@: graphs, diagrams and figures) +can accept a @code{:file filename} header argument in which case the results +are saved to the named file, and a link to the file is inserted into the +Org-mode buffer. +@end table + +@seealso{ +@uref{http://orgmode.org/manual/Literal-examples.html#Literal-examples, +Chapter 11.3 of the manual}@* +@uref{http://orgmode.org/worg/org-contrib/babel/index.php, +The Babel site on Worg}} + +@node Miscellaneous, , Working With Source Code, Top +@chapter Miscellaneous + +@menu +* Completion:: M-TAB knows what you need +* Clean view:: Getting rid of leading stars in the outline +* MobileOrg:: Org-mode on the iPhone +@end menu + +@node Completion, Clean view, Miscellaneous, Miscellaneous +@section Completion + +Org supports in-buffer completion with @kbd{M-@key{TAB}}. This type of +completion does not make use of the minibuffer. You simply type a few +letters into the buffer and use the key to complete text right there. For +example, this command will complete @TeX{} symbols after @samp{\}, TODO +keywords at the beginning of a headline, and tags after @samp{:} in a +headline. + +@node Clean view, MobileOrg, Completion, Miscellaneous +@section A cleaner outline view + +Some people find it noisy and distracting that the Org headlines start with a +potentially large number of stars, and that text below the headlines is not +indented. While this is no problem when writing a @emph{book-like} document +where the outline headings are really section headings, in a more +@emph{list-oriented} outline, indented structure is a lot cleaner: + +@smallexample +@group +* Top level headline | * Top level headline +** Second level | * Second level +*** 3rd level | * 3rd level +some text | some text +*** 3rd level | * 3rd level +more text | more text +* Another top level headline | * Another top level headline +@end group +@end smallexample + +@noindent +If you are using at least Emacs 23.1.50.3 and version 6.29 of Org, this kind +of view can be achieved dynamically at display time using +@code{org-indent-mode}, which will prepend intangible space to each line. +You can turn on @code{org-indent-mode} for all files by customizing the +variable @code{org-startup-indented}, or you can turn it on for individual +files using + +@smallexample +#+STARTUP: indent +@end smallexample + +If you want a similar effect in earlier version of Emacs and/or Org, or if +you want the indentation to be hard space characters so that the plain text +file looks as similar as possible to the Emacs display, Org supports you by +helping to indent (with @key{TAB}) text below each headline, by hiding +leading stars, and by only using levels 1, 3, etc to get two characters +indentation for each level. To get this support in a file, use + +@smallexample +#+STARTUP: hidestars odd +@end smallexample + +@node MobileOrg, , Clean view, Miscellaneous +@section MobileOrg + +@i{MobileOrg} is an application originally developed for the @i{iPhone/iPod +Touch} series of devices, developed by Richard Moreland. There is also an +independent implementation for Android devices, by Matt Jones. +For details, see the Org-mode manual. + +@seealso{ +@uref{http://orgmode.org/manual/Miscellaneous.html#Miscellaneous, Chapter 15 +of the manual}@* +@uref{http://orgmode.org/manual/MobileOrg.html#MobileOrg, Appendix B of the +manual}@* +@uref{http://orgmode.org/orgcard.pdf,Key reference card}} + +@bye + +@ignore + arch-tag: 8f0a8557-0acc-4436-b2b2-0197699e1452 +@end ignore + +@c Local variables: +@c fill-column: 77 +@c End: + + +@c LocalWords: webdavhost pre diff --git a/pack/acp/start/vim-orgmode/examples/mylife.gif b/pack/acp/start/vim-orgmode/examples/mylife.gif new file mode 100644 index 0000000..71e7f65 Binary files /dev/null and b/pack/acp/start/vim-orgmode/examples/mylife.gif differ diff --git a/pack/acp/start/vim-orgmode/examples/mylife.org b/pack/acp/start/vim-orgmode/examples/mylife.org new file mode 100644 index 0000000..090edfe --- /dev/null +++ b/pack/acp/start/vim-orgmode/examples/mylife.org @@ -0,0 +1,26 @@ +* My Life in plain text + - [X] birth + - [-] life [50%] + - [X] use vim + - [ ] get everything else done +* Write minutes of last meeting <2014-08-08 Fri> :work: +** DONE John said + this +** TODO Mary said + that +** WAITING What did Mark say? + [[http://example.com/here/is/the/recording][1st recording]] + [[http://example.com/here/is/the/recording][2nd recording]] +* Some folding headline 1 :one: +** Folded +*** Even more folded +* Some folding headline 2 +** Folded :two: +*** Even more folded +* Some folding headline 3 +** Folded +*** Even more folded :three: +* Some folding headline 4 +** Folded +*** Even more folded + completely unfolded diff --git a/pack/acp/start/vim-orgmode/examples/mylife.png b/pack/acp/start/vim-orgmode/examples/mylife.png new file mode 100644 index 0000000..70cca31 Binary files /dev/null and b/pack/acp/start/vim-orgmode/examples/mylife.png differ diff --git a/pack/acp/start/vim-orgmode/examples/plugins/PluginExample.py b/pack/acp/start/vim-orgmode/examples/plugins/PluginExample.py new file mode 100644 index 0000000..5e4ea8d --- /dev/null +++ b/pack/acp/start/vim-orgmode/examples/plugins/PluginExample.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +from orgmode import echo, echom, echoe, ORGMODE, apply_count, repeat +from orgmode.menu import Submenu, Separator, ActionEntry +from orgmode.keybinding import Keybinding, Plug, Command + +import vim + + +class Example(object): + u""" + Example plugin. + + TODO: Extend this doc! + """ + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'Example') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + # commands for this plugin + self.commands = [] + + @classmethod + def action(cls): + u""" + Some kind of action. + + :returns: TODO + """ + pass + + def register(self): + u""" + Registration of the plugin. + + Key bindings and other initialization should be done here. + """ + # an Action menu entry which binds "keybinding" to action ":action" + self.commands.append(Command(u'OrgActionCommand', + u':py ORGMODE.plugins["Example"].action()')) + self.keybindings.append(Keybinding(u'keybinding', + Plug(u'OrgAction', self.commands[-1]))) + self.menu + ActionEntry(u'&Action', self.keybindings[-1]) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftdetect/org.vim b/pack/acp/start/vim-orgmode/ftdetect/org.vim new file mode 100644 index 0000000..6eae3f8 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftdetect/org.vim @@ -0,0 +1,2 @@ +autocmd BufNewFile,BufRead *.org setfiletype org +"autocmd BufNewFile,BufReadPost org:todo* setfiletype orgtodo diff --git a/pack/acp/start/vim-orgmode/ftplugin/org.cnf b/pack/acp/start/vim-orgmode/ftplugin/org.cnf new file mode 100644 index 0000000..af1f7d0 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/org.cnf @@ -0,0 +1,5 @@ +--langdef=org +--langmap=org:.org +--regex-org=/^(\*+)[[:space:]]+(.*)([[:space:]]+:[^\t ]*:)?$/\1 \2/s,sections/ +--regex-org=/\[\[([^][]+)\]\]/\1/h,hyperlinks/ +--regex-org=/\[\[[^][]+\]\[([^][]+)\]\]/\1/h,hyperlinks/ diff --git a/pack/acp/start/vim-orgmode/ftplugin/org.vim b/pack/acp/start/vim-orgmode/ftplugin/org.vim new file mode 100644 index 0000000..f973ab6 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/org.vim @@ -0,0 +1,169 @@ +" org.vim -- Text outlining and task management for Vim based on Emacs' Org-Mode +" @Author : Jan Christoph Ebersbach (jceb@e-jc.de) +" @License : AGPL3 (see http://www.gnu.org/licenses/agpl.txt) +" @Created : 2010-10-03 +" @Last Modified: Tue 13. Sep 2011 20:52:57 +0200 CEST +" @Revision : 0.4 +" vi: ft=vim:tw=80:sw=4:ts=4:fdm=marker + +if v:version > 702 + if has('python3') + let s:py_version = 'python3 ' + let s:py_env = 'python3 << EOF' + elseif has('python') + let s:py_version = 'python ' + let s:py_env = 'python << EOF' + else + echoerr "Unable to start orgmode. Orgmode depends on Vim >= 7.3 with Python support complied in." + finish + endif +else + echoerr "Unable to start orgmode. Orgmode depends on Vim >= 7.3 with Python support complied in." + finish +endif + +" Init buffer for file {{{1 +if ! exists('b:did_ftplugin') + " default emacs settings + setlocal comments=fb:*,b:#,fb:- + setlocal commentstring=#\ %s + setlocal conceallevel=2 concealcursor=nc + " original emacs settings are: setlocal tabstop=6 shiftwidth=6, but because + " of checkbox indentation the following settings are used: + setlocal tabstop=6 shiftwidth=6 + if exists('g:org_tag_column') + exe 'setlocal textwidth='.g:org_tag_column + else + setlocal textwidth=77 + endif + + " expand tab for counting level of checkbox + setlocal expandtab + + " enable % for angle brackets < > + setlocal matchpairs+=<:> + + " register keybindings if they don't have been registered before + if exists("g:loaded_org") + exe s:py_version . 'ORGMODE.register_keybindings()' + endif +endif + +" Load orgmode just once {{{1 +if &cp || exists("g:loaded_org") + finish +endif +let g:loaded_org = 1 + +" Default org plugins that will be loaded (in the given order) {{{2 +if ! exists('g:org_plugins') && ! exists('b:org_plugins') + let g:org_plugins = ['ShowHide', '|', 'Navigator', 'EditStructure', 'EditCheckbox', '|', 'Hyperlinks', '|', 'Todo', 'TagsProperties', 'Date', 'Agenda', 'Misc', '|', 'Export'] +endif + +" Default org plugin settings {{{2 +" What does this do? +if ! exists('g:org_syntax_highlight_leading_stars') && ! exists('b:org_syntax_highlight_leading_stars') + let g:org_syntax_highlight_leading_stars = 1 +endif + +" setting to conceal aggresively +if ! exists('g:org_aggressive_conceal') && ! exists('b:org_aggressive_conceal') + let g:org_aggressive_conceal = 0 +endif + +" Defined in separate plugins +" Adding Behavior preference: +" 1: go into insert-mode when new heading/checkbox/plainlist added +" 0: retain original mode when new heading/checkbox/plainlist added +if ! exists('g:org_prefer_insert_mode') && ! exists('b:org_prefer_insert_mode') + let g:org_prefer_insert_mode = 1 +endif + +" Menu and document handling {{{1 +function! OrgRegisterMenu() + exe s:py_version . 'ORGMODE.register_menu()' +endfunction + +function! OrgUnregisterMenu() + exe s:py_version . 'ORGMODE.unregister_menu()' +endfunction + +function! OrgDeleteUnusedDocument(bufnr) + exe s:py_env +b = int(vim.eval('a:bufnr')) +if b in ORGMODE._documents: + del ORGMODE._documents[b] +EOF +endfunction + +" show and hide Org menu depending on the filetype +augroup orgmode + au BufEnter * :if &filetype == "org" | call OrgRegisterMenu() | endif + au BufLeave * :if &filetype == "org" | call OrgUnregisterMenu() | endif + au BufDelete * :call OrgDeleteUnusedDocument(expand('')) +augroup END + +" Start orgmode {{{1 +" Expand our path +exec s:py_env +import vim, os, sys + +for p in vim.eval("&runtimepath").split(','): + dname = os.path.join(p, "ftplugin") + if os.path.exists(os.path.join(dname, "orgmode")): + if dname not in sys.path: + sys.path.append(dname) + break + +from orgmode._vim import ORGMODE, insert_at_cursor, get_user_input, date_to_str +ORGMODE.start() + +from Date import Date +import datetime +EOF + +" 3rd Party Plugin Integration {{{1 +" * Repeat {{{2 +try + call repeat#set() +catch +endtry + +" * Tagbar {{{2 +let g:tagbar_type_org = { + \ 'ctagstype' : 'org', + \ 'kinds' : [ + \ 's:sections', + \ 'h:hyperlinks', + \ ], + \ 'sort' : 0, + \ 'deffile' : expand(':p:h') . '/org.cnf' + \ } + +" * Taglist {{{2 +if exists('g:Tlist_Ctags_Cmd') + " Pass parameters to taglist + let g:tlist_org_settings = 'org;s:section;h:hyperlinks' + let g:Tlist_Ctags_Cmd .= ' --options=' . expand(':p:h') . '/org.cnf ' +endif + +" * Calendar.vim {{{2 +fun CalendarAction(day, month, year, week, dir) + let g:org_timestamp = printf("%04d-%02d-%02d Fri", a:year, a:month, a:day) + let datetime_date = printf("datetime.date(%d, %d, %d)", a:year, a:month, a:day) + exe s:py_version . "selected_date = " . datetime_date + " get_user_input + let msg = printf("Inserting %s | Modify date", g:org_timestamp) + exe s:py_version . "modifier = get_user_input('" . msg . "')" + " change date according to user input + exe s:py_version . "newdate = Date._modify_time(selected_date, modifier)" + exe s:py_version . "newdate = date_to_str(newdate)" + " close Calendar + exe "q" + " goto previous window + exe "wincmd p" + exe s:py_version . "timestamp = '" . g:org_timestamp_template . "' % newdate" + exe s:py_version . "insert_at_cursor(timestamp)" + " restore calendar_action + let g:calendar_action = g:org_calendar_action_backup +endf diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/__init__.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/_vim.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/_vim.py new file mode 100644 index 0000000..73ba2c4 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/_vim.py @@ -0,0 +1,411 @@ +# -*- coding: utf-8 -*- + +""" + VIM ORGMODE + ~~~~~~~~~~~~ + + TODO +""" + +import imp +import re +import sys + +import vim +from datetime import datetime + +import orgmode.keybinding +import orgmode.menu +import orgmode.plugins +import orgmode.settings +from orgmode.exceptions import PluginError +from orgmode.vimbuffer import VimBuffer +from orgmode.liborgmode.agenda import AgendaManager + + +REPEAT_EXISTS = bool(int(vim.eval('exists("*repeat#set()")'))) +TAGSPROPERTIES_EXISTS = False + +cache_heading = None + +from orgmode.py3compat.unicode_compatibility import * +from orgmode.py3compat.encode_compatibility import * + + +def realign_tags(f): + u""" + Update tag alignment, dependency to TagsProperties plugin! + """ + def r(*args, **kwargs): + global TAGSPROPERTIES_EXISTS + res = f(*args, **kwargs) + + if not TAGSPROPERTIES_EXISTS and u'TagsProperties' in ORGMODE.plugins: + TAGSPROPERTIES_EXISTS = True + + if TAGSPROPERTIES_EXISTS: + ORGMODE.plugins[u'TagsProperties'].realign_tags() + + return res + return r + + +def repeat(f): + u""" + Integrate with the repeat plugin if available + + The decorated function must return the name of the command to + execute by the repeat plugin. + """ + def r(*args, **kwargs): + res = f(*args, **kwargs) + if REPEAT_EXISTS and isinstance(res, basestring): + vim.command(u_encode(u'silent! call repeat#set("\\%s")' % res)) + return res + return r + + +def apply_count(f): + u""" + Decorator which executes function v:count or v:prevount (not implemented, + yet) times. The decorated function must return a value that evaluates to + True otherwise the function is not repeated. + """ + def r(*args, **kwargs): + count = 0 + try: + count = int(vim.eval(u_encode(u'v:count'))) + + # visual count is not implemented yet + #if not count: + # count = int(vim.eval(u'v:prevcount'.encode(u'utf-8'))) + except BaseException as e: + pass + + res = f(*args, **kwargs) + count -= 1 + while res and count > 0: + f(*args, **kwargs) + count -= 1 + return res + return r + + +def echo(message): + u""" + Print a regular message that will not be visible to the user when + multiple lines are printed + """ + for m in message.split(u'\n'): + vim.command(u_encode(u':echo "%s"' % m)) + + +def echom(message): + u""" + Print a regular message that will be visible to the user, even when + multiple lines are printed + """ + # probably some escaping is needed here + for m in message.split(u'\n'): + vim.command(u_encode(u':echomsg "%s"' % m)) + + +def echoe(message): + u""" + Print an error message. This should only be used for serious errors! + """ + # probably some escaping is needed here + for m in message.split(u'\n'): + vim.command(u_encode(u':echoerr "%s"' % m)) + + +def insert_at_cursor(text, move=True, start_insertmode=False): + u"""Insert text at the position of the cursor. + + If move==True move the cursor with the inserted text. + """ + d = ORGMODE.get_document(allow_dirty=True) + line, col = vim.current.window.cursor + _text = d._content[line - 1] + d._content[line - 1] = _text[:col + 1] + text + _text[col + 1:] + if move: + vim.current.window.cursor = (line, col + len(text)) + if start_insertmode: + vim.command(u_encode(u'startinsert')) + + +def get_user_input(message): + u"""Print the message and take input from the user. + Return the input or None if there is no input. + """ + vim.command(u_encode(u'call inputsave()')) + vim.command(u_encode(u"let user_input = input('" + message + u": ')")) + vim.command(u_encode(u'call inputrestore()')) + try: + return u_decode(vim.eval(u_encode(u'user_input'))) + except: + return None + + +def get_bufnumber(bufname): + """ + Return the number of the buffer for the given bufname if it exist; + else None. + """ + for b in vim.buffers: + if b.name == bufname: + return int(b.number) + + +def get_bufname(bufnr): + """ + Return the name of the buffer for the given bufnr if it exist; else None. + """ + for b in vim.buffers: + if b.number == bufnr: + return b.name + + +def indent_orgmode(): + u""" Set the indent value for the current line in the variable + b:indent_level + + Vim prerequisites: + :setlocal indentexpr=Method-which-calls-indent_orgmode + + :returns: None + """ + line = int(vim.eval(u_encode(u'v:lnum'))) + d = ORGMODE.get_document() + heading = d.current_heading(line - 1) + if heading and line != heading.start_vim: + heading.init_checkboxes() + checkbox = heading.current_checkbox() + level = heading.level + 1 + if checkbox: + if line != checkbox.start_vim: + # indent body up to the beginning of the checkbox' text + # if checkbox isn't indented to the proper location, the body + # won't be indented either + level = checkbox.level + len(checkbox.type) + 1 + \ + (4 if checkbox.status else 0) + vim.command(u_encode((u'let b:indent_level = %d' % level))) + + +def fold_text(allow_dirty=False): + u""" Set the fold text + :setlocal foldtext=Method-which-calls-foldtext + + :allow_dirty: Perform a query without (re)building the DOM if True + :returns: None + """ + line = int(vim.eval(u_encode(u'v:foldstart'))) + d = ORGMODE.get_document(allow_dirty=allow_dirty) + heading = None + if allow_dirty: + heading = d.find_current_heading(line - 1) + else: + heading = d.current_heading(line - 1) + if heading: + str_heading = unicode(heading) + + # expand tabs + ts = int(vim.eval(u_encode(u'&ts'))) + idx = str_heading.find(u'\t') + if idx != -1: + tabs, spaces = divmod(idx, ts) + str_heading = str_heading.replace(u'\t', u' ' * (ts - spaces), 1) + str_heading = str_heading.replace(u'\t', u' ' * ts) + + # Workaround for vim.command seems to break the completion menu + vim.eval(u_encode(u'SetOrgFoldtext("%s...")' % (re.sub(r'\[\[([^[\]]*\]\[)?([^[\]]+)\]\]', r'\2', + str_heading).replace( u'\\', u'\\\\').replace(u'"', u'\\"'), ))) + + +def fold_orgmode(allow_dirty=False): + u""" Set the fold expression/value for the current line in the variable + b:fold_expr + + Vim prerequisites: + :setlocal foldmethod=expr + :setlocal foldexpr=Method-which-calls-fold_orgmode + + :allow_dirty: Perform a query without (re)building the DOM if True + :returns: None + """ + line = int(vim.eval(u_encode(u'v:lnum'))) + d = ORGMODE.get_document(allow_dirty=allow_dirty) + heading = None + if allow_dirty: + heading = d.find_current_heading(line - 1) + else: + heading = d.current_heading(line - 1) + + # if cache_heading != heading: + # heading.init_checkboxes() + # checkbox = heading.current_checkbox() + + # cache_heading = heading + if heading: + # if checkbox: + # vim.command((u'let b:fold_expr = ">%d"' % heading.level + checkbox.level).encode(u'utf-8')) + if 0: + pass + elif line == heading.start_vim: + vim.command(u_encode(u'let b:fold_expr = ">%d"' % heading.level)) + #elif line == heading.end_vim: + # vim.command((u'let b:fold_expr = "<%d"' % heading.level).encode(u'utf-8')) + # end_of_last_child_vim is a performance junky and is actually not needed + #elif line == heading.end_of_last_child_vim: + # vim.command((u'let b:fold_expr = "<%d"' % heading.level).encode(u'utf-8')) + else: + vim.command(u_encode(u'let b:fold_expr = %d' % heading.level)) + + +def date_to_str(date): + if isinstance(date, datetime): + date = date.strftime(u_decode(u_encode(u'%Y-%m-%d %a %H:%M'))) + else: + date = date.strftime(u_decode(u_encode(u'%Y-%m-%d %a'))) + return date + +class OrgMode(object): + u""" Vim Buffer """ + + def __init__(self): + object.__init__(self) + self.debug = bool(int(orgmode.settings.get(u'org_debug', False))) + + self.orgmenu = orgmode.menu.Submenu(u'&Org') + self._plugins = {} + # list of vim buffer objects + self._documents = {} + + # agenda manager + self.agenda_manager = AgendaManager() + + def get_document(self, bufnr=0, allow_dirty=False): + """ Retrieve instance of vim buffer document. This Document should be + used for manipulating the vim buffer. + + :bufnr: Retrieve document with bufnr + :allow_dirty: Allow the retrieved document to be dirty + + :returns: vim buffer instance + """ + if bufnr == 0: + bufnr = vim.current.buffer.number + + if bufnr in self._documents: + if allow_dirty or self._documents[bufnr].is_insync: + return self._documents[bufnr] + self._documents[bufnr] = VimBuffer(bufnr).init_dom() + return self._documents[bufnr] + + @property + def plugins(self): + return self._plugins.copy() + + @orgmode.keybinding.register_keybindings + @orgmode.keybinding.register_commands + @orgmode.menu.register_menu + def register_plugin(self, plugin): + if not isinstance(plugin, basestring): + raise ValueError(u'Parameter plugin is not of type string') + + if plugin == u'|': + self.orgmenu + orgmode.menu.Separator() + self.orgmenu.children[-1].create() + return + + if plugin in self._plugins: + raise PluginError(u'Plugin %s has already been loaded') + + # a python module + module = None + + # actual plugin class + _class = None + + # locate module and initialize plugin class + try: + module = imp.find_module(plugin, orgmode.plugins.__path__) + except ImportError as e: + echom(u'Plugin not found: %s' % plugin) + if self.debug: + raise e + return + + if not module: + echom(u'Plugin not found: %s' % plugin) + return + + try: + module = imp.load_module(plugin, *module) + if not hasattr(module, plugin): + echoe(u'Unable to find plugin: %s' % plugin) + if self.debug: + raise PluginError(u'Unable to find class %s' % plugin) + return + _class = getattr(module, plugin) + self._plugins[plugin] = _class() + self._plugins[plugin].register() + if self.debug: + echo(u'Plugin registered: %s' % plugin) + return self._plugins[plugin] + except BaseException as e: + echoe(u'Unable to activate plugin: %s' % plugin) + echoe(u"%s" % e) + if self.debug: + import traceback + echoe(traceback.format_exc()) + + def register_keybindings(self): + @orgmode.keybinding.register_keybindings + def dummy(plugin): + return plugin + + if sys.version_info < (3, ): + for p in self.plugins.itervalues(): + dummy(p) + else: + for p in self.plugins.values(): + dummy(p) + + def register_menu(self): + self.orgmenu.create() + + def unregister_menu(self): + vim.command(u_encode(u'silent! aunmenu Org')) + + def start(self): + u""" Start orgmode and load all requested plugins + """ + plugins = orgmode.settings.get(u"org_plugins") + + if not plugins: + echom(u'orgmode: No plugins registered.') + + if isinstance(plugins, basestring): + try: + self.register_plugin(plugins) + except BaseException as e: + import traceback + traceback.print_exc() + elif isinstance(plugins, list) or \ + isinstance(plugins, tuple): + for p in plugins: + try: + self.register_plugin(p) + except BaseException as e: + echoe('Error in %s plugin:' % p) + import traceback + traceback.print_exc() + + return plugins + + +ORGMODE = OrgMode() + + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/Makefile b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/Makefile new file mode 100644 index 0000000..ff92ec5 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/Makefile @@ -0,0 +1,230 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) + $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " epub3 to make an epub3" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + @echo " dummy to check syntax errors of document sources" + +.PHONY: clean +clean: + rm -rf $(BUILDDIR)/* + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/orgmode.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/orgmode.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/orgmode" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/orgmode" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: epub3 +epub3: + $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 + @echo + @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + +.PHONY: dummy +dummy: + $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy + @echo + @echo "Build finished. Dummy builder generates no files." diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/conf.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/conf.py new file mode 100644 index 0000000..246a80d --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/conf.py @@ -0,0 +1,387 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# orgmode documentation build configuration file, created by +# sphinx-quickstart on Sat May 21 15:51:55 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import mock + +# Mock vim +MOCK_MODULES = ['vim'] +for m in MOCK_MODULES: + sys.modules[m] = mock.Mock() + +import vim +vim.eval = mock.MagicMock(return_value=1) + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('../..')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.todo', + 'sphinx.ext.viewcode', + 'sphinx.ext.doctest', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', +] + +# Napoleon config +napoleon_google_docstring = True +napoleon_numpy_docstring = True +napoleon_include_private_with_doc = True +napoleon_include_special_with_doc = True +napoleon_use_admonition_for_examples = False +napoleon_use_admonition_for_notes = False +napoleon_use_admonition_for_references = False +napoleon_use_ivar = False +napoleon_use_param = True +napoleon_use_rtype = True + +# Add any paths that contain templates here, relative to this directory. +#templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'orgmode' +copyright = '2016, Author' +author = 'Author' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '' +# The full version, including alpha/beta/rc tags. +release = '' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. +# " v documentation" by default. +html_title = 'orgmode v0.5' + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not None, a 'Last updated on:' timestamp is inserted at every page +# bottom, using the given strftime format. +# The empty string is equivalent to '%b %d, %Y'. +#html_last_updated_fmt = None + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# 'ja' uses this config value. +# 'zh' user can custom change `jieba` dictionary path. +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'orgmodedoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'orgmode.tex', 'orgmode Documentation', + 'Author', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'orgmode', 'orgmode Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'orgmode', 'orgmode Documentation', + author, 'orgmode', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = copyright + +# The basename for the epub file. It defaults to the project name. +#epub_basename = project + +# The HTML theme for the epub output. Since the default themes are not +# optimized for small screen space, using the same theme for HTML and epub +# output is usually not wise. This defaults to 'epub', a theme designed to save +# visual space. +#epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or 'en' if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +#epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +#epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files that should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +#epub_tocscope = 'default' + +# Fix unsupported image types using the Pillow. +#epub_fix_images = False + +# Scale large images. +#epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#epub_show_urls = 'inline' + +# If false, no index is generated. +#epub_use_index = True diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/index.rst b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/index.rst new file mode 100644 index 0000000..3680d38 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/index.rst @@ -0,0 +1,22 @@ +.. orgmode documentation master file, created by + sphinx-quickstart on Sat May 21 16:35:00 2016. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to orgmode's documentation! +=================================== + +Contents: + +.. toctree:: + :maxdepth: 4 + + orgmode + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/make.bat b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/make.bat new file mode 100644 index 0000000..3de72e8 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/make.bat @@ -0,0 +1,281 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. epub3 to make an epub3 + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + echo. dummy to check syntax errors of document sources + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 1>NUL 2>NUL +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\orgmode.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\orgmode.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "epub3" ( + %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +if "%1" == "dummy" ( + %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. Dummy builder generates no files. + goto end +) + +:end diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.liborgmode.rst b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.liborgmode.rst new file mode 100644 index 0000000..3994f07 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.liborgmode.rst @@ -0,0 +1,78 @@ +orgmode.liborgmode package +========================== + +Submodules +---------- + +orgmode.liborgmode.agenda module +-------------------------------- + +.. automodule:: orgmode.liborgmode.agenda + :members: + :undoc-members: + :show-inheritance: + +orgmode.liborgmode.agendafilter module +-------------------------------------- + +.. automodule:: orgmode.liborgmode.agendafilter + :members: + :undoc-members: + :show-inheritance: + +orgmode.liborgmode.base module +------------------------------ + +.. automodule:: orgmode.liborgmode.base + :members: + :undoc-members: + :show-inheritance: + +orgmode.liborgmode.checkboxes module +------------------------------------ + +.. automodule:: orgmode.liborgmode.checkboxes + :members: + :undoc-members: + :show-inheritance: + +orgmode.liborgmode.documents module +----------------------------------- + +.. automodule:: orgmode.liborgmode.documents + :members: + :undoc-members: + :show-inheritance: + +orgmode.liborgmode.dom_obj module +--------------------------------- + +.. automodule:: orgmode.liborgmode.dom_obj + :members: + :undoc-members: + :show-inheritance: + +orgmode.liborgmode.headings module +---------------------------------- + +.. automodule:: orgmode.liborgmode.headings + :members: + :undoc-members: + :show-inheritance: + +orgmode.liborgmode.orgdate module +--------------------------------- + +.. automodule:: orgmode.liborgmode.orgdate + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: orgmode.liborgmode + :members: + :undoc-members: + :show-inheritance: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.plugins.rst b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.plugins.rst new file mode 100644 index 0000000..0722df5 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.plugins.rst @@ -0,0 +1,110 @@ +orgmode.plugins package +======================= + +Submodules +---------- + +orgmode.plugins.Agenda module +----------------------------- + +.. automodule:: orgmode.plugins.Agenda + :members: + :undoc-members: + :show-inheritance: + +orgmode.plugins.Date module +--------------------------- + +.. automodule:: orgmode.plugins.Date + :members: + :undoc-members: + :show-inheritance: + +orgmode.plugins.EditCheckbox module +----------------------------------- + +.. automodule:: orgmode.plugins.EditCheckbox + :members: + :undoc-members: + :show-inheritance: + +orgmode.plugins.EditStructure module +------------------------------------ + +.. automodule:: orgmode.plugins.EditStructure + :members: + :undoc-members: + :show-inheritance: + +orgmode.plugins.Export module +----------------------------- + +.. automodule:: orgmode.plugins.Export + :members: + :undoc-members: + :show-inheritance: + +orgmode.plugins.Hyperlinks module +--------------------------------- + +.. automodule:: orgmode.plugins.Hyperlinks + :members: + :undoc-members: + :show-inheritance: + +orgmode.plugins.LoggingWork module +---------------------------------- + +.. automodule:: orgmode.plugins.LoggingWork + :members: + :undoc-members: + :show-inheritance: + +orgmode.plugins.Misc module +--------------------------- + +.. automodule:: orgmode.plugins.Misc + :members: + :undoc-members: + :show-inheritance: + +orgmode.plugins.Navigator module +-------------------------------- + +.. automodule:: orgmode.plugins.Navigator + :members: + :undoc-members: + :show-inheritance: + +orgmode.plugins.ShowHide module +------------------------------- + +.. automodule:: orgmode.plugins.ShowHide + :members: + :undoc-members: + :show-inheritance: + +orgmode.plugins.TagsProperties module +------------------------------------- + +.. automodule:: orgmode.plugins.TagsProperties + :members: + :undoc-members: + :show-inheritance: + +orgmode.plugins.Todo module +--------------------------- + +.. automodule:: orgmode.plugins.Todo + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: orgmode.plugins + :members: + :undoc-members: + :show-inheritance: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.py3compat.rst b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.py3compat.rst new file mode 100644 index 0000000..4b37cc3 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.py3compat.rst @@ -0,0 +1,46 @@ +orgmode.py3compat package +========================= + +Submodules +---------- + +orgmode.py3compat.encode_compatibility module +--------------------------------------------- + +.. automodule:: orgmode.py3compat.encode_compatibility + :members: + :undoc-members: + :show-inheritance: + +orgmode.py3compat.py_py3_string module +-------------------------------------- + +.. automodule:: orgmode.py3compat.py_py3_string + :members: + :undoc-members: + :show-inheritance: + +orgmode.py3compat.unicode_compatibility module +---------------------------------------------- + +.. automodule:: orgmode.py3compat.unicode_compatibility + :members: + :undoc-members: + :show-inheritance: + +orgmode.py3compat.xrange_compatibility module +--------------------------------------------- + +.. automodule:: orgmode.py3compat.xrange_compatibility + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: orgmode.py3compat + :members: + :undoc-members: + :show-inheritance: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.rst b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.rst new file mode 100644 index 0000000..afddb3f --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.rst @@ -0,0 +1,71 @@ +orgmode package +=============== + +Subpackages +----------- + +.. toctree:: + + orgmode.liborgmode + orgmode.plugins + orgmode.py3compat + +Submodules +---------- + +orgmode._vim module +------------------- + +.. automodule:: orgmode._vim + :members: + :undoc-members: + :show-inheritance: + +orgmode.exceptions module +------------------------- + +.. automodule:: orgmode.exceptions + :members: + :undoc-members: + :show-inheritance: + +orgmode.keybinding module +------------------------- + +.. automodule:: orgmode.keybinding + :members: + :undoc-members: + :show-inheritance: + +orgmode.menu module +------------------- + +.. automodule:: orgmode.menu + :members: + :undoc-members: + :show-inheritance: + +orgmode.settings module +----------------------- + +.. automodule:: orgmode.settings + :members: + :undoc-members: + :show-inheritance: + +orgmode.vimbuffer module +------------------------ + +.. automodule:: orgmode.vimbuffer + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: orgmode + :members: + :undoc-members: + :show-inheritance: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/exceptions.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/exceptions.py new file mode 100644 index 0000000..52ffe4e --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/exceptions.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + + +class PluginError(BaseException): + def __init__(self, message): + BaseException.__init__(self, message) + + +class BufferNotFound(BaseException): + def __init__(self, message): + BaseException.__init__(self, message) + + +class BufferNotInSync(BaseException): + def __init__(self, message): + BaseException.__init__(self, message) + + +class HeadingDomError(BaseException): + def __init__(self, message): + BaseException.__init__(self, message) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/keybinding.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/keybinding.py new file mode 100644 index 0000000..ebee100 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/keybinding.py @@ -0,0 +1,217 @@ +# -*- coding: utf-8 -*- + +import vim + +MODE_ALL = u'a' +MODE_NORMAL = u'n' +MODE_VISUAL = u'v' +MODE_INSERT = u'i' +MODE_OPERATOR = u'o' + +OPTION_BUFFER_ONLY = u'' +OPTION_SLIENT = u'' + +from orgmode.py3compat.encode_compatibility import * + +def _register(f, name): + def r(*args, **kwargs): + p = f(*args, **kwargs) + if hasattr(p, name) and isinstance(getattr(p, name), list): + for i in getattr(p, name): + i.create() + return p + return r + + +def register_keybindings(f): + return _register(f, u'keybindings') + + +def register_commands(f): + return _register(f, u'commands') + + +class Command(object): + u""" A vim command """ + + def __init__(self, name, command, arguments=u'0', complete=None, overwrite_exisiting=False): + u""" + :name: The name of command, first character must be uppercase + :command: The actual command that is executed + :arguments: See :h :command-nargs, only the arguments need to be specified + :complete: See :h :command-completion, only the completion arguments need to be specified + """ + object.__init__(self) + + self._name = name + self._command = command + self._arguments = arguments + self._complete = complete + self._overwrite_exisiting = overwrite_exisiting + + def __unicode__(self): + return u':%s' % self.name + + def __str__(self): + return u_encode(self.__unicode__()) + + @property + def name(self): + return self._name + + @property + def command(self): + return self._command + + @property + def arguments(self): + return self._arguments + + @property + def complete(self): + return self._complete + + @property + def overwrite_exisiting(self): + return self._overwrite_exisiting + + def create(self): + u""" Register/create the command + """ + vim.command(u_encode(':command%(overwrite)s -nargs=%(arguments)s %(complete)s %(name)s %(command)s' % + {u'overwrite': '!' if self.overwrite_exisiting else '', + u'arguments': u_encode(self.arguments), + u'complete': '-complete=%s' % u_encode(self.complete) if self.complete else '', + u'name': self.name, + u'command': self.command} + )) + + +class Plug(object): + u""" Represents a to an abitrary command """ + + def __init__(self, name, command, mode=MODE_NORMAL): + u""" + :name: the name of the should be ScriptnameCommandname + :command: the actual command + """ + object.__init__(self) + + if mode not in (MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT, MODE_OPERATOR): + raise ValueError(u'Parameter mode not in MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT, MODE_OPERATOR') + self._mode = mode + + self.name = name + self.command = command + self.created = False + + def __unicode__(self): + return u'%s' % self.name + + def __str__(self): + return u_encode(self.__unicode__()) + + def create(self): + if not self.created: + self.created = True + cmd = self._mode + if cmd == MODE_ALL: + cmd = u'' + vim.command(u_encode(u':%snoremap %s %s' % (cmd, str(self), self.command))) + + @property + def mode(self): + return self._mode + + +class Keybinding(object): + u""" Representation of a single key binding """ + + def __init__(self, key, action, mode=None, options=None, remap=True, buffer_only=True, silent=True): + u""" + :key: the key(s) action is bound to + :action: the action triggered by key(s) + :mode: definition in which vim modes the key binding is valid. Should be one of MODE_* + :option: list of other options like , ... + :repmap: allow or disallow nested mapping + :buffer_only: define the key binding only for the current buffer + """ + object.__init__(self) + self._key = key + self._action = action + + # grab mode from plug if not set otherwise + if isinstance(self._action, Plug) and not mode: + mode = self._action.mode + + if mode not in (MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT, MODE_OPERATOR): + raise ValueError(u'Parameter mode not in MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT, MODE_OPERATOR') + self._mode = mode + self._options = options + if self._options is None: + self._options = [] + self._remap = remap + self._buffer_only = buffer_only + self._silent = silent + + if self._buffer_only and OPTION_BUFFER_ONLY not in self._options: + self._options.append(OPTION_BUFFER_ONLY) + + if self._silent and OPTION_SLIENT not in self._options: + self._options.append(OPTION_SLIENT) + + @property + def key(self): + return self._key + + @property + def action(self): + return str(self._action) + + @property + def mode(self): + return self._mode + + @property + def options(self): + return self._options[:] + + @property + def remap(self): + return self._remap + + @property + def buffer_only(self): + return self._buffer_only + + @property + def silent(self): + return self._silent + + def create(self): + from orgmode._vim import ORGMODE, echom + + cmd = self._mode + if cmd == MODE_ALL: + cmd = u'' + if not self._remap: + cmd += u'nore' + try: + create_mapping = True + if isinstance(self._action, Plug): + # create plug + self._action.create() + if int(vim.eval(u_encode(u'hasmapto("%s")' % (self._action, )))): + create_mapping = False + if isinstance(self._action, Command): + # create command + self._action.create() + + if create_mapping: + vim.command(u_encode(u':%smap %s %s %s' % (cmd, u' '.join(self._options), self._key, self._action))) + except BaseException as e: + if ORGMODE.debug: + echom(u'Failed to register key binding %s %s' % (self._key, self._action)) + + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/__init__.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/agenda.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/agenda.py new file mode 100644 index 0000000..5f34195 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/agenda.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +u""" + Agenda + ~~~~~~~~~~~~~~~~~~ + + The agenda is one of the main concepts of orgmode. It allows to + collect TODO items from multiple org documents in an agenda view. + + Features: + * filtering + * sorting +""" + +from orgmode.liborgmode.agendafilter import filter_items +from orgmode.liborgmode.agendafilter import is_within_week_and_active_todo +from orgmode.liborgmode.agendafilter import contains_active_todo +from orgmode.liborgmode.agendafilter import contains_active_date + + +class AgendaManager(object): + u"""Simple parsing of Documents to create an agenda.""" + # TODO Move filters in this file, they do the same thing + + def __init__(self): + super(AgendaManager, self).__init__() + + def get_todo(self, documents): + u""" + Get the todo agenda for the given documents (list of document). + """ + filtered = [] + for document in iter(documents): + # filter and return headings + filtered.extend(filter_items(document.all_headings(), + [contains_active_todo])) + return sorted(filtered) + + def get_next_week_and_active_todo(self, documents): + u""" + Get the agenda for next week for the given documents (list of + document). + """ + filtered = [] + for document in iter(documents): + # filter and return headings + filtered.extend(filter_items(document.all_headings(), + [is_within_week_and_active_todo])) + return sorted(filtered) + + def get_timestamped_items(self, documents): + u""" + Get all time-stamped items in a time-sorted way for the given + documents (list of document). + """ + filtered = [] + for document in iter(documents): + # filter and return headings + filtered.extend(filter_items(document.all_headings(), + [contains_active_date])) + return sorted(filtered) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/agendafilter.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/agendafilter.py new file mode 100644 index 0000000..64deff7 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/agendafilter.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- + +u""" + agendafilter + ~~~~~~~~~~~~~~~~ + + AgendaFilter contains all the filters that can be applied to create the + agenda. + + + All functions except filter_items() in the module are filters. Given a + heading they return if the heading meets the critera of the filter. + + The function filter_items() can combine different filters and only returns + the filtered headings. +""" +from datetime import datetime +from datetime import timedelta + +try: + from itertools import ifilter as filter +except: + pass + + +def filter_items(headings, filters): + u""" Filter the given headings. + + Args: + headings (list): Contains headings + filters (list): Filters that will be applied. All functions in + this module (except this function) are filters. + + Returns: + filter iterator: Headings which were not filtered. + + Examples: + >>> filtered = filter_items(headings, [contains_active_date, + contains_active_todo]) + """ + filtered = headings + for f in filters: + filtered = filter(f, filtered) + return filtered + + +def is_within_week(heading): + u""" Test if headings date is withing a week + + Returns: + bool: True if the date in the deading is within a week in the future (or + older False otherwise. + """ + if contains_active_date(heading): + next_week = datetime.today() + timedelta(days=7) + if heading.active_date < next_week: + return True + + +def is_within_week_and_active_todo(heading): + u""" + Returns: + bool: True if heading contains an active TODO and the date is within a + week. + """ + return is_within_week(heading) and contains_active_todo(heading) + + +def contains_active_todo(heading): + u""" + + Returns: + bool: True if heading contains an active TODO. + """ + # TODO make this more efficient by checking some val and not calling the + # function + # TODO why is this import failing at top level? circular dependecy... + from orgmode._vim import ORGMODE + active = [] + for act in ORGMODE.get_document().get_todo_states(): + active.extend(act[0]) + return heading.todo in active + + +def contains_active_date(heading): + u""" + + Returns: + bool: True if heading contains an active date. + """ + return not(heading.active_date is None) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/base.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/base.py new file mode 100644 index 0000000..4f3ea84 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/base.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- + +""" + base + ~~~~~~~~~~ + + Here are some really basic data structures that are used throughout + the liborgmode. +""" + +try: + from collections import UserList +except: + from UserList import UserList + +import collections +import sys +from orgmode.py3compat.unicode_compatibility import * + + +def flatten_list(lst): + """ Flattens a list + + Args: + lst (iterable): An iterable that will is non-flat + + Returns: + list: Flat list + """ + # TODO write tests + def gen_lst(item): + if isinstance(item, basestring) or isinstance(item, bytes): + yield item + elif isinstance(item, collections.Iterable): + # yield from would be so nice... but c'est la vie + for val in item: + for final in gen_lst(val): + yield final + else: + yield item + return [i for i in gen_lst(lst)] + + +class Direction(): + u""" + Direction is used to indicate the direction of certain actions. + + Example: it defines the direction headings get parted in. + """ + FORWARD = 1 + BACKWARD = 2 + + +class MultiPurposeList(UserList): + u""" + A Multi Purpose List is a list that calls a user defined hook on + change. The implementation is very basic - the hook is called without any + parameters. Otherwise the Multi Purpose List can be used like any other + list. + + The member element "data" can be used to fill the list without causing the + list to be marked dirty. This should only be used during initialization! + """ + + def __init__(self, initlist=None, on_change=None): + UserList.__init__(self, initlist) + self._on_change = on_change + + def _changed(self): + u""" Call hook """ + if callable(self._on_change): + self._on_change() + + def __setitem__(self, i, item): + if sys.version_info < (3, ) and isinstance(i, slice): + start, stop, _ = i.indices(len(self)) + UserList.__setslice__(self, start, stop, item) + else: + UserList.__setitem__(self, i, item) + self._changed() + + def __delitem__(self, i): + if sys.version_info < (3, ) and isinstance(i, slice): + start, stop, _ = i.indices(len(self)) + UserList.__delslice__(self, start, stop) + else: + UserList.__delitem__(self, i) + self._changed() + + def __getitem__(self, i): + if sys.version_info < (3, ): + if isinstance(i, slice): + # TODO Return just a list. Why? + return [self[i] for i in range(*i.indices(len(self)))] + # return UserList([self[i] for i in range(*i.indices(len(self)))]) + return UserList.__getitem__(self, i) + + # NOTE: These wrappers are necessary because of python 2 + def __setslice__(self, i, j, other): + self.__setitem__(slice(i, j), other) + + def __delslice__(self, i, j): + self.__delitem__(slice(i, j)) + + def __getslice__(self, i, j): + return self.__getitem__(slice(i, j)) + + def __iadd__(self, other): + res = UserList.__iadd__(self, other) + self._changed() + return res + + def __imul__(self, n): + res = UserList.__imul__(self, n) + self._changed() + return res + + def append(self, item): + UserList.append(self, item) + self._changed() + + def insert(self, i, item): + UserList.insert(self, i, item) + self._changed() + + def pop(self, i=-1): + item = self[i] + del self[i] + return item + + def remove(self, item): + self.__delitem__(self.index(item)) + + def reverse(self): + UserList.reverse(self) + self._changed() + + def sort(self, *args, **kwds): + UserList.sort(self, *args, **kwds) + self._changed() + + def extend(self, other): + UserList.extend(self, other) + self._changed() + + +def get_domobj_range(content=[], position=0, direction=Direction.FORWARD, identify_fun=None): + u""" + Get the start and end line number of the dom obj lines from content. + + :content: String to be recognized dom obj + :positon: Line number in content + :direction: Search direction + :identify_fun: A identify function to recognize dom obj(Heading, Checkbox) title string. + + :return: Start and end line number for the recognized dom obj. + """ + len_cb = len(content) + + if position < 0 or position > len_cb: + return (None, None) + + tmp_line = position + start = None + end = None + + if direction == Direction.FORWARD: + while tmp_line < len_cb: + if identify_fun(content[tmp_line]) is not None: + if start is None: + start = tmp_line + elif end is None: + end = tmp_line - 1 + if start is not None and end is not None: + break + tmp_line += 1 + else: + while tmp_line >= 0 and tmp_line < len_cb: + if identify_fun(content[tmp_line]) is not None: + if start is None: + start = tmp_line + elif end is None: + end = tmp_line - 1 + if start is not None and end is not None: + break + tmp_line -= 1 if start is None else -1 + + return (start, end) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/checkboxes.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/checkboxes.py new file mode 100644 index 0000000..fcf23a7 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/checkboxes.py @@ -0,0 +1,406 @@ +# -*- coding: utf-8 -*- + +""" + checkboxes + ~~~~~~~~~~ + + TODO: explain this :) +""" + +import re +try: + from collections import UserList +except: + from UserList import UserList + +import vim +from orgmode.liborgmode.base import MultiPurposeList, flatten_list +from orgmode.liborgmode.orgdate import OrgTimeRange +from orgmode.liborgmode.orgdate import get_orgdate +from orgmode.liborgmode.dom_obj import DomObj, DomObjList, REGEX_SUBTASK, REGEX_SUBTASK_PERCENT, REGEX_HEADING, REGEX_CHECKBOX + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.unicode_compatibility import * + + +class Checkbox(DomObj): + u""" Structural checkbox object """ + STATUS_ON = u'[X]' + STATUS_OFF = u'[ ]' + # intermediate status + STATUS_INT = u'[-]' + + def __init__(self, level=1, type=u'-', title=u'', status=u'[ ]', body=None): + u""" + :level: Indent level of the checkbox + :type: Type of the checkbox list (-, +, *) + :title: Title of the checkbox + :status: Status of the checkbox ([ ], [X], [-]) + :body: Body of the checkbox + """ + DomObj.__init__(self, level=level, title=title, body=body) + + # heading + self._heading = None + + self._children = CheckboxList(obj=self) + self._dirty_checkbox = False + # list type + self._type = u'-' + if type: + self.type = type + # status + self._status = Checkbox.STATUS_OFF + if status: + self.status = status + + def __unicode__(self): + return u' ' * self.level + self.type + u' ' + \ + (self.status + u' ' if self.status else u'') + self.title + + def __str__(self): + return u_encode(self.__unicode__()) + + def __len__(self): + # 1 is for the heading's title + return 1 + len(self.body) + + def copy(self, including_children=True, parent=None): + u""" + Create a copy of the current checkbox. The checkbox will be completely + detached and not even belong to a document anymore. + + :including_children: If True a copy of all children is create as + well. If False the returned checkbox doesn't + have any children. + :parent: Don't use this parameter. It's set + automatically. + """ + checkbox = self.__class__( + level=self.level, title=self.title, + body=self.body[:]) + if parent: + parent.children.append(checkbox) + if including_children and self.children: + for item in self.children: + item.copy( + including_children=including_children, + parent=checkbox) + checkbox._orig_start = self._orig_start + checkbox._orig_len = self._orig_len + + checkbox._dirty_heading = self.is_dirty_checkbox + + return checkbox + + @classmethod + def parse_checkbox_from_data(cls, data, heading=None, orig_start=None): + u""" Construct a new checkbox from the provided data + + :data: List of lines + :heading: The heading object this checkbox belongs to + :orig_start: The original start of the heading in case it was read + from a document. If orig_start is provided, the + resulting heading will not be marked dirty. + + :returns: The newly created checkbox + """ + def parse_title(heading_line): + # checkbox is not heading + if REGEX_HEADING.match(heading_line) is not None: + return None + m = REGEX_CHECKBOX.match(heading_line) + if m: + r = m.groupdict() + return (len(r[u'level']), r[u'type'], r[u'status'], r[u'title']) + + return None + + if not data: + raise ValueError(u'Unable to create checkbox, no data provided.') + + # create new checkbox + nc = cls() + nc.level, nc.type, nc.status, nc.title = parse_title(data[0]) + nc.body = data[1:] + if orig_start is not None: + nc._dirty_heading = False + nc._dirty_body = False + nc._orig_start = orig_start + nc._orig_len = len(nc) + if heading: + nc._heading = heading + + return nc + + def update_subtasks(self, total=0, on=0): + if total != 0: + percent = (on * 100) / total + else: + percent = 0 + + count = "%d/%d" % (on, total) + self.title = REGEX_SUBTASK.sub("[%s]" % (count), self.title) + self.title = REGEX_SUBTASK_PERCENT.sub("[%d%%]" % (percent), self.title) + d = self._heading.document.write_checkbox(self, including_children=False) + + @classmethod + def identify_checkbox(cls, line): + u""" Test if a certain line is a checkbox or not. + + :line: the line to check + + :returns: indent_level + """ + # checkbox is not heading + if REGEX_HEADING.match(line) is not None: + return None + m = REGEX_CHECKBOX.match(line) + if m: + r = m.groupdict() + return len(r[u'level']) + + return None + + @property + def is_dirty(self): + u""" Return True if the heading's body is marked dirty """ + return self._dirty_checkbox or self._dirty_body + + @property + def is_dirty_checkbox(self): + u""" Return True if the heading is marked dirty """ + return self._dirty_checkbox + + def get_index_in_parent_list(self): + """ Retrieve the index value of current checkbox in the parents list of + checkboxes. This works also for top level checkboxes. + + :returns: Index value or None if heading doesn't have a + parent/document or is not in the list of checkboxes + """ + if self.parent: + return super(Checkbox, self).get_index_in_parent_list() + elif self.document: + l = self.get_parent_list() + if l: + return l.index(self) + + def get_parent_list(self): + """ Retrieve the parents' list of headings. This works also for top + level headings. + + :returns: List of headings or None if heading doesn't have a + parent/document or is not in the list of headings + """ + if self.parent: + return super(Checkbox, self).get_parent_list() + elif self.document: + if self in self.document.checkboxes: + return self.document.checkboxes + + def set_dirty(self): + u""" Mark the heading and body dirty so that it will be rewritten when + saving the document """ + self._dirty_checkbox = True + self._dirty_body = True + if self._document: + self._document.set_dirty_document() + + def set_dirty_checkbox(self): + u""" Mark the checkbox dirty so that it will be rewritten when saving the + document """ + self._dirty_checkbox = True + if self._document: + self._document.set_dirty_document() + + @property + def previous_checkbox(self): + u""" Serialized access to the previous checkbox """ + return super(Checkbox, self).previous_item + + @property + def next_checkbox(self): + u""" Serialized access to the next checkbox """ + return super(Checkbox, self).next_item + + @property + def first_checkbox(self): + u""" Access to the first child heading or None if no children exist """ + if self.children: + return self.children[0] + + @property + def start(self): + u""" Access to the starting line of the checkbox """ + return super(Checkbox, self).start + + def toggle(self): + u""" Toggle status of this checkbox """ + if self.status == Checkbox.STATUS_OFF or self.status is None: + self.status = Checkbox.STATUS_ON + else: + self.status = Checkbox.STATUS_OFF + self.set_dirty() + + def all_siblings(self): + if not self.parent: + p = self._heading + else: + p = self.parent + if not p.children: + return + + c = p.first_checkbox + while c: + yield c + c = c.next_sibling + return + + def all_children(self): + if not self.children: + return + + c = self.first_checkbox + while c: + yield c + for d in c.all_children(): + yield d + c = c.next_sibling + + return + + def all_children_status(self): + u""" Return checkboxes status for currnet checkbox's all children + + :return: (total, on) + total: total # of checkboxes + on: # of checkboxes which are on + """ + total, on = 0, 0 + for c in self.all_children(): + if c.status is not None: + total += 1 + + if c.status == Checkbox.STATUS_ON: + on += 1 + + return (total, on) + + def all_siblings_status(self): + u""" Return checkboxes status for currnet checkbox's all siblings + + :return: (total, on) + total: total # of checkboxes + on: # of checkboxes which are on + """ + total, on = 0, 0 + for c in self.all_siblings(): + if c.status is not None: + total += 1 + + if c.status == Checkbox.STATUS_ON: + on += 1 + + return (total, on) + + def are_children_all(self, status): + u""" Check all children checkboxes status """ + clen = len(self.children) + for i in range(clen): + if self.children[i].status != status: + return False + # recursively check children's status + if not self.children[i].are_children_all(status): + return False + + return True + + def is_child_one(self, status): + u""" Return true, if there is one child with given status """ + clen = len(self.children) + for i in range(clen): + if self.children[i].status == status: + return True + + return False + + def are_siblings_all(self, status): + u""" Check all sibling checkboxes status """ + for c in self.all_siblings(): + if c.status != status: + return False + + return True + + @DomObj.level.setter + def level(self, value): + u""" Set the checkbox level and mark the checkbox and the document + dirty """ + self._level = int(value) + self.set_dirty_checkbox() + + @DomObj.title.setter + def title(self, value): + u""" Set the title and mark the document and the checkbox dirty """ + if type(value) not in (unicode, str): + raise ValueError(u'Title must be a string.') + v = value + if type(v) == str: + v = u_decode(v) + self._title = v.strip() + self.set_dirty_checkbox() + + @property + def status(self): + u""" status of current checkbox """ + return self._status + + @status.setter + def status(self, value): + self._status = value + self.set_dirty() + + @status.deleter + def status(self): + self._status = u'' + + @property + def type(self): + u""" type of current checkbox list type """ + return self._type + + @type.setter + def type(self, value): + self._type = value + + @type.deleter + def type(self): + self._type = u'' + + +class CheckboxList(DomObjList): + u""" + Checkbox List + """ + def __init__(self, initlist=None, obj=None): + """ + :initlist: Initial data + :obj: Link to a concrete Checkbox or Document object + """ + # it's not necessary to register a on_change hook because the heading + # list will itself take care of marking headings dirty or adding + # headings to the deleted headings list + DomObjList.__init__(self, initlist, obj) + + @classmethod + def is_checkbox(cls, obj): + return CheckboxList.is_domobj(obj) + + def _get_heading(self): + if self.__class__.is_checkbox(self._obj): + return self._obj._document + return self._obj + + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/documents.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/documents.py new file mode 100644 index 0000000..a2eeca7 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/documents.py @@ -0,0 +1,315 @@ +# -*- coding: utf-8 -*- + +""" + documents + ~~~~~~~~~ + + TODO: explain this :) +""" + +try: + from collections import UserList +except: + from UserList import UserList + +from orgmode.liborgmode.base import MultiPurposeList, flatten_list, Direction, get_domobj_range +from orgmode.liborgmode.headings import Heading, HeadingList + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.unicode_compatibility import * + +class Document(object): + u""" + Representation of a whole org-mode document. + + A Document consists basically of headings (see Headings) and some metadata. + + TODO: explain the 'dirty' mechanism + """ + + def __init__(self): + u""" + Don't call this constructor directly but use one of the concrete + implementations. + + TODO: what are the concrete implementatiions? + """ + object.__init__(self) + + # is a list - only the Document methods should work on this list! + self._content = None + self._dirty_meta_information = False + self._dirty_document = False + self._meta_information = MultiPurposeList( + on_change=self.set_dirty_meta_information) + self._orig_meta_information_len = None + self._headings = HeadingList(obj=self) + self._deleted_headings = [] + + # settings needed to align tags properly + self._tabstop = 8 + self._tag_column = 77 + + # TODO this doesn't differentiate between ACTIVE and FINISHED todo's + self.todo_states = [u'TODO', u'DONE'] + + def __unicode__(self): + if self.meta_information is None: + return u'\n'.join(self.all_headings()) + 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()]) + + def __str__(self): + return u_encode(self.__unicode__()) + + def get_all_todo_states(self): + u""" Convenience function that returns all todo and done states and + sequences in one big list. + + Returns: + list: [all todo/done states] + """ + # TODO This is not necessary remove + return flatten_list(self.get_todo_states()) + + def get_todo_states(self): + u""" Returns a list containing a tuple of two lists of allowed todo + states split by todo and done states. Multiple todo-done state + sequences can be defined. + + Returns: + list: [([todo states], [done states]), ..] + """ + # TODO this should be made into property so todo states can be set like + # this too.. or there was also some todo property around... oh well.. + # TODO there is the same method in vimbuffer + return self.todo_states + + @property + def tabstop(self): + u""" Tabstop for this document """ + return self._tabstop + + @tabstop.setter + def tabstop(self, value): + self._tabstop = value + + @property + def tag_column(self): + u""" The column all tags are right-aligned to """ + return self._tag_column + + @tag_column.setter + def tag_column(self, value): + self._tag_column = value + + def init_dom(self, heading=Heading): + u""" Initialize all headings in document - build DOM. This method + should be call prior to accessing the document. + + Returns: + self + """ + def init_heading(_h): + u""" + :returns the initialized heading + """ + start = _h.end + 1 + prev_heading = None + while True: + new_heading = self.find_heading(start, heading=heading) + + # * Heading 1 <- heading + # * Heading 1 <- sibling + # or + # * Heading 2 <- heading + # * Heading 1 <- parent's sibling + if not new_heading or \ + new_heading.level <= _h.level: + break + + # * Heading 1 <- heading + # * Heading 2 <- first child + # * Heading 2 <- another child + new_heading._parent = _h + if prev_heading: + prev_heading._next_sibling = new_heading + new_heading._previous_sibling = prev_heading + _h.children.data.append(new_heading) + # the start and end computation is only + # possible when the new heading was properly + # added to the document structure + init_heading(new_heading) + if new_heading.children: + # skip children + start = new_heading.end_of_last_child + 1 + else: + start = new_heading.end + 1 + prev_heading = new_heading + + return _h + + h = self.find_heading(heading=heading) + # initialize meta information + if h: + self._meta_information.data.extend(self._content[:h._orig_start]) + else: + self._meta_information.data.extend(self._content[:]) + self._orig_meta_information_len = len(self.meta_information) + + # initialize dom tree + prev_h = None + while h: + if prev_h: + prev_h._next_sibling = h + h._previous_sibling = prev_h + self.headings.data.append(h) + init_heading(h) + prev_h = h + h = self.find_heading(h.end_of_last_child + 1, heading=heading) + + return self + + @property + def meta_information(self): + u""" Meta information is text that precedes all headings in an org-mode + document. It might contain additional information about the document, + e.g. author + """ + return self._meta_information + + @meta_information.setter + def meta_information(self, value): + if self._orig_meta_information_len is None: + self._orig_meta_information_len = len(self.meta_information) + if type(value) in (list, tuple) or isinstance(value, UserList): + self._meta_information[:] = flatten_list(value) + elif type(value) in (str, ): + self._meta_information[:] = u_decode(value).split(u'\n') + elif type(value) in (unicode, ): + self._meta_information[:] = value.split(u'\n') + self.set_dirty_meta_information() + + @meta_information.deleter + def meta_information(self): + self.meta_information = u'' + + @property + def headings(self): + u""" List of top level headings """ + return self._headings + + @headings.setter + def headings(self, value): + self._headings[:] = value + + @headings.deleter + def headings(self): + del self.headings[:] + + def write(self): + u""" Write the document + + Returns: + bool: True if something was written, otherwise False + """ + raise NotImplementedError(u'Abstract method, please use concrete impelementation!') + + def set_dirty_meta_information(self): + u""" Mark the meta information dirty. + + Note: + Causes meta information to be rewritten when saving the document + """ + self._dirty_meta_information = True + + def set_dirty_document(self): + u""" Mark the whole document dirty. + + Note: + When changing a heading this method must be executed in order to + changed computation of start and end positions from a static to a + dynamic computation + """ + self._dirty_document = True + + @property + def is_dirty(self): + u""" Return information about unsaved changes for the document and all + related headings. + + Returns: + bool: True if document contains unsaved changes. + """ + if self.is_dirty_meta_information: + return True + + if self.is_dirty_document: + return True + + if self._deleted_headings: + return True + + return False + + @property + def is_dirty_meta_information(self): + u""" Return True if the meta information is marked dirty """ + return self._dirty_meta_information + + @property + def is_dirty_document(self): + u""" Return True if the document is marked dirty """ + return self._dirty_document + + def all_headings(self): + u""" Iterate over all headings of the current document in serialized + order + + :returns: Returns an iterator object which returns all headings of + the current file in serialized order + """ + if not self.headings: + return + + h = self.headings[0] + while h: + yield h + h = h.next_heading + return + + def find_heading( + self, position=0, direction=Direction.FORWARD, heading=Heading, + connect_with_document=True): + u""" Find heading in the given direction + + Args: + position (int): starting line, counting from 0 (in vim you start + counting from 1, don't forget) + direction: downwards == Direction.FORWARD, + upwards == Direction.BACKWARD + heading: Heading class from which new heading objects will be + instanciated + connect_with_document: if True, the newly created heading will be + connected with the document, otherwise not + + Returns: + heading or None: New heading + """ + start, end = get_domobj_range( + content=self._content, position=position, direction=direction, + identify_fun=heading.identify_heading) + + if start is None: + return None + + if end is None: + end = len(self._content) - 1 + + document = self if connect_with_document else None + + return heading.parse_heading_from_data( + self._content[start:end + 1], self.get_all_todo_states(), + document=document, orig_start=start) + + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/dom_obj.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/dom_obj.py new file mode 100644 index 0000000..5270d19 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/dom_obj.py @@ -0,0 +1,505 @@ +# -*- coding: utf-8 -*- + +""" + dom object + ~~~~~~~~~~ + + TODO: explain this :) +""" + +import re +from orgmode.liborgmode.base import MultiPurposeList, flatten_list + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.unicode_compatibility import * + +try: + from collections import UserList +except: + from UserList import UserList + +# breaking down tasks regex +REGEX_SUBTASK = re.compile(r'\[(\d*)/(\d*)\]') +REGEX_SUBTASK_PERCENT = re.compile(r'\[(\d*)%\]') + +# heading regex +REGEX_HEADING = re.compile( + r'^(?P\*+)(\s+(?P.*?))?\s*(\s(?P<tags>:[\w_:@]+:))?$', + flags=re.U) +REGEX_TAG = re.compile( + r'^\s*((?P<title>[^\s]*?)\s+)?(?P<tags>:[\w_:@]+:)$', + flags=re.U) +REGEX_TODO = re.compile(r'^[^\s]*$') + +# checkbox regex: +# - [ ] checkbox item +# - [X] checkbox item +# - [ ] +# - no status checkbox +UnOrderListType = [u'-', u'+', u'*'] +OrderListType = [u'.', u')'] +REGEX_CHECKBOX = re.compile( + r'^(?P<level>\s*)(?P<type>[%s]|([a-zA-Z]|[\d]+)[%s])(\s+(?P<status>\[.\]))?\s*(?P<title>.*)$' + % (''.join(UnOrderListType), ''.join(OrderListType)), flags=re.U) + + +class DomObj(object): + u""" + A DomObj is DOM structure element, like Heading and Checkbox. + Its purpose is to abstract the same parts of Heading and Checkbox objects, + and make code reusable. + + All methods and properties are extracted from Heading object. + Heading and Checkbox objects inherit from DomObj, and override some specific + methods in their own objects. + + Normally, we don't intend to use DomObj directly. However, we can add some more + DOM structure element based on this class to make code more concise. + """ + # TODO should this and DomObj_list be abstract methods? If so use ABC to + # force abstract methods + + def __init__(self, level=1, title=u'', body=None): + u""" + :level: Level of the dom object + :title: Title of the dom object + :body: Body of the dom object + """ + object.__init__(self) + + self._document = None + self._parent = None + self._previous_sibling = None + self._next_sibling = None + self._children = MultiPurposeList() + self._orig_start = None + self._orig_len = 0 + + self._level = level + # title + self._title = u'' + if title: + self.title = title + + # body + self._dirty_body = False + self._body = MultiPurposeList(on_change=self.set_dirty_body) + if body: + self.body = body + + def __unicode__(self): + return u'<dom obj level=%s, title=%s>' % (level, title) + + def __str__(self): + return u_encode(self.__unicode__()) + + def __len__(self): + # 1 is for the heading's title + return 1 + len(self.body) + + @property + def is_dirty(self): + u""" Return True if the dom obj body is marked dirty """ + return self._dirty_body + + @property + def is_dirty_body(self): + u""" Return True if the dom obj body is marked dirty """ + return self._dirty_body + + def get_index_in_parent_list(self): + """ Retrieve the index value of current dom obj in the parents list of + dom objs. This works also for top level dom objs. + + :returns: Index value or None if dom obj doesn't have a + parent/document or is not in the list of dom objs + """ + l = self.get_parent_list() + if l: + return l.index(self) + + def get_parent_list(self): + """ Retrieve the parents list of dom objs. This works also for top + level dom objs. + + :returns: List of dom objs or None if dom objs doesn't have a + parent/document or is not in the list of dom objs + """ + if self.parent: + if self in self.parent.children: + return self.parent.children + + def set_dirty(self): + u""" Mark the dom objs and body dirty so that it will be rewritten when + saving the document """ + if self._document: + self._document.set_dirty_document() + + def set_dirty_body(self): + u""" Mark the dom objs' body dirty so that it will be rewritten when + saving the document """ + self._dirty_body = True + if self._document: + self._document.set_dirty_document() + + @property + def document(self): + u""" Read only access to the document. If you want to change the + document, just assign the dom obj to another document """ + return self._document + + @property + def parent(self): + u""" Access to the parent dom obj """ + return self._parent + + @property + def number_of_parents(self): + u""" Access to the number of parent dom objs before reaching the root + document """ + def count_parents(h): + if h.parent: + return 1 + count_parents(h.parent) + else: + return 0 + return count_parents(self) + + @property + def previous_sibling(self): + u""" Access to the previous dom obj that's a sibling of the current one + """ + return self._previous_sibling + + @property + def next_sibling(self): + u""" Access to the next dom obj that's a sibling of the current one """ + return self._next_sibling + + @property + def previous_item(self): + u""" Serialized access to the previous dom obj """ + if self.previous_sibling: + h = self.previous_sibling + while h.children: + h = h.children[-1] + return h + elif self.parent: + return self.parent + + @property + def next_item(self): + u""" Serialized access to the next dom obj """ + if self.children: + return self.children[0] + elif self.next_sibling: + return self.next_sibling + else: + h = self.parent + while h: + if h.next_sibling: + return h.next_sibling + else: + h = h.parent + + @property + def start(self): + u""" Access to the starting line of the dom obj """ + if self.document is None or not self.document.is_dirty: + return self._orig_start + + def item_len_generator(h): + while h: + yield len(h) + h = h.previous_item + return sum(item for item in item_len_generator(self.previous_item)) + + @property + def start_vim(self): + if self.start is not None: + return self.start + 1 + + @property + def end(self): + u""" Access to the ending line of the dom obj """ + if self.start is not None: + return self.start + len(self.body) + + @property + def end_vim(self): + if self.end is not None: + return self.end + 1 + + @property + def end_of_last_child(self): + u""" Access to end of the last child """ + if self.children: + child = self.children[-1] + while child.children: + child = child.children[-1] + return child.end + return self.end + + @property + def end_of_last_child_vim(self): + return self.end_of_last_child + 1 + + @property + def children(self): + u""" MultiPurposeList[dom_objects??]: subheadings of the current DomObj + + Setter method takes list, tuple or userlist with DOMObjects + """ + return self._children + + @children.setter + def children(self, value): + v = value + if type(v) in (list, tuple) or isinstance(v, UserList): + v = flatten_list(v) + self._children[:] = v + + @children.deleter + def children(self): + del self.children[:] + + @property + def first_child(self): + u""" Access to the first child dom obj or None if no children exist """ + if self.children: + return self.children[0] + + @property + def last_child(self): + u""" Access to the last child dom obj or None if no children exist """ + if self.children: + return self.children[-1] + + @property + def level(self): + u""" int: Access the the dom obj level + + Setter sets the DOM object and the document as dirty if invoked. + """ + return self._level + + @level.setter + def level(self, value): + # TODO Shouldn't there be and error when values is not int? + self._level = int(value) + self.set_dirty() + + @level.deleter + def level(self): + self.level = None + + @property + def title(self): + u""" str: Get the title of current dom object + + Setter sets the DOM object and the document as dirty if invoked. + """ + return self._title.strip() + + @title.setter + def title(self, value): + if type(value) not in (unicode, str): + raise ValueError(u'Title must be a string.') + v = value + if type(v) == str: + v = u_decode(v) + self._title = v.strip() + self.set_dirty() + + @title.deleter + def title(self): + self._title = u'' + + @property + def body(self): + u""" MultiPurposeList[]: Holds the content belonging to the heading """ + return self._body + + @body.setter + def body(self, value): + if type(value) in (list, tuple) or isinstance(value, UserList): + self._body[:] = flatten_list(value) + elif type(value) in (str, ): + self._body[:] = u_decode(value).split(u'\n') + elif type(value) in (unicode, ): + self._body[:] = value.split(u'\n') + else: + self.body = list(unicode(value)) + + @body.deleter + def body(self): + # TODO write this as del self._body[:] because there is no reason to + # call so much code for deleting a list + self.body = [] + + +class DomObjList(MultiPurposeList): + u""" + A Dom Obj List + """ + def __init__(self, initlist=None, obj=None): + """ + :initlist: Initial data + :obj: Link to a concrete Heading or Document object + """ + # it's not necessary to register a on_change hook because the heading + # list will itself take care of marking headings dirty or adding + # headings to the deleted headings list + MultiPurposeList.__init__(self) + + self._obj = obj + + # initialization must be done here, because + # self._document is not initialized when the + # constructor of MultiPurposeList is called + if initlist: + self.extend(initlist) + + @classmethod + def is_domobj(cls, obj): + # TODO no reason for it to be class method. Does it even need to exist + # because it is quite clear what isinstance does and in derived methods + # isinstance(Heading, DomObj) would return True anyway. + return isinstance(obj, DomObj) + + # TODO this should be made into a property + def _get_document(self): + if self.__class__.is_domobj(self._obj): + return self._obj._document + return self._obj + + def __setitem__(self, i, item): + if isinstance(i, slice): + o = item + if self.__class__.is_domobj(o): + o = (o, ) + o = flatten_list(o) + for item in o: + if not self.__class__.is_domobj(item): + raise ValueError(u'List contains items that are not a Dom obj!') + + # self._add_to_deleted_domobjs(self[i:j]) + # self._associate_domobj(o, \ + # self[i - 1] if i - 1 >= 0 and i < len(self) else None, \ + # self[j] if j >= 0 and j < len(self) else None) + MultiPurposeList.__setitem__(self, i, o) + else: + if not self.__class__.is_domobj(item): + raise ValueError(u'Item is not a Dom obj!') + if item in self: + raise ValueError(u'Dom obj is already part of this list!') + # self._add_to_deleted_domobjs(self[i]) + + # self._associate_domobj(item, \ + # self[i - 1] if i - 1 >= 0 else None, \ + # self[i + 1] if i + 1 < len(self) else None) + MultiPurposeList.__setitem__(self, i, item) + + def __delitem__(self, i, taint=True): + if isinstance(i, slice): + items = self[i] + if items: + first = items[0] + last = items[-1] + if first.previous_sibling: + first.previous_sibling._next_sibling = last.next_sibling + if last.next_sibling: + last.next_sibling._previous_sibling = first.previous_sibling + # if taint: + # self._add_to_deleted_domobjs(items) + else: + item = self[i] + if item.previous_sibling: + item.previous_sibling._next_sibling = item.next_sibling + if item.next_sibling: + item.next_sibling._previous_sibling = item.previous_sibling + + # if taint: + # self._add_to_deleted_domobjs(item) + MultiPurposeList.__delitem__(self, i) + + def __setslice__(self, i, j, other): + self.__setitem__(slice(i, j), other) + + def __delslice__(self, i, j, taint=True): + self.__delitem__(slice(i, j), taint=taint) + + def __iadd__(self, other): + o = other + if self.__class__.is_domobj(o): + o = (o, ) + for item in flatten_list(o): + if not self.__class__.is_domobj(item): + raise ValueError(u'List contains items that are not a Dom obj!') + # self._associate_domobj(o, self[-1] if len(self) > 0 else None, None) + return MultiPurposeList.__iadd__(self, o) + + def __imul__(self, n): + # TODO das müsste eigentlich ein klonen von objekten zur Folge haben + return MultiPurposeList.__imul__(self, n) + + def append(self, item, taint=True): + if not self.__class__.is_domobj(item): + raise ValueError(u'Item is not a heading!') + if item in self: + raise ValueError(u'Heading is already part of this list!') + # self._associate_domobj( + # item, self[-1] if len(self) > 0 else None, + # None, taint=taint) + MultiPurposeList.append(self, item) + + def insert(self, i, item, taint=True): + # self._associate_domobj( + # item, + # self[i - 1] if i - 1 >= 0 and i - 1 < len(self) else None, + # self[i] if i >= 0 and i < len(self) else None, taint=taint) + MultiPurposeList.insert(self, i, item) + + def pop(self, i=-1): + item = self[i] + # self._add_to_deleted_domobjs(item) + del self[i] + return item + + def remove_slice(self, i, j, taint=True): + self.__delitem__(slice(i, j), taint=taint) + + def remove(self, item, taint=True): + self.__delitem__(self.index(item), taint=taint) + + def reverse(self): + MultiPurposeList.reverse(self) + prev_h = None + for h in self: + h._previous_sibling = prev_h + h._next_sibling = None + prev_h._next_sibling = h + h.set_dirty() + prev_h = h + + def sort(self, *args, **kwds): + MultiPurposeList.sort(*args, **kwds) + prev_h = None + for h in self: + h._previous_sibling = prev_h + h._next_sibling = None + prev_h._next_sibling = h + h.set_dirty() + prev_h = h + + def extend(self, other): + o = other + if self.__class__.is_domobj(o): + o = (o, ) + for item in o: + if not self.__class__.is_domobj(item): + raise ValueError(u'List contains items that are not a heading!') + # self._associate_domobj(o, self[-1] if len(self) > 0 else None, None) + MultiPurposeList.extend(self, o) + + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/headings.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/headings.py new file mode 100644 index 0000000..228a4ba --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/headings.py @@ -0,0 +1,889 @@ +# -*- coding: utf-8 -*- + +""" + headings + ~~~~~~~~~ + + TODO: explain this :) +""" + +import re + +import vim +from orgmode.liborgmode.base import MultiPurposeList, flatten_list, Direction, get_domobj_range +from orgmode.liborgmode.orgdate import OrgTimeRange +from orgmode.liborgmode.orgdate import get_orgdate +from orgmode.liborgmode.checkboxes import Checkbox, CheckboxList +from orgmode.liborgmode.dom_obj import DomObj, DomObjList, REGEX_SUBTASK, REGEX_SUBTASK_PERCENT, REGEX_HEADING, REGEX_TAG, REGEX_TODO + +from orgmode.py3compat.xrange_compatibility import * +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.unicode_compatibility import * + +try: + from collections import UserList +except: + from UserList import UserList + from itertools import ifilter as filter + +class Heading(DomObj): + u""" Structural heading object """ + + def __init__(self, level=1, title=u'', tags=None, todo=None, body=None, active_date=None): + u""" + :level: Level of the heading + :title: Title of the heading + :tags: Tags of the heading + :todo: Todo state of the heading + :body: Body of the heading + :active_date: active date that is used in the agenda + """ + DomObj.__init__(self, level=level, title=title, body=body) + + self._children = HeadingList(obj=self) + self._dirty_heading = False + + # todo + self._todo = None + if todo: + self.todo = todo + + # tags + self._tags = MultiPurposeList(on_change=self.set_dirty_heading) + if tags: + self.tags = tags + + # active date + self._active_date = active_date + if active_date: + self.active_date = active_date + + # checkboxes + self._checkboxes = CheckboxList(obj=self) + self._cached_checkbox = None + + def __unicode__(self): + res = u'*' * self.level + if self.todo: + res = u' '.join((res, self.todo)) + if self.title: + res = u' '.join((res, self.title)) + + # compute position of tags + if self.tags: + tabs = 0 + spaces = 2 + tags = u':%s:' % (u':'.join(self.tags), ) + + # FIXME this is broken because of missing associations for headings + ts = 6 + tag_column = 77 + if self.document: + ts = self.document.tabstop + tag_column = self.document.tag_column + + len_heading = len(res) + len_tags = len(tags) + if len_heading + spaces + len_tags < tag_column: + spaces_to_next_tabstop = ts - divmod(len_heading, ts)[1] + + if len_heading + spaces_to_next_tabstop + len_tags < tag_column: + tabs, spaces = divmod( + tag_column - (len_heading + spaces_to_next_tabstop + len_tags), + ts) + + if spaces_to_next_tabstop: + tabs += 1 + else: + spaces = tag_column - (len_heading + len_tags) + + res += u'\t' * tabs + u' ' * spaces + tags + + # append a trailing space when there are just * and no text + if len(res) == self.level: + res += u' ' + return res + + def __str__(self): + return u_encode(self.__unicode__()) + + def __len__(self): + # 1 is for the heading's title + return 1 + len(self.body) + + def __lt__(self, other): + """ + Headings can be sorted by date. + """ + try: + if self.active_date < other.active_date: + return True + elif self.active_date == other.active_date: + return False + elif self.active_date > other.active_date: + return False + except: + if self.active_date and not other.active_date: + return True + elif not self.active_date and other.active_date: + return False + elif not self.active_date and not other.active_date: + return False + + def __le__(self, other): + """ + Headings can be sorted by date. + """ + try: + if self.active_date < other.active_date: + return True + elif self.active_date == other.active_date: + return True + elif self.active_date > other.active_date: + return False + except: + if self.active_date and not other.active_date: + return True + elif not self.active_date and other.active_date: + return False + elif not self.active_date and not other.active: + return True + + def __ge__(self, other): + """ + Headings can be sorted by date. + """ + try: + if self.active_date > other.active_date: + return True + elif self.active_date == other.active_date: + return True + elif self.active_date < other.active_date: + return False + except: + if not self.active_date and other.active_date: + return True + elif self.active_date and not other.active_date: + return False + elif not self.active_date and not other.active: + return True + + def __gt__(self, other): + """ + Headings can be sorted by date. + """ + try: + if self.active_date > other.active_date: + return True + elif self.active_date == other.active_date: + return False + elif self.active_date < other.active_date: + return False + except: + if not self.active_date and other.active_date: + return True + elif self.active_date and not other.active_date: + return False + elif not self.active_date and not other.active: + return False + + def copy(self, including_children=True, parent=None): + u""" + Create a copy of the current heading. The heading will be completely + detached and not even belong to a document anymore. + + :including_children: If True a copy of all children is create as + well. If False the returned heading doesn't + have any children. + :parent: Don't use this parameter. It's set + automatically. + """ + heading = self.__class__( + level=self.level, title=self.title, + tags=self.tags, todo=self.todo, body=self.body[:]) + if parent: + parent.children.append(heading) + if including_children and self.children: + for item in self.children: + item.copy( + including_children=including_children, + parent=heading) + heading._orig_start = self._orig_start + heading._orig_len = self._orig_len + + heading._dirty_heading = self.is_dirty_heading + + return heading + + def all_checkboxes(self): + u""" Iterate over all checkboxes of the current heading in serialized + order + + :returns: Returns an iterator object which returns all checkboxes of + the current heading in serialized order + """ + if not self.checkboxes: + return + + c = self.first_checkbox + while c: + yield c + c = c.next_checkbox + return + + def all_toplevel_checkboxes(self): + u""" return all top level checkboxes for current heading """ + if not self.checkboxes: + return + + c = self.first_checkbox + while c: + yield c + c = c.next_sibling + return + + def find_checkbox(self, position=0, direction=Direction.FORWARD, + checkbox=Checkbox, connect_with_heading=True): + u""" Find checkbox in the given direction + + :postition: starting line, counting from 0 (in vim you start + counting from 1, don't forget) + :direction: downwards == Direction.FORWARD, + upwards == Direction.BACKWARD + :checkbox: Checkbox class from which new checkbox objects will be + instanciated + :connect_with_heading: if True, the newly created checkbox will be + connected with the heading, otherwise not + + :returns: New checkbox object or None + """ + doc = self.document + (start, end) = get_domobj_range(content=doc._content, position=position, direction=direction, identify_fun=checkbox.identify_checkbox) + # if out of current headinig range, reutrn None + heading_end = self.start + len(self) - 1 + if start is not None and start > heading_end: + return None + + if end is not None and end > heading_end: + end = heading_end + + if start is not None and end is None: + end = heading_end + if start is not None and end is not None: + return checkbox.parse_checkbox_from_data( + doc._content[start:end + 1], + heading=self if connect_with_heading else None, orig_start=start) + + def init_checkboxes(self, checkbox=Checkbox): + u""" Initialize all checkboxes in current heading - build DOM. + + :returns: self + """ + def init_checkbox(_c): + u""" + :returns the initialized checkbox + """ + start = _c.end + 1 + prev_checkbox = None + while True: + new_checkbox = self.find_checkbox(start, checkbox=checkbox) + + # * Checkbox 1 <- checkbox + # * Checkbox 1 <- sibling + # or + # * Checkbox 2 <- checkbox + # * Checkbox 1 <- parent's sibling + if not new_checkbox or \ + new_checkbox.level <= _c.level: + break + + # * Checkbox 1 <- heading + # * Checkbox 2 <- first child + # * Checkbox 2 <- another child + new_checkbox._parent = _c + if prev_checkbox: + prev_checkbox._next_sibling = new_checkbox + new_checkbox._previous_sibling = prev_checkbox + _c.children.data.append(new_checkbox) + # the start and end computation is only + # possible when the new checkbox was properly + # added to the document structure + init_checkbox(new_checkbox) + if new_checkbox.children: + # skip children + start = new_checkbox.end_of_last_child + 1 + else: + start = new_checkbox.end + 1 + prev_checkbox = new_checkbox + + return _c + + c = self.find_checkbox(checkbox=checkbox, position=self.start) + + # initialize dom tree + prev_c = None + while c: + if prev_c and prev_c.level == c.level: + prev_c._next_sibling = c + c._previous_sibling = prev_c + self.checkboxes.data.append(c) + init_checkbox(c) + prev_c = c + c = self.find_checkbox(c.end_of_last_child + 1, checkbox=checkbox) + + return self + + def current_checkbox(self, position=None): + u""" Find the current checkbox (search backward) and return the related object + :returns: Checkbox object or None + """ + if position is None: + position = vim.current.window.cursor[0] - 1 + + if not self.checkboxes: + return + + def binaryFindInHeading(): + hi = len(self.checkboxes) + lo = 0 + while lo < hi: + mid = (lo + hi) // 2 + c = self.checkboxes[mid] + if c.end_of_last_child < position: + lo = mid + 1 + elif c.start > position: + hi = mid + else: + return binaryFindCheckbox(c) + + def binaryFindCheckbox(checkbox): + if not checkbox.children or checkbox.end >= position: + return checkbox + + hi = len(checkbox.children) + lo = 0 + while lo < hi: + mid = (lo + hi) // 2 + c = checkbox.children[mid] + if c.end_of_last_child < position: + lo = mid + 1 + elif c.start > position: + hi = mid + else: + return binaryFindCheckbox(c) + + # look at the cache to find the heading + c_tmp = self._cached_checkbox + if c_tmp is not None: + if c_tmp.end_of_last_child > position and \ + c_tmp.start < position: + if c_tmp.end < position: + self._cached_checkbox = binaryFindCheckbox(c_tmp) + return self._cached_checkbox + + self._cached_checkbox = binaryFindInHeading() + return self._cached_checkbox + + @property + def first_checkbox(self): + u""" Access to the first child checkbox or None if no children exist """ + if self.checkboxes: + return self.checkboxes[0] + + @classmethod + def parse_heading_from_data( + cls, data, allowed_todo_states, document=None, + orig_start=None): + u""" Construct a new heading from the provided data + + :data: List of lines + :allowed_todo_states: TODO??? + :document: The document object this heading belongs to + :orig_start: The original start of the heading in case it was read + from a document. If orig_start is provided, the + resulting heading will not be marked dirty. + + :returns: The newly created heading + """ + test_not_empty = lambda x: x != u'' + + def parse_title(heading_line): + # WARNING this regular expression fails if there is just one or no + # word in the heading but a tag! + m = REGEX_HEADING.match(heading_line) + if m: + r = m.groupdict() + level = len(r[u'level']) + todo = None + title = u'' + tags = filter(test_not_empty, r[u'tags'].split(u':')) if r[u'tags'] else [] + tags = list(tags) + + # if there is just one or no word in the heading, redo the parsing + mt = REGEX_TAG.match(r[u'title']) + if not tags and mt: + r = mt.groupdict() + tags = filter(test_not_empty, r[u'tags'].split(u':')) if r[u'tags'] else [] + tags = list(tags) + if r[u'title'] is not None: + _todo_title = [i.strip() for i in r[u'title'].split(None, 1)] + if _todo_title and _todo_title[0] in allowed_todo_states: + todo = _todo_title[0] + if len(_todo_title) > 1: + title = _todo_title[1] + else: + title = r[u'title'].strip() + + return (level, todo, title, tags) + raise ValueError(u'Data doesn\'t start with a heading definition.') + + if not data: + raise ValueError(u'Unable to create heading, no data provided.') + + # create new heaing + new_heading = cls() + new_heading.level, new_heading.todo, new_heading.title, new_heading.tags = parse_title(data[0]) + new_heading.body = data[1:] + if orig_start is not None: + new_heading._dirty_heading = False + new_heading._dirty_body = False + new_heading._orig_start = orig_start + new_heading._orig_len = len(new_heading) + if document: + new_heading._document = document + + # try to find active dates + tmp_orgdate = get_orgdate(data) + if tmp_orgdate and tmp_orgdate.active \ + and not isinstance(tmp_orgdate, OrgTimeRange): + new_heading.active_date = tmp_orgdate + else: + new_heading.active_date = None + + return new_heading + + def update_subtasks(self, total=0, on=0): + u""" Update subtask information for current heading + :total: total # of top level checkboxes + :on: # of top level checkboxes which are on + """ + if total != 0: + percent = (on * 100) / total + else: + percent = 0 + + count = "%d/%d" % (on, total) + self.title = REGEX_SUBTASK.sub("[%s]" % (count), self.title) + self.title = REGEX_SUBTASK_PERCENT.sub("[%d%%]" % (percent), self.title) + self.document.write_heading(self, including_children=False) + + @staticmethod + def identify_heading(line): + u""" Test if a certain line is a heading or not. + + Args: + line (str): the line to check + + Returns: + int or None: level of heading or None if line is not heading + """ + # TODO would it make sense to return 0 for heading level? + # TODO add tests e.g. '*** abc', '**', '', '* ', '*\t', '*' + for i, item in enumerate(line): + if item == '*': + continue + elif i and item in ('\t', ' '): + return i + break + return None + + @property + def is_dirty(self): + u""" Return True if the heading's body is marked dirty """ + return self._dirty_heading or self._dirty_body + + @property + def is_dirty_heading(self): + u""" Return True if the heading is marked dirty """ + return self._dirty_heading + + def get_index_in_parent_list(self): + """ Retrieve the index value of current heading in the parents list of + headings. This works also for top level headings. + + :returns: Index value or None if heading doesn't have a + parent/document or is not in the list of headings + """ + if self.parent: + return super(Heading, self).get_index_in_parent_list() + elif self.document: + l = self.get_parent_list() + if l: + return l.index(self) + + def get_parent_list(self): + """ Retrieve the parents' list of headings. This works also for top + level headings. + + :returns: List of headings or None if heading doesn't have a + parent/document or is not in the list of headings + """ + if self.parent: + return super(Heading, self).get_parent_list() + elif self.document: + if self in self.document.headings: + return self.document.headings + + def set_dirty(self): + u""" Mark the heading and body dirty so that it will be rewritten when + saving the document """ + self._dirty_heading = True + self._dirty_body = True + if self._document: + self._document.set_dirty_document() + + def set_dirty_heading(self): + u""" Mark the heading dirty so that it will be rewritten when saving the + document """ + self._dirty_heading = True + if self._document: + self._document.set_dirty_document() + + @property + def previous_heading(self): + u""" Serialized access to the previous heading """ + return super(Heading, self).previous_item + + @property + def next_heading(self): + u""" Serialized access to the next heading """ + return super(Heading, self).next_item + + @property + def start(self): + u""" Access to the starting line of the heading """ + if self.document is None or not self.document.is_dirty: + return self._orig_start + + meta_len = len(self.document.meta_information) if \ + self.document.meta_information else 0 + return super(Heading, self).start + meta_len + + @DomObj.level.setter + def level(self, value): + u""" Set the heading level and mark the heading and the document dirty """ + self._level = int(value) + self.set_dirty_heading() + + @property + def todo(self): + u""" Todo state of current heading. When todo state is set""" + # extract todo state from heading + return self._todo + + @todo.setter + def todo(self, value): + # update todo state + if type(value) not in (unicode, str, type(None)): + raise ValueError(u'Todo state must be a string or None.') + if value and not REGEX_TODO.match(value): + raise ValueError(u'Found non allowed character in todo state! %s' % value) + if not value: + self._todo = None + else: + v = value + if type(v) == str: + v = u_decode(v) + self._todo = v + self.set_dirty_heading() + + @todo.deleter + def todo(self): + self.todo = None + + @property + def active_date(self): + u""" + active date of the hearing. + + active dates are used in the agenda view. they can be part of the + heading and/or the body. + """ + return self._active_date + + @active_date.setter + def active_date(self, value): + self._active_date = value + + @active_date.deleter + def active_date(self): + self._active_date = None + + @DomObj.title.setter + def title(self, value): + u""" Set the title and mark the document and the heading dirty """ + # TODO these setter should be rewriten to also reuse code from DOM OBJ + if type(value) not in (unicode, str): + raise ValueError(u'Title must be a string.') + v = value + if type(v) == str: + v = u_decode(v) + self._title = v.strip() + self.set_dirty_heading() + + @property + def tags(self): + u""" Tags of the current heading """ + return self._tags + + @tags.setter + def tags(self, value): + v = value + if type(v) in (unicode, str): + v = list(unicode(v)) + if type(v) not in (list, tuple) and not isinstance(v, UserList): + v = list(unicode(v)) + v = flatten_list(v) + v_decoded = [] + for i in v: + if type(i) not in (unicode, str): + raise ValueError(u'Found non string value in tags! %s' % unicode(i)) + if u':' in i: + raise ValueError(u'Found non allowed character in tag! %s' % i) + i_tmp = i.strip().replace(' ', '_').replace('\t', '_') + if type(i) == str: + i_tmp = u_decode(i) + v_decoded.append(i_tmp) + + self._tags[:] = v_decoded + + @tags.deleter + def tags(self): + self.tags = [] + + @property + def checkboxes(self): + u""" All checkboxes in current heading """ + return self._checkboxes + + @checkboxes.setter + def checkboxes(self, value): + self._checkboxes[:] = value + + @checkboxes.deleter + def checkboxes(self): + del self.checkboxes[:] + + +class HeadingList(DomObjList): + u""" + A Heading List just contains headings. It's used for documents to store top + level headings and for headings to store subheadings. + + A Heading List must be linked to a Document or Heading! + + See documenatation of MultiPurposeList for more information. + """ + def __init__(self, initlist=None, obj=None): + """ + :initlist: Initial data + :obj: Link to a concrete Heading or Document object + """ + # it's not necessary to register a on_change hook because the heading + # list will itself take care of marking headings dirty or adding + # headings to the deleted headings list + DomObjList.__init__(self, initlist, obj) + + @classmethod + def is_heading(cls, obj): + # TODO no need to make this or is_domobj a class methods + return HeadingList.is_domobj(obj) + + def _get_document(self): + if self.__class__.is_heading(self._obj): + return self._obj._document + return self._obj + + def _add_to_deleted_headings(self, item): + u""" + Serialize headings so that all subheadings are also marked for deletion + """ + if not self._get_document(): + # HeadingList has not yet been associated + return + + if type(item) in (list, tuple) or isinstance(item, UserList): + for i in flatten_list(item): + self._add_to_deleted_headings(i) + else: + self._get_document()._deleted_headings.append( + item.copy(including_children=False)) + self._add_to_deleted_headings(item.children) + self._get_document().set_dirty_document() + + def _associate_heading( + self, heading, previous_sibling, next_sibling, + children=False, taint=True): + """ + :heading: The heading or list to associate with the current heading + :previous_sibling: The previous sibling of the current heading. If + heading is a list the first heading will be + connected with the previous sibling and the last + heading with the next sibling. The items in between + will be linked with one another. + :next_sibling: The next sibling of the current heading. If + heading is a list the first heading will be + connected with the previous sibling and the last + heading with the next sibling. The items in between + will be linked with one another. + :children: Marks whether children are processed in the current + iteration or not (should not be use, it's set + automatically) + :taint: If not True, the heading is not marked dirty at the end + of the association process and its orig_start and + orig_len values are not updated. + """ + # TODO this method should be externalized and moved to the Heading class + # TODO should this method work with slice? + if type(heading) in (list, tuple) or isinstance(heading, UserList): + prev = previous_sibling + current = None + for _next in flatten_list(heading): + if current: + self._associate_heading( + current, prev, _next, + children=children, taint=taint) + prev = current + current = _next + if current: + self._associate_heading( + current, prev, next_sibling, + children=children, taint=taint) + else: + if taint: + heading._orig_start = None + heading._orig_len = None + d = self._get_document() + if heading._document != d: + heading._document = d + if not children: + # connect heading with previous and next headings + heading._previous_sibling = previous_sibling + if previous_sibling: + previous_sibling._next_sibling = heading + heading._next_sibling = next_sibling + if next_sibling: + next_sibling._previous_sibling = heading + + if d == self._obj: + # self._obj is a Document + heading._parent = None + elif heading._parent != self._obj: + # self._obj is a Heading + heading._parent = self._obj + if taint: + heading.set_dirty() + + self._associate_heading( + heading.children, None, None, + children=True, taint=taint) + + def __setitem__(self, i, item): + if isinstance(i, slice): + start, stop, step = i.indices(len(self)) + items = item + if self.__class__.is_heading(items): + items = (items, ) + items = flatten_list(items) + for head in items: + if not self.__class__.is_heading(head): + raise ValueError(u'List contains items that are not a heading!') + + self._add_to_deleted_headings(self[i]) + self._associate_heading( + items, + self[start - 1] if start - 1 >= 0 else None, + self[stop] if stop < len(self) else None) + MultiPurposeList.__setitem__(self, i, items) + else: + if not self.__class__.is_heading(item): + raise ValueError(u'Item is not a heading!') + if item in self: + raise ValueError(u'Heading is already part of this list!') + self._add_to_deleted_headings(self[i]) + self._associate_heading( + item, + self[i - 1] if i - 1 >= 0 else None, + self[i + 1] if i + 1 < len(self) else None) + MultiPurposeList.__setitem__(self, i, item) + + def __delitem__(self, i, taint=True): + # TODO refactor this item, it works the same in dom_obj except taint? + if isinstance(i, slice): + items = self[i] + if items: + first = items[0] + last = items[-1] + if first.previous_sibling: + first.previous_sibling._next_sibling = last.next_sibling + if last.next_sibling: + last.next_sibling._previous_sibling = first.previous_sibling + if taint: + self._add_to_deleted_headings(items) + MultiPurposeList.__delitem__(self, i) + else: + item = self[i] + if item.previous_sibling: + item.previous_sibling._next_sibling = item.next_sibling + if item.next_sibling: + item.next_sibling._previous_sibling = item.previous_sibling + + if taint: + self._add_to_deleted_headings(item) + MultiPurposeList.__delitem__(self, i) + + def __iadd__(self, other): + o = other + if self.__class__.is_heading(o): + o = (o, ) + for item in flatten_list(o): + if not self.__class__.is_heading(item): + raise ValueError(u'List contains items that are not a heading!') + self._associate_heading(o, self[-1] if len(self) > 0 else None, None) + return MultiPurposeList.__iadd__(self, o) + + def append(self, item, taint=True): + if not self.__class__.is_heading(item): + raise ValueError(u'Item is not a heading!') + if item in self: + raise ValueError(u'Heading is already part of this list!') + self._associate_heading( + item, self[-1] if len(self) > 0 else None, + None, taint=taint) + MultiPurposeList.append(self, item) + + def insert(self, i, item, taint=True): + self._associate_heading( + item, + self[i - 1] if i - 1 >= 0 and i - 1 < len(self) else None, + self[i] if i >= 0 and i < len(self) else None, taint=taint) + MultiPurposeList.insert(self, i, item) + + def pop(self, i=-1): + item = self[i] + self._add_to_deleted_headings(item) + del self[i] + return item + + def extend(self, other): + o = other + if self.__class__.is_heading(o): + o = (o, ) + for item in o: + if not self.__class__.is_heading(item): + raise ValueError(u'List contains items that are not a heading!') + self._associate_heading(o, self[-1] if len(self) > 0 else None, None) + MultiPurposeList.extend(self, o) + + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/orgdate.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/orgdate.py new file mode 100644 index 0000000..93a6776 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/orgdate.py @@ -0,0 +1,296 @@ +# -*- coding: utf-8 -*- +u""" + OrgDate + ~~~~~~~~~~~~~~~~~~ + + This module contains all date/time/timerange representations that exist in + orgmode. + + There exist three different kinds: + + * OrgDate: is similar to a date object in python and it looks like + '2011-09-07 Wed'. + + * OrgDateTime: is similar to a datetime object in python and looks like + '2011-09-07 Wed 10:30' + + * OrgTimeRange: indicates a range of time. It has a start and and end date: + * <2011-09-07 Wed>--<2011-09-08 Fri> + * <2011-09-07 Wed 10:00-13:00> + + All OrgTime oblects can be active or inactive. +""" + +import datetime +import re + +from orgmode.py3compat.encode_compatibility import * + +# <2011-09-12 Mon> +_DATE_REGEX = re.compile(r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>", re.UNICODE) +# [2011-09-12 Mon] +_DATE_PASSIVE_REGEX = re.compile(r"\[(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w\]", re.UNICODE) + +# <2011-09-12 Mon 10:20> +_DATETIME_REGEX = re.compile( + r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d{1,2}):(\d\d)>", re.UNICODE) +# [2011-09-12 Mon 10:20] +_DATETIME_PASSIVE_REGEX = re.compile( + r"\[(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d{1,2}):(\d\d)\]", re.UNICODE) + +# <2011-09-12 Mon>--<2011-09-13 Tue> +_DATERANGE_REGEX = re.compile( + # <2011-09-12 Mon>-- + r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>--" + # <2011-09-13 Tue> + "<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>", re.UNICODE) +# <2011-09-12 Mon 10:00>--<2011-09-12 Mon 11:00> +_DATETIMERANGE_REGEX = re.compile( + # <2011-09-12 Mon 10:00>-- + r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)>--" + # <2011-09-12 Mon 11:00> + "<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)>", re.UNICODE) +# <2011-09-12 Mon 10:00--12:00> +_DATETIMERANGE_SAME_DAY_REGEX = re.compile( + r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)-(\d\d):(\d\d)>", re.UNICODE) + + +def get_orgdate(data): + u""" + Parse the given data (can be a string or list). Return an OrgDate if data + contains a string representation of an OrgDate; otherwise return None. + + data can be a string or a list containing strings. + """ + # TODO maybe it should be checked just for iterable? Does it affect here if + # in base __getitem__(slice(i,j)) doesn't return a list but userlist... + if isinstance(data, list): + return _findfirst(_text2orgdate, data) + else: + return _text2orgdate(data) + # if no dates found + return None + + +def _findfirst(f, seq): + u""" + Return first item in sequence seq where f(item) == True. + + TODO: this is a general help function and it should be moved somewhere + else; preferably into the standard lib :) + """ + for found in (f(item) for item in seq if f(item)): + return found + + +def _text2orgdate(string): + u""" + Transform the given string into an OrgDate. + Return an OrgDate if data contains a string representation of an OrgDate; + otherwise return None. + """ + # handle active datetime with same day + result = _DATETIMERANGE_SAME_DAY_REGEX.search(string) + if result: + try: + (syear, smonth, sday, shour, smin, ehour, emin) = \ + [int(m) for m in result.groups()] + start = datetime.datetime(syear, smonth, sday, shour, smin) + end = datetime.datetime(syear, smonth, sday, ehour, emin) + return OrgTimeRange(True, start, end) + except BaseException: + return None + + # handle active datetime + result = _DATETIMERANGE_REGEX.search(string) + if result: + try: + tmp = [int(m) for m in result.groups()] + (syear, smonth, sday, shour, smin, eyear, emonth, eday, ehour, emin) = tmp + start = datetime.datetime(syear, smonth, sday, shour, smin) + end = datetime.datetime(eyear, emonth, eday, ehour, emin) + return OrgTimeRange(True, start, end) + except BaseException: + return None + + # handle active datetime + result = _DATERANGE_REGEX.search(string) + if result: + try: + tmp = [int(m) for m in result.groups()] + syear, smonth, sday, eyear, emonth, ehour = tmp + start = datetime.date(syear, smonth, sday) + end = datetime.date(eyear, emonth, ehour) + return OrgTimeRange(True, start, end) + except BaseException: + return None + + # handle active datetime + result = _DATETIME_REGEX.search(string) + if result: + try: + year, month, day, hour, minutes = [int(m) for m in result.groups()] + return OrgDateTime(True, year, month, day, hour, minutes) + except BaseException: + return None + + # handle passive datetime + result = _DATETIME_PASSIVE_REGEX.search(string) + if result: + try: + year, month, day, hour, minutes = [int(m) for m in result.groups()] + return OrgDateTime(False, year, month, day, hour, minutes) + except BaseException: + return None + + # handle passive dates + result = _DATE_PASSIVE_REGEX.search(string) + if result: + try: + year, month, day = [int(m) for m in result.groups()] + return OrgDate(False, year, month, day) + except BaseException: + return None + + # handle active dates + result = _DATE_REGEX.search(string) + if result: + try: + year, month, day = [int(m) for m in result.groups()] + return OrgDate(True, year, month, day) + except BaseException: + return None + + +class OrgDate(datetime.date): + u""" + OrgDate represents a normal date like '2011-08-29 Mon'. + + OrgDates can be active or inactive. + + NOTE: date is immutable. Thats why there needs to be __new__(). + See: http://docs.python.org/reference/datamodel.html#object.__new__ + """ + def __init__(self, active, year, month, day): + self.active = active + pass + + def __new__(cls, active, year, month, day): + return datetime.date.__new__(cls, year, month, day) + + def __unicode__(self): + u""" + Return a string representation. + """ + if self.active: + return self.strftime(u'<%Y-%m-%d %a>') + else: + return self.strftime(u'[%Y-%m-%d %a]') + + def __str__(self): + return u_encode(self.__unicode__()) + + def strftime(self, fmt): + return u_decode(datetime.date.strftime(self, u_encode(fmt))) + + +class OrgDateTime(datetime.datetime): + u""" + OrgDateTime represents a normal date like '2011-08-29 Mon'. + + OrgDateTime can be active or inactive. + + NOTE: date is immutable. Thats why there needs to be __new__(). + See: http://docs.python.org/reference/datamodel.html#object.__new__ + """ + + def __init__(self, active, year, month, day, hour, mins): + self.active = active + + def __new__(cls, active, year, month, day, hour, minute): + return datetime.datetime.__new__(cls, year, month, day, hour, minute) + + def __unicode__(self): + u""" + Return a string representation. + """ + if self.active: + return self.strftime(u'<%Y-%m-%d %a %H:%M>') + else: + return self.strftime(u'[%Y-%m-%d %a %H:%M]') + + def __str__(self): + return u_encode(self.__unicode__()) + + def strftime(self, fmt): + return u_decode(datetime.datetime.strftime(self, u_encode(fmt))) + + +class OrgTimeRange(object): + u""" + OrgTimeRange objects have a start and an end. Start and ent can be date + or datetime. Start and end have to be the same type. + + OrgTimeRange objects look like this: + * <2011-09-07 Wed>--<2011-09-08 Fri> + * <2011-09-07 Wed 20:00>--<2011-09-08 Fri 10:00> + * <2011-09-07 Wed 10:00-13:00> + """ + + def __init__(self, active, start, end): + u""" + stat and end must be datetime.date or datetime.datetime (both of the + same type). + """ + super(OrgTimeRange, self).__init__() + self.start = start + self.end = end + self.active = active + + def __unicode__(self): + u""" + Return a string representation. + """ + # active + if self.active: + # datetime + if isinstance(self.start, datetime.datetime): + # if start and end are on same the day + if self.start.year == self.end.year and\ + self.start.month == self.end.month and\ + self.start.day == self.end.day: + return u"<%s-%s>" % ( + self.start.strftime(u'%Y-%m-%d %a %H:%M'), + self.end.strftime(u'%H:%M')) + else: + return u"<%s>--<%s>" % ( + self.start.strftime(u'%Y-%m-%d %a %H:%M'), + self.end.strftime(u'%Y-%m-%d %a %H:%M')) + # date + if isinstance(self.start, datetime.date): + return u"<%s>--<%s>" % ( + self.start.strftime(u'%Y-%m-%d %a'), + self.end.strftime(u'%Y-%m-%d %a')) + # inactive + else: + if isinstance(self.start, datetime.datetime): + # if start and end are on same the day + if self.start.year == self.end.year and\ + self.start.month == self.end.month and\ + self.start.day == self.end.day: + return u"[%s-%s]" % ( + self.start.strftime(u'%Y-%m-%d %a %H:%M'), + self.end.strftime(u'%H:%M')) + else: + return u"[%s]--[%s]" % ( + self.start.strftime(u'%Y-%m-%d %a %H:%M'), + self.end.strftime(u'%Y-%m-%d %a %H:%M')) + if isinstance(self.start, datetime.date): + return u"[%s]--[%s]" % ( + self.start.strftime(u'%Y-%m-%d %a'), + self.end.strftime(u'%Y-%m-%d %a')) + + def __str__(self): + return u_encode(self.__unicode__()) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/menu.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/menu.py new file mode 100644 index 0000000..9ff70d7 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/menu.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- + +import vim + +from orgmode.keybinding import Command, Plug, Keybinding +from orgmode.keybinding import MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT + +from orgmode.py3compat.encode_compatibility import * + +def register_menu(f): + def r(*args, **kwargs): + p = f(*args, **kwargs) + def create(entry): + if isinstance(entry, Submenu) or isinstance(entry, Separator) \ + or isinstance(entry, ActionEntry): + entry.create() + + if hasattr(p, u'menu'): + if isinstance(p.menu, list) or isinstance(p.menu, tuple): + for e in p.menu: + create(e) + else: + create(p.menu) + return p + return r + + +def add_cmd_mapping_menu(plugin, name, function, key_mapping, menu_desrc): + u"""A helper function to create a vim command and keybinding and add these + to the menu for a given plugin. + + :plugin: the plugin to operate on. + :name: the name of the vim command (and the name of the Plug) + :function: the actual python function which is called when executing the + vim command. + :key_mapping: the keymapping to execute the command. + :menu_desrc: the text which appears in the menu. + """ + cmd = Command(name, function) + keybinding = Keybinding(key_mapping, Plug(name, cmd)) + + plugin.commands.append(cmd) + plugin.keybindings.append(keybinding) + plugin.menu + ActionEntry(menu_desrc, keybinding) + + +class Submenu(object): + u""" Submenu entry """ + + def __init__(self, name, parent=None): + object.__init__(self) + self.name = name + self.parent = parent + self._children = [] + + def __add__(self, entry): + if entry not in self._children: + self._children.append(entry) + entry.parent = self + return entry + + def __sub__(self, entry): + if entry in self._children: + idx = self._children.index(entry) + del self._children[idx] + + @property + def children(self): + return self._children[:] + + def get_menu(self): + n = self.name.replace(u' ', u'\\ ') + if self.parent: + return u'%s.%s' % (self.parent.get_menu(), n) + return n + + def create(self): + for c in self.children: + c.create() + + def __str__(self): + res = self.name + for c in self.children: + res += str(c) + return res + +class Separator(object): + u""" Menu entry for a Separator """ + + def __init__(self, parent=None): + object.__init__(self) + self.parent = parent + + def __unicode__(self): + return u'-----' + + def __str__(self): + return u_encode(self.__unicode__()) + + def create(self): + if self.parent: + menu = self.parent.get_menu() + vim.command(u_encode(u'menu %s.-%s- :' % (menu, id(self)))) + +class ActionEntry(object): + u""" ActionEntry entry """ + + def __init__(self, lname, action, rname=None, mode=MODE_NORMAL, parent=None): + u""" + :lname: menu title on the left hand side of the menu entry + :action: could be a vim command sequence or an actual Keybinding + :rname: menu title that appears on the right hand side of the menu + entry. If action is a Keybinding this value ignored and is + taken from the Keybinding + :mode: defines when the menu entry/action is executable + :parent: the parent instance of this object. The only valid parent is Submenu + """ + object.__init__(self) + self._lname = lname + self._action = action + self._rname = rname + if mode not in (MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT): + raise ValueError(u'Parameter mode not in MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT') + self._mode = mode + self.parent = parent + + def __str__(self): + return u'%s\t%s' % (self.lname, self.rname) + + @property + def lname(self): + return self._lname.replace(u' ', u'\\ ') + + @property + def action(self): + if isinstance(self._action, Keybinding): + return self._action.action + return self._action + + @property + def rname(self): + if isinstance(self._action, Keybinding): + return self._action.key.replace(u'<Tab>', u'Tab') + return self._rname + + @property + def mode(self): + if isinstance(self._action, Keybinding): + return self._action.mode + return self._mode + + def create(self): + menucmd = u':%smenu ' % self.mode + menu = u'' + cmd = u'' + + if self.parent: + menu = self.parent.get_menu() + menu += u'.%s' % self.lname + + if self.rname: + cmd = u'%s %s<Tab>%s %s' % (menucmd, menu, self.rname, self.action) + else: + cmd = u'%s %s %s' % (menucmd, menu, self.action) + + vim.command(u_encode(cmd)) + + # keybindings should be stored in the plugin.keybindings property and be registered by the appropriate keybinding registrar + #if isinstance(self._action, Keybinding): + # self._action.create() + + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Agenda.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Agenda.py new file mode 100644 index 0000000..40e7c81 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Agenda.py @@ -0,0 +1,314 @@ +# -*- coding: utf-8 -*- + +from datetime import date +import os +import glob + +import vim + +from orgmode._vim import ORGMODE, get_bufnumber, get_bufname, echoe +from orgmode import settings +from orgmode.keybinding import Keybinding, Plug, Command +from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.unicode_compatibility import * +from orgmode.py3compat.py_py3_string import * + +class Agenda(object): + u""" + The Agenda Plugin uses liborgmode.agenda to display the agenda views. + + The main task is to format the agenda from liborgmode.agenda. + Also all the mappings: jump from agenda to todo, etc are realized here. + """ + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'Agenda') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + # commands for this plugin + self.commands = [] + + @classmethod + def _switch_to(cls, bufname, vim_commands=None): + u""" + Swicht to the buffer with bufname. + + A list of vim.commands (if given) gets executed as well. + + TODO: this should be extracted and imporved to create an easy to use + way to create buffers/jump to buffers. Otherwise there are going to be + quite a few ways to open buffers in vimorgmode. + """ + cmds = [ + u'botright split org:%s' % bufname, + u'setlocal buftype=nofile', + u'setlocal modifiable', + u'setlocal nonumber', + # call opendoc() on enter the original todo item + u'nnoremap <silent> <buffer> <CR> :exec "%s ORGMODE.plugins[u\'Agenda\'].opendoc()"<CR>' % VIM_PY_CALL, + u'nnoremap <silent> <buffer> <TAB> :exec "%s ORGMODE.plugins[u\'Agenda\'].opendoc(switch=True)"<CR>' % VIM_PY_CALL, + u'nnoremap <silent> <buffer> <S-CR> :exec "%s ORGMODE.plugins[u\'Agenda\'].opendoc(split=True)"<CR>' % VIM_PY_CALL, + # statusline + u'setlocal statusline=Org\\ %s' % bufname] + if vim_commands: + cmds.extend(vim_commands) + for cmd in cmds: + vim.command(u_encode(cmd)) + + @classmethod + def _get_agendadocuments(self): + u""" + Return the org documents of the agenda files; return None if no + agenda documents are defined. + + TODO: maybe turn this into an decorator? + """ + # load org files of agenda + agenda_files = settings.get(u'org_agenda_files', u',') + if not agenda_files or agenda_files == ',': + echoe( + u"No org_agenda_files defined. Use :let " + u"g:org_agenda_files=['~/org/index.org'] to add " + u"files to the agenda view.") + return + return self._load_agendafiles(agenda_files) + + @classmethod + def _load_agendafiles(self, agenda_files): + # glob for files in agenda_files + resolved_files = [] + for f in agenda_files: + f = glob.glob(os.path.join( + os.path.expanduser(os.path.dirname(f)), + os.path.basename(f))) + resolved_files.extend(f) + + agenda_files = [os.path.realpath(f) for f in resolved_files] + + # load the agenda files into buffers + for agenda_file in agenda_files: + vim.command(u_encode(u'badd %s' % agenda_file.replace(" ", "\ "))) + + # determine the buffer nr of the agenda files + agenda_nums = [get_bufnumber(fn) for fn in agenda_files] + + # collect all documents of the agenda files and create the agenda + return [ORGMODE.get_document(i) for i in agenda_nums if i is not None] + + @classmethod + def opendoc(cls, split=False, switch=False): + u""" + If you are in the agenda view jump to the document the item in the + current line belongs to. cls.line2doc is used for that. + + :split: if True, open the document in a new split window. + :switch: if True, switch to another window and open the the document + there. + """ + row, _ = vim.current.window.cursor + try: + bufname, bufnr, destrow = cls.line2doc[row] + except: + return + + # reload source file if it is not loaded + if get_bufname(bufnr) is None: + vim.command(u_encode(u'badd %s' % bufname)) + bufnr = get_bufnumber(bufname) + tmp = cls.line2doc[row] + cls.line2doc[bufnr] = tmp + # delete old endry + del cls.line2doc[row] + + if split: + vim.command(u_encode(u"sbuffer %s" % bufnr)) + elif switch: + vim.command(u_encode(u"wincmd w")) + vim.command(u_encode(u"buffer %d" % bufnr)) + else: + vim.command(u_encode(u"buffer %s" % bufnr)) + vim.command(u_encode(u"normal! %dgg <CR>" % (destrow + 1))) + + @classmethod + def list_next_week(cls): + agenda_documents = cls._get_agendadocuments() + if not agenda_documents: + return + cls.list_next_week_for(agenda_documents) + + @classmethod + def list_next_week_for_buffer(cls): + agenda_documents = vim.current.buffer.name + loaded_agendafiles = cls._load_agendafiles([agenda_documents]) + cls.list_next_week_for(loaded_agendafiles) + + + @classmethod + def list_next_week_for(cls, agenda_documents): + raw_agenda = ORGMODE.agenda_manager.get_next_week_and_active_todo( + agenda_documents) + + # if raw_agenda is empty, return directly + if not raw_agenda: + vim.command('echom "All caught-up. No agenda or active todo next week."') + return + + # create buffer at bottom + cmd = [u'setlocal filetype=orgagenda', ] + cls._switch_to(u'AGENDA', cmd) + + # line2doc is a dic with the mapping: + # line in agenda buffer --> source document + # It's easy to jump to the right document this way + cls.line2doc = {} + # format text for agenda + last_date = raw_agenda[0].active_date + final_agenda = [u'Week Agenda:', unicode(last_date)] + for i, h in enumerate(raw_agenda): + # insert date information for every new date (not datetime) + if unicode(h.active_date)[1:11] != unicode(last_date)[1:11]: + today = date.today() + # insert additional "TODAY" string + if h.active_date.year == today.year and \ + h.active_date.month == today.month and \ + h.active_date.day == today.day: + section = unicode(h.active_date) + u" TODAY" + today_row = len(final_agenda) + 1 + else: + section = unicode(h.active_date) + final_agenda.append(section) + + # update last_date + last_date = h.active_date + + bufname = os.path.basename(vim.buffers[h.document.bufnr].name) + bufname = bufname[:-4] if bufname.endswith(u'.org') else bufname + formated = u" %(bufname)s (%(bufnr)d) %(todo)s %(title)s" % { + 'bufname': bufname, + 'bufnr': h.document.bufnr, + 'todo': h.todo, + 'title': h.title + } + final_agenda.append(formated) + cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start) + + # show agenda + vim.current.buffer[:] = [u_encode(i) for i in final_agenda] + vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc')) + # try to jump to the positon of today + try: + vim.command(u_encode(u'normal! %sgg<CR>' % today_row)) + except: + pass + + @classmethod + def list_all_todos(cls, current_buffer=False): + u""" List all todos in one buffer. + + Args: + current_buffer (bool): + False: all agenda files + True: current org_file + """ + if current_buffer: + agenda_documents = vim.current.buffer.name + loaded_agendafiles = cls._load_agendafiles([agenda_documents]) + else: + loaded_agendafiles = cls._get_agendadocuments() + if not loaded_agendafiles: + return + raw_agenda = ORGMODE.agenda_manager.get_todo(loaded_agendafiles) + + cls.line2doc = {} + # create buffer at bottom + cmd = [u'setlocal filetype=orgagenda'] + cls._switch_to(u'AGENDA', cmd) + + # format text of agenda + final_agenda = [] + for i, h in enumerate(raw_agenda): + tmp = u"%s %s" % (h.todo, h.title) + final_agenda.append(tmp) + cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start) + + # show agenda + vim.current.buffer[:] = [u_encode(i) for i in final_agenda] + vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc')) + + @classmethod + def list_timeline(cls): + """ + List a timeline of the current buffer to get an overview of the + current file. + """ + raw_agenda = ORGMODE.agenda_manager.get_timestamped_items( + [ORGMODE.get_document()]) + + # create buffer at bottom + cmd = [u'setlocal filetype=orgagenda'] + cls._switch_to(u'AGENDA', cmd) + + cls.line2doc = {} + # format text of agenda + final_agenda = [] + for i, h in enumerate(raw_agenda): + tmp = u"%s %s" % (h.todo, h.title) + final_agenda.append(tmp) + cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start) + + # show agenda + vim.current.buffer[:] = [u_encode(i) for i in final_agenda] + vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc')) + + def register(self): + u""" + Registration of the plugin. + + Key bindings and other initialization should be done here. + """ + add_cmd_mapping_menu( + self, + name=u"OrgAgendaTodo", + function=u'%s ORGMODE.plugins[u"Agenda"].list_all_todos()' % VIM_PY_CALL, + key_mapping=u'<localleader>cat', + menu_desrc=u'Agenda for all TODOs' + ) + add_cmd_mapping_menu( + self, + name=u"OrgBufferAgendaTodo", + function=u'%s ORGMODE.plugins[u"Agenda"].list_all_todos(current_buffer=True)' % VIM_PY_CALL, + key_mapping=u'<localleader>caT', + menu_desrc=u'Agenda for all TODOs based on current buffer' + ) + add_cmd_mapping_menu( + self, + name=u"OrgAgendaWeek", + function=u'%s ORGMODE.plugins[u"Agenda"].list_next_week()' % VIM_PY_CALL, + key_mapping=u'<localleader>caa', + menu_desrc=u'Agenda for the week' + ) + add_cmd_mapping_menu( + self, + name=u"OrgBufferAgendaWeek", + function=u'%s ORGMODE.plugins[u"Agenda"].list_next_week_for_buffer()' % VIM_PY_CALL, + key_mapping=u'<localleader>caA', + menu_desrc=u'Agenda for the week based on current buffer' + ) + add_cmd_mapping_menu( + self, + name=u'OrgAgendaTimeline', + function=u'%s ORGMODE.plugins[u"Agenda"].list_timeline()' % VIM_PY_CALL, + key_mapping=u'<localleader>caL', + menu_desrc=u'Timeline for this buffer' + ) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Date.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Date.py new file mode 100644 index 0000000..04a675e --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Date.py @@ -0,0 +1,318 @@ +# -*- coding: utf-8 -*- +import re +from datetime import timedelta, date, datetime + +import operator + +import vim + +from orgmode._vim import ORGMODE, echom, insert_at_cursor, get_user_input +from orgmode import settings +from orgmode.keybinding import Keybinding, Plug +from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.unicode_compatibility import * +from orgmode.py3compat.py_py3_string import * + +class Date(object): + u""" + Handles all date and timestamp related tasks. + + TODO: extend functionality (calendar, repetitions, ranges). See + http://orgmode.org/guide/Dates-and-Times.html#Dates-and-Times + """ + + date_regex = r"\d\d\d\d-\d\d-\d\d" + datetime_regex = r"[A-Z]\w\w \d\d\d\d-\d\d-\d\d \d\d:\d\d>" + + month_mapping = { + u'jan': 1, u'feb': 2, u'mar': 3, u'apr': 4, u'may': 5, + u'jun': 6, u'jul': 7, u'aug': 8, u'sep': 9, u'oct': 10, u'nov': 11, + u'dec': 12} + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'Dates and Scheduling') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + # commands for this plugin + self.commands = [] + + # set speeddating format that is compatible with orgmode + try: + if int(vim.eval(u_encode(u'exists(":SpeedDatingFormat")'))) == 2: + vim.command(u_encode(u':1SpeedDatingFormat %Y-%m-%d %a')) + vim.command(u_encode(u':1SpeedDatingFormat %Y-%m-%d %a %H:%M')) + else: + echom(u'Speeddating plugin not installed. Please install it.') + except: + echom(u'Speeddating plugin not installed. Please install it.') + + @classmethod + def _modify_time(cls, startdate, modifier): + u"""Modify the given startdate according to modifier. Return the new + date or datetime. + + See http://orgmode.org/manual/The-date_002ftime-prompt.html + """ + if modifier is None or modifier == '' or modifier == '.': + return startdate + + # rm crap from modifier + modifier = modifier.strip() + + ops = {'-': operator.sub, '+': operator.add} + + # check real date + date_regex = r"(\d\d\d\d)-(\d\d)-(\d\d)" + match = re.search(date_regex, modifier) + if match: + year, month, day = match.groups() + newdate = date(int(year), int(month), int(day)) + + # check abbreviated date, seperated with '-' + date_regex = u"(\d{1,2})-(\d+)-(\d+)" + match = re.search(date_regex, modifier) + if match: + year, month, day = match.groups() + newdate = date(2000 + int(year), int(month), int(day)) + + # check abbreviated date, seperated with '/' + # month/day + date_regex = u"(\d{1,2})/(\d{1,2})" + match = re.search(date_regex, modifier) + if match: + month, day = match.groups() + newdate = date(startdate.year, int(month), int(day)) + # date should be always in the future + if newdate < startdate: + newdate = date(startdate.year + 1, int(month), int(day)) + + # check full date, seperated with 'space' + # month day year + # 'sep 12 9' --> 2009 9 12 + date_regex = u"(\w\w\w) (\d{1,2}) (\d{1,2})" + match = re.search(date_regex, modifier) + if match: + gr = match.groups() + day = int(gr[1]) + month = int(cls.month_mapping[gr[0]]) + year = 2000 + int(gr[2]) + newdate = date(year, int(month), int(day)) + + # check days as integers + date_regex = u"^(\d{1,2})$" + match = re.search(date_regex, modifier) + if match: + newday, = match.groups() + newday = int(newday) + if newday > startdate.day: + newdate = date(startdate.year, startdate.month, newday) + else: + # TODO: DIRTY, fix this + # this does NOT cover all edge cases + newdate = startdate + timedelta(days=28) + newdate = date(newdate.year, newdate.month, newday) + + # check for full days: Mon, Tue, Wed, Thu, Fri, Sat, Sun + modifier_lc = modifier.lower() + match = re.search(u'mon|tue|wed|thu|fri|sat|sun', modifier_lc) + if match: + weekday_mapping = { + u'mon': 0, u'tue': 1, u'wed': 2, u'thu': 3, + u'fri': 4, u'sat': 5, u'sun': 6} + diff = (weekday_mapping[modifier_lc] - startdate.weekday()) % 7 + # use next weeks weekday if current weekday is the same as modifier + if diff == 0: + diff = 7 + newdate = startdate + timedelta(days=diff) + + # check for days modifier with appended d + match = re.search(u'^(\+|-)(\d*)d', modifier) + if match: + op, days = match.groups() + newdate = ops[op](startdate, timedelta(days=int(days))) + + # check for days modifier without appended d + match = re.search(u'^(\+|-)(\d*) |^(\+|-)(\d*)$', modifier) + if match: + groups = match.groups() + try: + op = groups[0] + days = int(groups[1]) + except: + op = groups[2] + days = int(groups[3]) + newdate = ops[op](startdate, timedelta(days=days)) + + # check for week modifier + match = re.search(u'^(\+|-)(\d+)w', modifier) + if match: + op, weeks = match.groups() + newdate = ops[op](startdate, timedelta(weeks=int(weeks))) + + # check for month modifier + match = re.search(u'^(\+|-)(\d+)m', modifier) + if match: + op, months = match.groups() + newdate = date(startdate.year, ops[op](startdate.month, int(months)), + startdate.day) + + # check for year modifier + match = re.search(u'^(\+|-)(\d*)y', modifier) + if match: + op, years = match.groups() + newdate = date(ops[op](startdate.year, int(years)), startdate.month, + startdate.day) + + # check for month day + match = re.search( + u'(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) (\d{1,2})', + modifier.lower()) + if match: + month = cls.month_mapping[match.groups()[0]] + day = int(match.groups()[1]) + newdate = date(startdate.year, int(month), int(day)) + # date should be always in the future + if newdate < startdate: + newdate = date(startdate.year + 1, int(month), int(day)) + + # check abbreviated date, seperated with '/' + # month/day/year + date_regex = u"(\d{1,2})/(\d+)/(\d+)" + match = re.search(date_regex, modifier) + if match: + month, day, year = match.groups() + newdate = date(2000 + int(year), int(month), int(day)) + + # check for month day year + # sep 12 2011 + match = re.search( + u'(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) (\d{1,2}) (\d{1,4})', + modifier.lower()) + if match: + month = int(cls.month_mapping[match.groups()[0]]) + day = int(match.groups()[1]) + if len(match.groups()[2]) < 4: + year = 2000 + int(match.groups()[2]) + else: + year = int(match.groups()[2]) + newdate = date(year, month, day) + + # check for time: HH:MM + # '12:45' --> datetime(2006, 06, 13, 12, 45)) + match = re.search(u'(\d{1,2}):(\d\d)$', modifier) + if match: + try: + startdate = newdate + except: + pass + return datetime( + startdate.year, startdate.month, startdate.day, + int(match.groups()[0]), int(match.groups()[1])) + + try: + return newdate + except: + return startdate + + @classmethod + def insert_timestamp(cls, active=True): + u""" + Insert a timestamp at the cursor position. + + TODO: show fancy calendar to pick the date from. + TODO: add all modifier of orgmode. + """ + today = date.today() + msg = u''.join([ + u'Inserting ', + unicode(u_decode(today.strftime(u'%Y-%m-%d %a'))), + u' | Modify date']) + modifier = get_user_input(msg) + + # abort if the user canceled the input promt + if modifier is None: + return + + newdate = cls._modify_time(today, modifier) + + # format + if isinstance(newdate, datetime): + newdate = newdate.strftime( + u_decode(u_encode(u'%Y-%m-%d %a %H:%M'))) + else: + newdate = newdate.strftime( + u_decode(u_encode(u'%Y-%m-%d %a'))) + timestamp = u'<%s>' % newdate if active else u'[%s]' % newdate + + insert_at_cursor(timestamp) + + @classmethod + def insert_timestamp_with_calendar(cls, active=True): + u""" + Insert a timestamp at the cursor position. + Show fancy calendar to pick the date from. + + TODO: add all modifier of orgmode. + """ + if int(vim.eval(u_encode(u'exists(":CalendarH")'))) != 2: + vim.command("echo 'Please install plugin Calendar to enable this function'") + return + vim.command("CalendarH") + # backup calendar_action + calendar_action = vim.eval("g:calendar_action") + vim.command("let g:org_calendar_action_backup = '" + calendar_action + "'") + vim.command("let g:calendar_action = 'CalendarAction'") + + timestamp_template = u'<%s>' if active else u'[%s]' + # timestamp template + vim.command("let g:org_timestamp_template = '" + timestamp_template + "'") + + def register(self): + u""" + Registration of the plugin. + + Key bindings and other initialization should be done here. + """ + add_cmd_mapping_menu( + self, + name=u'OrgDateInsertTimestampActiveCmdLine', + key_mapping=u'<localleader>sa', + function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp()' % VIM_PY_CALL, + menu_desrc=u'Timest&' + ) + add_cmd_mapping_menu( + self, + name=u'OrgDateInsertTimestampInactiveCmdLine', + key_mapping='<localleader>si', + function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp(False)' % VIM_PY_CALL, + menu_desrc=u'Timestamp (&inactive)' + ) + add_cmd_mapping_menu( + self, + name=u'OrgDateInsertTimestampActiveWithCalendar', + key_mapping=u'<localleader>pa', + function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp_with_calendar()' % VIM_PY_CALL, + menu_desrc=u'Timestamp with Calendar' + ) + add_cmd_mapping_menu( + self, + name=u'OrgDateInsertTimestampInactiveWithCalendar', + key_mapping=u'<localleader>pi', + function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp_with_calendar(False)' % VIM_PY_CALL, + menu_desrc=u'Timestamp with Calendar(inactive)' + ) + + submenu = self.menu + Submenu(u'Change &Date') + submenu + ActionEntry(u'Day &Earlier', u'<C-x>', u'<C-x>') + submenu + ActionEntry(u'Day &Later', u'<C-a>', u'<C-a>') + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/EditCheckbox.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/EditCheckbox.py new file mode 100644 index 0000000..bb93fc1 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/EditCheckbox.py @@ -0,0 +1,330 @@ +# -*- coding: utf-8 -*- + +import vim +from orgmode._vim import echo, echom, echoe, ORGMODE, apply_count, repeat, insert_at_cursor, indent_orgmode +from orgmode import settings +from orgmode.menu import Submenu, Separator, ActionEntry, add_cmd_mapping_menu +from orgmode.keybinding import Keybinding, Plug, Command +from orgmode.liborgmode.checkboxes import Checkbox +from orgmode.liborgmode.dom_obj import OrderListType + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.py_py3_string import * +from orgmode.py3compat.unicode_compatibility import * + +class EditCheckbox(object): + u""" + Checkbox plugin. + """ + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'Edit Checkbox') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + # commands for this plugin + self.commands = [] + + @classmethod + def new_checkbox(cls, below=None, plain=None): + ''' + if below is: + True -> create new list below current line + False/None -> create new list above current line + if plain is: + True -> create a plainlist item + False/None -> create an empty checkbox + ''' + d = ORGMODE.get_document() + h = d.current_heading() + if h is None: + return + # init checkboxes for current heading + h.init_checkboxes() + c = h.current_checkbox() + + nc = Checkbox() + nc._heading = h + + # default checkbox level + level = h.level + 1 + start = vim.current.window.cursor[0] - 1 + # if no checkbox is found, insert at current line with indent level=1 + if c is None: + h.checkboxes.append(nc) + else: + l = c.get_parent_list() + idx = c.get_index_in_parent_list() + if l is not None and idx is not None: + l.insert(idx + (1 if below else 0), nc) + # workaround for broken associations, Issue #165 + nc._parent = c.parent + if below: + if c.next_sibling: + c.next_sibling._previous_sibling = nc + nc._next_sibling = c.next_sibling + c._next_sibling = nc + nc._previous_sibling = c + else: + if c.previous_sibling: + c.previous_sibling._next_sibling = nc + nc._next_sibling = c + nc._previous_sibling = c.previous_sibling + c._previous_sibling = nc + + t = c.type + # increase key for ordered lists + if t[-1] in OrderListType: + try: + num = int(t[:-1]) + (1 if below else -1) + if num < 0: + # don't decrease to numbers below zero + echom(u"Can't decrement further than '0'") + return + t = '%d%s' % (num, t[-1]) + except ValueError: + try: + char = ord(t[:-1]) + (1 if below else -1) + if below: + if char == 91: + # stop incrementing at Z (90) + echom(u"Can't increment further than 'Z'") + return + elif char == 123: + # increment from z (122) to A + char = 65 + else: + if char == 96: + # stop decrementing at a (97) + echom(u"Can't decrement further than 'a'") + return + elif char == 64: + # decrement from A (65) to z + char = 122 + t = u'%s%s' % (chr(char), t[-1]) + except ValueError: + pass + nc.type = t + level = c.level + + if below: + start = c.end_of_last_child + else: + start = c.start + + if plain: # only create plainlist item when requested + nc.status = None + nc.level = level + + if below: + start += 1 + # vim's buffer behave just opposite to Python's list when inserting a + # new item. The new entry is appended in vim put prepended in Python! + vim.current.buffer.append("") # workaround for neovim + vim.current.buffer[start:start] = [unicode(nc)] + del vim.current.buffer[-1] # restore from workaround for neovim + + # update checkboxes status + cls.update_checkboxes_status() + + # do not start insert upon adding new checkbox, Issue #211 + if int(settings.get(u'org_prefer_insert_mode', u'1')): + vim.command(u_encode(u'exe "normal %dgg"|startinsert!' % (start + 1, ))) + else: + vim.command(u_encode(u'exe "normal %dgg$"' % (start + 1, ))) + + @classmethod + def toggle(cls, checkbox=None): + u""" + Toggle the checkbox given in the parameter. + If the checkbox is not given, it will toggle the current checkbox. + """ + d = ORGMODE.get_document() + current_heading = d.current_heading() + # init checkboxes for current heading + if current_heading is None: + return + current_heading = current_heading.init_checkboxes() + + if checkbox is None: + # get current_checkbox + c = current_heading.current_checkbox() + # no checkbox found + if c is None: + cls.update_checkboxes_status() + return + else: + c = checkbox + + if c.status == Checkbox.STATUS_OFF or c.status is None: + # set checkbox status on if all children are on + if c.all_children_status()[0] == 0 or c.are_children_all(Checkbox.STATUS_ON): + c.toggle() + d.write_checkbox(c) + elif c.status is None: + c.status = Checkbox.STATUS_OFF + d.write_checkbox(c) + + elif c.status == Checkbox.STATUS_ON: + if c.all_children_status()[0] == 0 or c.is_child_one(Checkbox.STATUS_OFF): + c.toggle() + d.write_checkbox(c) + + elif c.status == Checkbox.STATUS_INT: + # can't toggle intermediate state directly according to emacs orgmode + pass + # update checkboxes status + cls.update_checkboxes_status() + + @classmethod + def _update_subtasks(cls): + d = ORGMODE.get_document() + h = d.current_heading() + # init checkboxes for current heading + h.init_checkboxes() + # update heading subtask info + c = h.first_checkbox + if c is None: + return + total, on = c.all_siblings_status() + h.update_subtasks(total, on) + # update all checkboxes under current heading + cls._update_checkboxes_subtasks(c) + + @classmethod + def _update_checkboxes_subtasks(cls, checkbox): + # update checkboxes + for c in checkbox.all_siblings(): + if c.children: + total, on = c.first_child.all_siblings_status() + c.update_subtasks(total, on) + cls._update_checkboxes_subtasks(c.first_child) + + @classmethod + def update_checkboxes_status(cls): + d = ORGMODE.get_document() + h = d.current_heading() + if h is None: + return + # init checkboxes for current heading + h.init_checkboxes() + + cls._update_checkboxes_status(h.first_checkbox) + cls._update_subtasks() + + @classmethod + def _update_checkboxes_status(cls, checkbox=None): + u""" helper function for update checkboxes status + :checkbox: The first checkbox of this indent level + :return: The status of the parent checkbox + """ + if checkbox is None: + return + + status_off, status_on, status_int, total = 0, 0, 0, 0 + # update all top level checkboxes' status + for c in checkbox.all_siblings(): + current_status = c.status + # if this checkbox is not leaf, its status should determine by all its children + if c.all_children_status()[0] > 0: + current_status = cls._update_checkboxes_status(c.first_child) + + # don't update status if the checkbox has no status + if c.status is None: + current_status = None + # the checkbox needs to have status + else: + total += 1 + + # count number of status in this checkbox level + if current_status == Checkbox.STATUS_OFF: + status_off += 1 + elif current_status == Checkbox.STATUS_ON: + status_on += 1 + elif current_status == Checkbox.STATUS_INT: + status_int += 1 + + # write status if any update + if current_status is not None and c.status != current_status: + c.status = current_status + d = ORGMODE.get_document() + d.write_checkbox(c) + + parent_status = Checkbox.STATUS_INT + # all silbing checkboxes are off status + if total == 0: + pass + elif status_off == total: + parent_status = Checkbox.STATUS_OFF + # all silbing checkboxes are on status + elif status_on == total: + parent_status = Checkbox.STATUS_ON + # one silbing checkbox is on or int status + elif status_on != 0 or status_int != 0: + parent_status = Checkbox.STATUS_INT + # other cases + else: + parent_status = None + + return parent_status + + def register(self): + u""" + Registration of the plugin. + + Key bindings and other initialization should be done here. + """ +# default setting if it is not already set. + +# checkbox related operation + add_cmd_mapping_menu( + self, + name=u'OrgCheckBoxNewAbove', + function=u'%s ORGMODE.plugins[u"EditCheckbox"].new_checkbox()<CR>' % VIM_PY_CALL, + key_mapping=u'<localleader>cN', + menu_desrc=u'New CheckBox Above' + ) + add_cmd_mapping_menu( + self, + name=u'OrgCheckBoxNewBelow', + function=u'%s ORGMODE.plugins[u"EditCheckbox"].new_checkbox(below=True)<CR>' % VIM_PY_CALL, + key_mapping=u'<localleader>cn', + menu_desrc=u'New CheckBox Below' + ) + add_cmd_mapping_menu( + self, + name=u'OrgCheckBoxToggle', + function=u':silent! %s ORGMODE.plugins[u"EditCheckbox"].toggle()<CR>' % VIM_PY_CALL, + key_mapping=u'<localleader>cc', + menu_desrc=u'Toggle Checkbox' + ) + add_cmd_mapping_menu( + self, + name=u'OrgCheckBoxUpdate', + function=u':silent! %s ORGMODE.plugins[u"EditCheckbox"].update_checkboxes_status()<CR>' % VIM_PY_CALL, + key_mapping=u'<localleader>c#', + menu_desrc=u'Update Subtasks' + ) +# plainlist related operation + add_cmd_mapping_menu( + self, + name=u'OrgPlainListItemNewAbove', + function=u'%s ORGMODE.plugins[u"EditCheckbox"].new_checkbox(plain=True)<CR>' % VIM_PY_CALL, + key_mapping=u'<localleader>cL', + menu_desrc=u'New PlainList Item Above' + ) + add_cmd_mapping_menu( + self, + name=u'OrgPlainListItemNewBelow', + function=u'%s ORGMODE.plugins[u"EditCheckbox"].new_checkbox(below=True, plain=True)<CR>' % VIM_PY_CALL, + key_mapping=u'<localleader>cl', + menu_desrc=u'New PlainList Item Below' + ) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/EditStructure.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/EditStructure.py new file mode 100644 index 0000000..61a94b4 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/EditStructure.py @@ -0,0 +1,430 @@ +# -*- coding: utf-8 -*- + +import vim + +from orgmode._vim import ORGMODE, apply_count, repeat, realign_tags +from orgmode import settings +from orgmode.exceptions import HeadingDomError +from orgmode.keybinding import Keybinding, Plug, MODE_INSERT, MODE_NORMAL +from orgmode.menu import Submenu, Separator, ActionEntry +from orgmode.liborgmode.base import Direction +from orgmode.liborgmode.headings import Heading + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.py_py3_string import * + + +class EditStructure(object): + u""" EditStructure plugin """ + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'&Edit Structure') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + @classmethod + def new_heading(cls, below=None, insert_mode=False, end_of_last_child=False): + u""" + :below: True, insert heading below current heading, False, + insert heading above current heading, None, special + behavior for insert mode, use the current text as + heading + :insert_mode: True, if action is performed in insert mode + :end_of_last_child: True, insert heading at the end of last child, + otherwise the newly created heading will "take + over" the current heading's children + """ + d = ORGMODE.get_document() + current_heading = d.current_heading() + cursor = vim.current.window.cursor[:] + if not current_heading: + # the user is in meta data region + pos = cursor[0] - 1 + heading = Heading(title=d.meta_information[pos], body=d.meta_information[pos + 1:]) + d.headings.insert(0, heading) + del d.meta_information[pos:] + d.write() + vim.command(u_encode(u'exe "normal %dgg"|startinsert!' % (heading.start_vim, ))) + return heading + + # check for plain list(checkbox) + current_heading.init_checkboxes() + c = current_heading.current_checkbox() + if c is not None: + ORGMODE.plugins[u"EditCheckbox"].new_checkbox(below, not c.status) + return + + heading = Heading(level=current_heading.level) + + # it's weird but this is the behavior of original orgmode + if below is None: + below = cursor[1] != 0 or end_of_last_child + + # insert newly created heading + l = current_heading.get_parent_list() + idx = current_heading.get_index_in_parent_list() + if l is not None and idx is not None: + l.insert(idx + (1 if below else 0), heading) + else: + raise HeadingDomError(u'Current heading is not properly linked in DOM') + + if below and not end_of_last_child: + # append heading at the end of current heading and also take + # over the children of current heading + for child in current_heading.children: + heading.children.append(child, taint=False) + current_heading.children.remove_slice( + 0, len(current_heading.children), + taint=False) + + # if cursor is currently on a heading, insert parts of it into the + # newly created heading + if insert_mode and cursor[1] != 0 and cursor[0] == current_heading.start_vim: + offset = cursor[1] - current_heading.level - 1 - ( + len(current_heading.todo) + 1 if current_heading.todo else 0) + if offset < 0: + offset = 0 + if int(settings.get(u'org_improve_split_heading', u'1')) and \ + offset > 0 and len(current_heading.title) == offset + 1 \ + and current_heading.title[offset - 1] not in (u' ', u'\t'): + offset += 1 + heading.title = current_heading.title[offset:] + current_heading.title = current_heading.title[:offset] + heading.body = current_heading.body[:] + current_heading.body = [] + + d.write() + # do not start insert upon adding new headings, unless already in insert mode. Issue #211 + if int(settings.get(u'org_prefer_insert_mode', u'1')) or insert_mode: + vim.command(u_encode(u'exe "normal %dgg"|startinsert!' % (heading.start_vim, ))) + else: + vim.command(u_encode(u'exe "normal %dgg$"' % (heading.start_vim, ))) + + # return newly created heading + return heading + + @classmethod + def _append_heading(cls, heading, parent): + if heading.level <= parent.level: + raise ValueError('Heading level not is lower than parent level: %d ! > %d' % (heading.level, parent.level)) + + if parent.children and parent.children[-1].level < heading.level: + cls._append_heading(heading, parent.children[-1]) + else: + parent.children.append(heading, taint=False) + + @classmethod + def _change_heading_level(cls, level, including_children=True, on_heading=False, insert_mode=False): + u""" + Change level of heading realtively with or without including children. + + :level: the number of levels to promote/demote heading + :including_children: True if should should be included in promoting/demoting + :on_heading: True if promoting/demoting should only happen when the cursor is on the heading + :insert_mode: True if vim is in insert mode + """ + # TODO : current promote and demote works for only headings. Since + # checkboxes also have tree structure. We should think of + # expanding the functionality of promoting and demoting to + # checkboxes as well + d = ORGMODE.get_document() + current_heading = d.current_heading() + if not current_heading or on_heading and current_heading.start_vim != vim.current.window.cursor[0]: + # TODO figure out the actually pressed keybinding and feed these + # keys instead of making keys up like this + if level > 0: + if insert_mode: + vim.eval(u_encode(u'feedkeys("\<C-t>", "n")')) + elif including_children: + vim.eval(u_encode(u'feedkeys(">]]", "n")')) + elif on_heading: + vim.eval(u_encode(u'feedkeys(">>", "n")')) + else: + vim.eval(u_encode(u'feedkeys(">}", "n")')) + else: + if insert_mode: + vim.eval(u_encode(u'feedkeys("\<C-d>", "n")')) + elif including_children: + vim.eval(u_encode(u'feedkeys("<]]", "n")')) + elif on_heading: + vim.eval(u_encode(u'feedkeys("<<", "n")')) + else: + vim.eval(u_encode(u'feedkeys("<}", "n")')) + # return True because otherwise apply_count will not work + return True + + # don't allow demotion below level 1 + if current_heading.level == 1 and level < 1: + return False + + # reduce level of demotion to a minimum heading level of 1 + if (current_heading.level + level) < 1: + level = 1 + + def indent(heading, ic): + if not heading: + return + heading.level += level + + if ic: + for child in heading.children: + indent(child, ic) + + # save cursor position + c = vim.current.window.cursor[:] + + # indent the promoted/demoted heading + indent_end_vim = current_heading.end_of_last_child_vim if including_children else current_heading.end_vim + indent(current_heading, including_children) + + # when changing the level of a heading, its position in the DOM + # needs to be updated. It's likely that the heading gets a new + # parent and new children when demoted or promoted + + # find new parent + p = current_heading.parent + pl = current_heading.get_parent_list() + ps = current_heading.previous_sibling + nhl = current_heading.level + + if level > 0: + # demotion + # subheading or top level heading + if ps and nhl > ps.level: + pl.remove(current_heading, taint=False) + # find heading that is the new parent heading + oh = ps + h = ps + while nhl > h.level: + oh = h + if h.children: + h = h.children[-1] + else: + break + np = h if nhl > h.level else oh + + # append current heading to new heading + np.children.append(current_heading, taint=False) + + # if children are not included, distribute them among the + # parent heading and it's siblings + if not including_children: + for h in current_heading.children[:]: + if h and h.level <= nhl: + cls._append_heading(h, np) + current_heading.children.remove(h, taint=False) + else: + # promotion + if p and nhl <= p.level: + idx = current_heading.get_index_in_parent_list() + 1 + # find the new parent heading + oh = p + h = p + while nhl <= h.level: + # append new children to current heading + for child in h.children[idx:]: + cls._append_heading(child, current_heading) + h.children.remove_slice(idx, len(h.children), taint=False) + idx = h.get_index_in_parent_list() + 1 + if h.parent: + h = h.parent + else: + break + ns = oh.next_sibling + while ns and ns.level > current_heading.level: + nns = ns.next_sibling + cls._append_heading(ns, current_heading) + ns = nns + + # append current heading to new parent heading / document + pl.remove(current_heading, taint=False) + if nhl > h.level: + h.children.insert(idx, current_heading, taint=False) + else: + d.headings.insert(idx, current_heading, taint=False) + + d.write() + + # restore cursor position + vim.current.window.cursor = (c[0], c[1] + level) + + return True + + @classmethod + @realign_tags + @repeat + @apply_count + def demote_heading(cls, including_children=True, on_heading=False, insert_mode=False): + if cls._change_heading_level(1, including_children=including_children, on_heading=on_heading, insert_mode=insert_mode): + if including_children: + return u'OrgDemoteSubtree' + return u'OrgDemoteHeading' + + @classmethod + @realign_tags + @repeat + @apply_count + def promote_heading(cls, including_children=True, on_heading=False, insert_mode=False): + if cls._change_heading_level(-1, including_children=including_children, on_heading=on_heading, insert_mode=insert_mode): + if including_children: + return u'OrgPromoteSubtreeNormal' + return u'OrgPromoteHeadingNormal' + + @classmethod + def _move_heading(cls, direction=Direction.FORWARD, including_children=True): + u""" Move heading up or down + + :returns: heading or None + """ + d = ORGMODE.get_document() + current_heading = d.current_heading() + if not current_heading or \ + (direction == Direction.FORWARD and not current_heading.next_sibling) or \ + (direction == Direction.BACKWARD and not current_heading.previous_sibling): + return None + + cursor_offset = vim.current.window.cursor[0] - (current_heading._orig_start + 1) + l = current_heading.get_parent_list() + if l is None: + raise HeadingDomError(u'Current heading is not properly linked in DOM') + + if not including_children: + if current_heading.previous_sibling: + npl = current_heading.previous_sibling.children + for child in current_heading.children: + npl.append(child, taint=False) + elif current_heading.parent: + # if the current heading doesn't have a previous sibling it + # must be the first heading + np = current_heading.parent + for child in current_heading.children: + cls._append_heading(child, np) + else: + # if the current heading doesn't have a parent, its children + # must be added as top level headings to the document + npl = l + for child in current_heading.children[::-1]: + npl.insert(0, child, taint=False) + current_heading.children.remove_slice(0, len(current_heading.children), taint=False) + + idx = current_heading.get_index_in_parent_list() + if idx is None: + raise HeadingDomError(u'Current heading is not properly linked in DOM') + + offset = 1 if direction == Direction.FORWARD else -1 + del l[idx] + l.insert(idx + offset, current_heading) + + d.write() + + vim.current.window.cursor = ( + current_heading.start_vim + cursor_offset, + vim.current.window.cursor[1]) + + return True + + @classmethod + @repeat + @apply_count + def move_heading_upward(cls, including_children=True): + if cls._move_heading(direction=Direction.BACKWARD, including_children=including_children): + if including_children: + return u'OrgMoveSubtreeUpward' + return u'OrgMoveHeadingUpward' + + @classmethod + @repeat + @apply_count + def move_heading_downward(cls, including_children=True): + if cls._move_heading(direction=Direction.FORWARD, including_children=including_children): + if including_children: + return u'OrgMoveSubtreeDownward' + return u'OrgMoveHeadingDownward' + + def register(self): + u""" + Registration of plugin. Key bindings and other initialization should be done. + """ +# EditStructure related default settings + settings.set(u'org_improve_split_heading', u'1') +# EditStructure related keybindings + self.keybindings.append(Keybinding(u'<C-S-CR>', + Plug(u'OrgNewHeadingAboveNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=False)<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'New Heading &above', self.keybindings[-1]) + self.keybindings.append(Keybinding(u'<localleader>hN', u'<Plug>OrgNewHeadingAboveNormal', mode=MODE_NORMAL)) + self.keybindings.append(Keybinding(u'<localleader><CR>', u'<Plug>OrgNewHeadingAboveNormal', mode=MODE_NORMAL)) + + self.keybindings.append(Keybinding(u'<S-CR>', + Plug(u'OrgNewHeadingBelowNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=True)<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'New Heading &below', self.keybindings[-1]) + self.keybindings.append(Keybinding(u'<localleader>hh', u'<Plug>OrgNewHeadingBelowNormal', mode=MODE_NORMAL)) + self.keybindings.append(Keybinding(u'<leader><CR>', u'<Plug>OrgNewHeadingBelowNormal', mode=MODE_NORMAL)) + + self.keybindings.append(Keybinding(u'<C-CR>', Plug(u'OrgNewHeadingBelowAfterChildrenNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=True, end_of_last_child=True)<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'New Heading below, after &children', self.keybindings[-1]) + self.keybindings.append(Keybinding(u'<localleader>hn', u'<Plug>OrgNewHeadingBelowAfterChildrenNormal', mode=MODE_NORMAL)) + self.keybindings.append(Keybinding(u'<CR>', u'<Plug>OrgNewHeadingBelowAfterChildrenNormal', mode=MODE_NORMAL)) + + self.keybindings.append(Keybinding(u'<C-S-CR>', Plug(u'OrgNewHeadingAboveInsert', u'<C-o>:<C-u>silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=False, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT))) + self.keybindings.append(Keybinding(u'<S-CR>', Plug(u'OrgNewHeadingBelowInsert', u'<C-o>:<C-u>silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=True, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT))) + self.keybindings.append(Keybinding(u'<C-CR>', Plug(u'OrgNewHeadingBelowAfterChildrenInsert', u'<C-o>:<C-u>silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(insert_mode=True, end_of_last_child=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT))) + + self.menu + Separator() + + self.keybindings.append(Keybinding(u'm{', Plug(u'OrgMoveHeadingUpward', + u'%s ORGMODE.plugins[u"EditStructure"].move_heading_upward(including_children=False)<CR>' % VIM_PY_CALL))) + self.keybindings.append(Keybinding(u'm[[', + Plug(u'OrgMoveSubtreeUpward', u'%s ORGMODE.plugins[u"EditStructure"].move_heading_upward()<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'Move Subtree &Up', self.keybindings[-1]) + self.keybindings.append(Keybinding(u'm}', + Plug(u'OrgMoveHeadingDownward', u'%s ORGMODE.plugins[u"EditStructure"].move_heading_downward(including_children=False)<CR>' % VIM_PY_CALL))) + self.keybindings.append(Keybinding(u'm]]', + Plug(u'OrgMoveSubtreeDownward', u'%s ORGMODE.plugins[u"EditStructure"].move_heading_downward()<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'Move Subtree &Down', self.keybindings[-1]) + + self.menu + Separator() + + self.menu + ActionEntry(u'&Copy Heading', u'yah', u'yah') + self.menu + ActionEntry(u'C&ut Heading', u'dah', u'dah') + + self.menu + Separator() + + self.menu + ActionEntry(u'&Copy Subtree', u'yar', u'yar') + self.menu + ActionEntry(u'C&ut Subtree', u'dar', u'dar') + self.menu + ActionEntry(u'&Paste Subtree', u'p', u'p') + + self.menu + Separator() + + self.keybindings.append(Keybinding(u'<ah', Plug(u'OrgPromoteHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False)<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'&Promote Heading', self.keybindings[-1]) + self.keybindings.append(Keybinding(u'<<', Plug(u'OrgPromoteOnHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False, on_heading=True)<CR>' % VIM_PY_CALL))) + self.keybindings.append(Keybinding(u'<{', u'<Plug>OrgPromoteHeadingNormal', mode=MODE_NORMAL)) + self.keybindings.append(Keybinding(u'<ih', u'<Plug>OrgPromoteHeadingNormal', mode=MODE_NORMAL)) + + self.keybindings.append(Keybinding(u'<ar', Plug(u'OrgPromoteSubtreeNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading()<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'&Promote Subtree', self.keybindings[-1]) + self.keybindings.append(Keybinding(u'<[[', u'<Plug>OrgPromoteSubtreeNormal', mode=MODE_NORMAL)) + self.keybindings.append(Keybinding(u'<ir', u'<Plug>OrgPromoteSubtreeNormal', mode=MODE_NORMAL)) + + self.keybindings.append(Keybinding(u'>ah', Plug(u'OrgDemoteHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False)<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'&Demote Heading', self.keybindings[-1]) + self.keybindings.append(Keybinding(u'>>', Plug(u'OrgDemoteOnHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False, on_heading=True)<CR>' % VIM_PY_CALL))) + self.keybindings.append(Keybinding(u'>}', u'<Plug>OrgDemoteHeadingNormal', mode=MODE_NORMAL)) + self.keybindings.append(Keybinding(u'>ih', u'<Plug>OrgDemoteHeadingNormal', mode=MODE_NORMAL)) + + self.keybindings.append(Keybinding(u'>ar', Plug(u'OrgDemoteSubtreeNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading()<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'&Demote Subtree', self.keybindings[-1]) + self.keybindings.append(Keybinding(u'>]]', u'<Plug>OrgDemoteSubtreeNormal', mode=MODE_NORMAL)) + self.keybindings.append(Keybinding(u'>ir', u'<Plug>OrgDemoteSubtreeNormal', mode=MODE_NORMAL)) + + # other keybindings + self.keybindings.append(Keybinding(u'<C-d>', Plug(u'OrgPromoteOnHeadingInsert', u'<C-o>:silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False, on_heading=True, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT))) + self.keybindings.append(Keybinding(u'<C-t>', Plug(u'OrgDemoteOnHeadingInsert', u'<C-o>:silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False, on_heading=True, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT))) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Export.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Export.py new file mode 100644 index 0000000..d18f415 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Export.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- + +import os +import subprocess + +import vim + +from orgmode._vim import ORGMODE, echoe, echom +from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu +from orgmode.keybinding import Keybinding, Plug, Command +from orgmode import settings + +from orgmode.py3compat.py_py3_string import * + +class Export(object): + u""" + Export a orgmode file using emacs orgmode. + + This is a *very simple* wrapper of the emacs/orgmode export. emacs and + orgmode need to be installed. We simply call emacs with some options to + export the .org. + + TODO: Offer export options in vim. Don't use the menu. + TODO: Maybe use a native implementation. + """ + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'Export') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + # commands for this plugin + self.commands = [] + + @classmethod + def _get_init_script(cls): + init_script = settings.get(u'org_export_init_script', u'') + if init_script: + init_script = os.path.expandvars(os.path.expanduser(init_script)) + if os.path.exists(init_script): + return init_script + else: + echoe(u'Unable to find init script %s' % init_script) + + @classmethod + def _export(cls, format_): + """Export current file to format. + + Args: + format_: pdf or html + + Returns: + return code + """ + emacsbin = os.path.expandvars(os.path.expanduser( + settings.get(u'org_export_emacs', u'/usr/bin/emacs'))) + if not os.path.exists(emacsbin): + echoe(u'Unable to find emacs binary %s' % emacsbin) + + # build the export command + cmd = [ + emacsbin, + u'-nw', + u'--batch', + u'--visit=%s' % vim.eval(u'expand("%:p")'), + u'--funcall=%s' % format_ + ] + # source init script as well + init_script = cls._get_init_script() + if init_script: + cmd.extend(['--script', init_script]) + + # export + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p.wait() + + if p.returncode != 0 or settings.get(u'org_export_verbose') == 1: + echom('\n'.join(p.communicate())) + return p.returncode + + @classmethod + def topdf(cls): + u"""Export the current buffer as pdf using emacs orgmode.""" + ret = cls._export(u'org-latex-export-to-pdf') + if ret != 0: + echoe(u'PDF export failed.') + else: + echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'pdf')) + + @classmethod + def tobeamer(cls): + u"""Export the current buffer as beamer pdf using emacs orgmode.""" + ret = cls._export(u'org-beamer-export-to-pdf') + if ret != 0: + echoe(u'PDF export failed.') + else: + echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'pdf')) + + @classmethod + def tohtml(cls): + u"""Export the current buffer as html using emacs orgmode.""" + ret = cls._export(u'org-html-export-to-html') + if ret != 0: + echoe(u'HTML export failed.') + else: + echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'html')) + + @classmethod + def tolatex(cls): + u"""Export the current buffer as latex using emacs orgmode.""" + ret = cls._export(u'org-latex-export-to-latex') + if ret != 0: + echoe(u'latex export failed.') + else: + echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'tex')) + + @classmethod + def tomarkdown(cls): + u"""Export the current buffer as markdown using emacs orgmode.""" + ret = cls._export(u'org-md-export-to-markdown') + if ret != 0: + echoe('Markdown export failed. Make sure org-md-export-to-markdown is loaded in emacs, see the manual for details.') + else: + echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'md')) + + def register(self): + u"""Registration and keybindings.""" + + # path to emacs executable + settings.set(u'org_export_emacs', u'/usr/bin/emacs') + # verbose output for export + settings.set(u'org_export_verbose', 0) + # allow the user to define an initialization script + settings.set(u'org_export_init_script', u'') + + # to PDF + add_cmd_mapping_menu( + self, + name=u'OrgExportToPDF', + function=u':%s ORGMODE.plugins[u"Export"].topdf()<CR>' % VIM_PY_CALL, + key_mapping=u'<localleader>ep', + menu_desrc=u'To PDF (via Emacs)' + ) + # to Beamer PDF + add_cmd_mapping_menu( + self, + name=u'OrgExportToBeamerPDF', + function=u':%s ORGMODE.plugins[u"Export"].tobeamer()<CR>' % VIM_PY_CALL, + key_mapping=u'<localleader>eb', + menu_desrc=u'To Beamer PDF (via Emacs)' + ) + # to latex + add_cmd_mapping_menu( + self, + name=u'OrgExportToLaTeX', + function=u':%s ORGMODE.plugins[u"Export"].tolatex()<CR>' % VIM_PY_CALL, + key_mapping=u'<localleader>el', + menu_desrc=u'To LaTeX (via Emacs)' + ) + # to HTML + add_cmd_mapping_menu( + self, + name=u'OrgExportToHTML', + function=u':%s ORGMODE.plugins[u"Export"].tohtml()<CR>' % VIM_PY_CALL, + key_mapping=u'<localleader>eh', + menu_desrc=u'To HTML (via Emacs)' + ) + # to Markdown + add_cmd_mapping_menu( + self, + name=u'OrgExportToMarkdown', + function=u':%s ORGMODE.plugins[u"Export"].tomarkdown()<CR>' % VIM_PY_CALL, + key_mapping=u'<localleader>em', + menu_desrc=u'To Markdown (via Emacs)' + ) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Hyperlinks.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Hyperlinks.py new file mode 100644 index 0000000..dd4ce04 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Hyperlinks.py @@ -0,0 +1,221 @@ +# -*- coding: utf-8 -*- + +import re + +import vim + +from orgmode._vim import echom, ORGMODE, realign_tags +from orgmode.menu import Submenu, Separator, ActionEntry +from orgmode.keybinding import Keybinding, Plug, Command + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.py_py3_string import * + +class Hyperlinks(object): + u""" Hyperlinks plugin """ + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'Hyperlinks') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + # commands for this plugin + self.commands = [] + + uri_match = re.compile( + r'^\[{2}(?P<uri>[^][]*)(\]\[(?P<description>[^][]*))?\]{2}') + + @classmethod + def _get_link(cls, cursor=None): + u""" + Get the link the cursor is on and return it's URI and description + + :cursor: None or (Line, Column) + :returns: None if no link was found, otherwise {uri:URI, + description:DESCRIPTION, line:LINE, start:START, end:END} + or uri and description could be None if not set + """ + cursor = cursor if cursor else vim.current.window.cursor + line = u_decode(vim.current.buffer[cursor[0] - 1]) + + # if the cursor is on the last bracket, it's not recognized as a hyperlink + start = line.rfind(u'[[', 0, cursor[1]) + if start == -1: + start = line.rfind(u'[[', 0, cursor[1] + 2) + end = line.find(u']]', cursor[1]) + if end == -1: + end = line.find(u']]', cursor[1] - 1) + + # extract link + if start != -1 and end != -1: + end += 2 + match = Hyperlinks.uri_match.match(line[start:end]) + + res = { + u'line': line, + u'start': start, + u'end': end, + u'uri': None, + u'description': None} + if match: + res.update(match.groupdict()) + # reverse character escaping(partly done due to matching) + res[u'uri'] = res[u'uri'].replace(u'\\\\', u'\\') + return res + + @classmethod + def follow(cls, action=u'openLink', visual=u''): + u""" Follow hyperlink. If called on a regular string UTL determines the + outcome. Normally a file with that name will be opened. + + :action: "copy" if the link should be copied to clipboard, otherwise + the link will be opened + :visual: "visual" if Universal Text Linking should be triggered in + visual mode + + :returns: URI or None + """ + if not int(vim.eval(u'exists(":Utl")')): + echom(u'Universal Text Linking plugin not installed, unable to proceed.') + return + + action = u'copyLink' \ + if (action and action.startswith(u'copy')) \ + else u'openLink' + visual = u'visual' if visual and visual.startswith(u'visual') else u'' + + link = Hyperlinks._get_link() + + if link and link[u'uri'] is not None: + # call UTL with the URI + vim.command(u_encode(u'Utl %s %s %s' % (action, visual, link[u'uri']))) + return link[u'uri'] + else: + # call UTL and let it decide what to do + vim.command(u_encode(u'Utl %s %s' % (action, visual))) + + @classmethod + @realign_tags + def insert(cls, uri=None, description=None): + u""" Inserts a hyperlink. If no arguments are provided, an interactive + query will be started. + + :uri: The URI that will be opened + :description: An optional description that will be displayed instead of + the URI + + :returns: (URI, description) + """ + link = Hyperlinks._get_link() + if link: + if uri is None and link[u'uri'] is not None: + uri = link[u'uri'] + if description is None and link[u'description'] is not None: + description = link[u'description'] + + if uri is None: + uri = vim.eval(u'input("Link: ", "", "file")') + elif link: + uri = vim.eval(u'input("Link: ", "%s", "file")' % link[u'uri']) + if uri is None: + return + else: + uri = u_decode(uri) + + # character escaping + uri = uri.replace(u'\\', u'\\\\\\\\') + uri = uri.replace(u' ', u'\ ') + + if description is None: + description = u_decode(vim.eval(u'input("Description: ")')) + elif link: + description = vim.eval( + u'input("Description: ", "%s")' % + u_decode(link[u'description'])) + if description is None: + return + + cursor = vim.current.window.cursor + cl = u_decode(vim.current.buffer[cursor[0] - 1]) + head = cl[:cursor[1] + 1] if not link else cl[:link[u'start']] + tail = cl[cursor[1] + 1:] if not link else cl[link[u'end']:] + + separator = u'' + if description: + separator = u'][' + + if uri or description: + vim.current.buffer[cursor[0] - 1] = \ + u_encode(u''.join((head, u'[[%s%s%s]]' % (uri, separator, description), tail))) + elif link: + vim.current.buffer[cursor[0] - 1] = \ + u_encode(u''.join((head, tail))) + + def register(self): + u""" + Registration of plugin. Key bindings and other initialization should be done. + """ + cmd = Command( + u'OrgHyperlinkFollow', + u'%s ORGMODE.plugins[u"Hyperlinks"].follow()' % VIM_PY_CALL) + self.commands.append(cmd) + self.keybindings.append( + Keybinding(u'gl', Plug(u'OrgHyperlinkFollow', self.commands[-1]))) + self.menu + ActionEntry(u'&Follow Link', self.keybindings[-1]) + + cmd = Command( + u'OrgHyperlinkCopy', + u'%s ORGMODE.plugins[u"Hyperlinks"].follow(action=u"copy")' % VIM_PY_CALL) + self.commands.append(cmd) + self.keybindings.append( + Keybinding(u'gyl', Plug(u'OrgHyperlinkCopy', self.commands[-1]))) + self.menu + ActionEntry(u'&Copy Link', self.keybindings[-1]) + + cmd = Command( + u'OrgHyperlinkInsert', + u'%s ORGMODE.plugins[u"Hyperlinks"].insert(<f-args>)' % VIM_PY_CALL, + arguments=u'*') + self.commands.append(cmd) + self.keybindings.append( + Keybinding(u'gil', Plug(u'OrgHyperlinkInsert', self.commands[-1]))) + self.menu + ActionEntry(u'&Insert Link', self.keybindings[-1]) + + self.menu + Separator() + + # find next link + cmd = Command( + u'OrgHyperlinkNextLink', + u":if search('\[\{2}\zs[^][]*\(\]\[[^][]*\)\?\ze\]\{2}', 's') == 0 | echo 'No further link found.' | endif") + self.commands.append(cmd) + self.keybindings.append( + Keybinding(u'gn', Plug(u'OrgHyperlinkNextLink', self.commands[-1]))) + self.menu + ActionEntry(u'&Next Link', self.keybindings[-1]) + + # find previous link + cmd = Command( + u'OrgHyperlinkPreviousLink', + u":if search('\[\{2}\zs[^][]*\(\]\[[^][]*\)\?\ze\]\{2}', 'bs') == 0 | echo 'No further link found.' | endif") + self.commands.append(cmd) + self.keybindings.append( + Keybinding(u'go', Plug(u'OrgHyperlinkPreviousLink', self.commands[-1]))) + self.menu + ActionEntry(u'&Previous Link', self.keybindings[-1]) + + self.menu + Separator() + + # Descriptive Links + cmd = Command(u'OrgHyperlinkDescriptiveLinks', u':setlocal cole=2') + self.commands.append(cmd) + self.menu + ActionEntry(u'&Descriptive Links', self.commands[-1]) + + # Literal Links + cmd = Command(u'OrgHyperlinkLiteralLinks', u':setlocal cole=0') + self.commands.append(cmd) + self.menu + ActionEntry(u'&Literal Links', self.commands[-1]) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/LoggingWork.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/LoggingWork.py new file mode 100644 index 0000000..1767984 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/LoggingWork.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +import vim + +from orgmode._vim import echo, echom, echoe, ORGMODE, apply_count, repeat +from orgmode.menu import Submenu, Separator, ActionEntry +from orgmode.keybinding import Keybinding, Plug, Command + +from orgmode.py3compat.py_py3_string import * + +class LoggingWork(object): + u""" LoggingWork plugin """ + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'&Logging work') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + # commands for this plugin + self.commands = [] + + @classmethod + def action(cls): + u""" Some kind of action + + :returns: TODO + """ + pass + + def register(self): + u""" + Registration of plugin. Key bindings and other initialization should be done. + """ + # an Action menu entry which binds "keybinding" to action ":action" + self.commands.append(Command(u'OrgLoggingRecordDoneTime', u'%s ORGMODE.plugins[u"LoggingWork"].action()' % VIM_PY_CALL)) + self.menu + ActionEntry(u'&Record DONE time', self.commands[-1]) diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Misc.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Misc.py new file mode 100644 index 0000000..3bbb8d9 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Misc.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- + +import vim + +from orgmode._vim import ORGMODE, apply_count +from orgmode.menu import Submenu +from orgmode.keybinding import Keybinding, Plug, MODE_VISUAL, MODE_OPERATOR + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.py_py3_string import * + +class Misc(object): + u""" Miscellaneous functionality """ + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'Misc') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + @classmethod + def jump_to_first_character(cls): + heading = ORGMODE.get_document().current_heading() + if not heading or heading.start_vim != vim.current.window.cursor[0]: + vim.eval(u_encode(u'feedkeys("^", "n")')) + return + + vim.current.window.cursor = (vim.current.window.cursor[0], heading.level + 1) + + @classmethod + def edit_at_first_character(cls): + heading = ORGMODE.get_document().current_heading() + if not heading or heading.start_vim != vim.current.window.cursor[0]: + vim.eval(u_encode(u'feedkeys("I", "n")')) + return + + vim.current.window.cursor = (vim.current.window.cursor[0], heading.level + 1) + vim.command(u_encode(u'startinsert')) + + # @repeat + @classmethod + @apply_count + def i_heading(cls, mode=u'visual', selection=u'inner', skip_children=False): + u""" + inner heading text object + """ + heading = ORGMODE.get_document().current_heading() + if heading: + if selection != u'inner': + heading = heading if not heading.parent else heading.parent + + line_start, col_start = [int(i) for i in vim.eval(u_encode(u'getpos("\'<")'))[1:3]] + line_end, col_end = [int(i) for i in vim.eval(u_encode(u'getpos("\'>")'))[1:3]] + + if mode != u'visual': + line_start = vim.current.window.cursor[0] + line_end = line_start + + start = line_start + end = line_end + move_one_character_back = u'' if mode == u'visual' else u'h' + + if heading.start_vim < line_start: + start = heading.start_vim + if heading.end_vim > line_end and not skip_children: + end = heading.end_vim + elif heading.end_of_last_child_vim > line_end and skip_children: + end = heading.end_of_last_child_vim + + if mode != u'visual' and not vim.current.buffer[end - 1]: + end -= 1 + move_one_character_back = u'' + + swap_cursor = u'o' if vim.current.window.cursor[0] == line_start else u'' + + if selection == u'inner' and vim.current.window.cursor[0] != line_start: + h = ORGMODE.get_document().current_heading() + if h: + heading = h + + visualmode = u_decode(vim.eval(u'visualmode()')) if mode == u'visual' else u'v' + + if line_start == start and line_start != heading.start_vim: + if col_start in (0, 1): + vim.command(u_encode(u'normal! %dgg0%s%dgg$%s%s' % (start, visualmode, end, move_one_character_back, swap_cursor))) + else: + vim.command(u_encode(u'normal! %dgg0%dl%s%dgg$%s%s' % (start, col_start - 1, visualmode, end, move_one_character_back, swap_cursor))) + else: + vim.command(u_encode(u'normal! %dgg0%dl%s%dgg$%s%s' % (start, heading.level + 1, visualmode, end, move_one_character_back, swap_cursor))) + + if selection == u'inner': + if mode == u'visual': + return u'OrgInnerHeadingVisual' if not skip_children else u'OrgInnerTreeVisual' + else: + return u'OrgInnerHeadingOperator' if not skip_children else u'OrgInnerTreeOperator' + else: + if mode == u'visual': + return u'OrgOuterHeadingVisual' if not skip_children else u'OrgOuterTreeVisual' + else: + return u'OrgOuterHeadingOperator' if not skip_children else u'OrgOuterTreeOperator' + elif mode == u'visual': + vim.command(u_encode(u'normal! gv')) + + # @repeat + @classmethod + @apply_count + def a_heading(cls, selection=u'inner', skip_children=False): + u""" + a heading text object + """ + heading = ORGMODE.get_document().current_heading() + if heading: + if selection != u'inner': + heading = heading if not heading.parent else heading.parent + + line_start, col_start = [int(i) for i in vim.eval(u_encode(u'getpos("\'<")'))[1:3]] + line_end, col_end = [int(i) for i in vim.eval(u_encode(u'getpos("\'>")'))[1:3]] + + start = line_start + end = line_end + + if heading.start_vim < line_start: + start = heading.start_vim + if heading.end_vim > line_end and not skip_children: + end = heading.end_vim + elif heading.end_of_last_child_vim > line_end and skip_children: + end = heading.end_of_last_child_vim + + swap_cursor = u'o' if vim.current.window.cursor[0] == line_start else u'' + + vim.command(u_encode(u'normal! %dgg%s%dgg$%s' % (start, vim.eval(u_encode(u'visualmode()')), end, swap_cursor))) + if selection == u'inner': + return u'OrgAInnerHeadingVisual' if not skip_children else u'OrgAInnerTreeVisual' + else: + return u'OrgAOuterHeadingVisual' if not skip_children else u'OrgAOuterTreeVisual' + else: + vim.command(u_encode(u'normal! gv')) + + def register(self): + u""" + Registration of plugin. Key bindings and other initialization should be done. + """ + self.keybindings.append(Keybinding(u'^', + Plug(u'OrgJumpToFirstCharacter', u'%s ORGMODE.plugins[u"Misc"].jump_to_first_character()<CR>' % VIM_PY_CALL))) + self.keybindings.append(Keybinding(u'I', + Plug(u'OrgEditAtFirstCharacter', u'%s ORGMODE.plugins[u"Misc"].edit_at_first_character()<CR>' % VIM_PY_CALL))) + + self.keybindings.append(Keybinding(u'ih', Plug(u'OrgInnerHeadingVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading()<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + self.keybindings.append(Keybinding(u'ah', Plug(u'OrgAInnerHeadingVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].a_heading()<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + self.keybindings.append(Keybinding(u'Oh', Plug(u'OrgOuterHeadingVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(selection=u"outer")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + self.keybindings.append(Keybinding(u'OH', Plug(u'OrgAOuterHeadingVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].a_heading(selection=u"outer")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + + self.keybindings.append(Keybinding(u'ih', Plug(u'OrgInnerHeadingOperator', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR))) + self.keybindings.append(Keybinding(u'ah', u':normal Vah<CR>', mode=MODE_OPERATOR)) + self.keybindings.append(Keybinding(u'Oh', Plug(u'OrgOuterHeadingOperator', ':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator", selection=u"outer")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR))) + self.keybindings.append(Keybinding(u'OH', u':normal VOH<CR>', mode=MODE_OPERATOR)) + + self.keybindings.append(Keybinding(u'ir', Plug(u'OrgInnerTreeVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + self.keybindings.append(Keybinding(u'ar', Plug(u'OrgAInnerTreeVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].a_heading(skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + self.keybindings.append(Keybinding(u'Or', Plug(u'OrgOuterTreeVisual', u'<:<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(selection=u"outer", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + self.keybindings.append(Keybinding(u'OR', Plug(u'OrgAOuterTreeVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].a_heading(selection=u"outer", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + + self.keybindings.append(Keybinding(u'ir', Plug(u'OrgInnerTreeOperator', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR))) + self.keybindings.append(Keybinding(u'ar', u':normal Var<CR>', mode=MODE_OPERATOR)) + self.keybindings.append(Keybinding(u'Or', Plug(u'OrgOuterTreeOperator', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator", selection=u"outer", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR))) + self.keybindings.append(Keybinding(u'OR', u':normal VOR<CR>', mode=MODE_OPERATOR)) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Navigator.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Navigator.py new file mode 100644 index 0000000..2ae5741 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Navigator.py @@ -0,0 +1,326 @@ +# -*- coding: utf-8 -*- + +import vim + +from orgmode._vim import echo, ORGMODE, apply_count +from orgmode.menu import Submenu, ActionEntry +from orgmode.keybinding import Keybinding, MODE_VISUAL, MODE_OPERATOR, Plug +from orgmode.liborgmode.documents import Direction + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.py_py3_string import * + +class Navigator(object): + u""" Implement navigation in org-mode documents """ + + def __init__(self): + object.__init__(self) + self.menu = ORGMODE.orgmenu + Submenu(u'&Navigate Headings') + self.keybindings = [] + + @classmethod + @apply_count + def parent(cls, mode): + u""" + Focus parent heading + + :returns: parent heading or None + """ + heading = ORGMODE.get_document().current_heading() + if not heading: + if mode == u'visual': + vim.command(u_encode(u'normal! gv')) + else: + echo(u'No heading found') + return + + if not heading.parent: + if mode == u'visual': + vim.command(u_encode(u'normal! gv')) + else: + echo(u'No parent heading found') + return + + p = heading.parent + + if mode == u'visual': + cls._change_visual_selection(heading, p, direction=Direction.BACKWARD, parent=True) + else: + vim.current.window.cursor = (p.start_vim, p.level + 1) + return p + + @classmethod + @apply_count + def parent_next_sibling(cls, mode): + u""" + Focus the parent's next sibling + + :returns: parent's next sibling heading or None + """ + heading = ORGMODE.get_document().current_heading() + if not heading: + if mode == u'visual': + vim.command(u_encode(u'normal! gv')) + else: + echo(u'No heading found') + return + + if not heading.parent or not heading.parent.next_sibling: + if mode == u'visual': + vim.command(u_encode(u'normal! gv')) + else: + echo(u'No parent heading found') + return + + ns = heading.parent.next_sibling + + if mode == u'visual': + cls._change_visual_selection(heading, ns, direction=Direction.FORWARD, parent=False) + elif mode == u'operator': + vim.current.window.cursor = (ns.start_vim, 0) + else: + vim.current.window.cursor = (ns.start_vim, ns.level + 1) + return ns + + @classmethod + def _change_visual_selection(cls, current_heading, heading, direction=Direction.FORWARD, noheadingfound=False, parent=False): + current = vim.current.window.cursor[0] + line_start, col_start = [int(i) for i in vim.eval(u_encode(u'getpos("\'<")'))[1:3]] + line_end, col_end = [int(i) for i in vim.eval(u_encode(u'getpos("\'>")'))[1:3]] + + f_start = heading.start_vim + f_end = heading.end_vim + swap_cursor = True + + # << |visual start + # selection end >> + if current == line_start: + if (direction == Direction.FORWARD and line_end < f_start) or noheadingfound and not direction == Direction.BACKWARD: + swap_cursor = False + + # focus heading HERE + # << |visual start + # selection end >> + + # << |visual start + # focus heading HERE + # selection end >> + if f_start < line_start and direction == Direction.BACKWARD: + if current_heading.start_vim < line_start and not parent: + line_start = current_heading.start_vim + else: + line_start = f_start + + elif (f_start < line_start or f_start < line_end) and not noheadingfound: + line_start = f_start + + # << |visual start + # selection end >> + # focus heading HERE + else: + if direction == Direction.FORWARD: + if line_end < f_start and not line_start == f_start - 1 and current_heading: + # focus end of previous heading instead of beginning of next heading + line_start = line_end + line_end = f_start - 1 + else: + # focus end of next heading + line_start = line_end + line_end = f_end + elif direction == Direction.BACKWARD: + if line_end < f_end: + pass + else: + line_start = line_end + line_end = f_end + + # << visual start + # selection end| >> + else: + # focus heading HERE + # << visual start + # selection end| >> + if line_start > f_start and line_end > f_end and not parent: + line_end = f_end + swap_cursor = False + + elif (line_start > f_start or line_start == f_start) and \ + line_end <= f_end and direction == Direction.BACKWARD: + line_end = line_start + line_start = f_start + + # << visual start + # selection end and focus heading end HERE| >> + + # << visual start + # focus heading HERE + # selection end| >> + + # << visual start + # selection end| >> + # focus heading HERE + else: + if direction == Direction.FORWARD: + if line_end < f_start - 1: + # focus end of previous heading instead of beginning of next heading + line_end = f_start - 1 + else: + # focus end of next heading + line_end = f_end + else: + line_end = f_end + swap_cursor = False + + move_col_start = u'%dl' % (col_start - 1) if (col_start - 1) > 0 and (col_start - 1) < 2000000000 else u'' + move_col_end = u'%dl' % (col_end - 1) if (col_end - 1) > 0 and (col_end - 1) < 2000000000 else u'' + swap = u'o' if swap_cursor else u'' + + vim.command(u_encode(u'normal! %dgg%s%s%dgg%s%s' % (line_start, move_col_start, vim.eval(u_encode(u'visualmode()')), line_end, move_col_end, swap))) + + @classmethod + def _focus_heading(cls, mode, direction=Direction.FORWARD, skip_children=False): + u""" + Focus next or previous heading in the given direction + + :direction: True for next heading, False for previous heading + :returns: next heading or None + """ + d = ORGMODE.get_document() + current_heading = d.current_heading() + heading = current_heading + focus_heading = None + # FIXME this is just a piece of really ugly and unmaintainable code. It + # should be rewritten + if not heading: + if direction == Direction.FORWARD and d.headings \ + and vim.current.window.cursor[0] < d.headings[0].start_vim: + # the cursor is in the meta information are, therefore focus + # first heading + focus_heading = d.headings[0] + if not (heading or focus_heading): + if mode == u'visual': + # restore visual selection when no heading was found + vim.command(u_encode(u'normal! gv')) + else: + echo(u'No heading found') + return + elif direction == Direction.BACKWARD: + if vim.current.window.cursor[0] != heading.start_vim: + # the cursor is in the body of the current heading, therefore + # the current heading will be focused + if mode == u'visual': + line_start, col_start = [int(i) for i in + vim.eval(u_encode(u'getpos("\'<")'))[1:3]] + line_end, col_end = [int(i) for i in vim.eval(u_encode(u'getpos("\'>")'))[1:3]] + if line_start >= heading.start_vim and line_end > heading.start_vim: + focus_heading = heading + else: + focus_heading = heading + + # so far no heading has been found that the next focus should be on + if not focus_heading: + if not skip_children and direction == Direction.FORWARD and heading.children: + focus_heading = heading.children[0] + elif direction == Direction.FORWARD and heading.next_sibling: + focus_heading = heading.next_sibling + elif direction == Direction.BACKWARD and heading.previous_sibling: + focus_heading = heading.previous_sibling + if not skip_children: + while focus_heading.children: + focus_heading = focus_heading.children[-1] + else: + if direction == Direction.FORWARD: + focus_heading = current_heading.next_heading + else: + focus_heading = current_heading.previous_heading + + noheadingfound = False + if not focus_heading: + if mode in (u'visual', u'operator'): + # the cursor seems to be on the last or first heading of this + # document and performes another next/previous operation + focus_heading = heading + noheadingfound = True + else: + if direction == Direction.FORWARD: + echo(u'Already focussing last heading') + else: + echo(u'Already focussing first heading') + return + + if mode == u'visual': + cls._change_visual_selection(current_heading, focus_heading, direction=direction, noheadingfound=noheadingfound) + elif mode == u'operator': + if direction == Direction.FORWARD and vim.current.window.cursor[0] >= focus_heading.start_vim: + vim.current.window.cursor = (focus_heading.end_vim, len(u_decode(vim.current.buffer[focus_heading.end]))) + else: + vim.current.window.cursor = (focus_heading.start_vim, 0) + else: + vim.current.window.cursor = (focus_heading.start_vim, focus_heading.level + 1) + if noheadingfound: + return + return focus_heading + + @classmethod + @apply_count + def previous(cls, mode, skip_children=False): + u""" + Focus previous heading + """ + return cls._focus_heading(mode, direction=Direction.BACKWARD, skip_children=skip_children) + + @classmethod + @apply_count + def next(cls, mode, skip_children=False): + u""" + Focus next heading + """ + return cls._focus_heading(mode, direction=Direction.FORWARD, skip_children=skip_children) + + def register(self): + # normal mode + self.keybindings.append(Keybinding(u'g{', Plug('OrgJumpToParentNormal', + u'%s ORGMODE.plugins[u"Navigator"].parent(mode=u"normal")<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'&Up', self.keybindings[-1]) + self.keybindings.append(Keybinding(u'g}', + Plug('OrgJumpToParentsSiblingNormal', u'%s ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"normal")<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'&Down', self.keybindings[-1]) + self.keybindings.append(Keybinding(u'{', + Plug(u'OrgJumpToPreviousNormal', u'%s ORGMODE.plugins[u"Navigator"].previous(mode=u"normal")<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'&Previous', self.keybindings[-1]) + self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextNormal', + u'%s ORGMODE.plugins[u"Navigator"].next(mode=u"normal")<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'&Next', self.keybindings[-1]) + + # visual mode + self.keybindings.append(Keybinding(u'g{', Plug(u'OrgJumpToParentVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].parent(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + self.keybindings.append(Keybinding(u'g}', Plug('OrgJumpToParentsSiblingVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + self.keybindings.append(Keybinding(u'{', Plug(u'OrgJumpToPreviousVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + + # operator-pending mode + self.keybindings.append(Keybinding(u'g{', Plug(u'OrgJumpToParentOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].parent(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR))) + self.keybindings.append(Keybinding(u'g}', Plug('OrgJumpToParentsSiblingOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR))) + self.keybindings.append(Keybinding(u'{', Plug(u'OrgJumpToPreviousOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR))) + self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR))) + + # section wise movement (skip children) + # normal mode + self.keybindings.append(Keybinding(u'[[', + Plug(u'OrgJumpToPreviousSkipChildrenNormal', + u'%s ORGMODE.plugins[u"Navigator"].previous(mode=u"normal", skip_children=True)<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'Ne&xt Same Level', self.keybindings[-1]) + self.keybindings.append(Keybinding(u']]', + Plug(u'OrgJumpToNextSkipChildrenNormal', + u'%s ORGMODE.plugins[u"Navigator"].next(mode=u"normal", skip_children=True)<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'Pre&vious Same Level', self.keybindings[-1]) + + # visual mode + self.keybindings.append(Keybinding(u'[[', Plug(u'OrgJumpToPreviousSkipChildrenVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"visual", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + self.keybindings.append(Keybinding(u']]', Plug(u'OrgJumpToNextSkipChildrenVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"visual", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL))) + + # operator-pending mode + self.keybindings.append(Keybinding(u'[[', Plug(u'OrgJumpToPreviousSkipChildrenOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"operator", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR))) + self.keybindings.append(Keybinding(u']]', Plug(u'OrgJumpToNextSkipChildrenOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"operator", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR))) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/ShowHide.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/ShowHide.py new file mode 100644 index 0000000..5ae67a1 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/ShowHide.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- + +import vim + +from orgmode.liborgmode.headings import Heading +from orgmode._vim import ORGMODE, apply_count +from orgmode import settings +from orgmode.menu import Submenu, ActionEntry +from orgmode.keybinding import Keybinding, Plug, MODE_NORMAL + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.xrange_compatibility import * +from orgmode.py3compat.py_py3_string import * + +class ShowHide(object): + u""" Show Hide plugin """ + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'&Show Hide') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + @classmethod + def _fold_depth(cls, h): + """ Find the deepest level of open folds + + :h: Heading + :returns: Tuple (int - level of open folds, boolean - found fold) or None if h is not a Heading + """ + if not isinstance(h, Heading): + return + + if int(vim.eval(u_encode(u'foldclosed(%d)' % h.start_vim))) != -1: + return (h.number_of_parents, True) + + res = [h.number_of_parents + 1] + found = False + for c in h.children: + d, f = cls._fold_depth(c) + res.append(d) + found |= f + + return (max(res), found) + + @classmethod + @apply_count + def toggle_folding(cls, reverse=False): + u""" Toggle folding similar to the way orgmode does + + This is just a convenience function, don't hesitate to use the z* + keybindings vim offers to deal with folding! + + :reverse: If False open folding by one level otherwise close it by one. + """ + d = ORGMODE.get_document() + heading = d.current_heading() + if not heading: + vim.eval(u_encode(u'feedkeys("<Tab>", "n")')) + return + + cursor = vim.current.window.cursor[:] + + if int(vim.eval(u_encode(u'foldclosed(%d)' % heading.start_vim))) != -1: + if not reverse: + # open closed fold + p = heading.number_of_parents + if not p: + p = heading.level + vim.command(u_encode(u'normal! %dzo' % p)) + else: + # reverse folding opens all folds under the cursor + vim.command(u_encode(u'%d,%dfoldopen!' % (heading.start_vim, heading.end_of_last_child_vim))) + vim.current.window.cursor = cursor + return heading + + def open_fold(h): + if h.number_of_parents <= open_depth: + vim.command(u_encode(u'normal! %dgg%dzo' % (h.start_vim, open_depth))) + for c in h.children: + open_fold(c) + + def close_fold(h): + for c in h.children: + close_fold(c) + if h.number_of_parents >= open_depth - 1 and \ + int(vim.eval(u_encode(u'foldclosed(%d)' % h.start_vim))) == -1: + vim.command(u_encode(u'normal! %dggzc' % (h.start_vim, ))) + + # find deepest fold + open_depth, found_fold = cls._fold_depth(heading) + + if not reverse: + # recursively open folds + if found_fold: + for child in heading.children: + open_fold(child) + else: + vim.command(u_encode(u'%d,%dfoldclose!' % (heading.start_vim, heading.end_of_last_child_vim))) + + if heading.number_of_parents: + # restore cursor position, it might have been changed by open_fold + vim.current.window.cursor = cursor + + p = heading.number_of_parents + if not p: + p = heading.level + # reopen fold again beacause the former closing of the fold closed all levels, including parents! + vim.command(u_encode(u'normal! %dzo' % (p, ))) + else: + # close the last level of folds + close_fold(heading) + + # restore cursor position + vim.current.window.cursor = cursor + return heading + + @classmethod + @apply_count + def global_toggle_folding(cls, reverse=False): + """ Toggle folding globally + + :reverse: If False open folding by one level otherwise close it by one. + """ + d = ORGMODE.get_document() + if reverse: + foldlevel = int(vim.eval(u_encode(u'&foldlevel'))) + if foldlevel == 0: + # open all folds because the user tries to close folds beyound 0 + vim.eval(u_encode(u'feedkeys("zR", "n")')) + else: + # vim can reduce the foldlevel on its own + vim.eval(u_encode(u'feedkeys("zm", "n")')) + else: + found = False + for h in d.headings: + res = cls._fold_depth(h) + if res: + found = res[1] + if found: + break + if not found: + # no fold found and the user tries to advance the fold level + # beyond maximum so close everything + vim.eval(u_encode(u'feedkeys("zM", "n")')) + else: + # fold found, vim can increase the foldlevel on its own + vim.eval(u_encode(u'feedkeys("zr", "n")')) + + return d + + def register(self): + u""" + Registration of plugin. Key bindings and other initialization should be done. + """ + # register plug + + self.keybindings.append(Keybinding(u'<Tab>', + Plug(u'OrgToggleFoldingNormal', u'%s ORGMODE.plugins[u"ShowHide"].toggle_folding()<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'&Cycle Visibility', self.keybindings[-1]) + + self.keybindings.append(Keybinding(u'<S-Tab>', + Plug(u'OrgToggleFoldingReverse', u'%s ORGMODE.plugins[u"ShowHide"].toggle_folding(reverse=True)<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'Cycle Visibility &Reverse', self.keybindings[-1]) + + self.keybindings.append(Keybinding(u'<localleader>.', + Plug(u'OrgGlobalToggleFoldingNormal', u'%s ORGMODE.plugins[u"ShowHide"].global_toggle_folding()<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'Cycle Visibility &Globally', self.keybindings[-1]) + + self.keybindings.append(Keybinding(u'<localleader>,', + Plug(u'OrgGlobalToggleFoldingReverse', + u'%s ORGMODE.plugins[u"ShowHide"].global_toggle_folding(reverse=True)<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'Cycle Visibility Reverse G&lobally', self.keybindings[-1]) + + for i in range(0, 10): + self.keybindings.append(Keybinding(u'<localleader>%d' % (i, ), u'zM:set fdl=%d<CR>' % i, mode=MODE_NORMAL)) diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/TagsProperties.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/TagsProperties.py new file mode 100644 index 0000000..95aba90 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/TagsProperties.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- + +import vim + +from orgmode._vim import ORGMODE, repeat +from orgmode.menu import Submenu, ActionEntry +from orgmode.keybinding import Keybinding, Plug, Command +from orgmode import settings + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.py_py3_string import * + +class TagsProperties(object): + u""" TagsProperties plugin """ + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'&TAGS and Properties') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + # commands for this plugin + self.commands = [] + + @classmethod + def complete_tags(cls): + u""" build a list of tags and store it in variable b:org_tag_completion + """ + d = ORGMODE.get_document() + heading = d.current_heading() + if not heading: + return + + leading_portion = u_decode(vim.eval(u'a:ArgLead')) + cursor = int(vim.eval(u'a:CursorPos')) + + # extract currently completed tag + idx_orig = leading_portion.rfind(u':', 0, cursor) + if idx_orig == -1: + idx = 0 + else: + idx = idx_orig + + current_tag = leading_portion[idx: cursor].lstrip(u':') + head = leading_portion[:idx + 1] + if idx_orig == -1: + head = u'' + tail = leading_portion[cursor:] + + # extract all tags of the current file + all_tags = set() + for h in d.all_headings(): + for t in h.tags: + all_tags.add(t) + + ignorecase = bool(int(settings.get(u'org_tag_completion_ignorecase', int(vim.eval(u'&ignorecase'))))) + possible_tags = [] + # TODO current tags never used... + current_tags = heading.tags + for t in all_tags: + if ignorecase: + if t.lower().startswith(current_tag.lower()): + possible_tags.append(t) + elif t.startswith(current_tag): + possible_tags.append(t) + + vim.command(u_encode(u'let b:org_complete_tags = [%s]' % u', '.join([u'"%s%s:%s"' % (head, i, tail) for i in possible_tags]))) + + @classmethod + @repeat + def set_tags(cls): + u""" Set tags for current heading + """ + d = ORGMODE.get_document() + heading = d.current_heading() + if not heading: + return + + # retrieve tags + res = None + if heading.tags: + res = vim.eval(u'input("Tags: ", ":%s:", "customlist,Org_complete_tags")' % u':'.join(heading.tags)) + else: + res = vim.eval(u'input("Tags: ", "", "customlist,Org_complete_tags")') + + if res is None: + # user pressed <Esc> abort any further processing + return + + # remove empty tags + heading.tags = [x for x in u_decode(res).strip().strip(u':').split(u':') if x.strip() != u''] + + d.write() + + return u'OrgSetTags' + + @classmethod + def find_tags(cls): + """ Find tags in current file + """ + tags = vim.eval(u'input("Find Tags: ", "", "customlist,Org_complete_tags")') + if tags is None: + # user pressed <Esc> abort any further processing + return + + tags = [x for x in u_decode(tags).strip().strip(u':').split(u':') if x.strip() != u''] + if tags: + searchstring = u'\\(' + first = True + for t1 in tags: + if first: + first = False + searchstring += u'%s' % t1 + else: + searchstring += u'\\|%s' % t1 + + for t2 in tags: + if t1 == t2: + continue + searchstring += u'\\(:[a-zA-Z:]*\\)\?:%s' % t2 + searchstring += u'\\)' + + vim.command(u'/\\zs:%s:\\ze' % searchstring) + return u'OrgFindTags' + + @classmethod + def realign_tags(cls): + u""" + Updates tags when user finished editing a heading + """ + d = ORGMODE.get_document(allow_dirty=True) + heading = d.find_current_heading() + if not heading: + return + + if vim.current.window.cursor[0] == heading.start_vim: + heading.set_dirty_heading() + d.write_heading(heading, including_children=False) + + @classmethod + def realign_all_tags(cls): + u""" + Updates tags when user finishes editing a heading + """ + d = ORGMODE.get_document() + for heading in d.all_headings(): + heading.set_dirty_heading() + + d.write() + + def register(self): + u""" + Registration of plugin. Key bindings and other initialization should be done. + """ + # an Action menu entry which binds "keybinding" to action ":action" + settings.set(u'org_tag_column', vim.eval(u'&textwidth')) + settings.set(u'org_tag_completion_ignorecase', int(vim.eval(u'&ignorecase'))) + + cmd = Command( + u'OrgSetTags', + u'%s ORGMODE.plugins[u"TagsProperties"].set_tags()' % VIM_PY_CALL) + self.commands.append(cmd) + keybinding = Keybinding( + u'<localleader>st', + Plug(u'OrgSetTags', cmd)) + self.keybindings.append(keybinding) + self.menu + ActionEntry(u'Set &Tags', keybinding) + + cmd = Command( + u'OrgFindTags', + u'%s ORGMODE.plugins[u"TagsProperties"].find_tags()' % VIM_PY_CALL) + self.commands.append(cmd) + keybinding = Keybinding( + u'<localleader>ft', + Plug(u'OrgFindTags', cmd)) + self.keybindings.append(keybinding) + self.menu + ActionEntry(u'&Find Tags', keybinding) + + cmd = Command( + u'OrgTagsRealign', + u"%s ORGMODE.plugins[u'TagsProperties'].realign_all_tags()" % VIM_PY_CALL) + self.commands.append(cmd) + + # workaround to align tags when user is leaving insert mode + vim.command(u_encode(u"""function Org_complete_tags(ArgLead, CmdLine, CursorPos) +python << EOF +ORGMODE.plugins[u'TagsProperties'].complete_tags() +EOF +if exists('b:org_complete_tags') + let tmp = b:org_complete_tags + unlet b:org_complete_tags + return tmp +else + return [] +endif +endfunction""")) + + vim.command(u_encode(u"""function Org_realign_tags_on_insert_leave() +if !exists('b:org_complete_tag_on_insertleave_au') + :au orgmode InsertLeave <buffer> %s ORGMODE.plugins[u'TagsProperties'].realign_tags() + let b:org_complete_tag_on_insertleave_au = 1 +endif +endfunction""" % VIM_PY_CALL)) + + # this is for all org files opened after this file + vim.command(u_encode(u"au orgmode FileType org call Org_realign_tags_on_insert_leave()")) + # this is for the current file + vim.command(u_encode(u"call Org_realign_tags_on_insert_leave()")) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Todo.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Todo.py new file mode 100644 index 0000000..ad1a1a0 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Todo.py @@ -0,0 +1,345 @@ +# -*- coding: utf-8 -*- + +import vim +import itertools as it + +from orgmode._vim import echom, ORGMODE, apply_count, repeat, realign_tags +from orgmode import settings +from orgmode.liborgmode.base import Direction +from orgmode.menu import Submenu, ActionEntry +from orgmode.keybinding import Keybinding, Plug +from orgmode.exceptions import PluginError + +# temporary todo states for differnent orgmode buffers +ORGTODOSTATES = {} + +from orgmode.py3compat.xrange_compatibility import * +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.unicode_compatibility import * +from orgmode.py3compat.py_py3_string import * + + +def split_access_key(t, sub=None): + u""" Split access key + + Args: + t (str): Todo state + sub: A value that will be returned instead of access key if there was + not access key + + Returns: + tuple: Todo state and access key separated (TODO, ACCESS_KEY) + + Example: + >>> split_access_key('TODO(t)') + >>> ('TODO', '(t)') + >>> split_access_key('WANT', sub='(hi)') + >>> ('WANT', '(hi)') + """ + if type(t) != unicode: + echom("String must be unicode") + return (None, None) + + idx = t.find(u'(') + + v, k = (t, sub) + if idx != -1 and t[idx + 1:-1]: + v, k = (t[:idx], t[idx + 1:-1]) + return (v, k) + + +class Todo(object): + u""" + Todo plugin. + + Description taken from orgmode.org: + + You can use TODO keywords to indicate different sequential states in the + process of working on an item, for example: + + ["TODO", "FEEDBACK", "VERIFY", "|", "DONE", "DELEGATED"] + + The vertical bar separates the TODO keywords (states that need action) from + the DONE states (which need no further action). If you don't provide the + separator bar, the last state is used as the DONE state. With this setup, + the command ``,d`` will cycle an entry from TODO to FEEDBACK, then to + VERIFY, and finally to DONE and DELEGATED. + """ + + def __init__(self): + u""" Initialize plugin """ + object.__init__(self) + # menu entries this plugin should create + self.menu = ORGMODE.orgmenu + Submenu(u'&TODO Lists') + + # key bindings for this plugin + # key bindings are also registered through the menu so only additional + # bindings should be put in this variable + self.keybindings = [] + + @classmethod + def _process_all_states(cls, all_states): + u""" verify if states defined by user is valid. + Return cleaned_todo and flattened if is. Raise Exception if not. + Valid checking: + * no two state share a same name + """ + # TODO Write tests. -- Ron89 + cleaned_todos = [[ + split_access_key(todo)[0] for todo in it.chain.from_iterable(x)] + for x in all_states] + [[None]] + + flattened_todos = list(it.chain.from_iterable(cleaned_todos)) + if len(flattened_todos) != len(set(flattened_todos)): + raise PluginError(u"Duplicate names detected in TODO keyword list. Please examine `g/b:org_todo_keywords`") + # TODO This is the case when there are 2 todo states with the same + # name. It should be handled by making a simple class to hold TODO + # states, which would avoid mixing 2 todo states with the same name + # since they would have a different reference (but same content), + # albeit this can fail because python optimizes short strings (i.e. + # they hold the same ref) so care should be taken in implementation + return (cleaned_todos, flattened_todos) + + @classmethod + def _get_next_state( + cls, current_state, all_states, direction=Direction.FORWARD, + next_set=False): + u""" Get the next todo state + + Args: + current_state (str): The current todo state + all_states (list): A list containing all todo states within + sublists. The todo states may contain access keys + direction: Direction of state or keyword set change (forward or + backward) + next_set: Advance to the next keyword set in defined direction. + + Returns: + str or None: next todo state, or None if there is no next state. + + Note: all_states should have the form of: + [(['TODO(t)'], ['DONE(d)']), + (['REPORT(r)', 'BUG(b)', 'KNOWNCAUSE(k)'], ['FIXED(f)']), + ([], ['CANCELED(c)'])] + """ + cleaned_todos, flattened_todos = cls._process_all_states(all_states) + + # backward direction should really be -1 not 2 + next_dir = -1 if direction == Direction.BACKWARD else 1 + # work only with top level index + if next_set: + top_set = next(( + todo_set[0] for todo_set in enumerate(cleaned_todos) + if current_state in todo_set[1]), -1) + ind = (top_set + next_dir) % len(cleaned_todos) + if ind != len(cleaned_todos) - 1: + echom("Using set: %s" % str(all_states[ind])) + else: + echom("Keyword removed.") + return cleaned_todos[ind][0] + # No next set, cycle around everything + else: + ind = next(( + todo_iter[0] for todo_iter in enumerate(flattened_todos) + if todo_iter[1] == current_state), -1) + return flattened_todos[(ind + next_dir) % len(flattened_todos)] + + @classmethod + @realign_tags + @repeat + @apply_count + def toggle_todo_state( + cls, direction=Direction.FORWARD, interactive=False, next_set=False): + u""" Toggle state of TODO item + + :returns: The changed heading + """ + d = ORGMODE.get_document(allow_dirty=True) + + # get heading + heading = d.find_current_heading() + if not heading: + vim.eval(u'feedkeys("^", "n")') + return + + todo_states = d.get_todo_states(strip_access_key=False) + # get todo states + if not todo_states: + echom(u'No todo keywords configured.') + return + + current_state = heading.todo + + # get new state interactively + if interactive: + # determine position of the interactive prompt + prompt_pos = settings.get(u'org_todo_prompt_position', u'botright') + if prompt_pos not in [u'botright', u'topleft']: + prompt_pos = u'botright' + + # pass todo states to new window + ORGTODOSTATES[d.bufnr] = todo_states + settings.set( + u'org_current_state_%d' % d.bufnr, + current_state if current_state is not None else u'', overwrite=True) + todo_buffer_exists = bool(int(vim.eval(u_encode( + u'bufexists("org:todo/%d")' % (d.bufnr, ))))) + if todo_buffer_exists: + # if the buffer already exists, reuse it + vim.command(u_encode( + u'%s sbuffer org:todo/%d' % (prompt_pos, d.bufnr, ))) + else: + # create a new window + vim.command(u_encode( + u'keepalt %s %dsplit org:todo/%d' % (prompt_pos, len(todo_states), d.bufnr))) + else: + new_state = Todo._get_next_state( + current_state, todo_states, direction=direction, + next_set=next_set) + + cls.set_todo_state(new_state) + + # plug + plug = u'OrgTodoForward' + if direction == Direction.BACKWARD: + plug = u'OrgTodoBackward' + + return plug + + @classmethod + def set_todo_state(cls, state): + u""" Set todo state for buffer. + + :bufnr: Number of buffer the todo state should be updated for + :state: The new todo state + """ + lineno, colno = vim.current.window.cursor + d = ORGMODE.get_document(allow_dirty=True) + heading = d.find_current_heading() + + if not heading: + return + + current_state = heading.todo + + # set new headline + heading.todo = state + d.write_heading(heading) + + # move cursor along with the inserted state only when current position + # is in the heading; otherwite do nothing + if heading.start_vim == lineno and colno > heading.level: + if current_state is not None and \ + colno <= heading.level + len(current_state): + # the cursor is actually on the todo keyword + # move it back to the beginning of the keyword in that case + vim.current.window.cursor = (lineno, heading.level + 1) + else: + # the cursor is somewhere in the text, move it along + if current_state is None and state is None: + offset = 0 + elif current_state is None and state is not None: + offset = len(state) + 1 + elif current_state is not None and state is None: + offset = -len(current_state) - 1 + else: + offset = len(state) - len(current_state) + vim.current.window.cursor = (lineno, colno + offset) + + @classmethod + def init_org_todo(cls): + u""" Initialize org todo selection window. + """ + bufnr = int(vim.current.buffer.name.split('/')[-1]) + all_states = ORGTODOSTATES.get(bufnr, None) + + vim_commands = [ + u'let g:org_sav_timeoutlen=&timeoutlen', + u'au orgmode BufEnter <buffer> :if ! exists("g:org_sav_timeoutlen")|let g:org_sav_timeoutlen=&timeoutlen|set timeoutlen=1|endif', + u'au orgmode BufLeave <buffer> :if exists("g:org_sav_timeoutlen")|let &timeoutlen=g:org_sav_timeoutlen|unlet g:org_sav_timeoutlen|endif', + u'setlocal nolist tabstop=16 buftype=nofile timeout timeoutlen=1 winfixheight', + u'setlocal statusline=Org\\ todo\\ (%s)' % vim.eval(u_encode(u'fnameescape(fnamemodify(bufname(%d), ":t"))' % bufnr)), + u'nnoremap <silent> <buffer> <Esc> :%sbw<CR>' % vim.eval(u_encode(u'bufnr("%")')), + u'nnoremap <silent> <buffer> <CR> :let g:org_state = fnameescape(expand("<cword>"))<Bar>bw<Bar>exec "%s ORGMODE.plugins[u\'Todo\'].set_todo_state(\'".g:org_state."\')"<Bar>unlet! g:org_state<CR>' % VIM_PY_CALL, + ] + # because timeoutlen can only be set globally it needs to be stored and + # restored later + # make window a scratch window and set the statusline differently + for cmd in vim_commands: + vim.command(u_encode(cmd)) + + if all_states is None: + vim.command(u_encode(u'bw')) + echom(u'No todo states avaiable for buffer %s' % vim.current.buffer.name) + + for idx, state in enumerate(all_states): + pairs = [split_access_key(x, sub=u' ') for x in it.chain(*state)] + line = u'\t'.join(u''.join((u'[%s] ' % x[1], x[0])) for x in pairs) + vim.current.buffer.append(u_encode(line)) + for todo, key in pairs: + # FIXME if double key is used for access modified this doesn't work + vim.command(u_encode(u'nnoremap <silent> <buffer> %s :bw<CR><c-w><c-p>%s ORGMODE.plugins[u"Todo"].set_todo_state("%s")<CR>' % (key, VIM_PY_CALL, u_decode(todo)))) + + # position the cursor of the current todo item + vim.command(u_encode(u'normal! G')) + current_state = settings.unset(u'org_current_state_%d' % bufnr) + if current_state is not None and current_state != '': + for i, buf in enumerate(vim.current.buffer): + idx = buf.find(current_state) + if idx != -1: + vim.current.window.cursor = (i + 1, idx) + break + else: + vim.current.window.cursor = (2, 4) + + # finally make buffer non modifiable + vim.command(u_encode(u'setfiletype orgtodo')) + vim.command(u_encode(u'setlocal nomodifiable')) + + # remove temporary todo states for the current buffer + del ORGTODOSTATES[bufnr] + + def register(self): + u""" + Registration of plugin. Key bindings and other initialization should be done. + """ + self.keybindings.append(Keybinding(u'<localleader>ct', Plug( + u'OrgTodoToggleNonInteractive', + u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(interactive=False)<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'&TODO/DONE/-', self.keybindings[-1]) + + self.keybindings.append(Keybinding(u'<localleader>d', Plug( + u'OrgTodoToggleInteractive', + u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(interactive=True)<CR>' % VIM_PY_CALL))) + self.menu + ActionEntry(u'&TODO/DONE/- (interactiv)', self.keybindings[-1]) + + # add submenu + submenu = self.menu + Submenu(u'Select &keyword') + + self.keybindings.append(Keybinding(u'<S-Right>', Plug( + u'OrgTodoForward', + u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state()<CR>' % VIM_PY_CALL))) + submenu + ActionEntry(u'&Next keyword', self.keybindings[-1]) + + self.keybindings.append(Keybinding(u'<S-Left>', Plug( + u'OrgTodoBackward', + u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(direction=2)<CR>' % VIM_PY_CALL))) + submenu + ActionEntry(u'&Previous keyword', self.keybindings[-1]) + + self.keybindings.append(Keybinding(u'<C-S-Right>', Plug( + u'OrgTodoSetForward', + u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(next_set=True)<CR>' % VIM_PY_CALL))) + submenu + ActionEntry(u'Next keyword &set', self.keybindings[-1]) + + self.keybindings.append(Keybinding(u'<C-S-Left>', Plug( + u'OrgTodoSetBackward', + u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(direction=2, next_set=True)<CR>' % VIM_PY_CALL))) + submenu + ActionEntry(u'Previous &keyword set', self.keybindings[-1]) + + settings.set(u'org_todo_keywords', [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')]) + + settings.set(u'org_todo_prompt_position', u'botright') + + vim.command(u_encode(u'au orgmode BufReadCmd org:todo/* %s ORGMODE.plugins[u"Todo"].init_org_todo()' % VIM_PY_CALL)) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/__init__.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/__init__.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/encode_compatibility.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/encode_compatibility.py new file mode 100644 index 0000000..50ba796 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/encode_compatibility.py @@ -0,0 +1,11 @@ +import sys +if sys.version_info < (3,): + def u_encode(string): + return string.encode('utf8') + def u_decode(string): + return string.decode('utf8') +else: + def u_encode(string): + return string + def u_decode(string): + return string diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/py_py3_string.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/py_py3_string.py new file mode 100644 index 0000000..4ab11ab --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/py_py3_string.py @@ -0,0 +1,7 @@ +import sys + +if sys.version_info < (3,): + VIM_PY_CALL = u':py' +else: + VIM_PY_CALL = u':py3' + diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/unicode_compatibility.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/unicode_compatibility.py new file mode 100644 index 0000000..c0d2684 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/unicode_compatibility.py @@ -0,0 +1,4 @@ +try: + unicode +except NameError: + basestring = unicode = str diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/xrange_compatibility.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/xrange_compatibility.py new file mode 100644 index 0000000..2f0e442 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/xrange_compatibility.py @@ -0,0 +1,4 @@ +try: + from __builtin__ import xrange as range +except: + pass diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/settings.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/settings.py new file mode 100644 index 0000000..5d61512 --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/settings.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- + +import vim + +import sys +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.unicode_compatibility import * + +SCOPE_ALL = 1 + +# for all vim-orgmode buffers +SCOPE_GLOBAL = 2 + +# just for the current buffer - has priority before the global settings +SCOPE_BUFFER = 4 + +VARIABLE_LEADER = {SCOPE_GLOBAL: u'g', SCOPE_BUFFER: u'b'} + +u""" Evaluate and store settings """ + + +def get(setting, default=None, scope=SCOPE_ALL): + u""" Evaluate setting in scope of the current buffer, + globally and also from the contents of the current buffer + + WARNING: Only string values are converted to unicode. If a different value + is received, e.g. a list or dict, no conversion is done. + + :setting: name of the variable to evaluate + :default: default value in case the variable is empty + + :returns: variable value + """ + # TODO first read setting from org file which take precedence over vim + # variable settings + if (scope & SCOPE_ALL | SCOPE_BUFFER) and \ + int(vim.eval(u_encode(u'exists("b:%s")' % setting))): + res = vim.eval(u_encode(u"b:%s" % setting)) + if type(res) in (unicode, str): + return u_decode(res) + return res + + elif (scope & SCOPE_ALL | SCOPE_GLOBAL) and \ + int(vim.eval(u_encode(u'exists("g:%s")' % setting))): + res = vim.eval(u_encode(u"g:%s" % setting)) + if type(res) in (unicode, str): + return u_decode(res) + return res + return default + + +def set(setting, value, scope=SCOPE_GLOBAL, overwrite=False): + u""" Store setting in the defined scope + + WARNING: For the return value, only string are converted to unicode. If a + different value is received by vim.eval, e.g. a list or dict, no conversion + is done. + + :setting: name of the setting + :value: the actual value, repr is called on the value to create a string + representation + :scope: the scope o the setting/variable + :overwrite: overwrite existing settings (probably user defined settings) + + :returns: the new value in case of overwrite==False the current value + """ + if (not overwrite) and ( + int(vim.eval(u_encode(u'exists("%s:%s")' % \ + (VARIABLE_LEADER[scope], setting))))): + res = vim.eval( + u_encode(u'%s:%s' % (VARIABLE_LEADER[scope], setting))) + if type(res) in (unicode, str): + return u_decode(res) + return res + v = repr(value) + if type(value) == unicode and sys.version_info < (3,): + # strip leading u of unicode string representations + v = v[1:] + + cmd = u'let %s:%s = %s' % (VARIABLE_LEADER[scope], setting, v) + vim.command(u_encode(cmd)) + return value + + +def unset(setting, scope=SCOPE_GLOBAL): + u""" Unset setting in the defined scope + :setting: name of the setting + :scope: the scope o the setting/variable + + :returns: last value of setting + """ + value = get(setting, scope=scope) + cmd = u'unlet! %s:%s' % (VARIABLE_LEADER[scope], setting) + vim.command(u_encode(cmd)) + return value + + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/ftplugin/orgmode/vimbuffer.py b/pack/acp/start/vim-orgmode/ftplugin/orgmode/vimbuffer.py new file mode 100644 index 0000000..b4760fb --- /dev/null +++ b/pack/acp/start/vim-orgmode/ftplugin/orgmode/vimbuffer.py @@ -0,0 +1,503 @@ +# -*- coding: utf-8 -*- + +""" + vimbuffer + ~~~~~~~~~~ + + VimBuffer and VimBufferContent are the interface between liborgmode and + vim. + + VimBuffer extends the liborgmode.document.Document(). + Document() is just a general implementation for loading an org file. It + has no interface to an actual file or vim buffer. This is the task of + vimbuffer.VimBuffer(). It is the interfaces to vim. The main tasks for + VimBuffer are to provide read and write access to a real vim buffer. + + VimBufferContent is a helper class for VimBuffer. Basically, it hides the + details of encoding - everything read from or written to VimBufferContent + is UTF-8. +""" + +try: + from collections import UserList +except: + from UserList import UserList + +import vim + +from orgmode import settings +from orgmode.exceptions import BufferNotFound, BufferNotInSync +from orgmode.liborgmode.documents import Document, MultiPurposeList, Direction +from orgmode.liborgmode.headings import Heading + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.unicode_compatibility import * + + +class VimBuffer(Document): + def __init__(self, bufnr=0): + u""" + :bufnr: 0: current buffer, every other number refers to another buffer + """ + Document.__init__(self) + self._bufnr = vim.current.buffer.number if bufnr == 0 else bufnr + self._changedtick = -1 + self._cached_heading = None + if self._bufnr == vim.current.buffer.number: + self._content = VimBufferContent(vim.current.buffer) + else: + _buffer = None + for b in vim.buffers: + if self._bufnr == b.number: + _buffer = b + break + + if not _buffer: + raise BufferNotFound(u'Unable to locate buffer number #%d' % self._bufnr) + self._content = VimBufferContent(_buffer) + + self.update_changedtick() + self._orig_changedtick = self._changedtick + + @property + def tabstop(self): + return int(vim.eval(u_encode(u'&ts'))) + + @property + def tag_column(self): + return int(settings.get(u'org_tag_column', u'77')) + + @property + def is_insync(self): + if self._changedtick == self._orig_changedtick: + self.update_changedtick() + return self._changedtick == self._orig_changedtick + + @property + def bufnr(self): + u""" + :returns: The buffer's number for the current document + """ + return self._bufnr + + @property + def changedtick(self): + u""" Number of changes in vimbuffer """ + return self._changedtick + + @changedtick.setter + def changedtick(self, value): + self._changedtick = value + + def get_todo_states(self, strip_access_key=True): + u""" Returns a list containing a tuple of two lists of allowed todo + states split by todo and done states. Multiple todo-done state + sequences can be defined. + + :returns: [([todo states], [done states]), ..] + """ + states = settings.get(u'org_todo_keywords', []) + # TODO this function gets called too many times when change of state of + # one todo is triggered, check with: + # print(states) + # this should be changed by saving todo states into some var and only + # if new states are set hook should be called to register them again + # into a property + # TODO move this to documents.py, it is all tangled up like this, no + # structure... + if type(states) not in (list, tuple): + return [] + + def parse_states(s, stop=0): + res = [] + if not s: + return res + if type(s[0]) in (unicode, str): + r = [] + for i in s: + _i = i + if type(_i) == str: + _i = u_decode(_i) + if type(_i) == unicode and _i: + if strip_access_key and u'(' in _i: + _i = _i[:_i.index(u'(')] + if _i: + r.append(_i) + else: + r.append(_i) + if not u'|' in r: + if not stop: + res.append((r[:-1], [r[-1]])) + else: + res = (r[:-1], [r[-1]]) + else: + seperator_pos = r.index(u'|') + if not stop: + res.append((r[0:seperator_pos], r[seperator_pos + 1:])) + else: + res = (r[0:seperator_pos], r[seperator_pos + 1:]) + elif type(s) in (list, tuple) and not stop: + for i in s: + r = parse_states(i, stop=1) + if r: + res.append(r) + return res + + return parse_states(states) + + def update_changedtick(self): + if self.bufnr == vim.current.buffer.number: + self._changedtick = int(vim.eval(u_encode(u'b:changedtick'))) + else: + vim.command(u_encode(u'unlet! g:org_changedtick | let g:org_lz = &lz | let g:org_hidden = &hidden | set lz hidden')) + # TODO is this likely to fail? maybe some error hangling should be added + vim.command(u_encode(u'keepalt buffer %d | let g:org_changedtick = b:changedtick | buffer %d' % \ + (self.bufnr, vim.current.buffer.number))) + vim.command(u_encode(u'let &lz = g:org_lz | let &hidden = g:org_hidden | unlet! g:org_lz g:org_hidden | redraw')) + self._changedtick = int(vim.eval(u_encode(u'g:org_changedtick'))) + + def write(self): + u""" write the changes to the vim buffer + + :returns: True if something was written, otherwise False + """ + if not self.is_dirty: + return False + + self.update_changedtick() + if not self.is_insync: + raise BufferNotInSync(u'Buffer is not in sync with vim!') + + # write meta information + if self.is_dirty_meta_information: + meta_end = 0 if self._orig_meta_information_len is None else self._orig_meta_information_len + self._content[:meta_end] = self.meta_information + self._orig_meta_information_len = len(self.meta_information) + + # remove deleted headings + already_deleted = [] + for h in sorted(self._deleted_headings, key=lambda x: x._orig_start, reverse=True): + if h._orig_start is not None and h._orig_start not in already_deleted: + # this is a heading that actually exists on the buffer and it + # needs to be removed + del self._content[h._orig_start:h._orig_start + h._orig_len] + already_deleted.append(h._orig_start) + del self._deleted_headings[:] + del already_deleted + + # update changed headings and add new headings + for h in self.all_headings(): + if h.is_dirty: + vim.current.buffer.append("") # workaround for neovim bug + if h._orig_start is not None: + # this is a heading that existed before and was changed. It + # needs to be replaced + if h.is_dirty_heading: + self._content[h.start:h.start + 1] = [unicode(h)] + if h.is_dirty_body: + self._content[h.start + 1:h.start + h._orig_len] = h.body + else: + # this is a new heading. It needs to be inserted + self._content[h.start:h.start] = [unicode(h)] + h.body + del vim.current.buffer[-1] # restore workaround for neovim bug + h._dirty_heading = False + h._dirty_body = False + # for all headings the length and start offset needs to be updated + h._orig_start = h.start + h._orig_len = len(h) + + self._dirty_meta_information = False + self._dirty_document = False + + self.update_changedtick() + self._orig_changedtick = self._changedtick + return True + + def write_heading(self, heading, including_children=True): + """ WARNING: use this function only when you know what you are doing! + This function writes a heading to the vim buffer. It offers performance + advantages over the regular write() function. This advantage is + combined with no sanity checks! Whenever you use this function, make + sure the heading you are writing contains the right offsets + (Heading._orig_start, Heading._orig_len). + + Usage example: + # Retrieve a potentially dirty document + d = ORGMODE.get_document(allow_dirty=True) + # Don't rely on the DOM, retrieve the heading afresh + h = d.find_heading(direction=Direction.FORWARD, position=100) + # Update tags + h.tags = ['tag1', 'tag2'] + # Write the heading + d.write_heading(h) + + This function can't be used to delete a heading! + + :heading: Write this heading with to the vim buffer + :including_children: Also include children in the update + + :returns The written heading + """ + if including_children and heading.children: + for child in heading.children[::-1]: + self.write_heading(child, including_children) + + if heading.is_dirty: + if heading._orig_start is not None: + # this is a heading that existed before and was changed. It + # needs to be replaced + if heading.is_dirty_heading: + self._content[heading._orig_start:heading._orig_start + 1] = [unicode(heading)] + if heading.is_dirty_body: + self._content[heading._orig_start + 1:heading._orig_start + heading._orig_len] = heading.body + else: + # this is a new heading. It needs to be inserted + raise ValueError('Heading must contain the attribute _orig_start! %s' % heading) + heading._dirty_heading = False + heading._dirty_body = False + # for all headings the length offset needs to be updated + heading._orig_len = len(heading) + + return heading + + def write_checkbox(self, checkbox, including_children=True): + if including_children and checkbox.children: + for child in checkbox.children[::-1]: + self.write_checkbox(child, including_children) + + if checkbox.is_dirty: + if checkbox._orig_start is not None: + # this is a heading that existed before and was changed. It + # needs to be replaced + # print "checkbox is dirty? " + str(checkbox.is_dirty_checkbox) + # print checkbox + if checkbox.is_dirty_checkbox: + self._content[checkbox._orig_start:checkbox._orig_start + 1] = [unicode(checkbox)] + if checkbox.is_dirty_body: + self._content[checkbox._orig_start + 1:checkbox._orig_start + checkbox._orig_len] = checkbox.body + else: + # this is a new checkbox. It needs to be inserted + raise ValueError('Checkbox must contain the attribute _orig_start! %s' % checkbox) + checkbox._dirty_checkbox = False + checkbox._dirty_body = False + # for all headings the length offset needs to be updated + checkbox._orig_len = len(checkbox) + + return checkbox + + def write_checkboxes(self, checkboxes): + pass + + def previous_heading(self, position=None): + u""" Find the next heading (search forward) and return the related object + :returns: Heading object or None + """ + h = self.current_heading(position=position) + if h: + return h.previous_heading + + def current_heading(self, position=None): + u""" Find the current heading (search backward) and return the related object + :returns: Heading object or None + """ + if position is None: + position = vim.current.window.cursor[0] - 1 + + if not self.headings: + return + + def binaryFindInDocument(): + hi = len(self.headings) + lo = 0 + while lo < hi: + mid = (lo+hi)//2 + h = self.headings[mid] + if h.end_of_last_child < position: + lo = mid + 1 + elif h.start > position: + hi = mid + else: + return binaryFindHeading(h) + + def binaryFindHeading(heading): + if not heading.children or heading.end >= position: + return heading + + hi = len(heading.children) + lo = 0 + while lo < hi: + mid = (lo+hi)//2 + h = heading.children[mid] + if h.end_of_last_child < position: + lo = mid + 1 + elif h.start > position: + hi = mid + else: + return binaryFindHeading(h) + + # look at the cache to find the heading + h_tmp = self._cached_heading + if h_tmp is not None: + if h_tmp.end_of_last_child > position and \ + h_tmp.start < position: + if h_tmp.end < position: + self._cached_heading = binaryFindHeading(h_tmp) + return self._cached_heading + + self._cached_heading = binaryFindInDocument() + return self._cached_heading + + def next_heading(self, position=None): + u""" Find the next heading (search forward) and return the related object + :returns: Heading object or None + """ + h = self.current_heading(position=position) + if h: + return h.next_heading + + def find_current_heading(self, position=None, heading=Heading): + u""" Find the next heading backwards from the position of the cursor. + The difference to the function current_heading is that the returned + object is not built into the DOM. In case the DOM doesn't exist or is + out of sync this function is much faster in fetching the current + heading. + + :position: The position to start the search from + + :heading: The base class for the returned heading + + :returns: Heading object or None + """ + return self.find_heading(vim.current.window.cursor[0] - 1 \ + if position is None else position, \ + direction=Direction.BACKWARD, heading=heading, \ + connect_with_document=False) + + +class VimBufferContent(MultiPurposeList): + u""" Vim Buffer Content is a UTF-8 wrapper around a vim buffer. When + retrieving or setting items in the buffer an automatic conversion is + performed. + + This ensures UTF-8 usage on the side of liborgmode and the vim plugin + vim-orgmode. + """ + + def __init__(self, vimbuffer, on_change=None): + MultiPurposeList.__init__(self, on_change=on_change) + + # replace data with vimbuffer to make operations change the actual + # buffer + self.data = vimbuffer + + def __contains__(self, item): + i = item + if type(i) is unicode: + i = u_encode(item) + return MultiPurposeList.__contains__(self, i) + + def __getitem__(self, i): + if isinstance(i, slice): + return [u_decode(item) if type(item) is str else item \ + for item in MultiPurposeList.__getitem__(self, i)] + else: + item = MultiPurposeList.__getitem__(self, i) + if type(item) is str: + return u_decode(item) + return item + + def __setitem__(self, i, item): + if isinstance(i, slice): + o = [] + o_tmp = item + if type(o_tmp) not in (list, tuple) and not isinstance(o_tmp, UserList): + o_tmp = list(o_tmp) + for item in o_tmp: + if type(item) == unicode: + o.append(u_encode(item)) + else: + o.append(item) + MultiPurposeList.__setitem__(self, i, o) + else: + _i = item + if type(_i) is unicode: + _i = u_encode(item) + + # TODO: fix this bug properly, it is really strange that it fails on + # python3 without it. Problem is that when _i = ['* '] it fails in + # UserList.__setitem__() but if it is changed in debuggr in __setitem__ + # like item[0] = '* ' it works, hence this is some quirk with unicode + # stuff but very likely vim 7.4 BUG too. + if isinstance(_i, UserList) and sys.version_info > (3, ): + _i = [s.encode('utf8').decode('utf8') for s in _i] + + MultiPurposeList.__setitem__(self, i, _i) + + def __add__(self, other): + raise NotImplementedError() + # TODO: implement me + if isinstance(other, UserList): + return self.__class__(self.data + other.data) + elif isinstance(other, type(self.data)): + return self.__class__(self.data + other) + else: + return self.__class__(self.data + list(other)) + + def __radd__(self, other): + raise NotImplementedError() + # TODO: implement me + if isinstance(other, UserList): + return self.__class__(other.data + self.data) + elif isinstance(other, type(self.data)): + return self.__class__(other + self.data) + else: + return self.__class__(list(other) + self.data) + + def __iadd__(self, other): + o = [] + o_tmp = other + if type(o_tmp) not in (list, tuple) and not isinstance(o_tmp, UserList): + o_tmp = list(o_tmp) + for i in o_tmp: + if type(i) is unicode: + o.append(u_encode(i)) + else: + o.append(i) + + return MultiPurposeList.__iadd__(self, o) + + def append(self, item): + i = item + if type(item) is str: + i = u_encode(item) + MultiPurposeList.append(self, i) + + def insert(self, i, item): + _i = item + if type(_i) is str: + _i = u_encode(item) + MultiPurposeList.insert(self, i, _i) + + def index(self, item, *args): + i = item + if type(i) is unicode: + i = u_encode(item) + MultiPurposeList.index(self, i, *args) + + def pop(self, i=-1): + return u_decode(MultiPurposeList.pop(self, i)) + + def extend(self, other): + o = [] + o_tmp = other + if type(o_tmp) not in (list, tuple) and not isinstance(o_tmp, UserList): + o_tmp = list(o_tmp) + for i in o_tmp: + if type(i) is unicode: + o.append(u_encode(i)) + else: + o.append(i) + MultiPurposeList.extend(self, o) + + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/indent/org.vim b/pack/acp/start/vim-orgmode/indent/org.vim new file mode 100644 index 0000000..8cfc1a9 --- /dev/null +++ b/pack/acp/start/vim-orgmode/indent/org.vim @@ -0,0 +1,133 @@ +" Delete the next line to avoid the special indention of items +if !exists("g:org_indent") + let g:org_indent = 0 +endif + +setlocal foldtext=GetOrgFoldtext() +setlocal fillchars-=fold:- +setlocal fillchars+=fold:\ +setlocal foldexpr=GetOrgFolding() +setlocal foldmethod=expr +setlocal indentexpr=GetOrgIndent() +setlocal nolisp +setlocal nosmartindent +setlocal autoindent + +if has('python3') + let s:py_env = 'python3 << EOF' +else + let s:py_env = 'python << EOF' +endif + +function! GetOrgIndent() + if g:org_indent == 0 + return -1 + endif + +exe s:py_env +from orgmode._vim import indent_orgmode +indent_orgmode() +EOF + + if exists('b:indent_level') + let l:tmp = b:indent_level + unlet b:indent_level + return l:tmp + else + return -1 + endif +endfunction + +function! GetOrgFolding() + let l:mode = mode() + if l:mode == 'i' + " the cache size is limited to 3, because vim queries the current and + " both surrounding lines when the user is typing in insert mode. The + " cache is shared between GetOrgFolding and GetOrgFoldtext + if ! exists('b:org_folding_cache') + let b:org_folding_cache = {} + endif + + if has_key(b:org_folding_cache, v:lnum) + if match(b:org_folding_cache[v:lnum], '^>') == 0 && + \ match(getline(v:lnum), '^\*\+\s') != 0 + " when the user pastes text or presses enter, it happens that + " the cache starts to confuse vim's folding abilities + " these entries can safely be removed + unlet b:org_folding_cache[v:lnum] + + " the fold text cache is probably also damaged, delete it as + " well + unlet! b:org_foldtext_cache + else + return b:org_folding_cache[v:lnum] + endif + endif + + exe s:py_env +from orgmode._vim import fold_orgmode +fold_orgmode(allow_dirty=True) +EOF + else + + exe s:py_env +from orgmode._vim import fold_orgmode +fold_orgmode() +EOF + endif + + if exists('b:fold_expr') + let l:tmp = b:fold_expr + unlet b:fold_expr + if l:mode == 'i' + if ! has_key(b:org_folding_cache, v:lnum) + if len(b:org_folding_cache) > 3 + let b:org_folding_cache = {} + endif + let b:org_folding_cache[v:lnum] = l:tmp + endif + endif + return l:tmp + else + return -1 + endif +endfunction + +function! SetOrgFoldtext(text) + let b:foldtext = a:text +endfunction + +function! GetOrgFoldtext() + let l:mode = mode() + if l:mode == 'i' + " add a separate cache for fold text + if ! exists('b:org_foldtext_cache') || + \ ! has_key(b:org_foldtext_cache, 'timestamp') || + \ b:org_foldtext_cache['timestamp'] > (localtime() + 10) + let b:org_foldtext_cache = {'timestamp': localtime()} + endif + + if has_key(b:org_foldtext_cache, v:foldstart) + return b:org_foldtext_cache[v:foldstart] + endif + exe s:py_env +from orgmode._vim import fold_text +fold_text(allow_dirty=True) +EOF + else + unlet! b:org_foldtext_cache + exec s:py_env +from orgmode._vim import fold_text +fold_text() +EOF + endif + + if exists('b:foldtext') + let l:tmp = b:foldtext + unlet b:foldtext + if l:mode == 'i' + let b:org_foldtext_cache[v:foldstart] = l:tmp + endif + return l:tmp + endif +endfunction diff --git a/pack/acp/start/vim-orgmode/install_vba.vim b/pack/acp/start/vim-orgmode/install_vba.vim new file mode 100644 index 0000000..7bc4825 --- /dev/null +++ b/pack/acp/start/vim-orgmode/install_vba.vim @@ -0,0 +1,3 @@ +:exec 'set rtp='.g:installdir +:so orgmode.vba +:q! diff --git a/pack/acp/start/vim-orgmode/install_vmb.vim b/pack/acp/start/vim-orgmode/install_vmb.vim new file mode 100644 index 0000000..874ca0e --- /dev/null +++ b/pack/acp/start/vim-orgmode/install_vmb.vim @@ -0,0 +1,3 @@ +:exec 'set rtp='.g:installdir +:so orgmode.vmb +:q! diff --git a/pack/acp/start/vim-orgmode/org b/pack/acp/start/vim-orgmode/org new file mode 100755 index 0000000..5d44b5f --- /dev/null +++ b/pack/acp/start/vim-orgmode/org @@ -0,0 +1,54 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# org +# Command line utility for working with orgmode documents +# +# Depends: python-liborgmode +# +# Copyright (C) 2012 Jan Christoph Ebersbach +# +# http://www.e-jc.de/ +# +# All rights reserved. +# +# The source code of this program is made available +# under the terms of the GNU Affero General Public License version 3 +# (GNU AGPL V3) as published by the Free Software Foundation. +# +# Binary versions of this program provided by Univention to you as +# well as other copyrighted, protected or trademarked materials like +# Logos, graphics, fonts, specific documentations and configurations, +# cryptographic keys etc. are subject to a license agreement between +# you and Univention and not subject to the GNU AGPL V3. +# +# In the case you use this program under the terms of the GNU AGPL V3, +# the program is provided in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public +# License with the Debian GNU/Linux or Univention distribution in file +# /usr/share/common-licenses/AGPL-3; if not, see +# <http://www.gnu.org/licenses/>. + +import sys +sys.path.append('ftplugin') + +from orgmode.liborgmode.documents import Document + + +class OrgFile(Document): + def __init__(self, filename): + u""" + :filename: Name of the file that shall be opened + """ + Document.__init__(self) + with file(filename) as f: + self._content = [line.decode(u'utf-8') for line in f.readlines()] + +if __name__ == '__main__': + import cProfile + for filename in sys.argv[1:]: + cProfile.run('OrgFile(filename).init_dom()') diff --git a/pack/acp/start/vim-orgmode/syntax/org.vim b/pack/acp/start/vim-orgmode/syntax/org.vim new file mode 100644 index 0000000..2acb407 --- /dev/null +++ b/pack/acp/start/vim-orgmode/syntax/org.vim @@ -0,0 +1,383 @@ +" Support org authoring markup as closely as possible +" (we're adding two markdown-like variants for =code= and blockquotes) +" ----------------------------------------------------------------------------- +" +" Do we use aggresive conceal? +if exists("b:org_aggressive_conceal") + let s:conceal_aggressively=b:org_aggressive_conceal +elseif exists("g:org_aggressive_conceal") + let s:conceal_aggressively=g:org_aggressive_conceal +else + let s:conceal_aggressively=0 +endif + +" Inline markup {{{1 +" *bold*, /italic/, _underline_, +strike-through+, =code=, ~verbatim~ +" Note: +" - /italic/ is rendered as reverse in most terms (works fine in gVim, though) +" - +strike-through+ doesn't work on Vim / gVim +" - the non-standard `code' markup is also supported +" - =code= and ~verbatim~ are also supported as block-level markup, see below. +" Ref: http://orgmode.org/manual/Emphasis-and-monospace.html +"syntax match org_bold /\*[^ ]*\*/ + +" FIXME: Always make org_bold syntax define before org_heading syntax +" to make sure that org_heading syntax got higher priority(help :syn-priority) than org_bold. +" If there is any other good solution, please help fix it. +" \\\\*sinuate* +if (s:conceal_aggressively == 1) + syntax region org_bold matchgroup=org_border_bold start="[^ \\]\zs\*\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\*\|\(^\|[^\\]\)\@<=\*\S\@=" end="[^ \\]\zs\*\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\*\|[^\\]\zs\*\S\@=" concealends oneline + syntax region org_italic matchgroup=org_border_ital start="[^ \\]\zs\/\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\/\|\(^\|[^\\]\)\@<=\/\S\@=" end="[^ \\]\zs\/\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\/\|[^\\]\zs\/\S\@=" concealends oneline + syntax region org_underline matchgroup=org_border_undl start="[^ \\]\zs_\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs_\|\(^\|[^\\]\)\@<=_\S\@=" end="[^ \\]\zs_\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs_\|[^\\]\zs_\S\@=" concealends oneline + syntax region org_code matchgroup=org_border_code start="[^ \\]\zs=\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs=\|\(^\|[^\\]\)\@<==\S\@=" end="[^ \\]\zs=\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs=\|[^\\]\zs=\S\@=" concealends oneline + syntax region org_code matchgroup=org_border_code start="[^ \\]\zs`\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs`\|\(^\|[^\\]\)\@<=`\S\@=" end="[^ \\]\zs'\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs'\|[^\\]\zs'\S\@=" concealends oneline + syntax region org_verbatim matchgroup=org_border_verb start="[^ \\]\zs\~\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\~\|\(^\|[^\\]\)\@<=\~\S\@=" end="[^ \\]\zs\~\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\~\|[^\\]\zs\~\S\@=" concealends oneline +else + syntax region org_bold start="\S\zs\*\|\*\S\@=" end="\S\zs\*\|\*\S\@=" keepend oneline + syntax region org_italic start="\S\zs\/\|\/\S\@=" end="\S\zs\/\|\/\S\@=" keepend oneline + syntax region org_underline start="\S\zs_\|_\S\@=" end="\S\zs_\|_\S\@=" keepend oneline + syntax region org_code start="\S\zs=\|=\S\@=" end="\S\zs=\|=\S\@=" keepend oneline + syntax region org_code start="\S\zs`\|`\S\@=" end="\S\zs'\|'\S\@=" keepend oneline + syntax region org_verbatim start="\S\zs\~\|\~\S\@=" end="\S\zs\~\|\~\S\@=" keepend oneline +endif + +hi def org_bold term=bold cterm=bold gui=bold +hi def org_italic term=italic cterm=italic gui=italic +hi def org_underline term=underline cterm=underline gui=underline + +if (s:conceal_aggressively == 1) + hi link org_border_bold org_bold + hi link org_border_ital org_italic + hi link org_border_undl org_underline +endif + +" Headings: {{{1 +" Load Settings: {{{2 +if !exists('g:org_heading_highlight_colors') + let g:org_heading_highlight_colors = ['Title', 'Constant', 'Identifier', 'Statement', 'PreProc', 'Type', 'Special'] +endif + +if !exists('g:org_heading_highlight_levels') + let g:org_heading_highlight_levels = len(g:org_heading_highlight_colors) +endif + +if !exists('g:org_heading_shade_leading_stars') + let g:org_heading_shade_leading_stars = 1 +endif + +" Enable Syntax HL: {{{2 +unlet! s:i s:j s:contains +let s:i = 1 +let s:j = len(g:org_heading_highlight_colors) +let 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' +if g:org_heading_shade_leading_stars == 1 + let s:contains = s:contains . ',org_shade_stars' + syntax match org_shade_stars /^\*\{2,\}/me=e-1 contained + hi def link org_shade_stars Ignore +else + hi clear org_shade_stars +endif + +while s:i <= g:org_heading_highlight_levels + exec 'syntax match org_heading' . s:i . ' /^\*\{' . s:i . '\}\s.*/' . s:contains + exec 'hi def link org_heading' . s:i . ' ' . g:org_heading_highlight_colors[(s:i - 1) % s:j] + let s:i += 1 +endwhile +unlet! s:i s:j s:contains + +" Todo Keywords: {{{1 +" Load Settings: {{{2 +if !exists('g:org_todo_keywords') + let g:org_todo_keywords = ['TODO', '|', 'DONE'] +endif + +if !exists('g:org_todo_keyword_faces') + let g:org_todo_keyword_faces = [] +endif + +" Enable Syntax HL: {{{2 +let s:todo_headings = '' +let s:i = 1 +while s:i <= g:org_heading_highlight_levels + if s:todo_headings == '' + let s:todo_headings = 'containedin=org_heading' . s:i + else + let s:todo_headings = s:todo_headings . ',org_heading' . s:i + endif + let s:i += 1 +endwhile +unlet! s:i + +if !exists('g:loaded_org_syntax') + let g:loaded_org_syntax = 1 + + function! OrgExtendHighlightingGroup(base_group, new_group, settings) + let l:base_hi = '' + redir => l:base_hi + silent execute 'highlight ' . a:base_group + redir END + let l:group_hi = substitute(split(l:base_hi, '\n')[0], '^' . a:base_group . '\s\+xxx', '', '') + execute 'highlight ' . a:new_group . l:group_hi . ' ' . a:settings + endfunction + + function! OrgInterpretFaces(faces) + let l:res_faces = '' + if type(a:faces) == 3 + let l:style = [] + for l:f in a:faces + let l:_f = [l:f] + if type(l:f) == 3 + let l:_f = l:f + endif + for l:g in l:_f + if type(l:g) == 1 && l:g =~ '^:' + if l:g !~ '[\t ]' + continue + endif + let l:k_v = split(l:g) + if l:k_v[0] == ':foreground' + let l:gui_color = '' + let l:found_gui_color = 0 + for l:color in split(l:k_v[1], ',') + if l:color =~ '^#' + let l:found_gui_color = 1 + let l:res_faces = l:res_faces . ' guifg=' . l:color + elseif l:color != '' + let l:gui_color = l:color + let l:res_faces = l:res_faces . ' ctermfg=' . l:color + endif + endfor + if ! l:found_gui_color && l:gui_color != '' + let l:res_faces = l:res_faces . ' guifg=' . l:gui_color + endif + elseif l:k_v[0] == ':background' + let l:gui_color = '' + let l:found_gui_color = 0 + for l:color in split(l:k_v[1], ',') + if l:color =~ '^#' + let l:found_gui_color = 1 + let l:res_faces = l:res_faces . ' guibg=' . l:color + elseif l:color != '' + let l:gui_color = l:color + let l:res_faces = l:res_faces . ' ctermbg=' . l:color + endif + endfor + if ! l:found_gui_color && l:gui_color != '' + let l:res_faces = l:res_faces . ' guibg=' . l:gui_color + endif + elseif l:k_v[0] == ':weight' || l:k_v[0] == ':slant' || l:k_v[0] == ':decoration' + if index(l:style, l:k_v[1]) == -1 + call add(l:style, l:k_v[1]) + endif + endif + elseif type(l:g) == 1 + " TODO emacs interprets the color and automatically determines + " whether it should be set as foreground or background color + let l:res_faces = l:res_faces . ' ctermfg=' . l:k_v[1] . ' guifg=' . l:k_v[1] + endif + endfor + endfor + let l:s = '' + for l:i in l:style + if l:s == '' + let l:s = l:i + else + let l:s = l:s . ','. l:i + endif + endfor + if l:s != '' + let l:res_faces = l:res_faces . ' term=' . l:s . ' cterm=' . l:s . ' gui=' . l:s + endif + elseif type(a:faces) == 1 + " TODO emacs interprets the color and automatically determines + " whether it should be set as foreground or background color + let l:res_faces = l:res_faces . ' ctermfg=' . a:faces . ' guifg=' . a:faces + endif + return l:res_faces + endfunction + + function! s:ReadTodoKeywords(keywords, todo_headings) + let l:default_group = 'Todo' + for l:i in a:keywords + if type(l:i) == 3 + call s:ReadTodoKeywords(l:i, a:todo_headings) + continue + endif + if l:i == '|' + let l:default_group = 'Question' + continue + endif + " strip access key + let l:_i = substitute(l:i, "\(.*$", "", "") + + let l:group = l:default_group + for l:j in g:org_todo_keyword_faces + if l:j[0] == l:_i + let l:group = 'org_todo_keyword_face_' . l:_i + call OrgExtendHighlightingGroup(l:default_group, l:group, OrgInterpretFaces(l:j[1])) + break + endif + endfor + silent! exec 'syntax match org_todo_keyword_' . l:_i . ' /\*\{1,\}\s\{1,\}\zs' . l:_i .'\(\s\|$\)/ ' . a:todo_headings + silent! exec 'hi def link org_todo_keyword_' . l:_i . ' ' . l:group + endfor + endfunction +endif + +call s:ReadTodoKeywords(g:org_todo_keywords, s:todo_headings) +unlet! s:todo_headings + +" Timestamps: {{{1 +"<2003-09-16 Tue> +"<2003-09-16 Sáb> +syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k>\)/ +"<2003-09-16 Tue 12:00> +syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>\)/ +"<2003-09-16 Tue 12:00-12:30> +syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d-\d\d:\d\d>\)/ + +"<2003-09-16 Tue>--<2003-09-16 Tue> +syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k>--<\d\d\d\d-\d\d-\d\d \k\k\k>\)/ +"<2003-09-16 Tue 12:00>--<2003-09-16 Tue 12:00> +syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>--<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>\)/ + +syn match org_timestamp /\(<%%(diary-float.\+>\)/ + +"[2003-09-16 Tue] +syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \k\k\k\]\)/ +"[2003-09-16 Tue 12:00] +syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d\]\)/ + +"[2003-09-16 Tue]--[2003-09-16 Tue] +syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \k\k\k\]--\[\d\d\d\d-\d\d-\d\d \k\k\k\]\)/ +"[2003-09-16 Tue 12:00]--[2003-09-16 Tue 12:00] +syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d\]--\[\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d\]\)/ + +syn match org_timestamp_inactive /\(\[%%(diary-float.\+\]\)/ + +hi def link org_timestamp PreProc +hi def link org_timestamp_inactive Comment + +" Deadline And Schedule: {{{1 +syn match org_deadline_scheduled /^\s*\(DEADLINE\|SCHEDULED\):/ +hi def link org_deadline_scheduled PreProc + +" Tables: {{{1 +syn match org_table /^\s*|.*/ contains=org_timestamp,org_timestamp_inactive,hyperlink,org_table_separator,org_table_horizontal_line +syn match org_table_separator /\(^\s*|[-+]\+|\?\||\)/ contained +hi def link org_table_separator Type + +" Hyperlinks: {{{1 +syntax match hyperlink "\[\{2}[^][]*\(\]\[[^][]*\)\?\]\{2}" contains=hyperlinkBracketsLeft,hyperlinkURL,hyperlinkBracketsRight containedin=ALL +if (s:conceal_aggressively == 1) + syntax match hyperlinkBracketsLeft contained "\[\{2}#\?" conceal +else + syntax match hyperlinkBracketsLeft contained "\[\{2}" conceal +endif +syntax match hyperlinkURL contained "[^][]*\]\[" conceal +syntax match hyperlinkBracketsRight contained "\]\{2}" conceal +hi def link hyperlink Underlined + +" Comments: {{{1 +syntax match org_comment /^#.*/ +hi def link org_comment Comment + +" Bullet Lists: {{{1 +" Ordered Lists: +" 1. list item +" 1) list item +" a. list item +" a) list item +syn match org_list_ordered "^\s*\(\a\|\d\+\)[.)]\(\s\|$\)" nextgroup=org_list_item +hi def link org_list_ordered Identifier + +" Unordered Lists: +" - list item +" * list item +" + list item +" + and - don't need a whitespace prefix +syn match org_list_unordered "^\(\s*[-+]\|\s\+\*\)\(\s\|$\)" nextgroup=org_list_item +hi def link org_list_unordered Identifier + +" Definition Lists: +" - Term :: expl. +" 1) Term :: expl. +syntax match org_list_def /.*\s\+::/ contained +hi def link org_list_def PreProc + +syntax 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 +syntax match org_list_checkbox /\[[ X-]]/ contained +hi def link org_list_bullet Identifier +hi def link org_list_checkbox PreProc + +" Block Delimiters: {{{1 +syntax case ignore +syntax match org_block_delimiter /^#+BEGIN_.*/ +syntax match org_block_delimiter /^#+END_.*/ +syntax match org_key_identifier /^#+[^ ]*:/ +syntax match org_title /^#+TITLE:.*/ contains=org_key_identifier +hi def link org_block_delimiter Comment +hi def link org_key_identifier Comment +hi def link org_title Title + +" Block Markup: {{{1 +" we consider all BEGIN/END sections as 'verbatim' blocks (inc. 'quote', 'verse', 'center') +" except 'example' and 'src' which are treated as 'code' blocks. +" Note: the non-standard '>' prefix is supported for quotation lines. +" Note: the '^:.*" rule must be defined before the ':PROPERTIES:' one below. +" TODO: http://vim.wikia.com/wiki/Different_syntax_highlighting_within_regions_of_a_file +syntax match org_verbatim /^\s*>.*/ +syntax match org_code /^\s*:.*/ + +syntax region org_verbatim start="^\s*#+BEGIN_.*" end="^\s*#+END_.*" keepend contains=org_block_delimiter +syntax region org_code start="^\s*#+BEGIN_SRC" end="^\s*#+END_SRC" keepend contains=org_block_delimiter +syntax region org_code start="^\s*#+BEGIN_EXAMPLE" end="^\s*#+END_EXAMPLE" keepend contains=org_block_delimiter + +hi def link org_code String +hi def link org_verbatim String + +if (s:conceal_aggressively==1) + hi link org_border_code org_code + hi link org_border_verb org_verbatim +endif + +" Properties: {{{1 +syn region Error matchgroup=org_properties_delimiter start=/^\s*:PROPERTIES:\s*$/ end=/^\s*:END:\s*$/ contains=org_property keepend +syn match org_property /^\s*:[^\t :]\+:\s\+[^\t ]/ contained contains=org_property_value +syn match org_property_value /:\s\zs.*/ contained +hi def link org_properties_delimiter PreProc +hi def link org_property Statement +hi def link org_property_value Constant +" Break down subtasks +syntax match org_subtask_number /\[\d*\/\d*]/ contained +syntax match org_subtask_percent /\[\d*%\]/ contained +syntax match org_subtask_number_all /\[\(\d\+\)\/\1\]/ contained +syntax match org_subtask_percent_100 /\[100%\]/ contained + +hi def link org_subtask_number String +hi def link org_subtask_percent String +hi def link org_subtask_percent_100 Identifier +hi def link org_subtask_number_all Identifier + +" Plugin SyntaxRange: {{{1 +" This only works if you have SyntaxRange installed: +" https://github.com/vim-scripts/SyntaxRange + +" BEGIN_SRC +if exists('g:loaded_SyntaxRange') + call SyntaxRange#Include('#+BEGIN_SRC vim', '#+END_SRC', 'vim', 'comment') + call SyntaxRange#Include('#+BEGIN_SRC python', '#+END_SRC', 'python', 'comment') + call SyntaxRange#Include('#+BEGIN_SRC c', '#+END_SRC', 'c', 'comment') + " cpp must be below c, otherwise you get c syntax hl for cpp files + call SyntaxRange#Include('#+BEGIN_SRC cpp', '#+END_SRC', 'cpp', 'comment') + call SyntaxRange#Include('#+BEGIN_SRC ruby', '#+END_SRC', 'ruby', 'comment') + " call SyntaxRange#Include('#+BEGIN_SRC lua', '#+END_SRC', 'lua', 'comment') + " call SyntaxRange#Include('#+BEGIN_SRC lisp', '#+END_SRC', 'lisp', 'comment') + + " LaTeX + call SyntaxRange#Include('\\begin[.*]{.*}', '\\end{.*}', 'tex') + call SyntaxRange#Include('\\begin{.*}', '\\end{.*}', 'tex') + call SyntaxRange#Include('\\\[', '\\\]', 'tex') +endif + +" vi: ft=vim:tw=80:sw=4:ts=4:fdm=marker diff --git a/pack/acp/start/vim-orgmode/syntax/orgagenda.vim b/pack/acp/start/vim-orgmode/syntax/orgagenda.vim new file mode 100644 index 0000000..3ea4fad --- /dev/null +++ b/pack/acp/start/vim-orgmode/syntax/orgagenda.vim @@ -0,0 +1,79 @@ +" TODO do we really need a separate syntax file for the agenda? +" - Most of the stuff here is also in syntax.org +" - DRY! + +syn match org_todo_key /\[\zs[^]]*\ze\]/ +hi def link org_todo_key Identifier + +let s:todo_headings = '' +let s:i = 1 +while s:i <= g:org_heading_highlight_levels + if s:todo_headings == '' + let s:todo_headings = 'containedin=org_heading' . s:i + else + let s:todo_headings = s:todo_headings . ',org_heading' . s:i + endif + let s:i += 1 +endwhile +unlet! s:i + +if !exists('g:loaded_orgagenda_syntax') + let g:loaded_orgagenda_syntax = 1 + function! s:ReadTodoKeywords(keywords, todo_headings) + let l:default_group = 'Todo' + for l:i in a:keywords + if type(l:i) == 3 + call s:ReadTodoKeywords(l:i, a:todo_headings) + continue + endif + if l:i == '|' + let l:default_group = 'Question' + continue + endif + " strip access key + let l:_i = substitute(l:i, "\(.*$", "", "") + + let l:group = l:default_group + for l:j in g:org_todo_keyword_faces + if l:j[0] == l:_i + let l:group = 'orgtodo_todo_keyword_face_' . l:_i + call OrgExtendHighlightingGroup(l:default_group, l:group, OrgInterpretFaces(l:j[1])) + break + endif + endfor + silent! exec 'syntax match orgtodo_todo_keyword_' . l:_i . ' /' . l:_i .'/ ' . a:todo_headings + silent! exec 'hi def link orgtodo_todo_keyword_' . l:_i . ' ' . l:group + endfor + endfunction +endif + +call s:ReadTodoKeywords(g:org_todo_keywords, s:todo_headings) +unlet! s:todo_headings + +" Timestamps +"<2003-09-16 Tue> +syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k>\)/ +"<2003-09-16 Tue 12:00> +syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>\)/ +"<2003-09-16 Tue 12:00-12:30> +syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d-\d\d:\d\d>\)/ +"<2003-09-16 Tue>--<2003-09-16 Tue> +syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k>--<\d\d\d\d-\d\d-\d\d \k\k\k>\)/ +"<2003-09-16 Tue 12:00>--<2003-09-16 Tue 12:00> +syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>--<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>\)/ +syn match org_timestamp /\(<%%(diary-float.\+>\)/ +hi def link org_timestamp PreProc + +" special words +syn match today /TODAY$/ +hi def link today PreProc + +syn match week_agenda /^Week Agenda:$/ +hi def link week_agenda PreProc + +" Hyperlinks +syntax match hyperlink "\[\{2}[^][]*\(\]\[[^][]*\)\?\]\{2}" contains=hyperlinkBracketsLeft,hyperlinkURL,hyperlinkBracketsRight containedin=ALL +syntax match hyperlinkBracketsLeft contained "\[\{2}" conceal +syntax match hyperlinkURL contained "[^][]*\]\[" conceal +syntax match hyperlinkBracketsRight contained "\]\{2}" conceal +hi def link hyperlink Underlined diff --git a/pack/acp/start/vim-orgmode/syntax/orgtodo.vim b/pack/acp/start/vim-orgmode/syntax/orgtodo.vim new file mode 100644 index 0000000..77c9aa6 --- /dev/null +++ b/pack/acp/start/vim-orgmode/syntax/orgtodo.vim @@ -0,0 +1,47 @@ +syn match org_todo_key /\[\zs[^]]*\ze\]/ +hi def link org_todo_key Identifier + +let s:todo_headings = '' +let s:i = 1 +while s:i <= g:org_heading_highlight_levels + if s:todo_headings == '' + let s:todo_headings = 'containedin=org_heading' . s:i + else + let s:todo_headings = s:todo_headings . ',org_heading' . s:i + endif + let s:i += 1 +endwhile +unlet! s:i + +if !exists('g:loaded_orgtodo_syntax') + let g:loaded_orgtodo_syntax = 1 + function! s:ReadTodoKeywords(keywords, todo_headings) + let l:default_group = 'Todo' + for l:i in a:keywords + if type(l:i) == 3 + call s:ReadTodoKeywords(l:i, a:todo_headings) + continue + endif + if l:i == '|' + let l:default_group = 'Question' + continue + endif + " strip access key + let l:_i = substitute(l:i, "\(.*$", "", "") + + let l:group = l:default_group + for l:j in g:org_todo_keyword_faces + if l:j[0] == l:_i + let l:group = 'orgtodo_todo_keyword_face_' . l:_i + call OrgExtendHighlightingGroup(l:default_group, l:group, OrgInterpretFaces(l:j[1])) + break + endif + endfor + silent! exec 'syntax match orgtodo_todo_keyword_' . l:_i . ' /' . l:_i .'/ ' . a:todo_headings + silent! exec 'hi def link orgtodo_todo_keyword_' . l:_i . ' ' . l:group + endfor + endfunction +endif + +call s:ReadTodoKeywords(g:org_todo_keywords, s:todo_headings) +unlet! s:todo_headings diff --git a/pack/acp/start/vim-orgmode/tests/orgmode_testfile.org b/pack/acp/start/vim-orgmode/tests/orgmode_testfile.org new file mode 100644 index 0000000..7eda72f --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/orgmode_testfile.org @@ -0,0 +1,37 @@ + +* bold, italics and underline syntax matching +** Should match: + +*foo* *foo* +*Really, quite long sentence*. +_foo_ _foo_ +_really, quite long sentence._. + + *Übermensch á* *eä* *ý€* + _Ÿ ï_ + + *sdf l.* + *sdfsdf ,.* + *foo_ sdf /* + /sdf sdf sdf ./ + + /google.com/ + + *[sdf]* +*a* /a/ =b= ~b~ `d` + +*abc* /abc/ =bde= ~bde~ `def` + *=*a*=* +** Should not match +http://google.com/ + //google.com/ + * sdf* _ sdf_ + *sdfsdf sdf,* + *foo * + foo_not underlined_bar + + *.sdf*[ + [*.sdf* + [*sdf*] + *=*a*= + diff --git a/pack/acp/start/vim-orgmode/tests/run_tests.py b/pack/acp/start/vim-orgmode/tests/run_tests.py new file mode 100755 index 0000000..6b31816 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/run_tests.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import test_vimbuffer + +import test_libagendafilter +import test_libcheckbox +import test_libbase +import test_libheading +import test_liborgdate +import test_liborgdate_utf8 +import test_liborgdate_parsing +import test_liborgdatetime +import test_liborgtimerange + +import test_plugin_date +import test_plugin_edit_structure +import test_plugin_edit_checkbox +import test_plugin_misc +import test_plugin_navigator +import test_plugin_show_hide +import test_plugin_tags_properties +import test_plugin_todo +import test_plugin_mappings + +import unittest + + +if __name__ == '__main__': + tests = unittest.TestSuite() + + tests.addTests(test_vimbuffer.suite()) + + # lib + tests.addTests(test_libbase.suite()) + tests.addTests(test_libcheckbox.suite()) + tests.addTests(test_libagendafilter.suite()) + tests.addTests(test_libheading.suite()) + tests.addTests(test_liborgdate.suite()) + tests.addTests(test_liborgdate_utf8.suite()) + tests.addTests(test_liborgdate_parsing.suite()) + tests.addTests(test_liborgdatetime.suite()) + tests.addTests(test_liborgtimerange.suite()) + + # plugins + tests.addTests(test_plugin_date.suite()) + tests.addTests(test_plugin_edit_structure.suite()) + tests.addTests(test_plugin_edit_checkbox.suite()) + tests.addTests(test_plugin_misc.suite()) + tests.addTests(test_plugin_navigator.suite()) + tests.addTests(test_plugin_show_hide.suite()) + tests.addTests(test_plugin_tags_properties.suite()) + tests.addTests(test_plugin_todo.suite()) + tests.addTests(test_plugin_mappings.suite()) + + runner = unittest.TextTestRunner() + runner.run(tests) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/tests/test_libagendafilter.py b/pack/acp/start/vim-orgmode/tests/test_libagendafilter.py new file mode 100644 index 0000000..e594333 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_libagendafilter.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- + + +import sys +sys.path.append(u'../ftplugin') + +import unittest +from datetime import date +from datetime import timedelta + +from orgmode.liborgmode.headings import Heading +from orgmode.liborgmode.orgdate import OrgDate +from orgmode.liborgmode.agendafilter import contains_active_todo +from orgmode.liborgmode.agendafilter import contains_active_date +from orgmode.liborgmode.orgdate import OrgDateTime +from orgmode.liborgmode.agendafilter import is_within_week +from orgmode.liborgmode.agendafilter import is_within_week_and_active_todo +from orgmode.liborgmode.agendafilter import filter_items + +import vim + +from orgmode.py3compat.encode_compatibility import * + +counter = 0 + +class AgendaFilterTestCase(unittest.TestCase): + u"""Tests all the functionality of the Agenda filter module.""" + + def setUp(self): + global counter + counter += 1 + + vim.EVALHISTORY = [] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')], + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("b:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u'b:changedtick'): u_encode(u'%d' % counter), + u_encode(u"v:count"): u_encode(u'0') + } + vim.current.buffer[:] = [u_encode(i) for i in u""" +* TODO Heading 1 + some text +""".split(u'\n')] + + def test_contains_active_todo(self): + heading = Heading(title=u'Refactor the code', todo='TODO') + self.assertTrue(contains_active_todo(heading)) + + heading = Heading(title=u'Refactor the code', todo='DONE') + self.assertFalse(contains_active_todo(heading)) + + heading = Heading(title=u'Refactor the code', todo=None) + self.assertFalse(contains_active_todo(heading)) + + def test_contains_active_date(self): + heading = Heading(title=u'Refactor the code', active_date=None) + self.assertFalse(contains_active_date(heading)) + + odate = OrgDate(True, 2011, 11, 1) + heading = Heading(title=u'Refactor the code', active_date=odate) + self.assertTrue(contains_active_date(heading)) + + def test_is_within_week_with_orgdate(self): + # to far in the future + tmpdate = date.today() + timedelta(days=8) + odate = OrgDate(True, tmpdate.year, tmpdate.month, tmpdate.day) + heading = Heading(title=u'Refactor the code', active_date=odate) + self.assertFalse(is_within_week(heading)) + + # within a week + tmpdate = date.today() + timedelta(days=5) + odate = OrgDate(True, tmpdate.year, tmpdate.month, tmpdate.day) + heading = Heading(title=u'Refactor the code', active_date=odate) + self.assertTrue(is_within_week(heading)) + + # in the past + tmpdate = date.today() - timedelta(days=105) + odate = OrgDate(True, tmpdate.year, tmpdate.month, tmpdate.day) + heading = Heading(title=u'Refactor the code', active_date=odate) + self.assertTrue(is_within_week(heading)) + + def test_is_within_week_with_orgdatetime(self): + # to far in the future + tmp = date.today() + timedelta(days=1000) + odate = OrgDateTime(True, tmp.year, tmp.month, tmp.day, 10, 10) + heading = Heading(title=u'Refactor the code', active_date=odate) + self.assertFalse(is_within_week(heading)) + + # within a week + tmpdate = date.today() + timedelta(days=5) + odate = OrgDateTime(True, tmpdate.year, tmpdate.month, tmpdate.day, 1, 0) + heading = Heading(title=u'Refactor the code', active_date=odate) + self.assertTrue(is_within_week(heading)) + + # in the past + tmpdate = date.today() - timedelta(days=5) + odate = OrgDateTime(True, tmpdate.year, tmpdate.month, tmpdate.day, 1, 0) + heading = Heading(title=u'Refactor the code', active_date=odate) + self.assertTrue(is_within_week(heading)) + + def test_filter_items(self): + # only headings with date and todo should be returned + vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = \ + [u_encode(u'TODO'), u_encode(u'STARTED'), u_encode(u'|'), u_encode(u'DONE')] + tmpdate = date.today() + odate = OrgDate(True, tmpdate.year, tmpdate.month, tmpdate.day) + tmp_head = Heading(title=u'Refactor the code', todo=u'TODO', active_date=odate) + tmp_head_01 = Heading(title=u'Refactor the code', todo=u'STARTED', active_date=odate) + # TODO add more tests + headings = [tmp_head, tmp_head_01] + filtered = list(filter_items(headings, + [contains_active_date, contains_active_todo])) + + self.assertEqual(len(filtered), 2) + self.assertEqual(filtered, headings) + + # try a longer list + headings = headings * 3 + filtered = list(filter_items(headings, + [contains_active_date, contains_active_todo])) + + self.assertEqual(len(filtered), 6) + self.assertEqual(filtered, headings) + + # date does not contain all needed fields thus gets ignored + tmpdate = date.today() + odate = OrgDate(True, tmpdate.year, tmpdate.month, tmpdate.day) + tmp_head = Heading(title=u'Refactor the code', active_date=odate) + headings = [tmp_head] + filtered = list(filter_items(headings, [contains_active_date, + contains_active_todo])) + self.assertEqual([], filtered) + + def test_filter_items_with_some_todos_and_dates(self): + u""" + Only the headings with todo and dates should be retunrned. + """ + tmp = [u"* TODO OrgMode Demo und Tests" + u"<2011-08-22 Mon>"] + headings = [Heading.parse_heading_from_data(tmp, [u'TODO'])] + filtered = list(filter_items(headings, + [is_within_week_and_active_todo])) + self.assertEqual(len(filtered), 1) + self.assertEqual(headings, filtered) + + tmp = [Heading.parse_heading_from_data([u"** DONE something <2011-08-10 Wed>"], [u'TODO']), + Heading.parse_heading_from_data([u"*** TODO rsitenaoritns more <2011-08-25 Thu>"], [u'TODO']), + Heading.parse_heading_from_data([u"*** DONE some more <2011-08-25 Thu>"], [u'TODO']), + Heading.parse_heading_from_data([u"*** TODO some more <2011-08-25 Thu>"], [u'TODO']), + Heading.parse_heading_from_data([u"** DONE something2 <2011-08-10 Wed>"], [u'TODO']) + ] + for h in tmp: + headings.append(h) + + filtered = list(filter_items(headings, + [is_within_week_and_active_todo])) + self.assertEqual(len(filtered), 3) + self.assertEqual(filtered, [headings[0], headings[2], headings[4]]) + + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(AgendaFilterTestCase) + + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/tests/test_libbase.py b/pack/acp/start/vim-orgmode/tests/test_libbase.py new file mode 100644 index 0000000..b27ecfd --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_libbase.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +import unittest +import sys +sys.path.append(u'../ftplugin') + +from orgmode.liborgmode.base import Direction, get_domobj_range +from orgmode.liborgmode.headings import Heading + + +class LibBaseTestCase(unittest.TestCase): + + def setUp(self): + self.case1 = """ +* head1 + heading body + for testing +* head2 +** head3 + """.split("\n") + + def test_base_functions(self): + # direction FORWARD + (start, end) = get_domobj_range(content=self.case1, position=1, identify_fun=Heading.identify_heading) + self.assertEqual((start, end), (1, 3)) + (start, end) = get_domobj_range(content=self.case1, position=3, direction=Direction.BACKWARD, \ + identify_fun=Heading.identify_heading) + self.assertEqual((start, end), (1, 3)) + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase( + LibBaseTestCase) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/tests/test_libcheckbox.py b/pack/acp/start/vim-orgmode/tests/test_libcheckbox.py new file mode 100644 index 0000000..b2f485d --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_libcheckbox.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- + +import unittest +import sys +sys.path.append(u'../ftplugin') + +import vim +from orgmode.liborgmode.checkboxes import Checkbox +from orgmode._vim import ORGMODE + +from orgmode.py3compat.encode_compatibility import * + +def set_vim_buffer(buf=None, cursor=(2, 0), bufnr=0): + if buf is None: + buf = [] + vim.current.buffer[:] = buf + vim.current.window.cursor = cursor + vim.current.buffer.number = bufnr + + +class CheckboxTestCase(unittest.TestCase): + + def setUp(self): + counter = 0 + vim.CMDHISTORY = [] + vim.CMDRESULTS = {} + vim.EVALHISTORY = [] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')], + u_encode(u'exists("g:org_improve_split_heading")'): u_encode(u'0'), + u_encode(u'exists("b:org_improve_split_heading")'): u_encode(u'0'), + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("b:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u'b:changedtick'): u_encode(u'%d' % counter), + u_encode(u'&ts'): u_encode(u'8'), + u_encode(u'exists("g:org_tag_column")'): u_encode(u'0'), + u_encode(u'exists("b:org_tag_column")'): u_encode(u'0'), + u_encode(u"v:count"): u_encode(u'0')} + + self.c1 = """ +* heading1 [/] + - [-] checkbox1 [%] + - [X] checkbox2 + - [ ] checkbox3 + - [X] checkbox4 +""".split("\n") + + self.c2 = """ +* heading1 + - [ ] checkbox1 + - [ ] checkbox2 + - [ ] checkbox3 + - [ ] checkbox4 + - [ ] checkbox5 + - [ ] checkbox6 +""".split("\n") + + def test_init(self): + # test initialize Checkbox + c = Checkbox(level=1, title="checkbox1") + self.assertEqual(str(c), " - [ ] checkbox1") + c = Checkbox(level=3, title="checkbox2", status="[X]") + self.assertEqual(str(c), " - [X] checkbox2") + + def test_basic(self): + bufnr = 1 + set_vim_buffer(buf=self.c1, bufnr=bufnr) + h = ORGMODE.get_document(bufnr=bufnr).current_heading() + h.init_checkboxes() + + c = h.current_checkbox(position=2) + self.assertEqual(str(c), self.c1[2]) + self.assertFalse(c.are_children_all(Checkbox.STATUS_ON)) + self.assertTrue(c.is_child_one(Checkbox.STATUS_OFF)) + self.assertFalse(c.are_siblings_all(Checkbox.STATUS_ON)) + + for child in c.all_children(): + pass + for sibling in c.all_siblings(): + pass + c = h.current_checkbox(position=3) + new_checkbox = c.copy() + self.assertEqual(str(c), self.c1[3]) + c.get_parent_list() + c.get_index_in_parent_list() + + def test_identify(self): + # test identify_checkbox + self.assertEqual(Checkbox.identify_checkbox(self.c1[2]), 2) + self.assertEqual(Checkbox.identify_checkbox(self.c1[3]), 8) + # check for corner case + self.assertEqual(Checkbox.identify_checkbox(" - [ ]"), 1) + + def test_toggle(self): + bufnr = 2 + # test init_checkboxes + set_vim_buffer(buf=self.c1, bufnr=bufnr) + h = ORGMODE.get_document(bufnr=bufnr).current_heading() + h.init_checkboxes() + + # toggle checkbox + c = h.current_checkbox(position=4) + c.toggle() + self.assertEqual(str(c), " - [X] checkbox3") + c.toggle() + self.assertEqual(str(c), " - [ ] checkbox3") + + (total, on) = c.all_siblings_status() + self.assertEqual((total, on), (2, 1)) + + def test_subtasks(self): + bufnr = 3 + set_vim_buffer(buf=self.c1, bufnr=bufnr) + h = ORGMODE.get_document(bufnr=bufnr).current_heading() + h.init_checkboxes() + c = h.current_checkbox(position=3) + c.toggle() + c = h.current_checkbox(position=2) + (total, on) = c.all_siblings_status() + c.update_subtasks(total=total, on=on) + self.assertEqual(str(c), " - [-] checkbox1 [50%]") + + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(CheckboxTestCase) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/tests/test_libheading.py b/pack/acp/start/vim-orgmode/tests/test_libheading.py new file mode 100644 index 0000000..335b8dc --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_libheading.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- + +import unittest +import sys +sys.path.append(u'../ftplugin') + +from orgmode.liborgmode.headings import Heading +from orgmode.liborgmode.orgdate import OrgDate +from orgmode.liborgmode.orgdate import OrgDateTime + + +class TestHeadingRecognizeDatesInHeading(unittest.TestCase): + + def setUp(self): + self.allowed_todo_states = ["TODO"] + + tmp = ["* This heading is earlier <2011-08-24 Wed>"] + self.h1 = Heading.parse_heading_from_data(tmp, self.allowed_todo_states) + + tmp = ["* This heading is later <2011-08-25 Thu>"] + self.h2 = Heading.parse_heading_from_data(tmp, self.allowed_todo_states) + + tmp = ["* This heading is later <2011-08-25 Thu 10:20>"] + self.h2_datetime = Heading.parse_heading_from_data(tmp, self.allowed_todo_states) + + tmp = ["* This heading is later <2011-08-26 Fri 10:20>"] + self.h3 = Heading.parse_heading_from_data(tmp, self.allowed_todo_states) + + tmp = ["* This heading has no date and should be later than the rest"] + self.h_no_date = Heading.parse_heading_from_data(tmp, + self.allowed_todo_states) + + def test_heading_parsing_no_date(self): + """"" + 'text' doesn't contain any valid date. + """ + text = ["* TODO This is a test :hallo:"] + h = Heading.parse_heading_from_data(text, self.allowed_todo_states) + self.assertEqual(None, h.active_date) + + text = ["* TODO This is a test <2011-08-25>"] + h = Heading.parse_heading_from_data(text, self.allowed_todo_states) + self.assertEqual(None, h.active_date) + + text = ["* TODO This is a test <2011-08-25 Wednesday>"] + h = Heading.parse_heading_from_data(text, self.allowed_todo_states) + self.assertEqual(None, h.active_date) + + text = ["* TODO This is a test <20110825>"] + h = Heading.parse_heading_from_data(text, self.allowed_todo_states) + self.assertEqual(None, h.active_date) + + def test_heading_parsing_with_date(self): + """"" + 'text' does contain valid dates. + """ + # orgdate + text = ["* TODO This is a test <2011-08-24 Wed> :hallo:"] + odate = OrgDate(True, 2011, 8, 24) + h = Heading.parse_heading_from_data(text, self.allowed_todo_states) + self.assertEqual(odate, h.active_date) + + # orgdatetime + text = ["* TODO This is a test <2011-08-25 Thu 10:10> :hallo:"] + odate = OrgDateTime(True, 2011, 8, 25, 10, 10) + h = Heading.parse_heading_from_data(text, self.allowed_todo_states) + self.assertEqual(odate, h.active_date) + + def test_heading_parsing_with_date_and_body(self): + """"" + 'text' contains valid dates (in the body). + """ + # orgdatetime + text = ["* TODO This is a test <2011-08-25 Thu 10:10> :hallo:", + "some body text", + "some body text"] + h = Heading.parse_heading_from_data(text, self.allowed_todo_states) + self.assertTrue(isinstance(h.active_date, OrgDateTime)) + self.assertEqual("<2011-08-25 Thu 10:10>", str(h.active_date)) + + text = ["* TODO This is a test :hallo:", + "some body text", + "some body text<2011-08-25 Thu 10:10>"] + h = Heading.parse_heading_from_data(text, self.allowed_todo_states) + self.assertTrue(isinstance(h.active_date, OrgDateTime)) + self.assertEqual("<2011-08-25 Thu 10:10>", str(h.active_date)) + + text = ["* TODO This is a test :hallo:", + "some body text <2011-08-24 Wed>", + "some body text<2011-08-25 Thu 10:10>"] + h = Heading.parse_heading_from_data(text, self.allowed_todo_states) + odate = OrgDate(True, 2011, 8, 24) + self.assertEqual(odate, h.active_date) + + def test_less_than_for_dates_in_heading(self): + self.assertTrue(self.h1 < self.h2) + self.assertTrue(self.h1 < self.h3) + self.assertTrue(self.h1 < self.h_no_date) + self.assertTrue(self.h2 < self.h_no_date) + self.assertTrue(self.h2 < self.h3) + self.assertTrue(self.h3 < self.h_no_date) + + self.assertFalse(self.h2 < self.h1) + self.assertFalse(self.h3 < self.h2) + + def test_less_equal_for_dates_in_heading(self): + self.assertTrue(self.h1 <= self.h2) + self.assertTrue(self.h1 <= self.h_no_date) + self.assertTrue(self.h2 <= self.h_no_date) + self.assertTrue(self.h2 <= self.h2_datetime) + self.assertTrue(self.h2 <= self.h3) + + def test_greate_than_for_dates_in_heading(self): + self.assertTrue(self.h2 > self.h1) + self.assertTrue(self.h_no_date > self.h1) + self.assertTrue(self.h_no_date > self.h2) + + self.assertFalse(self.h2 > self.h2_datetime) + + def test_greate_equal_for_dates_in_heading(self): + self.assertTrue(self.h2 >= self.h1) + self.assertTrue(self.h_no_date >= self.h1) + self.assertTrue(self.h_no_date >= self.h2) + self.assertTrue(self.h2 >= self.h2_datetime) + + def test_sorting_of_headings(self): + """Headings should be sortable.""" + self.assertEqual([self.h1, self.h2], sorted([self.h2, self.h1])) + + self.assertEqual([self.h1, self.h2_datetime], + sorted([self.h2_datetime, self.h1])) + + self.assertEqual([self.h2_datetime, self.h2], + sorted([self.h2_datetime, self.h2])) + + self.assertEqual([self.h1, self.h2], sorted([self.h1, self.h2])) + + self.assertEqual([self.h1, self.h_no_date], + sorted([self.h1, self.h_no_date])) + + self.assertEqual([self.h1, self.h_no_date], + sorted([self.h_no_date, self.h1])) + + self.assertEqual([self.h1, self.h2, self.h_no_date], + sorted([self.h2, self.h_no_date, self.h1])) + + self.assertEqual( + [self.h1, self.h2_datetime, self.h2, self.h3, self.h_no_date], + sorted([self.h2_datetime, self.h3, self.h2, self.h_no_date, self.h1])) + + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase( + TestHeadingRecognizeDatesInHeading) + +# vim: set noexpandtab: diff --git a/pack/acp/start/vim-orgmode/tests/test_liborgdate.py b/pack/acp/start/vim-orgmode/tests/test_liborgdate.py new file mode 100644 index 0000000..1b37351 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_liborgdate.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + + +import sys +import unittest +from datetime import date + +sys.path.append(u'../ftplugin') +from orgmode.liborgmode.orgdate import OrgDate + +from orgmode.py3compat.unicode_compatibility import * + +class OrgDateTestCase(unittest.TestCase): + u""" + Tests all the functionality of the OrgDate + """ + + def setUp(self): + self.date = date(2011, 8, 29) + self.year = 2011 + self.month = 8 + self.day = 29 + self.text = u'<2011-08-29 Mon>' + self.textinactive = u'[2011-08-29 Mon]' + + def test_OrgDate_ctor_active(self): + u"""OrdDate should be created.""" + today = date.today() + od = OrgDate(True, today.year, today.month, today.day) + self.assertTrue(isinstance(od, OrgDate)) + self.assertTrue(od.active) + + def test_OrgDate_ctor_inactive(self): + u"""OrdDate should be created.""" + today = date.today() + od = OrgDate(False, today.year, today.month, today.day) + self.assertTrue(isinstance(od, OrgDate)) + self.assertFalse(od.active) + + def test_OrdDate_str_active(self): + u"""Representation of OrgDates""" + od = OrgDate(True, self.year, self.month, self.day) + self.assertEqual(self.text, unicode(od)) + + def test_OrdDate_str_inactive(self): + od = OrgDate(False, self.year, self.month, self.day) + self.assertEqual(self.textinactive, unicode(od)) + + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(OrgDateTestCase) + +# vi: noexpandtab diff --git a/pack/acp/start/vim-orgmode/tests/test_liborgdate_parsing.py b/pack/acp/start/vim-orgmode/tests/test_liborgdate_parsing.py new file mode 100644 index 0000000..ae49f0d --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_liborgdate_parsing.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- + + +import sys +import unittest + +sys.path.append(u'../ftplugin') +from orgmode.liborgmode.orgdate import get_orgdate +from orgmode.liborgmode.orgdate import OrgDate +from orgmode.liborgmode.orgdate import OrgDateTime +from orgmode.liborgmode.orgdate import OrgTimeRange + +from orgmode.py3compat.unicode_compatibility import * + +class OrgDateParsingTestCase(unittest.TestCase): + u""" + Tests the functionality of the parsing function of OrgDate. + + Mostly function get_orgdate(). + """ + + def setUp(self): + self.text = u'<2011-08-29 Mon>' + self.textinactive = u'[2011-08-29 Mon]' + + def test_get_orgdate_parsing_active(self): + u""" + get_orgdate should recognize all orgdates in a given text + """ + result = get_orgdate(self.text) + self.assertNotEqual(result, None) + self.assertTrue(isinstance(result, OrgDate)) + self.assertTrue(isinstance(get_orgdate(u"<2011-08-30 Tue>"), OrgDate)) + self.assertEqual(get_orgdate(u"<2011-08-30 Tue>").year, 2011) + self.assertEqual(get_orgdate(u"<2011-08-30 Tue>").month, 8) + self.assertEqual(get_orgdate(u"<2011-08-30 Tue>").day, 30) + self.assertTrue(get_orgdate(u"<2011-08-30 Tue>").active) + + datestr = u"This date <2011-08-30 Tue> is embedded" + self.assertTrue(isinstance(get_orgdate(datestr), OrgDate)) + + + def test_get_orgdatetime_parsing_active(self): + u""" + get_orgdate should recognize all orgdatetimes in a given text + """ + result = get_orgdate(u"<2011-09-12 Mon 10:20>") + self.assertNotEqual(result, None) + self.assertTrue(isinstance(result, OrgDateTime)) + self.assertEqual(result.year, 2011) + self.assertEqual(result.month, 9) + self.assertEqual(result.day, 12) + self.assertEqual(result.hour, 10) + self.assertEqual(result.minute, 20) + self.assertTrue(result.active) + + result = get_orgdate(u"some datetime <2011-09-12 Mon 10:20> stuff") + self.assertTrue(isinstance(result, OrgDateTime)) + + + def test_get_orgtimerange_parsing_active(self): + u""" + get_orgdate should recognize all orgtimeranges in a given text + """ + daterangestr = u"<2011-09-12 Mon>--<2011-09-13 Tue>" + result = get_orgdate(daterangestr) + self.assertNotEqual(result, None) + self.assertTrue(isinstance(result, OrgTimeRange)) + self.assertEqual(unicode(result), daterangestr) + self.assertTrue(result.active) + + daterangestr = u"<2011-09-12 Mon 10:20>--<2011-09-13 Tue 13:20>" + result = get_orgdate(daterangestr) + self.assertNotEqual(result, None) + self.assertTrue(isinstance(result, OrgTimeRange)) + self.assertEqual(unicode(result), daterangestr) + self.assertTrue(result.active) + + daterangestr = u"<2011-09-12 Mon 10:20-13:20>" + result = get_orgdate(daterangestr) + self.assertNotEqual(result, None) + self.assertTrue(isinstance(result, OrgTimeRange)) + self.assertEqual(unicode(result), daterangestr) + self.assertTrue(result.active) + + def test_get_orgdate_parsing_inactive(self): + u""" + get_orgdate should recognize all inactive orgdates in a given text + """ + result = get_orgdate(self.textinactive) + self.assertNotEqual(result, None) + self.assertTrue(isinstance(result, OrgDate)) + self.assertTrue(isinstance(get_orgdate(u"[2011-08-30 Tue]"), OrgDate)) + self.assertEqual(get_orgdate(u"[2011-08-30 Tue]").year, 2011) + self.assertEqual(get_orgdate(u"[2011-08-30 Tue]").month, 8) + self.assertEqual(get_orgdate(u"[2011-08-30 Tue]").day, 30) + self.assertFalse(get_orgdate(u"[2011-08-30 Tue]").active) + + datestr = u"This date [2011-08-30 Tue] is embedded" + self.assertTrue(isinstance(get_orgdate(datestr), OrgDate)) + + def test_get_orgdatetime_parsing_passive(self): + u""" + get_orgdate should recognize all orgdatetimes in a given text + """ + result = get_orgdate(u"[2011-09-12 Mon 10:20]") + self.assertNotEqual(result, None) + self.assertTrue(isinstance(result, OrgDateTime)) + self.assertEqual(result.year, 2011) + self.assertEqual(result.month, 9) + self.assertEqual(result.day, 12) + self.assertEqual(result.hour, 10) + self.assertEqual(result.minute, 20) + self.assertFalse(result.active) + + result = get_orgdate(u"some datetime [2011-09-12 Mon 10:20] stuff") + self.assertTrue(isinstance(result, OrgDateTime)) + + def test_get_orgdate_parsing_with_list_of_texts(self): + u""" + get_orgdate should return the first date in the list. + """ + datelist = [u"<2011-08-29 Mon>"] + result = get_orgdate(datelist) + self.assertNotEquals(result, None) + self.assertTrue(isinstance(result, OrgDate)) + self.assertEqual(result.year, 2011) + self.assertEqual(result.month, 8) + self.assertEqual(result.day, 29) + + datelist = [u"<2011-08-29 Mon>", + u"<2012-03-30 Fri>"] + result = get_orgdate(datelist) + self.assertNotEquals(result, None) + self.assertTrue(isinstance(result, OrgDate)) + self.assertEqual(result.year, 2011) + self.assertEqual(result.month, 8) + self.assertEqual(result.day, 29) + + datelist = [u"some <2011-08-29 Mon>text", + u"<2012-03-30 Fri> is here"] + result = get_orgdate(datelist) + self.assertNotEquals(result, None) + self.assertTrue(isinstance(result, OrgDate)) + self.assertEqual(result.year, 2011) + self.assertEqual(result.month, 8) + self.assertEqual(result.day, 29) + + datelist = [u"here is no date", + u"some <2011-08-29 Mon>text", + u"<2012-03-30 Fri> is here"] + result = get_orgdate(datelist) + self.assertNotEquals(result, None) + self.assertTrue(isinstance(result, OrgDate)) + self.assertEqual(result.year, 2011) + self.assertEqual(result.month, 8) + self.assertEqual(result.day, 29) + + datelist = [u"here is no date", + u"some <2011-08-29 Mon 20:10> text", + u"<2012-03-30 Fri> is here"] + result = get_orgdate(datelist) + self.assertNotEquals(result, None) + self.assertTrue(isinstance(result, OrgDateTime)) + self.assertEqual(result.year, 2011) + self.assertEqual(result.month, 8) + self.assertEqual(result.day, 29) + self.assertEqual(result.hour, 20) + self.assertEqual(result.minute, 10) + + def test_get_orgdate_parsing_with_invalid_input(self): + self.assertEquals(get_orgdate(u"NONSENSE"), None) + self.assertEquals(get_orgdate(u"No D<2011- Date 08-29 Mon>"), None) + self.assertEquals(get_orgdate(u"2011-08-r9 Mon]"), None) + self.assertEquals(get_orgdate(u"<2011-08-29 Mon"), None) + self.assertEquals(get_orgdate(u"<2011-08-29 Mon]"), None) + self.assertEquals(get_orgdate(u"2011-08-29 Mon"), None) + self.assertEquals(get_orgdate(u"2011-08-29"), None) + self.assertEquals(get_orgdate(u"2011-08-29 mon"), None) + self.assertEquals(get_orgdate(u"<2011-08-29 mon>"), None) + + self.assertEquals(get_orgdate(u"wrong date embedded <2011-08-29 mon>"), None) + self.assertEquals(get_orgdate(u"wrong date <2011-08-29 mon>embedded "), None) + + def test_get_orgdate_parsing_with_invalid_dates(self): + u""" + Something like <2011-14-29 Mon> (invalid dates, they don't exist) + should not be parsed + """ + datestr = u"<2011-14-30 Tue>" + self.assertEqual(get_orgdate(datestr), None) + + datestr = u"<2012-03-40 Tue>" + self.assertEqual(get_orgdate(datestr), None) + + datestr = u"<2012-03-40 Tue 24:70>" + self.assertEqual(get_orgdate(datestr), None) + + def test_get_orgdate_parsing_with_utf8(self): + u""" + get_orgdate should recognize all orgdates within a given utf-8 text + """ + result = get_orgdate(u'<2016-05-07 Sáb>') + self.assertNotEqual(result, None) + self.assertTrue(isinstance(result, OrgDate)) + self.assertEqual(result.year, 2016) + self.assertEqual(result.month, 5) + self.assertEqual(result.day, 7) + self.assertTrue(result.active) + + datestr = u"This date <2016-05-07 Sáb> is embedded" + self.assertTrue(isinstance(get_orgdate(datestr), OrgDate)) + + result = get_orgdate(u'[2016-05-07 Sáb]') + self.assertFalse(result.active) + + datestr = u"This date [2016-05-07 Sáb] is embedded" + self.assertTrue(isinstance(get_orgdate(datestr), OrgDate)) + + def test_get_orgdatetime_parsing_with_utf8(self): + u""" + get_orgdate should recognize all orgdatetimes in a given utf-8 text + """ + result = get_orgdate(u"<2016-05-07 Sáb 10:20>") + self.assertNotEqual(result, None) + self.assertTrue(isinstance(result, OrgDateTime)) + self.assertEqual(result.year, 2016) + self.assertEqual(result.month, 5) + self.assertEqual(result.day, 7) + self.assertEqual(result.hour, 10) + self.assertEqual(result.minute, 20) + self.assertTrue(result.active) + + result = get_orgdate(u"some datetime <2016-05-07 Sáb 10:20> stuff") + self.assertTrue(isinstance(result, OrgDateTime)) + + result = get_orgdate(u"[2016-05-07 Sáb 10:20]") + self.assertFalse(result.active) + + result = get_orgdate(u"some datetime [2016-05-07 Sáb 10:20] stuff") + self.assertTrue(isinstance(result, OrgDateTime)) + + + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(OrgDateParsingTestCase) + +# vim: noexpandtab diff --git a/pack/acp/start/vim-orgmode/tests/test_liborgdate_utf8.py b/pack/acp/start/vim-orgmode/tests/test_liborgdate_utf8.py new file mode 100644 index 0000000..079d788 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_liborgdate_utf8.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +import sys +import unittest +import locale +import threading + +from datetime import date +from contextlib import contextmanager + +from orgmode.py3compat.unicode_compatibility import * + +sys.path.append(u'../ftplugin') +from orgmode.liborgmode.orgdate import OrgDate + +class OrgDateUtf8TestCase(unittest.TestCase): + u""" + Tests OrgDate with utf-8 enabled locales + """ + LOCALE_LOCK = threading.Lock() + UTF8_LOCALE = "pt_BR.utf-8" + + @contextmanager + def setlocale(self, name): + with self.LOCALE_LOCK: + saved = locale.setlocale(locale.LC_ALL) + try: + yield locale.setlocale(locale.LC_ALL, name) + finally: + locale.setlocale(locale.LC_ALL, saved) + + def setUp(self): + self.year = 2016 + self.month = 5 + self.day = 7 + self.text = u'<2016-05-07 Sáb>' + self.textinactive = u'[2016-05-07 Sáb]' + + def test_OrdDate_str_unicode_active(self): + with self.setlocale(self.UTF8_LOCALE): + od = OrgDate(True, self.year, self.month, self.day) + self.assertEqual(self.text, unicode(od)) + + def test_OrdDate_str_unicode_inactive(self): + with self.setlocale(self.UTF8_LOCALE): + od = OrgDate(False, self.year, self.month, self.day) + self.assertEqual(self.textinactive, unicode(od)) + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(OrgDateUtf8TestCase) + +# vi: noexpandtab diff --git a/pack/acp/start/vim-orgmode/tests/test_liborgdatetime.py b/pack/acp/start/vim-orgmode/tests/test_liborgdatetime.py new file mode 100644 index 0000000..315dfe0 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_liborgdatetime.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +import sys +import unittest +from datetime import datetime + +sys.path.append(u'../ftplugin') +from orgmode.liborgmode.orgdate import OrgDateTime + +from orgmode.py3compat.unicode_compatibility import * + +class OrgDateTimeTestCase(unittest.TestCase): + u""" + Tests all the functionality of the OrgDateTime + """ + + def test_OrgDateTime_ctor_active(self): + u"""OrdDateTime should be created.""" + today = datetime.today() + odt = OrgDateTime(True, today.year, today.month, today.day, today.hour, + today.minute) + self.assertTrue(isinstance(odt, OrgDateTime)) + self.assertTrue(odt.active) + + def test_OrgDateTime_ctor_inactive(self): + u"""OrdDateTime should be created.""" + today = datetime.today() + odt = OrgDateTime(False, today.year, today.month, today.day, today.hour, + today.minute) + self.assertTrue(isinstance(odt, OrgDateTime)) + self.assertFalse(odt.active) + + def test_OrdDateTime_str_active(self): + u"""Representation of OrgDateTime""" + t = 2011, 9, 8, 10, 20 + odt = OrgDateTime(False, t[0], t[1], t[2], t[3], t[4]) + self.assertEqual(u"[2011-09-08 Thu 10:20]", unicode(odt)) + + def test_OrdDateTime_str_inactive(self): + u"""Representation of OrgDateTime""" + t = 2011, 9, 8, 10, 20 + odt = OrgDateTime(True, t[0], t[1], t[2], t[3], t[4]) + self.assertEqual(u"<2011-09-08 Thu 10:20>", unicode(odt)) + + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(OrgDateTimeTestCase) + + +# vim: noexpandtab diff --git a/pack/acp/start/vim-orgmode/tests/test_liborgtimerange.py b/pack/acp/start/vim-orgmode/tests/test_liborgtimerange.py new file mode 100644 index 0000000..5439c82 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_liborgtimerange.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- + + +import sys +import unittest +from datetime import date +from datetime import datetime + +sys.path.append(u'../ftplugin') +from orgmode.liborgmode.orgdate import OrgTimeRange + + +class OrgTimeRangeTestCase(unittest.TestCase): + + def setUp(self): + self.date = date(2011, 8, 29) + self.year = 2011 + self.month = 8 + self.day = 29 + self.text = '<2011-08-29 Mon>' + self.textinactive = '[2011-08-29 Mon]' + + def test_OrgTimeRange_ctor_active(self): + u""" + timerange should be created. + """ + start = date(2011, 9 , 12) + end = date(2011, 9 , 13) + timerange = OrgTimeRange(True, start, end) + self.assertTrue(isinstance(timerange, OrgTimeRange)) + self.assertTrue(timerange.active) + + def test_OrgTimeRange_ctor_inactive(self): + u""" + timerange should be created. + """ + start = date(2011, 9 , 12) + end = date(2011, 9 , 13) + timerange = OrgTimeRange(False, start, end) + self.assertTrue(isinstance(timerange, OrgTimeRange)) + self.assertFalse(timerange.active) + + def test_OrdDate_str_active(self): + u"""Representation of OrgDates""" + start = date(2011, 9 , 12) + end = date(2011, 9 , 13) + timerange = OrgTimeRange(True, start, end) + expected = "<2011-09-12 Mon>--<2011-09-13 Tue>" + self.assertEqual(str(timerange), expected) + + start = datetime(2011, 9 , 12, 20, 00) + end = datetime(2011, 9 , 13, 21, 59) + timerange = OrgTimeRange(True, start, end) + expected = "<2011-09-12 Mon 20:00>--<2011-09-13 Tue 21:59>" + self.assertEqual(str(timerange), expected) + + start = datetime(2011, 9 , 12, 20, 00) + end = datetime(2011, 9 , 12, 21, 00) + timerange = OrgTimeRange(True, start, end) + expected = "<2011-09-12 Mon 20:00-21:00>" + self.assertEqual(str(timerange), expected) + + def test_OrdDate_str_inactive(self): + u"""Representation of OrgDates""" + start = date(2011, 9 , 12) + end = date(2011, 9 , 13) + timerange = OrgTimeRange(False, start, end) + expected = "[2011-09-12 Mon]--[2011-09-13 Tue]" + self.assertEqual(str(timerange), expected) + + start = datetime(2011, 9 , 12, 20, 00) + end = datetime(2011, 9 , 13, 21, 59) + timerange = OrgTimeRange(False, start, end) + expected = "[2011-09-12 Mon 20:00]--[2011-09-13 Tue 21:59]" + self.assertEqual(str(timerange), expected) + + start = datetime(2011, 9 , 12, 20, 00) + end = datetime(2011, 9 , 12, 21, 00) + timerange = OrgTimeRange(False, start, end) + expected = "[2011-09-12 Mon 20:00-21:00]" + self.assertEqual(str(timerange), expected) + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(OrgTimeRangeTestCase) + +# vim: noexpandtab diff --git a/pack/acp/start/vim-orgmode/tests/test_plugin_date.py b/pack/acp/start/vim-orgmode/tests/test_plugin_date.py new file mode 100644 index 0000000..86b1838 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_plugin_date.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- + +from __future__ import print_function + +import unittest +import sys +sys.path.append(u'../ftplugin') + +from datetime import date +from datetime import datetime + +from orgmode.plugins.Date import Date + + +class DateTestCase(unittest.TestCase): + u"""Tests all the functionality of the Date plugin. + + Also see: + http://orgmode.org/manual/The-date_002ftime-prompt.html#The-date_002ftime-prompt + """ + + def setUp(self): + self.d = date(2011, 5, 22) + + def test_modify_time_with_None(self): + # no modification should happen + res = Date._modify_time(self.d, None) + self.assertEquals(self.d, res) + + def test_modify_time_with_dot(self): + # no modification should happen + res = Date._modify_time(self.d, u'.') + self.assertEquals(self.d, res) + + def test_modify_time_with_given_relative_days(self): + # modifier and expected result + test_data = [(u'+0d', self.d), + (u'+1d', date(2011, 5, 23)), + (u'+2d', date(2011, 5, 24)), + (u'+7d', date(2011, 5, 29)), + (u'+9d', date(2011, 5, 31)), + (u'+10d', date(2011, 6, 1)), + (u'7d', self.d)] # wrong format: plus is missing + + for modifier, expected in test_data: + self.assertEquals(expected, Date._modify_time(self.d, modifier)) + + def test_modify_time_with_given_relative_days_without_d(self): + # modifier and expected result + test_data = [(u'+0', self.d), + (u'+1', date(2011, 5, 23)), + (u'+2', date(2011, 5, 24)), + (u'+7', date(2011, 5, 29)), + (u'+9', date(2011, 5, 31)), + (u'+10', date(2011, 6, 1))] + + for modifier, expected in test_data: + result = Date._modify_time(self.d, modifier) + self.assertEquals(expected, result) + + def test_modify_time_with_given_relative_weeks(self): + # modifier and expected result + test_data = [(u'+1w', date(2011, 5, 29)), + (u'+2w', date(2011, 6, 5)), + (u'+3w', date(2011, 6, 12)), + (u'+3w', date(2011, 6, 12)), + (u'+0w', self.d), + (u'3w', self.d), # wrong format + (u'+w', self.d)] # wrong format + + for modifier, expected in test_data: + self.assertEquals(expected, Date._modify_time(self.d, modifier)) + + def test_modify_time_with_given_relative_months(self): + test_data = [(u'+0m', self.d), + (u'+1m', date(2011, 6, 22)), + (u'+2m', date(2011, 7, 22))] + + for modifier, expected in test_data: + self.assertEquals(expected, Date._modify_time(self.d, modifier)) + + def test_modify_time_with_given_relative_years(self): + test_data = [(u'+1y', date(2012, 5, 22)), + (u'+10y', date(2021, 5, 22)), + (u'+0y', self.d)] + + for modifier, expected in test_data: + self.assertEquals(expected, Date._modify_time(self.d, modifier)) + + + def test_modify_time_with_given_weekday(self): + # use custom day instead of self.d to ease testing + cust_day = date(2011, 5, 25) # it's a Wednesday + #print(cust_day.weekday()) # 2 + test_data = [(u'Thu', date(2011, 5, 26)), + (u'thu', date(2011, 5, 26)), + (u'tHU', date(2011, 5, 26)), + (u'THU', date(2011, 5, 26)), + (u'Fri', date(2011, 5, 27)), + (u'sat', date(2011, 5, 28)), + (u'sun', date(2011, 5, 29)), + (u'mon', date(2011, 5, 30)), + (u'tue', date(2011, 5, 31)), + (u'wed', date(2011, 6, 1))] + + for modifier, expected in test_data: + self.assertEquals(expected, Date._modify_time(cust_day, modifier)) + + def test_modify_time_with_month_and_day(self): + cust_date = date(2006, 6, 13) + test_data = [(u'sep 15', date(2006, 9, 15)), + (u'Sep 15', date(2006, 9, 15)), + (u'SEP 15', date(2006, 9, 15)), + (u'feb 15', date(2007, 2, 15)), + (u'jan 1', date(2007, 1, 1)), + (u'7/5', date(2006, 7, 5)), + (u'2/5', date(2007, 2, 5)),] + + for modifier, expected in test_data: + self.assertEquals(expected, Date._modify_time(cust_date, modifier)) + + def test_modify_time_with_time(self): + cust_date = date(2006, 6, 13) + test_data = [(u'12:45', datetime(2006, 6, 13, 12, 45)), + (u'1:45', datetime(2006, 6, 13, 1, 45)), + (u'1:05', datetime(2006, 6, 13, 1, 5)),] + + for modifier, expected in test_data: + res = Date._modify_time(cust_date, modifier) + self.assertTrue(isinstance(res, datetime)) + self.assertEquals(expected, res) + + def test_modify_time_with_full_dates(self): + result = Date._modify_time(self.d, u'2011-01-12') + expected = date(2011, 1, 12) + self.assertEquals(expected, result) + + reults = Date._modify_time(self.d, u'2015-03-12') + expected = date(2015, 3, 12) + self.assertEquals(expected, reults) + + cust_date = date(2006, 6, 13) + test_data = [(u'3-2-5', date(2003, 2, 5)), + (u'12-2-28', date(2012, 2, 28)), + (u'2/5/3', date(2003, 2, 5)), + (u'sep 12 9', date(2009, 9, 12)), + (u'jan 2 99', date(2099, 1, 2)),] + + for modifier, expected in test_data: + self.assertEquals(expected, Date._modify_time(cust_date, modifier)) + + def test_modify_time_with_only_days(self): + cust_date = date(2006, 6, 13) + test_data = [(u'14', date(2006, 6, 14)), + (u'12', date(2006, 7, 12)), + (u'1', date(2006, 7, 1)), + (u'29', date(2006, 6, 29)),] + for modifier, expected in test_data: + self.assertEquals(expected, Date._modify_time(cust_date, modifier)) + + def test_modify_time_with_day_and_time(self): + cust_date = date(2006, 6, 13) + test_data = [(u'+1 10:20', datetime(2006, 6, 14, 10, 20)), + (u'+1w 10:20', datetime(2006, 6, 20, 10, 20)), + (u'+2 10:30', datetime(2006, 6, 15, 10, 30)), + (u'+2d 10:30', datetime(2006, 6, 15, 10, 30))] + for modifier, expected in test_data: + result = Date._modify_time(cust_date, modifier) + self.assertEquals(expected, result) + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(DateTestCase) + +# vi: noexpandtab diff --git a/pack/acp/start/vim-orgmode/tests/test_plugin_edit_checkbox.py b/pack/acp/start/vim-orgmode/tests/test_plugin_edit_checkbox.py new file mode 100644 index 0000000..1c94a98 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_plugin_edit_checkbox.py @@ -0,0 +1,241 @@ +# -*- coding: utf-8 -*- + +import unittest +import sys +sys.path.append(u'../ftplugin') + +import vim + +from orgmode._vim import ORGMODE + +from orgmode.py3compat.encode_compatibility import * + +PLUGIN_NAME = u'EditCheckbox' + +bufnr = 10 + +def set_vim_buffer(buf=None, cursor=(2, 0), bufnr=0): + if buf is None: + buf = [] + vim.current.buffer[:] = buf + vim.current.window.cursor = cursor + vim.current.buffer.number = bufnr + + +counter = 0 +class EditCheckboxTestCase(unittest.TestCase): + def setUp(self): + if PLUGIN_NAME not in ORGMODE.plugins: + ORGMODE.register_plugin(PLUGIN_NAME) + self.editcheckbox = ORGMODE.plugins[PLUGIN_NAME] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')], + u_encode(u'exists("g:org_improve_split_heading")'): u_encode(u'0'), + u_encode(u'exists("b:org_improve_split_heading")'): u_encode(u'0'), + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("b:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u'b:changedtick'): u_encode(u'%d' % counter), + u_encode(u'&ts'): u_encode(u'8'), + u_encode(u'exists("g:org_tag_column")'): u_encode(u'0'), + u_encode(u'exists("b:org_tag_column")'): u_encode(u'0'), + u_encode(u"v:count"): u_encode(u'0'), + # jump to insert mode after adding heading/checkbox + u_encode(u'exists("g:org_prefer_insert_mode")'): u_encode(u'0'), + u_encode(u'exists("b:org_prefer_insert_mode")'): u_encode(u'0')} + + self.c1 = u""" +* heading1 [%] + - [ ] checkbox1 [/] + - [ ] checkbox2 + - [ ] checkbox3 + - [ ] checkbox4 + - [ ] checkbox5 + - [ ] checkbox6 + - [ ] checkbox7 + - [ ] checkbox8 +""".split(u'\n') + + self.c2 = u""" +* a checkbox list [%] + - checkbox [0%] + - [ ] test1 + - [ ] test2 + - [ ] test3 +""".split(u'\n') + + self.c3 = u""" +* heading + 1. [ ] another main task [%] + - [ ] sub task 1 + - [ ] sub task 2 + 2. [ ] another main task +""".split(u'\n') + + self.c4 = u""" +* heading +""".split(u'\n') + + self.c5 = u""" +* heading1 + 1. item + 9. item + }. item + a. item + z. item + A. item + Z. item + aa. item +""".split("\n") + + def test_toggle(self): + global bufnr + bufnr += 1 + # test on self.c1 + set_vim_buffer(buf=self.c1, cursor=(6, 0), bufnr=bufnr) + # update_checkboxes_status + self.editcheckbox.update_checkboxes_status() + self.assertEqual(vim.current.buffer[1], u"* heading1 [0%]") + # toggle + self.editcheckbox.toggle() + self.assertEqual(vim.current.buffer[5], u" - [X] checkbox4") + + bufnr += 1 + set_vim_buffer(buf=self.c1, cursor=(9, 0), bufnr=bufnr) + # toggle and check checkbox status + self.editcheckbox.toggle() + self.assertEqual(vim.current.buffer[8], u" - [X] checkbox7") + self.assertEqual(vim.current.buffer[7], u" - [-] checkbox6") + self.assertEqual(vim.current.buffer[6], u" - [-] checkbox5") + + # new_checkbox + bufnr += 1 + set_vim_buffer(buf=self.c1, cursor=(9, 0), bufnr=bufnr) + vim.current.window.cursor = (9, 0) + self.assertEqual(vim.current.buffer[9], u' - [ ] checkbox8') + self.editcheckbox.new_checkbox(below=True) + # vim's buffer behave just opposite to Python's list when inserting a + # new item. The new entry is appended in vim put prepended in Python! + self.assertEqual(vim.current.buffer[10], u' - [ ] checkbox8') + self.assertEqual(vim.current.buffer[9], u' - [ ] ') + self.editcheckbox.update_checkboxes_status() + + def test_no_status_checkbox(self): + global bufnr + bufnr += 1 + # test on self.c2 + set_vim_buffer(buf=self.c2, bufnr=bufnr) + self.assertEqual(vim.current.buffer[2], u" - checkbox [0%]") + # toggle + vim.current.window.cursor = (4, 0) + self.editcheckbox.toggle() + self.assertEqual(vim.current.buffer[3], u" - [X] test1") + + # self.editcheckbox.update_checkboxes_status() + # see if the no status checkbox update its status + self.assertEqual(vim.current.buffer[2], u" - checkbox [33%]") + + def test_number_list(self): + global bufnr + bufnr += 1 + set_vim_buffer(buf=self.c3, bufnr=bufnr) + vim.current.window.cursor = (6, 0) + self.editcheckbox.toggle() + self.assertEqual(vim.current.buffer[5], u" 2. [X] another main task") + + def test_new_checkbox(self): + global bufnr + bufnr += 1 + set_vim_buffer(buf=self.c4, bufnr=bufnr) + vim.current.window.cursor = (2, 1) + self.editcheckbox.new_checkbox(below=True) + self.assertEqual(vim.current.buffer[2], u" - [ ] ") + + def test_item_decrement(self): + global bufnr + bufnr += 1 + set_vim_buffer(buf=self.c5, bufnr=bufnr) + + vim.current.window.cursor = (3, 1) + self.editcheckbox.new_checkbox(below=False, plain=True) + self.assertEqual(vim.current.buffer[2], u" 0. ") + self.assertEqual(vim.current.buffer[3], u" 1. item") + + vim.current.window.cursor = (3, 1) + self.editcheckbox.new_checkbox(below=False, plain=True) + self.assertEqual(vim.current.buffer[1], u"* heading1") + self.assertEqual(vim.current.buffer[2], u" 0. ") + self.assertEqual(vim.current.buffer[3], u" 1. item") + + vim.current.window.cursor = (5, 1) + self.editcheckbox.new_checkbox(below=False, plain=True) + self.assertEqual(vim.current.buffer[4], u" 8. ") + self.assertEqual(vim.current.buffer[5], u" 9. item") + + vim.current.window.cursor = (8, 1) + self.editcheckbox.new_checkbox(below=False, plain=True) + # no further decrement than a + self.assertEqual(vim.current.buffer[6], u" }. item") + self.assertEqual(vim.current.buffer[7], u" a. item") + self.assertEqual(vim.current.buffer[8], u" z. item") + + def test_item_decrementA(self): + global bufnr + bufnr += 1 + set_vim_buffer(buf=self.c5, bufnr=bufnr) + vim.current.window.cursor = (8, 1) + self.editcheckbox.new_checkbox(below=False, plain=True) + # decrement from A to z + self.assertEqual(vim.current.buffer[7], u" z. ") + self.assertEqual(vim.current.buffer[8], u" A. item") + + def test_item_increment(self): + global bufnr + bufnr += 1 + set_vim_buffer(buf=self.c5, bufnr=bufnr) + + vim.current.window.cursor = (3, 1) + self.editcheckbox.new_checkbox(below=True, plain=True) + self.assertEqual(vim.current.buffer[2], u" 1. item") + self.assertEqual(vim.current.buffer[3], u" 2. ") + + vim.current.window.cursor = (5, 1) + self.editcheckbox.new_checkbox(below=True, plain=True) + self.assertEqual(vim.current.buffer[4], u" 9. item") + self.assertEqual(vim.current.buffer[5], u" }. item") + self.assertEqual(vim.current.buffer[6], u" 10. ") + + def test_item_incrementz(self): + global bufnr + bufnr += 1 + set_vim_buffer(buf=self.c5, bufnr=bufnr) + + vim.current.window.cursor = (6, 1) + self.editcheckbox.new_checkbox(below=True, plain=True) + self.assertEqual(vim.current.buffer[5], u" a. item") + self.assertEqual(vim.current.buffer[6], u" b. ") + + vim.current.window.cursor = (8, 1) + self.editcheckbox.new_checkbox(below=True, plain=True) + self.assertEqual(vim.current.buffer[7], u" z. item") + self.assertEqual(vim.current.buffer[8], u" A. ") + + vim.current.window.cursor = (11, 1) + self.editcheckbox.new_checkbox(below=True, plain=True) + self.assertEqual(vim.current.buffer[10], u" Z. item") + self.assertEqual(vim.current.buffer[11], u" aa. item") + self.assertEqual(vim.current.buffer[12], u"") + + vim.current.window.cursor = (12, 1) + self.editcheckbox.new_checkbox(below=True, plain=True) + self.assertEqual(vim.current.buffer[11], u" aa. item") + self.assertEqual(vim.current.buffer[12], u"") + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(EditCheckboxTestCase) + +# vim: set noexpandtab diff --git a/pack/acp/start/vim-orgmode/tests/test_plugin_edit_structure.py b/pack/acp/start/vim-orgmode/tests/test_plugin_edit_structure.py new file mode 100644 index 0000000..64a9338 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_plugin_edit_structure.py @@ -0,0 +1,387 @@ +# -*- coding: utf-8 -*- + +import unittest +import sys +sys.path.append(u'../ftplugin') + +import vim + +from orgmode._vim import ORGMODE + +from orgmode.py3compat.encode_compatibility import * + +counter = 0 +class EditStructureTestCase(unittest.TestCase): + def setUp(self): + global counter + counter += 1 + vim.CMDHISTORY = [] + vim.CMDRESULTS = {} + vim.EVALHISTORY = [] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')], + u_encode(u'exists("g:org_improve_split_heading")'): u_encode(u'0'), + u_encode(u'exists("b:org_improve_split_heading")'): u_encode(u'0'), + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("b:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u'b:changedtick'): u_encode(u'%d' % counter), + u_encode(u'&ts'): u_encode(u'8'), + u_encode(u'exists("g:org_tag_column")'): u_encode(u'0'), + u_encode(u'exists("b:org_tag_column")'): u_encode(u'0'), + u_encode(u"v:count"): u_encode(u'0'), + # jump to insert mode after adding heading/checkbox + u_encode(u'exists("g:org_prefer_insert_mode")'): u_encode(u'0'), + u_encode(u'exists("b:org_prefer_insert_mode")'): u_encode(u'0')} + if not u'EditStructure' in ORGMODE.plugins: + ORGMODE.register_plugin(u'EditStructure') + self.editstructure = ORGMODE.plugins[u'EditStructure'] + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Überschrift 1 +Text 1 + +Bla bla +** Überschrift 1.1 +Text 2 + +Bla Bla bla +** Überschrift 1.2 +Text 3 + +**** Überschrift 1.2.1.falsch + +Bla Bla bla bla +*** Überschrift 1.2.1 +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n')] + + def test_new_heading_below_normal_behavior(self): + vim.current.window.cursor = (1, 0) + self.assertNotEqual(self.editstructure.new_heading(below=True), None) + self.assertEqual(vim.current.buffer[0], u_encode(u'* ')) + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1')) + + def test_new_heading_above_normal_behavior(self): + vim.current.window.cursor = (1, 1) + self.assertNotEqual(self.editstructure.new_heading(below=False), None) + self.assertEqual(vim.current.buffer[0], u_encode(u'* ')) + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1')) + + def test_new_heading_below(self): + vim.current.window.cursor = (2, 0) + vim.current.buffer[5] = u_encode(u'** Überschrift 1.1 :Tag:') + self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=False), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 6gg"|startinsert!')) + self.assertEqual(vim.current.buffer[4], u_encode(u'Bla bla')) + self.assertEqual(vim.current.buffer[5], u_encode(u'* ')) + self.assertEqual(vim.current.buffer[6], u_encode(u'** Überschrift 1.1 :Tag:')) + self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2')) + self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2')) + + def test_new_heading_below_insert_mode(self): + vim.current.window.cursor = (2, 1) + self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 3gg"|startinsert!')) + self.assertEqual(vim.current.buffer[2], u_encode(u'* Überschrift 1')) + self.assertEqual(vim.current.buffer[5], u_encode(u'Bla bla')) + self.assertEqual(vim.current.buffer[6], u_encode(u'** Überschrift 1.1')) + self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2')) + self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2')) + + def test_new_heading_below_split_text_at_the_end(self): + vim.current.buffer[1] = u_encode(u'* Überschriftx1') + vim.current.window.cursor = (2, 14) + self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 3gg"|startinsert!')) + self.assertEqual(vim.current.buffer[2], u_encode(u'* ')) + self.assertEqual(vim.current.buffer[5], u_encode(u'Bla bla')) + self.assertEqual(vim.current.buffer[6], u_encode(u'** Überschrift 1.1')) + self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2')) + self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2')) + + def test_new_heading_below_split_text_at_the_end_insert_parts(self): + vim.current.window.cursor = (2, 14) + self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 3gg"|startinsert!')) + self.assertEqual(vim.current.buffer[2], u_encode(u'* 1')) + self.assertEqual(vim.current.buffer[5], u_encode(u'Bla bla')) + self.assertEqual(vim.current.buffer[6], u_encode(u'** Überschrift 1.1')) + self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2')) + self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2')) + + def test_new_heading_below_in_the_middle(self): + vim.current.window.cursor = (10, 0) + self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 13gg"|startinsert!')) + self.assertEqual(vim.current.buffer[11], u_encode(u'')) + self.assertEqual(vim.current.buffer[12], u_encode(u'** ')) + self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch')) + + def test_new_heading_below_in_the_middle2(self): + vim.current.window.cursor = (13, 0) + self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 16gg"|startinsert!')) + self.assertEqual(vim.current.buffer[14], u_encode(u'Bla Bla bla bla')) + self.assertEqual(vim.current.buffer[15], u_encode(u'**** ')) + self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1')) + + def test_new_heading_below_in_the_middle3(self): + vim.current.window.cursor = (16, 0) + self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 17gg"|startinsert!')) + self.assertEqual(vim.current.buffer[15], u_encode(u'*** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[16], u_encode(u'*** ')) + self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2')) + + def test_new_heading_below_at_the_end(self): + vim.current.window.cursor = (18, 0) + self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 21gg"|startinsert!')) + self.assertEqual(vim.current.buffer[19], u_encode(u'')) + self.assertEqual(vim.current.buffer[20], u_encode(u'* ')) + self.assertEqual(len(vim.current.buffer), 21) + + def test_new_heading_above(self): + vim.current.window.cursor = (2, 0) + self.assertNotEqual(self.editstructure.new_heading(below=False, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 2gg"|startinsert!')) + self.assertEqual(vim.current.buffer[0], u_encode(u'')) + self.assertEqual(vim.current.buffer[1], u_encode(u'* ')) + self.assertEqual(vim.current.buffer[2], u_encode(u'* Überschrift 1')) + + def test_new_heading_above_in_the_middle(self): + vim.current.window.cursor = (10, 0) + self.assertNotEqual(self.editstructure.new_heading(below=False, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 10gg"|startinsert!')) + self.assertEqual(vim.current.buffer[8], u_encode(u'Bla Bla bla')) + self.assertEqual(vim.current.buffer[9], u_encode(u'** ')) + self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2')) + + def test_new_heading_above_in_the_middle2(self): + vim.current.window.cursor = (13, 0) + self.assertNotEqual(self.editstructure.new_heading(below=False, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 13gg"|startinsert!')) + self.assertEqual(vim.current.buffer[11], u_encode(u'')) + self.assertEqual(vim.current.buffer[12], u_encode(u'**** ')) + self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch')) + + def test_new_heading_above_in_the_middle3(self): + vim.current.window.cursor = (16, 0) + self.assertNotEqual(self.editstructure.new_heading(below=False, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 16gg"|startinsert!')) + self.assertEqual(vim.current.buffer[14], u_encode(u'Bla Bla bla bla')) + self.assertEqual(vim.current.buffer[15], u_encode(u'*** ')) + self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1')) + + def test_new_heading_above_at_the_end(self): + vim.current.window.cursor = (18, 0) + self.assertNotEqual(self.editstructure.new_heading(below=False, insert_mode=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 18gg"|startinsert!')) + self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2')) + self.assertEqual(vim.current.buffer[17], u_encode(u'* ')) + self.assertEqual(vim.current.buffer[18], u_encode(u'* Überschrift 3')) + + def test_new_heading_below_split_heading_title(self): + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Überschrift 1 :Tag: +Text 1 + +Bla bla +** Überschrift 1.1 +Text 2 + +Bla Bla bla +** Überschrift 1.2 +Text 3 + +**** Überschrift 1.2.1.falsch + +Bla Bla bla bla +*** Überschrift 1.2.1 +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n')] + vim.current.window.cursor = (2, 6) + self.assertNotEqual(self.editstructure.new_heading(insert_mode=True), None) + self.assertEqual(vim.current.buffer[0], u_encode(u'')) + self.assertEqual(vim.current.buffer[1], u_encode(u'* Über :Tag:')) + self.assertEqual(vim.current.buffer[2], u_encode(u'* schrift 1')) + self.assertEqual(vim.current.buffer[3], u_encode(u'Text 1')) + + def test_new_heading_below_split_heading_title_with_todo(self): + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* TODO Überschrift 1 :Tag: +Text 1 + +Bla bla +** Überschrift 1.1 +Text 2 + +Bla Bla bla +** Überschrift 1.2 +Text 3 + +**** Überschrift 1.2.1.falsch + +Bla Bla bla bla +*** Überschrift 1.2.1 +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n')] + vim.current.window.cursor = (2, 5) + self.assertNotEqual(self.editstructure.new_heading(insert_mode=True), None) + self.assertEqual(vim.current.buffer[0], u_encode(u'')) + self.assertEqual(vim.current.buffer[1], u_encode(u'* TODO :Tag:')) + self.assertEqual(vim.current.buffer[2], u_encode(u'* Überschrift 1')) + self.assertEqual(vim.current.buffer[3], u_encode(u'Text 1')) + + def test_demote_heading(self): + vim.current.window.cursor = (13, 0) + self.assertNotEqual(self.editstructure.demote_heading(), None) + self.assertEqual(vim.current.buffer[10], u_encode(u'Text 3')) + self.assertEqual(vim.current.buffer[11], u_encode(u'')) + self.assertEqual(vim.current.buffer[12], u_encode(u'***** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[13], u_encode(u'')) + # actually the indentation comes through vim, just the heading is updated + self.assertEqual(vim.current.buffer[14], u_encode(u'Bla Bla bla bla')) + self.assertEqual(vim.current.buffer[15], u_encode(u'*** Überschrift 1.2.1')) + self.assertEqual(vim.current.window.cursor, (13, 1)) + + def test_demote_newly_created_level_one_heading(self): + vim.current.window.cursor = (2, 0) + self.assertNotEqual(self.editstructure.new_heading(below=True), None) + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1')) + self.assertEqual(vim.current.buffer[5], u_encode(u'* ')) + self.assertEqual(vim.current.buffer[6], u_encode(u'** Überschrift 1.1')) + self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2')) + self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2')) + + vim.current.window.cursor = (6, 2) + self.assertNotEqual(self.editstructure.demote_heading(), None) + self.assertEqual(vim.current.buffer[5], u_encode(u'** ')) + self.assertEqual(vim.current.buffer[6], u_encode(u'*** Überschrift 1.1')) + self.assertEqual(vim.current.buffer[10], u_encode(u'*** Überschrift 1.2')) + self.assertEqual(vim.current.buffer[13], u_encode(u'***** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[16], u_encode(u'**** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2')) + + def test_demote_newly_created_level_two_heading(self): + vim.current.window.cursor = (10, 0) + self.assertNotEqual(self.editstructure.new_heading(below=True), None) + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1')) + self.assertEqual(vim.current.buffer[5], u_encode(u'** Überschrift 1.1')) + self.assertEqual(vim.current.buffer[9], u_encode(u'** Überschrift 1.2')) + self.assertEqual(vim.current.buffer[12], u_encode(u'** ')) + self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2')) + + vim.current.window.cursor = (13, 3) + self.assertNotEqual(self.editstructure.demote_heading(including_children=False, on_heading=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 13gg"|startinsert!')) + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1')) + self.assertEqual(vim.current.buffer[5], u_encode(u'** Überschrift 1.1')) + self.assertEqual(vim.current.buffer[9], u_encode(u'** Überschrift 1.2')) + self.assertEqual(vim.current.buffer[12], u_encode(u'*** ')) + self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2')) + + def test_demote_last_heading(self): + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Überschrift 2 +* Überschrift 3""".split('\n')] + vim.current.window.cursor = (3, 0) + h = ORGMODE.get_document().current_heading() + self.assertNotEqual(self.editstructure.demote_heading(), None) + self.assertEqual(h.end, 2) + self.assertFalse(vim.CMDHISTORY) + self.assertEqual(vim.current.buffer[2], u_encode(u'** Überschrift 3')) + self.assertEqual(vim.current.window.cursor, (3, 1)) + + def test_promote_heading(self): + vim.current.window.cursor = (13, 0) + self.assertNotEqual(self.editstructure.promote_heading(), None) + self.assertEqual(vim.current.buffer[10], u_encode(u'Text 3')) + self.assertEqual(vim.current.buffer[11], u_encode(u'')) + self.assertEqual(vim.current.buffer[12], u_encode(u'*** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[13], u_encode(u'')) + # actually the indentation comes through vim, just the heading is updated + self.assertEqual(vim.current.buffer[14], u_encode(u'Bla Bla bla bla')) + self.assertEqual(vim.current.buffer[15], u_encode(u'*** Überschrift 1.2.1')) + self.assertEqual(vim.current.window.cursor, (13, -1)) + + def test_promote_level_one_heading(self): + vim.current.window.cursor = (2, 0) + self.assertEqual(self.editstructure.promote_heading(), None) + self.assertEqual(len(vim.CMDHISTORY), 0) + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_demote_parent_heading(self): + vim.current.window.cursor = (2, 0) + self.assertNotEqual(self.editstructure.demote_heading(), None) + self.assertEqual(vim.current.buffer[1], u_encode(u'** Überschrift 1')) + self.assertEqual(vim.current.buffer[5], u_encode(u'*** Überschrift 1.1')) + self.assertEqual(vim.current.buffer[9], u_encode(u'*** Überschrift 1.2')) + self.assertEqual(vim.current.buffer[12], u_encode(u'***** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[15], u_encode(u'**** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2')) + self.assertEqual(vim.current.window.cursor, (2, 1)) + + def test_promote_parent_heading(self): + vim.current.window.cursor = (10, 0) + self.assertNotEqual(self.editstructure.promote_heading(), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal 10ggV16gg=')) + self.assertEqual(vim.current.buffer[5], u_encode(u'** Überschrift 1.1')) + self.assertEqual(vim.current.buffer[9], u_encode(u'* Überschrift 1.2')) + self.assertEqual(vim.current.buffer[12], u_encode(u'*** Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[15], u_encode(u'** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2')) + self.assertEqual(vim.current.window.cursor, (10, -1)) + + # run tests with count + def test_demote_parent_heading_count(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u"v:count"] = u_encode(u'3') + self.assertNotEqual(self.editstructure.demote_heading(), None) + self.assertEqual(vim.current.buffer[1], u_encode(u'**** Überschrift 1')) + self.assertEqual(vim.current.buffer[5], u_encode(u'***** Überschrift 1.1')) + self.assertEqual(vim.current.buffer[9], u_encode(u'***** Überschrift 1.2')) + self.assertEqual(vim.current.buffer[12], u_encode(u'******* Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[15], u_encode(u'****** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2')) + self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2')) + self.assertEqual(vim.current.window.cursor, (2, 3)) + + def test_promote_parent_heading(self): + vim.current.window.cursor = (13, 0) + vim.EVALRESULTS[u"v:count"] = u_encode(u'3') + self.assertNotEqual(self.editstructure.promote_heading(), None) + self.assertEqual(vim.current.buffer[5], u_encode(u'** Überschrift 1.1')) + self.assertEqual(vim.current.buffer[9], u_encode(u'** Überschrift 1.2')) + self.assertEqual(vim.current.buffer[12], u_encode(u'* Überschrift 1.2.1.falsch')) + self.assertEqual(vim.current.buffer[15], u_encode(u'** Überschrift 1.2.1')) + self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2')) + self.assertEqual(vim.current.window.cursor, (13, -3)) + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(EditStructureTestCase) diff --git a/pack/acp/start/vim-orgmode/tests/test_plugin_mappings.py b/pack/acp/start/vim-orgmode/tests/test_plugin_mappings.py new file mode 100644 index 0000000..fc16da4 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_plugin_mappings.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +from __future__ import print_function + +import sys +sys.path.append(u'../ftplugin') + +import unittest +import orgmode.settings +from orgmode.exceptions import PluginError +from orgmode._vim import ORGMODE +from orgmode.keybinding import MODE_ALL, Plug + +import vim + +from orgmode.py3compat.encode_compatibility import * + +ORG_PLUGINS = ['ShowHide', '|', 'Navigator', 'EditStructure', '|', 'Hyperlinks', '|', 'Todo', 'TagsProperties', 'Date', 'Agenda', 'Misc', '|', 'Export'] + + +class MappingTestCase(unittest.TestCase): + u"""Tests all plugins for overlapping mappings.""" + def test_non_overlapping_plug_mappings(self): + def find_overlapping_mappings(kb, all_keybindings): + found_overlapping_mapping = False + for tkb in all_keybindings: + if kb.mode == tkb.mode or MODE_ALL in (kb.mode, tkb.mode): + if isinstance(kb._action, Plug) and isinstance(tkb._action, Plug): + akb = kb.action + atkb = tkb.action + if (akb.startswith(atkb) or atkb.startswith(akb)) and akb != atkb: + print(u'\nERROR: Found overlapping mapping: %s (%s), %s (%s)' % (kb.key, akb, tkb.key, atkb)) + found_overlapping_mapping = True + + if all_keybindings: + res = find_overlapping_mappings(all_keybindings[0], all_keybindings[1:]) + if not found_overlapping_mapping: + return res + return found_overlapping_mapping + + if self.keybindings: + self.assertFalse(find_overlapping_mappings(self.keybindings[0], self.keybindings[1:])) + + def setUp(self): + self.keybindings = [] + + vim.EVALRESULTS = { + u'exists("g:org_debug")': 0, + u'exists("b:org_debug")': 0, + u'exists("*repeat#set()")': 0, + u'b:changedtick': 0, + u_encode(u'exists("b:org_plugins")'): 0, + u_encode(u'exists("g:org_plugins")'): 1, + u_encode(u'g:org_plugins'): ORG_PLUGINS, + } + for plugin in filter(lambda p: p != '|', ORG_PLUGINS): + try: + ORGMODE.register_plugin(plugin) + except PluginError: + pass + if plugin in ORGMODE._plugins: + self.keybindings.extend(ORGMODE._plugins[plugin].keybindings) + + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(MappingTestCase) + +# vi: noexpandtab diff --git a/pack/acp/start/vim-orgmode/tests/test_plugin_misc.py b/pack/acp/start/vim-orgmode/tests/test_plugin_misc.py new file mode 100644 index 0000000..53e5cb0 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_plugin_misc.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +import sys +sys.path.append(u'../ftplugin') + +import vim + +from orgmode._vim import indent_orgmode, fold_orgmode, ORGMODE + +from orgmode.py3compat.encode_compatibility import * + +ORGMODE.debug = True + +START = True +END = False + +counter = 0 +class MiscTestCase(unittest.TestCase): + def setUp(self): + global counter + counter += 1 + vim.CMDHISTORY = [] + vim.CMDRESULTS = {} + vim.EVALHISTORY = [] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')], + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u"v:count"): u_encode(u'0'), + u_encode(u'b:changedtick'): u_encode(u'%d' % counter), + u_encode(u"v:lnum"): u_encode(u'0')} + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Überschrift 1 +Text 1 + +Bla bla +** Überschrift 1.1 +Text 2 + +Bla Bla bla +** Überschrift 1.2 +Text 3 + +**** Überschrift 1.2.1.falsch + +Bla Bla bla bla +*** Überschrift 1.2.1 +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n') ] + + def test_indent_noheading(self): + # test first heading + vim.current.window.cursor = (1, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'1') + indent_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 0) + + def test_indent_heading(self): + # test first heading + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'2') + indent_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 0) + + def test_indent_heading_middle(self): + # test first heading + vim.current.window.cursor = (3, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'3') + indent_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:indent_level = 2')) + + def test_indent_heading_middle2(self): + # test first heading + vim.current.window.cursor = (4, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'4') + indent_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:indent_level = 2')) + + def test_indent_heading_end(self): + # test first heading + vim.current.window.cursor = (5, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'5') + indent_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:indent_level = 2')) + + def test_fold_heading_start(self): + # test first heading + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'2') + fold_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = ">1"')) + + def test_fold_heading_middle(self): + # test first heading + vim.current.window.cursor = (3, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'3') + fold_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = 1')) + + def test_fold_heading_end(self): + # test first heading + vim.current.window.cursor = (5, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'5') + fold_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = 1')) + + def test_fold_heading_end_of_last_child(self): + # test first heading + vim.current.window.cursor = (16, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'16') + fold_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 1) + # which is also end of the parent heading <1 + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = ">3"')) + + def test_fold_heading_end_of_last_child_next_heading(self): + # test first heading + vim.current.window.cursor = (17, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'17') + fold_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = ">1"')) + + def test_fold_middle_subheading(self): + # test first heading + vim.current.window.cursor = (13, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'13') + fold_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = ">4"')) + + def test_fold_middle_subheading2(self): + # test first heading + vim.current.window.cursor = (14, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'14') + fold_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = 4')) + + def test_fold_middle_subheading3(self): + # test first heading + vim.current.window.cursor = (15, 0) + vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'15') + fold_orgmode() + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = 4')) + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(MiscTestCase) diff --git a/pack/acp/start/vim-orgmode/tests/test_plugin_navigator.py b/pack/acp/start/vim-orgmode/tests/test_plugin_navigator.py new file mode 100644 index 0000000..bb3bcca --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_plugin_navigator.py @@ -0,0 +1,633 @@ +# -*- coding: utf-8 -*- + +import unittest +import sys +sys.path.append(u'../ftplugin') + +import vim + +from orgmode._vim import ORGMODE + +from orgmode.py3compat.encode_compatibility import * + +START = True +END = False + +def set_visual_selection(visualmode, line_start, line_end, col_start=1, + col_end=1, cursor_pos=START): + + if visualmode not in (u'', u'V', u'v'): + raise ValueError(u'Illegal value for visualmode, must be in , V, v') + + vim.EVALRESULTS['visualmode()'] = visualmode + + # getpos results [bufnum, lnum, col, off] + vim.EVALRESULTS['getpos("\'<")'] = ('', '%d' % line_start, '%d' % + col_start, '') + vim.EVALRESULTS['getpos("\'>")'] = ('', '%d' % line_end, '%d' % + col_end, '') + if cursor_pos == START: + vim.current.window.cursor = (line_start, col_start) + else: + vim.current.window.cursor = (line_end, col_end) + + +counter = 0 +class NavigatorTestCase(unittest.TestCase): + def setUp(self): + global counter + counter += 1 + vim.CMDHISTORY = [] + vim.CMDRESULTS = {} + vim.EVALHISTORY = [] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')], + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u'b:changedtick'): u_encode(u'%d' % counter), + u_encode(u"v:count"): u_encode(u'0'), + } + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Überschrift 1 +Text 1 + +Bla bla +** Überschrift 1.1 +Text 2 + +Bla Bla bla +** Überschrift 1.2 +Text 3 + +**** Überschrift 1.2.1.falsch + +Bla Bla bla bla +*** Überschrift 1.2.1 +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n') ] + + if not u'Navigator' in ORGMODE.plugins: + ORGMODE.register_plugin(u'Navigator') + self.navigator = ORGMODE.plugins[u'Navigator'] + + def test_movement(self): + # test movement outside any heading + vim.current.window.cursor = (1, 0) + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (1, 0)) + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (2, 2)) + + def test_forward_movement(self): + # test forward movement + vim.current.window.cursor = (2, 0) + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (6, 3)) + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (10, 3)) + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (13, 5)) + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (16, 4)) + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (17, 2)) + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (18, 2)) + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (18, 2)) + + ## don't move cursor if last heading is already focussed + vim.current.window.cursor = (19, 6) + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (19, 6)) + + ## test movement with count + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'-1') + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (6, 3)) + + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'0') + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (6, 3)) + + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'1') + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (6, 3)) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'3') + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (16, 4)) + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (18, 2)) + self.navigator.next(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (18, 2)) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'0') + + def test_backward_movement(self): + # test backward movement + vim.current.window.cursor = (19, 6) + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (18, 2)) + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (17, 2)) + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (16, 4)) + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (13, 5)) + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (10, 3)) + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (6, 3)) + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (2, 2)) + + ## test movement with count + vim.current.window.cursor = (19, 6) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'-1') + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (18, 2)) + + vim.current.window.cursor = (19, 6) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'0') + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (18, 2)) + + vim.current.window.cursor = (19, 6) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'3') + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (16, 4)) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'4') + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (2, 2)) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'4') + self.navigator.previous(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (2, 2)) + + def test_parent_movement(self): + # test movement to parent + vim.current.window.cursor = (2, 0) + self.assertEqual(self.navigator.parent(mode=u'normal'), None) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + vim.current.window.cursor = (3, 4) + self.navigator.parent(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (3, 4)) + + vim.current.window.cursor = (16, 4) + self.navigator.parent(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (10, 3)) + self.navigator.parent(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (2, 2)) + + vim.current.window.cursor = (15, 6) + self.navigator.parent(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (10, 3)) + self.navigator.parent(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (2, 2)) + + ## test movement with count + vim.current.window.cursor = (16, 4) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'-1') + self.navigator.parent(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (10, 3)) + + vim.current.window.cursor = (16, 4) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'0') + self.navigator.parent(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (10, 3)) + + vim.current.window.cursor = (16, 4) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'1') + self.navigator.parent(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (10, 3)) + + vim.current.window.cursor = (16, 4) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'2') + self.navigator.parent(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (2, 2)) + + vim.current.window.cursor = (16, 4) + vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'3') + self.navigator.parent(mode=u'normal') + self.assertEqual(vim.current.window.cursor, (2, 2)) + + def test_next_parent_movement(self): + # test movement to parent + vim.current.window.cursor = (6, 0) + self.assertNotEqual(self.navigator.parent_next_sibling(mode=u'normal'), None) + self.assertEqual(vim.current.window.cursor, (17, 2)) + + def test_forward_movement_visual(self): + # selection start: << + # selection end: >> + # cursor poistion: | + + # << text + # text| >> + # text + # heading + set_visual_selection(u'V', 2, 4, cursor_pos=END) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV5gg')) + + # << text + # text + # text| >> + # heading + set_visual_selection(u'V', 2, 5, cursor_pos=END) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV9gg')) + + # << text + # x. heading + # text| >> + # heading + set_visual_selection(u'V', 12, 14, cursor_pos=END) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV15gg')) + + set_visual_selection(u'V', 12, 15, cursor_pos=END) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV16gg')) + + set_visual_selection(u'V', 12, 16, cursor_pos=END) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV17gg')) + + # << text + # text + # text| >> + # heading + # EOF + set_visual_selection(u'V', 15, 17, cursor_pos=END) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 15ggV20gg')) + + # << text >> + # heading + set_visual_selection(u'V', 1, 1, cursor_pos=START) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 1ggV5gg')) + + # << heading >> + # text + # heading + set_visual_selection(u'V', 2, 2, cursor_pos=START) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV5gg')) + + # << text >> + # heading + set_visual_selection(u'V', 1, 1, cursor_pos=END) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 1ggV5gg')) + + # << |text + # heading + # text + # heading + # text >> + set_visual_selection(u'V', 1, 8, cursor_pos=START) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV8ggo')) + + # << |heading + # text + # heading + # text >> + set_visual_selection(u'V', 2, 8, cursor_pos=START) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 6ggV8ggo')) + + # << |heading + # text >> + # heading + set_visual_selection(u'V', 6, 8, cursor_pos=START) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 8ggV9gg')) + + # << |x. heading + # text >> + # heading + set_visual_selection(u'V', 13, 15, cursor_pos=START) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 15ggV15gg')) + + set_visual_selection(u'V', 13, 16, cursor_pos=START) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16ggV16ggo')) + + set_visual_selection(u'V', 16, 16, cursor_pos=START) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16ggV17gg')) + + # << |x. heading + # text >> + # heading + # EOF + set_visual_selection(u'V', 17, 17, cursor_pos=START) + self.assertNotEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 17ggV20gg')) + + # << |heading + # text>> + # text + # EOF + set_visual_selection(u'V', 18, 19, cursor_pos=START) + self.assertEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 19ggV20gg')) + + # << heading + # text|>> + # text + # EOF + set_visual_selection(u'V', 18, 19, cursor_pos=END) + self.assertEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 18ggV20gg')) + + # << heading + # text|>> + # EOF + set_visual_selection(u'V', 18, 20, cursor_pos=END) + self.assertEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 18ggV20gg')) + + # << |heading + # text>> + # EOF + set_visual_selection(u'V', 20, 20, cursor_pos=START) + self.assertEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 20ggV20gg')) + + def test_forward_movement_visual_to_the_end_of_the_file(self): + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Überschrift 1 +Text 1 + +Bla bla +** Überschrift 1.1 +Text 2 + +Bla Bla bla +** Überschrift 1.2 +Text 3 + +**** Überschrift 1.2.1.falsch + +Bla Bla bla bla +test +""".split(u'\n') ] + # << |heading + # text>> + # EOF + set_visual_selection(u'V', 15, 15, cursor_pos=START) + self.assertEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 15ggV17gg')) + + set_visual_selection(u'V', 15, 17, cursor_pos=END) + self.assertEqual(self.navigator.next(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 15ggV17gg')) + + def test_backward_movement_visual(self): + # selection start: << + # selection end: >> + # cursor poistion: | + + # << text | >> + # text + # heading + set_visual_selection(u'V', 1, 1, cursor_pos=START) + self.assertEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! gv')) + + set_visual_selection(u'V', 1, 1, cursor_pos=END) + self.assertEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! gv')) + + # << heading| >> + # text + # heading + set_visual_selection(u'V', 2, 2, cursor_pos=START) + self.assertEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV2ggo')) + + set_visual_selection(u'V', 2, 2, cursor_pos=END) + self.assertEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV2ggo')) + + # heading + # text + # << |text + # text >> + set_visual_selection(u'V', 3, 5, cursor_pos=START) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV5ggo')) + + # heading + # text + # << text + # text| >> + set_visual_selection(u'V', 3, 5, cursor_pos=END) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV3ggo')) + + # heading + # text + # << text + # text| >> + set_visual_selection(u'V', 8, 9, cursor_pos=END) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 6ggV8ggo')) + + # heading + # << text + # x. heading + # text| >> + set_visual_selection(u'V', 12, 14, cursor_pos=END) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV12gg')) + + set_visual_selection(u'V', 12, 15, cursor_pos=END) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV12gg')) + + # heading + # << |text + # x. heading + # text >> + set_visual_selection(u'V', 12, 15, cursor_pos=START) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 10ggV15ggo')) + + # heading + # << text + # x. heading| >> + set_visual_selection(u'V', 12, 13, cursor_pos=END) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV12gg')) + + # heading + # << text + # heading + # text + # x. heading| >> + set_visual_selection(u'V', 12, 16, cursor_pos=END) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV15gg')) + + # << text + # heading + # text + # heading| >> + set_visual_selection(u'V', 15, 17, cursor_pos=END) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 15ggV16gg')) + + # heading + # << |text + # text + # heading + # text >> + set_visual_selection(u'V', 4, 8, cursor_pos=START) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV8ggo')) + + # heading + # << text + # text + # heading + # text| >> + set_visual_selection(u'V', 4, 8, cursor_pos=END) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 4ggV5gg')) + + # heading + # << text + # text + # heading + # text| >> + set_visual_selection(u'V', 4, 5, cursor_pos=END) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV4ggo')) + + # BOF + # << |heading + # text + # heading + # text >> + set_visual_selection(u'V', 2, 8, cursor_pos=START) + self.assertEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV8ggo')) + + # BOF + # heading + # << text + # text| >> + set_visual_selection(u'V', 3, 4, cursor_pos=END) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV3ggo')) + + # BOF + # << heading + # text + # text| >> + set_visual_selection(u'V', 2, 4, cursor_pos=END) + self.assertNotEqual(self.navigator.previous(mode=u'visual'), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV2ggo')) + + # << text + # heading + # text + # x. heading + # text| >> + set_visual_selection(u'V', 8, 14, cursor_pos=END) + self.navigator.previous(mode=u'visual') + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 8ggV12gg')) + + def test_parent_movement_visual(self): + # selection start: << + # selection end: >> + # cursor poistion: | + + # heading + # << text| + # text + # text >> + set_visual_selection(u'V', 4, 8, cursor_pos=START) + self.navigator.parent(mode=u'visual') + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! gv')) + + # heading + # << text| + # text + # text >> + set_visual_selection(u'V', 6, 8, cursor_pos=START) + self.navigator.parent(mode=u'visual') + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV8ggo')) + + # heading + # << text + # text + # text| >> + set_visual_selection(u'V', 6, 8, cursor_pos=END) + self.navigator.parent(mode=u'visual') + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 6ggV5gg')) + + # << |heading + # text + # text + # text >> + set_visual_selection(u'V', 2, 8, cursor_pos=START) + self.assertEqual(self.navigator.parent(mode=u'visual'), None) + + # << heading + # text + # heading + # text| >> + set_visual_selection(u'V', 2, 8, cursor_pos=END) + self.navigator.parent(mode=u'visual') + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV5gg')) + + set_visual_selection(u'V', 7, 8, cursor_pos=START) + self.navigator.parent(mode=u'visual') + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV8ggo')) + + # heading + # heading + # << text + # text| >> + set_visual_selection(u'V', 12, 13, cursor_pos=END) + self.navigator.parent(mode=u'visual') + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV12gg')) + + set_visual_selection(u'V', 10, 12, cursor_pos=START) + self.navigator.parent(mode=u'visual') + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV12ggo')) + + # heading + # << text + # text + # heading| >> + set_visual_selection(u'V', 11, 17, cursor_pos=END) + self.assertEqual(self.navigator.parent(mode=u'visual'), None) + + # << text + # heading + # text + # x. heading + # text| >> + set_visual_selection(u'V', 8, 14, cursor_pos=END) + self.navigator.parent(mode=u'visual') + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 8ggV12gg')) + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(NavigatorTestCase) diff --git a/pack/acp/start/vim-orgmode/tests/test_plugin_show_hide.py b/pack/acp/start/vim-orgmode/tests/test_plugin_show_hide.py new file mode 100644 index 0000000..05dd640 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_plugin_show_hide.py @@ -0,0 +1,385 @@ +# -*- coding: utf-8 -*- + +import unittest +import sys +sys.path.append(u'../ftplugin') + +import vim + +from orgmode._vim import ORGMODE + +from orgmode.py3compat.encode_compatibility import * + +counter = 0 +class ShowHideTestCase(unittest.TestCase): + def setUp(self): + global counter + counter += 1 + vim.CMDHISTORY = [] + vim.CMDRESULTS = {} + vim.EVALHISTORY = [] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')], + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("b:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u'b:changedtick'): u_encode(u'%d' % counter), + u_encode(u"v:count"): u_encode(u'0')} + if not u'ShowHide' in ORGMODE.plugins: + ORGMODE.register_plugin(u'ShowHide') + self.showhide = ORGMODE.plugins[u'ShowHide'] + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Überschrift 1 +Text 1 + +Bla bla +** Überschrift 1.1 +Text 2 + +Bla Bla bla +** Überschrift 1.2 +Text 3 + +**** Überschrift 1.2.1.falsch + +Bla Bla bla bla +*** Überschrift 1.2.1 +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n') ] + + def test_no_heading_toggle_folding(self): + vim.current.window.cursor = (1, 0) + self.assertEqual(self.showhide.toggle_folding(), None) + self.assertEqual(vim.EVALHISTORY[-1], u_encode(u'feedkeys("<Tab>", "n")')) + self.assertEqual(vim.current.window.cursor, (1, 0)) + + def test_toggle_folding_first_heading_with_no_children(self): + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Überschrift 1 +Text 1 + +Bla bla +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n') ] + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'2'), + u_encode(u'foldclosed(6)'): u_encode(u'-1'), + u_encode(u'foldclosed(7)'): u_encode(u'-1'), + }) + vim.current.window.cursor = (2, 0) + + self.assertNotEqual(self.showhide.toggle_folding(), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 1zo')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_close_one(self): + vim.current.window.cursor = (13, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(13)'): u_encode(u'-1'), + }) + self.assertNotEqual(self.showhide.toggle_folding(), None) + self.assertEqual(len(vim.CMDHISTORY), 2) + self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'13,15foldclose!')) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2zo')) + self.assertEqual(vim.current.window.cursor, (13, 0)) + + def test_toggle_folding_open_one(self): + vim.current.window.cursor = (10, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(10)'): u_encode(u'10'), + }) + self.assertNotEqual(self.showhide.toggle_folding(), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 1zo')) + self.assertEqual(vim.current.window.cursor, (10, 0)) + + def test_toggle_folding_close_multiple_all_open(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'-1'), + u_encode(u'foldclosed(10)'): u_encode(u'-1'), + u_encode(u'foldclosed(13)'): u_encode(u'-1'), + u_encode(u'foldclosed(16)'): u_encode(u'-1'), + }) + self.assertNotEqual(self.showhide.toggle_folding(), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'2,16foldclose!')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_all_closed(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'2'), + }) + self.assertNotEqual(self.showhide.toggle_folding(), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 1zo')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_first_level_open(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'6'), + u_encode(u'foldclosed(10)'): u_encode(u'10'), + u_encode(u'foldclosed(13)'): u_encode(u'13'), + u_encode(u'foldclosed(16)'): u_encode(u'16'), + }) + self.assertNotEqual(self.showhide.toggle_folding(), None) + self.assertEqual(len(vim.CMDHISTORY), 2) + self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 6gg1zo')) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 10gg1zo')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_second_level_half_open(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'-1'), + u_encode(u'foldclosed(10)'): u_encode(u'10'), + u_encode(u'foldclosed(13)'): u_encode(u'13'), + u_encode(u'foldclosed(16)'): u_encode(u'16'), + }) + self.assertNotEqual(self.showhide.toggle_folding(), None) + self.assertEqual(len(vim.CMDHISTORY), 4) + self.assertEqual(vim.CMDHISTORY[-4], u_encode(u'normal! 6gg2zo')) + self.assertEqual(vim.CMDHISTORY[-3], u_encode(u'normal! 10gg2zo')) + self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13gg2zo')) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16gg2zo')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_other_second_level_half_open(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'6'), + u_encode(u'foldclosed(10)'): u_encode(u'-1'), + u_encode(u'foldclosed(13)'): u_encode(u'13'), + u_encode(u'foldclosed(16)'): u_encode(u'16'), + }) + self.assertNotEqual(self.showhide.toggle_folding(), None) + self.assertEqual(len(vim.CMDHISTORY), 4) + self.assertEqual(vim.CMDHISTORY[-4], u_encode(u'normal! 6gg2zo')) + self.assertEqual(vim.CMDHISTORY[-3], u_encode(u'normal! 10gg2zo')) + self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13gg2zo')) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16gg2zo')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_third_level_half_open(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'-1'), + u_encode(u'foldclosed(10)'): u_encode(u'-1'), + u_encode(u'foldclosed(13)'): u_encode(u'-1'), + u_encode(u'foldclosed(16)'): u_encode(u'16'), + }) + self.assertNotEqual(self.showhide.toggle_folding(), None) + self.assertEqual(len(vim.CMDHISTORY), 4) + self.assertEqual(vim.CMDHISTORY[-4], u_encode(u'normal! 6gg3zo')) + self.assertEqual(vim.CMDHISTORY[-3], u_encode(u'normal! 10gg3zo')) + self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13gg3zo')) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16gg3zo')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_other_third_level_half_open(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'-1'), + u_encode(u'foldclosed(10)'): u_encode(u'-1'), + u_encode(u'foldclosed(13)'): u_encode(u'13'), + u_encode(u'foldclosed(16)'): u_encode(u'-1'), + }) + self.assertNotEqual(self.showhide.toggle_folding(), None) + self.assertEqual(len(vim.CMDHISTORY), 4) + self.assertEqual(vim.CMDHISTORY[-4], u_encode(u'normal! 6gg3zo')) + self.assertEqual(vim.CMDHISTORY[-3], u_encode(u'normal! 10gg3zo')) + self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13gg3zo')) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16gg3zo')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_other_third_level_half_open_second_level_half_closed(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'6'), + u_encode(u'foldclosed(10)'): u_encode(u'-1'), + u_encode(u'foldclosed(13)'): u_encode(u'13'), + u_encode(u'foldclosed(16)'): u_encode(u'-1'), + }) + self.assertNotEqual(self.showhide.toggle_folding(), None) + self.assertEqual(len(vim.CMDHISTORY), 4) + self.assertEqual(vim.CMDHISTORY[-4], u_encode(u'normal! 6gg3zo')) + self.assertEqual(vim.CMDHISTORY[-3], u_encode(u'normal! 10gg3zo')) + self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13gg3zo')) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16gg3zo')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_no_heading_toggle_folding_reverse(self): + vim.current.window.cursor = (1, 0) + self.assertEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(vim.EVALHISTORY[-1], u_encode(u'feedkeys("<Tab>", "n")')) + self.assertEqual(vim.current.window.cursor, (1, 0)) + + def test_toggle_folding_first_heading_with_no_children_reverse(self): + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Überschrift 1 +Text 1 + +Bla bla +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n') ] + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'2'), + u_encode(u'foldclosed(6)'): u_encode(u'-1'), + u_encode(u'foldclosed(7)'): u_encode(u'-1'), + }) + vim.current.window.cursor = (2, 0) + + self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'2,5foldopen!')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_close_one_reverse(self): + vim.current.window.cursor = (13, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(13)'): u_encode(u'-1'), + }) + self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 13ggzc')) + self.assertEqual(vim.current.window.cursor, (13, 0)) + + def test_toggle_folding_open_one_reverse(self): + vim.current.window.cursor = (10, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(10)'): u_encode(u'10'), + }) + self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'10,16foldopen!')) + self.assertEqual(vim.current.window.cursor, (10, 0)) + + def test_toggle_folding_close_multiple_all_open_reverse(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'-1'), + u_encode(u'foldclosed(10)'): u_encode(u'-1'), + u_encode(u'foldclosed(13)'): u_encode(u'-1'), + u_encode(u'foldclosed(16)'): u_encode(u'-1'), + }) + self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(len(vim.CMDHISTORY), 2) + self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13ggzc')) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16ggzc')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_all_closed_reverse(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'2'), + }) + self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'2,16foldopen!')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_first_level_open_reverse(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'6'), + u_encode(u'foldclosed(10)'): u_encode(u'10'), + u_encode(u'foldclosed(13)'): u_encode(u'13'), + u_encode(u'foldclosed(16)'): u_encode(u'16'), + }) + self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggzc')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_second_level_half_open_reverse(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'-1'), + u_encode(u'foldclosed(10)'): u_encode(u'10'), + u_encode(u'foldclosed(13)'): u_encode(u'13'), + u_encode(u'foldclosed(16)'): u_encode(u'16'), + }) + self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 6ggzc')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_other_second_level_half_open_reverse(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'6'), + u_encode(u'foldclosed(10)'): u_encode(u'-1'), + u_encode(u'foldclosed(13)'): u_encode(u'13'), + u_encode(u'foldclosed(16)'): u_encode(u'16'), + }) + self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 10ggzc')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_third_level_half_open_reverse(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'-1'), + u_encode(u'foldclosed(10)'): u_encode(u'-1'), + u_encode(u'foldclosed(13)'): u_encode(u'-1'), + u_encode(u'foldclosed(16)'): u_encode(u'16'), + }) + self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 13ggzc')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_other_third_level_half_open_reverse(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'-1'), + u_encode(u'foldclosed(10)'): u_encode(u'-1'), + u_encode(u'foldclosed(13)'): u_encode(u'13'), + u_encode(u'foldclosed(16)'): u_encode(u'-1'), + }) + self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16ggzc')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + + def test_toggle_folding_open_multiple_other_third_level_half_open_second_level_half_closed_reverse(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS.update({ + u_encode(u'foldclosed(2)'): u_encode(u'-1'), + u_encode(u'foldclosed(6)'): u_encode(u'6'), + u_encode(u'foldclosed(10)'): u_encode(u'-1'), + u_encode(u'foldclosed(13)'): u_encode(u'13'), + u_encode(u'foldclosed(16)'): u_encode(u'-1'), + }) + self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None) + self.assertEqual(len(vim.CMDHISTORY), 1) + self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16ggzc')) + self.assertEqual(vim.current.window.cursor, (2, 0)) + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(ShowHideTestCase) diff --git a/pack/acp/start/vim-orgmode/tests/test_plugin_tags_properties.py b/pack/acp/start/vim-orgmode/tests/test_plugin_tags_properties.py new file mode 100644 index 0000000..8929ba2 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_plugin_tags_properties.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +import sys +sys.path.append(u'../ftplugin') + +import vim + +from orgmode._vim import indent_orgmode, fold_orgmode, ORGMODE + +from orgmode.py3compat.encode_compatibility import * + +ORGMODE.debug = True + +START = True +END = False + +counter = 0 +class TagsPropertiesTestCase(unittest.TestCase): + def setUp(self): + global counter + counter += 1 + vim.CMDHISTORY = [] + vim.CMDRESULTS = {} + vim.EVALHISTORY = [] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')], + u_encode(u'&ts'): u_encode(u'6'), + u_encode(u'exists("b:org_tag_column")'): u_encode(u'0'), + u_encode(u'exists("g:org_tag_column")'): u_encode(u'0'), + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("b:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u'b:changedtick'): (u_encode(u'%d' % counter)), + u_encode(u"v:count"): u_encode(u'0')} + if not u'TagsProperties' in ORGMODE.plugins: + ORGMODE.register_plugin(u'TagsProperties') + self.tagsproperties = ORGMODE.plugins[u'TagsProperties'] + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Überschrift 1 +Text 1 + +Bla bla +** Überschrift 1.1 +Text 2 + +Bla Bla bla +** Überschrift 1.2 +Text 3 + +**** Überschrift 1.2.1.falsch + +Bla Bla bla bla +*** Überschrift 1.2.1 +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n') ] + + def test_new_property(self): + u""" TODO: Docstring for test_new_property + + :returns: TODO + """ + pass + + def test_set_tags(self): + # set first tag + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u':hello:') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:')) + + # set second tag + vim.EVALRESULTS[u_encode(u'input("Tags: ", ":hello:", "customlist,Org_complete_tags")')] = u_encode(u':hello:world:') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:')) + + def test_parse_tags_no_colons_single_tag(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u'hello') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:')) + + def test_parse_tags_no_colons_multiple_tags(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u'hello:world') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:')) + + def test_parse_tags_single_colon_left_single_tag(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u':hello') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:')) + + def test_parse_tags_single_colon_left_multiple_tags(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u':hello:world') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:')) + + def test_parse_tags_single_colon_right_single_tag(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u'hello:') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:')) + + def test_parse_tags_single_colon_right_multiple_tags(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u'hello:world:') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:')) + + def test_filter_empty_tags(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u'::hello::') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:')) + + def test_delete_tags(self): + # set up + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u':hello:world:') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:')) + + # delete second of two tags + vim.EVALRESULTS[u_encode(u'input("Tags: ", ":hello:world:", "customlist,Org_complete_tags")')] = u_encode(u':hello:') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:')) + + # delete last tag + vim.EVALRESULTS[u_encode(u'input("Tags: ", ":hello:", "customlist,Org_complete_tags")')] = u_encode(u'') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1')) + + def test_realign_tags_noop(self): + vim.current.window.cursor = (2, 0) + self.tagsproperties.realign_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1')) + + def test_realign_tags_remove_spaces(self): + # remove spaces in multiple locations + vim.current.buffer[1] = u_encode(u'* Überschrift 1 ') + vim.current.window.cursor = (2, 0) + self.tagsproperties.realign_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1')) + + # remove tabs and spaces in multiple locations + vim.current.buffer[1] = u_encode(u'*\t \tÜberschrift 1 \t') + vim.current.window.cursor = (2, 0) + self.tagsproperties.realign_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1')) + + def test_realign_tags(self): + vim.current.window.cursor = (2, 0) + vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u':hello:world:') + self.tagsproperties.set_tags() + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:')) + + d = ORGMODE.get_document() + heading = d.find_current_heading() + self.assertEqual(str(heading), u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:')) + self.tagsproperties.realign_tags() + heading = d.find_current_heading() + self.assertEqual(str(heading), u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:')) + self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:')) + + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(TagsPropertiesTestCase) diff --git a/pack/acp/start/vim-orgmode/tests/test_plugin_todo.py b/pack/acp/start/vim-orgmode/tests/test_plugin_todo.py new file mode 100644 index 0000000..5cbd020 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_plugin_todo.py @@ -0,0 +1,424 @@ +# -*- coding: utf-8 -*- + + +import sys +sys.path.append(u'../ftplugin') + +import unittest +from orgmode.liborgmode.base import Direction +from orgmode.vimbuffer import VimBuffer +from orgmode.plugins.Todo import Todo + +import vim + +from orgmode.py3compat.encode_compatibility import * + +counter = 0 + +class TodoTestCase(unittest.TestCase): + u"""Tests all the functionality of the TODO module.""" + + def setUp(self): + # set content of the buffer + global counter + counter += 1 + vim.EVALHISTORY = [] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')], + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("b:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u'b:changedtick'): u_encode(u'%d' % counter), + u_encode(u"v:count"): u_encode(u'0') + } + + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Heading 1 +** Text 1 +*** Text 2 +* Text 1 +** Text 1 + some text that is + no heading + +""".split(u'\n') ] + + # toggle + def test_toggle_todo_with_no_heading(self): + # nothing should happen + vim.current.window.cursor = (1, 0) + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[0], u'') + # and repeat it -> it should not change + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[0], u'') + + def test_todo_toggle_NOTODO(self): + vim.current.window.cursor = (2, 0) + vim.current.buffer[1] = u_encode(u'** NOTODO Überschrift 1.1') + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u_encode(u'** TODO NOTODO Überschrift 1.1')) + + def test_toggle_todo_in_heading_with_no_todo_state_different_levels(self): + # level 1 + vim.current.window.cursor = (2, 0) + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u'* TODO Heading 1') + self.assertEqual((2, 0), vim.current.window.cursor) + + # level 2 + vim.current.window.cursor = (3, 0) + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[2], u'** TODO Text 1') + + # level 2 + vim.current.window.cursor = (4, 4) + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[3], u'*** TODO Text 2') + self.assertEqual((4, 9), vim.current.window.cursor) + + def test_circle_through_todo_states(self): + # * Heading 1 --> + # * TODO Heading 1 --> + # * DONE Heading 1 --> + # * Heading 1 --> + # * TODO Heading 1 --> + # * DONE Heading 1 + vim.current.window.cursor = (2, 6) + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u'* TODO Heading 1') + self.assertEqual((2, 11), vim.current.window.cursor) + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u'* DONE Heading 1') + self.assertEqual((2, 11), vim.current.window.cursor) + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u'* Heading 1') + self.assertEqual((2, 6), vim.current.window.cursor) + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u'* TODO Heading 1') + self.assertEqual((2, 11), vim.current.window.cursor) + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u'* DONE Heading 1') + self.assertEqual((2, 11), vim.current.window.cursor) + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u'* Heading 1') + self.assertEqual((2, 6), vim.current.window.cursor) + + def test_circle_through_todo_states_with_more_states(self): + # * Heading 1 --> + # * TODO Heading 1 --> + # * STARTED Heading 1 --> + # * DONE Heading 1 --> + # * Heading 1 --> + vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'STARTED'), u_encode(u'DONE'), + u_encode(u'|')] + vim.current.window.cursor = (2, 0) + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u'* TODO Heading 1') + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u'* STARTED Heading 1') + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u'* DONE Heading 1') + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[1], u'* Heading 1') + + def test_toggle_todo_with_cursor_in_text_not_heading(self): + # nothing should happen + vim.current.window.cursor = (7, 0) + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[5], u'** TODO Text 1') + self.assertEqual(vim.current.window.cursor, (7, 0)) + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[5], u'** DONE Text 1') + self.assertEqual(vim.current.window.cursor, (7, 0)) + + Todo.toggle_todo_state() + self.assertEqual(vim.current.buffer[5], u'** Text 1') + self.assertEqual(vim.current.window.cursor, (7, 0)) + + # get_states + def test_get_states_without_seperator(self): + u"""The last element in the todostates shouold be used as DONE-state when no sperator is given""" + vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'DONE')] + states_todo, states_done = VimBuffer().get_todo_states()[0] + expected_todo, expected_done = [u'TODO'], [u'DONE'] + self.assertEqual(states_todo, expected_todo) + self.assertEqual(states_done, expected_done) + + vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'INPROGRESS'), u_encode(u'DONE')] + states_todo, states_done = VimBuffer().get_todo_states()[0] + expected_todo = [u'TODO', u'INPROGRESS'] + expected_done = [u'DONE'] + self.assertEqual(states_todo, expected_todo) + self.assertEqual(states_done, expected_done) + + vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'INPROGRESS'), + u_encode(u'DUMMY'), u_encode(u'DONE')] + states_todo, states_done = VimBuffer().get_todo_states()[0] + expected_todo = [u'TODO', u'INPROGRESS', u'DUMMY'] + expected_done = [u'DONE'] + self.assertEqual(states_todo, expected_todo) + self.assertEqual(states_done, expected_done) + + def test_get_states_with_seperator(self): + vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')] + states_todo, states_done = VimBuffer().get_todo_states()[0] + expected_todo = [u'TODO'] + expected_done = [u'DONE'] + self.assertEqual(states_todo, expected_todo) + self.assertEqual(states_done, expected_done) + + vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'INPROGRESS'), u_encode(u'|'), + u_encode(u'DONE')] + states_todo, states_done = VimBuffer().get_todo_states()[0] + expected_todo = [u'TODO', u'INPROGRESS'] + expected_done = [u'DONE'] + self.assertEqual(states_todo, expected_todo) + self.assertEqual(states_done, expected_done) + + vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'INPROGRESS'), + u_encode(u'DUMMY'), u_encode(u'|'), u_encode(u'DONE')] + states_todo, states_done = VimBuffer().get_todo_states()[0] + expected_todo = [u'TODO', u'INPROGRESS', u'DUMMY'] + expected_done = [u'DONE'] + self.assertEqual(states_todo, expected_todo) + self.assertEqual(states_done, expected_done) + + vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'INPROGRESS'), + u_encode(u'DUMMY'), u_encode(u'|'), u_encode(u'DELEGATED'), u_encode(u'DONE')] + states_todo, states_done = VimBuffer().get_todo_states()[0] + expected_todo =[u'TODO', u'INPROGRESS', u'DUMMY'] + expected_done = [u'DELEGATED', u'DONE'] + self.assertEqual(states_todo, expected_todo) + self.assertEqual(states_done, expected_done) + + vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONEX'), + u_encode(u'DUMMY'), u_encode(u'DELEGATED'), u_encode(u'DONE')] + states_todo, states_done = VimBuffer().get_todo_states()[0] + expected_todo = [u'TODO'] + expected_done = [u'DONEX', u'DUMMY', u'DELEGATED', u'DONE'] + self.assertEqual(states_todo, expected_todo) + self.assertEqual(states_done, expected_done) + + vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [[u_encode(u'TODO(t)'), u_encode(u'|'), u_encode(u'DONEX')], + [u_encode(u'DUMMY'), u_encode(u'DELEGATED'), u_encode(u'DONE')]] + states_todo, states_done = VimBuffer().get_todo_states()[0] + expected_todo = [u'TODO'] + expected_done = [u'DONEX'] + self.assertEqual(states_todo, expected_todo) + self.assertEqual(states_done, expected_done) + + # get_next_state + def test_get_next_state_with_no_current_state(self): + states = [((u'TODO', ), (u'DONE', ))] + current_state = u'' + self.assertEquals(Todo._get_next_state(current_state, states), u'TODO') + + states = [((u'TODO', u'NEXT'), (u'DELEGATED', u'DONE'))] + self.assertEquals(Todo._get_next_state(current_state, states), u'TODO') + + states = [((u'NEXT', ), (u'DELEGATED', u'DONE'))] + self.assertEquals(Todo._get_next_state(current_state, states), u'NEXT') + + def test_get_next_state_backward_with_no_current_state(self): + states = [((u'TODO', ), (u'DONE', ))] + current_state = u'' + self.assertEquals(Todo._get_next_state(current_state, states, + Direction.BACKWARD), u'DONE') + + states = [((u'TODO', u'NEXT'), (u'DELEGATED', u'DONE'))] + self.assertEquals(Todo._get_next_state(current_state, states, + Direction.BACKWARD), u'DONE') + + states = [((u'NEXT', ), (u'DELEGATED', u'DONE'))] + self.assertEquals(Todo._get_next_state(current_state, states, + Direction.BACKWARD), u'DONE') + + def test_get_next_state_with_invalid_current_state(self): + states = [((u'TODO', ), (u'DONE', ))] + current_state = u'STI' + self.assertEquals(Todo._get_next_state(current_state, states), u'TODO') + + states = [((u'TODO', u'NEXT'), (u'DELEGATED', u'DONE'))] + self.assertEquals(Todo._get_next_state(current_state, states), u'TODO') + + states = [((u'NEXT', ), (u'DELEGATED', u'DONE'))] + self.assertEquals(Todo._get_next_state(current_state, states), u'NEXT') + + def test_get_next_state_backward_with_invalid_current_state(self): + states = [((u'TODO', ), (u'DONE', ))] + current_state = u'STI' + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, u'DONE') + + states = [((u'TODO', u'NEXT'), (u'DELEGATED', u'DONE'))] + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, u'DONE') + + states = [((u'NEXT', ), (u'DELEGATED', u'DONE'))] + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, u'DONE') + + def test_get_next_state_with_current_state_equals_todo_state(self): + states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE'))] + current_state = u'TODO' + self.assertEquals(Todo._get_next_state(current_state, states), u'NEXT') + + current_state = u'NEXT' + self.assertEquals(Todo._get_next_state(current_state, states), u'NOW') + + def test_get_next_state_backward_with_current_state_equals_todo_state(self): + states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE'))] + current_state = u'TODO' + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, None) + + def test_get_next_state_backward_misc(self): + states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE'))] + current_state = u'DONE' + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, u'DELEGATED') + + current_state = u'DELEGATED' + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, u'NOW') + + current_state = u'NOW' + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, u'NEXT') + + current_state = u'NEXT' + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, u'TODO') + + current_state = u'TODO' + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, None) + + current_state = None + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, u'DONE') + + def test_get_next_state_with_jump_from_todo_to_done(self): + states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE'))] + current_state = u'NOW' + self.assertEquals(Todo._get_next_state(current_state, states), u'DELEGATED') + + def test_get_next_state_with_jump_from_done_to_todo(self): + states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE'))] + current_state = u'DONE' + self.assertEquals(Todo._get_next_state(current_state, states), None) + + def test_get_next_state_in_current_sequence(self): + states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE')), ((u'QA', ), (u'RELEASED', ))] + current_state = u'QA' + result = Todo._get_next_state(current_state, states, + Direction.FORWARD) + self.assertEquals(result, u'RELEASED') + + def test_get_next_state_in_current_sequence_with_access_keys(self): + states = [((u'TODO(t)', u'NEXT(n)', u'NOW(w)'), (u'DELEGATED(g)', u'DONE(d)')), ((u'QA(q)', ), (u'RELEASED(r)', ))] + current_state = u'QA' + result = Todo._get_next_state(current_state, states, + Direction.FORWARD) + self.assertEquals(result, u'RELEASED') + + current_state = u'NEXT' + result = Todo._get_next_state(current_state, states, + Direction.FORWARD) + self.assertEquals(result, u'NOW') + + current_state = u'TODO' + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, None) + + current_state = None + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD) + self.assertEquals(result, u'RELEASED') + + def test_get_next_keyword_sequence(self): + states = [((u'TODO(t)', u'NEXT(n)', u'NOW(w)'), (u'DELEGATED(g)', u'DONE(d)')), ((u'QA(q)', ), (u'RELEASED(r)', ))] + current_state = None + result = Todo._get_next_state(current_state, states, + Direction.FORWARD, next_set=True) + self.assertEquals(result, u'TODO') + + current_state = None + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD, next_set=True) + self.assertEquals(result, u'QA') + + current_state = u'TODO' + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD, next_set=True) + self.assertEquals(result, None) + + current_state = u'TODO' + result = Todo._get_next_state(current_state, states, + Direction.FORWARD, next_set=True) + self.assertEquals(result, u'QA') + + current_state = u'NOW' + result = Todo._get_next_state(current_state, states, + Direction.FORWARD, next_set=True) + self.assertEquals(result, u'QA') + + current_state = u'DELEGATED' + result = Todo._get_next_state(current_state, states, + Direction.FORWARD, next_set=True) + self.assertEquals(result, u'QA') + + current_state = u'QA' + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD, next_set=True) + self.assertEquals(result, u'TODO') + + current_state = u'QA' + result = Todo._get_next_state(current_state, states, + Direction.FORWARD, next_set=True) + self.assertEquals(result, None) + + current_state = u'RELEASED' + result = Todo._get_next_state(current_state, states, + Direction.FORWARD, next_set=True) + self.assertEquals(result, None) + + current_state = u'RELEASED' + result = Todo._get_next_state(current_state, states, + Direction.BACKWARD, next_set=True) + self.assertEquals(result, u'TODO') + + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(TodoTestCase) + +# vi: noexpandtab diff --git a/pack/acp/start/vim-orgmode/tests/test_vimbuffer.py b/pack/acp/start/vim-orgmode/tests/test_vimbuffer.py new file mode 100644 index 0000000..423276d --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/test_vimbuffer.py @@ -0,0 +1,1257 @@ +# -*- coding: utf-8 -*- + +import unittest +import sys +sys.path.append(u'../ftplugin') + +import vim + +from orgmode.liborgmode.headings import Heading +from orgmode.vimbuffer import VimBuffer + +from orgmode.py3compat.encode_compatibility import * +from orgmode.py3compat.unicode_compatibility import * + +counter = 0 +class VimBufferTestCase(unittest.TestCase): + def setUp(self): + global counter + counter += 1 + vim.CMDHISTORY = [] + vim.CMDRESULTS = {} + vim.EVALHISTORY = [] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), + u_encode(u'DONE'), u_encode(u'|')], + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u'b:changedtick'): u_encode(u'%d' % counter), + u_encode(u'&ts'): u_encode(u'8'), + u_encode(u'exists("g:org_tag_column")'): u_encode(u'0'), + u_encode(u'exists("b:org_tag_column")'): u_encode(u'0'), + u_encode(u"v:count"): u_encode(u'0')} + vim.current.buffer[:] = [u_encode(i) for i in u"""#Meta information +#more meta information +* Überschrift 1 +Text 1 + +Bla bla +** Überschrift 1.1 +Text 2 + +Bla Bla bla +** Überschrift 1.2 +Text 3 + +**** Überschrift 1.2.1.falsch + +Bla Bla bla bla +*** Überschrift 1.2.1 +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n') ] + self.document = VimBuffer().init_dom() + + def test_write_heading_tags(self): + self.assertEqual(self.document.is_dirty, False) + h = self.document.find_heading() + self.assertEqual(h._orig_start, 2) + self.assertEqual(h.title, u'Überschrift 1') + h.tags = [u'test', u'tag'] + self.assertEqual(h.tags[0], u'test') + self.document.write_heading(h) + + # sanity check + d = VimBuffer().init_dom() + h2 = self.document.find_heading() + self.assertEqual(d.headings[0].title, u'Überschrift 1') + self.assertEqual(len(d.headings[0].tags), 2) + self.assertEqual(d.headings[0].tags[0], u'test') + self.assertEqual(d.headings[0]._orig_start, 2) + self.assertEqual(d.headings[0].children[0]._orig_start, 6) + + def test_write_multi_heading_bodies(self): + self.assertEqual(self.document.is_dirty, False) + h = self.document.headings[0].copy() + self.assertEqual(h._orig_start, 2) + self.assertEqual(h.title, u'Überschrift 1') + h.body.append(u'test') + h.children[0].body.append(u'another line') + self.document.write_heading(h) + + # sanity check + d = VimBuffer().init_dom() + h2 = self.document.find_heading() + self.assertEqual(len(d.headings[0].body), 4) + self.assertEqual(d.headings[0]._orig_start, 2) + self.assertEqual(d.headings[0].children[0]._orig_start, 7) + self.assertEqual(d.headings[0].children[0].title, u'Überschrift 1.1') + self.assertEqual(len(d.headings[0].children[0].body), 4) + self.assertEqual(d.headings[0].children[1]._orig_start, 12) + self.assertEqual(d.headings[0].children[1].title, u'Überschrift 1.2') + self.assertEqual(len(d.headings[0].children[1].body), 2) + + def test_meta_information_assign_directly(self): + # read meta information from document + self.assertEqual(u'\n'.join(self.document.meta_information), u'#Meta information\n#more meta information') + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].start, 2) + + # assign meta information directly to an element in array + self.document.meta_information[0] = u'#More or less meta information' + self.assertEqual(u'\n'.join(self.document.meta_information), u'#More or less meta information\n#more meta information') + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.is_dirty_meta_information, True) + self.assertEqual(self.document.headings[0].start, 2) + + def test_meta_information_assign_string(self): + # assign a single line string + self.document.meta_information = u'#Less meta information' + self.assertEqual('\n'.join(self.document.meta_information), u'#Less meta information') + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.is_dirty_meta_information, True) + self.assertEqual(self.document.headings[0].start, 1) + + def test_meta_information_assign_multi_line_string(self): + # assign a multi line string + self.document.meta_information = u'#Less meta information\n#lesser information' + self.assertEqual(u'\n'.join(self.document.meta_information), u'#Less meta information\n#lesser information') + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.is_dirty_meta_information, True) + self.assertEqual(self.document.headings[0].start, 2) + + def test_meta_information_assign_one_element_array(self): + # assign a single element array of strings + self.document.meta_information = u'#More or less meta information'.split(u'\n') + self.assertEqual(u'\n'.join(self.document.meta_information), u'#More or less meta information') + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.is_dirty_meta_information, True) + self.assertEqual(self.document.headings[0].start, 1) + + def test_meta_information_assign_multi_element_array(self): + # assign a multi element array of strings + self.document.meta_information = u'#More or less meta information\n#lesser information'.split(u'\n') + self.assertEqual(u'\n'.join(self.document.meta_information), u'#More or less meta information\n#lesser information') + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.is_dirty_meta_information, True) + self.assertEqual(self.document.headings[0].start, 2) + + def test_meta_information_read_no_meta_information(self): + vim.current.buffer[:] = [ u_encode(i) for i in u"""* Überschrift 1 +Text 1 + +Bla bla +** Überschrift 1.1 +Text 2 + +Bla Bla bla +** Überschrift 1.2 +Text 3 + +**** Überschrift 1.2.1.falsch + +Bla Bla bla bla +*** Überschrift 1.2.1 +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n') ] + self.document = VimBuffer().init_dom() + + # read no meta information from document + self.assertEqual(self.document.meta_information, []) + self.assertEqual(self.document.headings[0].start, 0) + self.assertEqual(self.document.is_dirty, False) + + # assign meta information to a former empty field + self.document.meta_information = u'#More or less meta information\n#lesser information'.split('\n') + self.assertEqual(u'\n'.join(self.document.meta_information), u'#More or less meta information\n#lesser information') + self.assertEqual(self.document.headings[0].start, 2) + self.assertEqual(self.document.is_dirty, True) + + def test_meta_information_assign_empty_array(self): + # assign an empty array as meta information + self.document.meta_information = [] + self.assertEqual(self.document.meta_information, []) + self.assertEqual(self.document.headings[0].start, 0) + self.assertEqual(self.document.is_dirty, True) + + def test_meta_information_assign_empty_string(self): + # assign an empty string as meta information + self.document.meta_information = u'' + self.assertEqual(self.document.meta_information, [u'']) + self.assertEqual(self.document.headings[0].start, 1) + self.assertEqual(self.document.is_dirty, True) + + def test_bufnr(self): + self.assertEqual(self.document.bufnr, vim.current.buffer.number) + # TODO add more tests as soon as multi buffer support has been implemented + + def test_write_meta_information(self): + # write nothing + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.write(), False) + self.assertEqual(u'\n'.join(self.document.meta_information), u'#Meta information\n#more meta information') + + # write changed meta information + self.assertEqual(self.document.is_dirty, False) + self.document.meta_information = u'#More or less meta information\n#lesser information'.split('\n') + self.assertEqual(u'\n'.join(self.document.meta_information), u'#More or less meta information\n#lesser information') + self.assertEqual(self.document.headings[0].start, 2) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].start, 2) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(u'\n'.join(VimBuffer().init_dom().meta_information), u'#More or less meta information\n#lesser information') + + # shorten meta information + self.assertEqual(self.document.is_dirty, False) + self.document.meta_information = u'!More or less meta information'.split(u'\n') + self.assertEqual(u'\n'.join(self.document.meta_information), u'!More or less meta information') + self.assertEqual(self.document.headings[0].start, 1) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].start, 1) + self.assertEqual(self.document.headings[0]._orig_start, 1) + self.assertEqual(u'\n'.join(VimBuffer().init_dom().meta_information), u'!More or less meta information') + + # lengthen meta information + self.assertEqual(self.document.is_dirty, False) + self.document.meta_information = u'!More or less meta information\ntest\ntest' + self.assertEqual(u'\n'.join(self.document.meta_information), u'!More or less meta information\ntest\ntest') + self.assertEqual(self.document.headings[0].start, 3) + self.assertEqual(self.document.headings[0]._orig_start, 1) + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].start, 3) + self.assertEqual(self.document.headings[0]._orig_start, 3) + self.assertEqual(u'\n'.join(VimBuffer().init_dom().meta_information), u'!More or less meta information\ntest\ntest') + + # write empty meta information + self.assertEqual(self.document.is_dirty, False) + self.document.meta_information = [] + self.assertEqual(self.document.meta_information, []) + self.assertEqual(self.document.headings[0].start, 0) + self.assertEqual(self.document.headings[0]._orig_start, 3) + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].start, 0) + self.assertEqual(self.document.headings[0]._orig_start, 0) + self.assertEqual(VimBuffer().init_dom().meta_information, []) + + def test_write_changed_title(self): + # write a changed title + self.document.headings[0].title = u'Heading 1' + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.is_dirty_meta_information, False) + self.assertEqual(self.document.headings[0].is_dirty_body, False) + self.assertEqual(self.document.headings[0].is_dirty_heading, True) + self.assertEqual(self.document.headings[0].title, u'Heading 1') + self.assertEqual(self.document.headings[0].end, 5) + self.assertEqual(len(self.document.headings[0]), 4) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 4) + self.assertEqual(self.document.headings[0].children[0].start, 6) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 6) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].end, 5) + self.assertEqual(len(self.document.headings[0]), 4) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 4) + self.assertEqual(self.document.headings[0].children[0].start, 6) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 6) + self.assertEqual(VimBuffer().init_dom().headings[0].title, u'Heading 1') + + def test_write_changed_body(self): + # write a changed body + self.assertEqual(self.document.headings[0].end, 5) + self.document.headings[0].body[0] = u'Another text' + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.is_dirty_meta_information, False) + self.assertEqual(self.document.headings[0].is_dirty_body, True) + self.assertEqual(self.document.headings[0].is_dirty_heading, False) + self.assertEqual(self.document.headings[0].end, 5) + self.assertEqual(len(self.document.headings[0]), 4) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 4) + self.assertEqual(self.document.headings[0].children[0].start, 6) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 6) + self.assertEqual(self.document.headings[0].body, [u'Another text', u'', u'Bla bla']) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].end, 5) + self.assertEqual(len(self.document.headings[0]), 4) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 4) + self.assertEqual(self.document.headings[0].children[0].start, 6) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 6) + self.assertEqual(VimBuffer().init_dom().headings[0].body, [u'Another text', u'', u'Bla bla']) + + def test_write_shortened_body(self): + # write a shortened body + self.document.headings[0].body = u'Another text' + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.is_dirty_meta_information, False) + self.assertEqual(self.document.headings[0].is_dirty_body, True) + self.assertEqual(self.document.headings[0].is_dirty_heading, False) + self.assertEqual(self.document.headings[0].end, 3) + self.assertEqual(len(self.document.headings[0]), 2) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 4) + self.assertEqual(self.document.headings[0].children[0].start, 4) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 6) + self.assertEqual(self.document.headings[0].body, [u'Another text']) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].end, 3) + self.assertEqual(len(self.document.headings[0]), 2) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 2) + self.assertEqual(self.document.headings[0].children[0].start, 4) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 4) + self.assertEqual(VimBuffer().init_dom().headings[0].body, [u'Another text']) + + def test_write_lengthened_body(self): + # write a lengthened body + self.document.headings[0].body = [u'Another text', u'more', u'and more', u'and more'] + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.is_dirty_meta_information, False) + self.assertEqual(self.document.headings[0].is_dirty_body, True) + self.assertEqual(self.document.headings[0].is_dirty_heading, False) + self.assertEqual(self.document.headings[0].end, 6) + self.assertEqual(len(self.document.headings[0]), 5) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 4) + self.assertEqual(self.document.headings[0].children[0].start, 7) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 6) + self.assertEqual(self.document.headings[0].body, [u'Another text', u'more', u'and more', u'and more']) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].end, 6) + self.assertEqual(len(self.document.headings[0]), 5) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 5) + self.assertEqual(self.document.headings[0].children[0].start, 7) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 7) + self.assertEqual(VimBuffer().init_dom().headings[0].body, [u'Another text', u'more', u'and more', u'and more']) + + def test_write_delete_heading(self): + # delete a heading + self.assertEqual(len(self.document.headings[0].children), 2) + del self.document.headings[0].children[0] + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(len(self.document.headings[0].children), 1) + self.assertEqual(self.document.headings[0].end, 5) + self.assertEqual(len(self.document.headings[0]), 4) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 4) + self.assertEqual(self.document.headings[0].children[0].start, 6) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 10) + self.assertEqual(self.document.headings[0].children[0]._orig_len, 3) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].end, 5) + self.assertEqual(len(self.document.headings[0]), 4) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 4) + self.assertEqual(self.document.headings[0].children[0].start, 6) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 6) + self.assertEqual(self.document.headings[0].children[0]._orig_len, 3) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(self.document.headings[0].children), 1) + self.assertEqual(self.document.headings[0].end, 5) + self.assertEqual(d.headings[0].end, 5) + self.assertEqual(len(d.headings[0]), 4) + self.assertEqual(d.headings[0]._orig_start, 2) + self.assertEqual(d.headings[0]._orig_len, 4) + self.assertEqual(d.headings[0].children[0].start, 6) + self.assertEqual(d.headings[0].children[0]._orig_start, 6) + self.assertEqual(d.headings[0].children[0]._orig_len, 3) + + def test_write_delete_first_heading(self): + # delete the first heading + self.assertEqual(len(self.document.headings), 3) + del self.document.headings[0] + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(len(self.document.headings), 2) + self.assertEqual(self.document.headings[0].end, 2) + self.assertEqual(len(self.document.headings[0]), 1) + self.assertEqual(self.document.headings[0]._orig_start, 17) + self.assertEqual(self.document.headings[0]._orig_len, 1) + self.assertEqual(self.document.headings[1].start, 3) + self.assertEqual(self.document.headings[1]._orig_start, 18) + self.assertEqual(self.document.headings[1]._orig_len, 3) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].end, 2) + self.assertEqual(len(self.document.headings[0]), 1) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 1) + self.assertEqual(self.document.headings[1].start, 3) + self.assertEqual(self.document.headings[1]._orig_start, 3) + self.assertEqual(self.document.headings[1]._orig_len, 3) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(self.document.headings), 2) + self.assertEqual(d.headings[0].end, 2) + self.assertEqual(len(d.headings[0]), 1) + self.assertEqual(d.headings[0]._orig_start, 2) + self.assertEqual(d.headings[0]._orig_len, 1) + self.assertEqual(d.headings[1].start, 3) + self.assertEqual(d.headings[1]._orig_start, 3) + self.assertEqual(d.headings[1]._orig_len, 3) + + def test_write_delete_last_heading(self): + # delete the last heading + self.assertEqual(len(self.document.headings), 3) + del self.document.headings[-1] + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(len(self.document.headings), 2) + self.assertEqual(self.document.headings[0].end, 5) + self.assertEqual(self.document.headings[0].end_of_last_child, 16) + self.assertEqual(len(self.document.headings[0]), 4) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 4) + self.assertEqual(self.document.headings[-1].start, 17) + self.assertEqual(self.document.headings[-1]._orig_start, 17) + self.assertEqual(self.document.headings[-1]._orig_len, 1) + self.assertEqual(self.document.headings[-1].end, 17) + self.assertEqual(self.document.headings[-1].end_of_last_child, 17) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].end, 5) + self.assertEqual(self.document.headings[0].end_of_last_child, 16) + self.assertEqual(len(self.document.headings[0]), 4) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0]._orig_len, 4) + self.assertEqual(self.document.headings[-1].start, 17) + self.assertEqual(self.document.headings[-1]._orig_start, 17) + self.assertEqual(self.document.headings[-1]._orig_len, 1) + self.assertEqual(self.document.headings[-1].end, 17) + self.assertEqual(self.document.headings[-1].end_of_last_child, 17) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(self.document.headings), 2) + self.assertEqual(d.headings[0].end, 5) + self.assertEqual(d.headings[0].end_of_last_child, 16) + self.assertEqual(len(d.headings[0]), 4) + self.assertEqual(d.headings[0]._orig_start, 2) + self.assertEqual(d.headings[0]._orig_len, 4) + self.assertEqual(d.headings[-1].start, 17) + self.assertEqual(d.headings[-1]._orig_start, 17) + self.assertEqual(d.headings[-1]._orig_len, 1) + self.assertEqual(d.headings[-1].end, 17) + self.assertEqual(d.headings[-1].end_of_last_child, 17) + + def test_write_delete_multiple_headings(self): + # delete multiple headings + self.assertEqual(len(self.document.headings), 3) + del self.document.headings[1] + del self.document.headings[0].children[1].children[0] + del self.document.headings[0].children[0] + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(len(self.document.headings), 2) + self.assertEqual(len(self.document.headings[0].children), 1) + self.assertEqual(len(self.document.headings[0].children[0].children), 1) + self.assertEqual(self.document.headings[0].end, 5) + self.assertEqual(self.document.headings[0].end_of_last_child, 9) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 10) + self.assertEqual(self.document.headings[0].children[0].children[0]._orig_start, 16) + self.assertEqual(self.document.headings[-1]._orig_start, 18) + self.assertEqual(self.document.headings[0].start, 2) + self.assertEqual(self.document.headings[0].children[0].start, 6) + self.assertEqual(self.document.headings[0].children[0].children[0].start, 9) + self.assertEqual(self.document.headings[-1].start, 10) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].end, 5) + self.assertEqual(self.document.headings[0].end_of_last_child, 9) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(self.document.headings[0].children[0]._orig_start, 6) + self.assertEqual(self.document.headings[0].children[0].children[0]._orig_start, 9) + self.assertEqual(self.document.headings[-1]._orig_start, 10) + self.assertEqual(self.document.headings[0].start, 2) + self.assertEqual(self.document.headings[0].children[0].start, 6) + self.assertEqual(self.document.headings[0].children[0].children[0].start, 9) + self.assertEqual(self.document.headings[-1].start, 10) + self.assertEqual(self.document.headings[0].title, u'Überschrift 1') + self.assertEqual(self.document.headings[0].children[0].title, u'Überschrift 1.2') + self.assertEqual(self.document.headings[0].children[0].children[0].title, u'Überschrift 1.2.1') + self.assertEqual(self.document.headings[-1].title, u'Überschrift 3') + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(self.document.headings), 2) + self.assertEqual(len(self.document.headings[0].children), 1) + self.assertEqual(len(self.document.headings[0].children[0].children), 1) + self.assertEqual(d.headings[0].end, 5) + self.assertEqual(d.headings[0].end_of_last_child, 9) + self.assertEqual(d.headings[0]._orig_start, 2) + self.assertEqual(d.headings[0].children[0]._orig_start, 6) + self.assertEqual(d.headings[0].children[0].children[0]._orig_start, 9) + self.assertEqual(d.headings[-1]._orig_start, 10) + self.assertEqual(d.headings[0].start, 2) + self.assertEqual(d.headings[0].children[0].start, 6) + self.assertEqual(d.headings[0].children[0].children[0].start, 9) + self.assertEqual(d.headings[-1].start, 10) + self.assertEqual(d.headings[0].title, u'Überschrift 1') + self.assertEqual(d.headings[0].children[0].title, u'Überschrift 1.2') + self.assertEqual(d.headings[0].children[0].children[0].title, u'Überschrift 1.2.1') + self.assertEqual(d.headings[-1].title, u'Überschrift 3') + + + def test_write_add_heading(self): + # add a heading + self.assertEqual(len(self.document.headings), 3) + self.assertEqual(len(self.document.headings[0].children), 2) + h = Heading() + h.title = u'Test heading' + h.level = 2 + h.body = u'Text, text\nmore text' + self.document.headings[0].children.append(h) + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(len(self.document.headings[0].children), 3) + self.assertEqual(self.document.headings[0].children[-1].title, u'Test heading') + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(len(self.document.headings[0].children), 3) + self.assertEqual(self.document.headings[0].children[-1].title, u'Test heading') + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(d.headings[0].children), 3) + self.assertEqual(d.headings[0].children[-1].title, u'Test heading') + + def test_write_add_heading_before_first_heading(self): + # add a heading before the first heading + self.assertEqual(len(self.document.headings), 3) + h = Heading() + h.title = u'Test heading' + h.level = 2 + h.body = u'Text, text\nmore text' + self.assertEqual(h.start, None) + self.document.headings[0:0] = h + self.assertEqual(h.start, 2) + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(len(self.document.headings), 4) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].title, u'Test heading') + self.assertEqual(self.document.headings[0].start, 2) + self.assertEqual(self.document.headings[0]._orig_start, 2) + self.assertEqual(len(self.document.headings[0]), 3) + self.assertEqual(self.document.headings[1].title, u'Überschrift 1') + self.assertEqual(self.document.headings[1].start, 5) + self.assertEqual(len(self.document.headings[1]), 4) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(d.headings), 4) + self.assertEqual(d.headings[0].title, u'Test heading') + self.assertEqual(d.headings[0].start, 2) + self.assertEqual(d.headings[0]._orig_start, 2) + self.assertEqual(len(d.headings[0]), 3) + self.assertEqual(d.headings[1].title, u'Überschrift 1') + self.assertEqual(d.headings[1].start, 5) + self.assertEqual(len(d.headings[1]), 4) + + def test_write_add_heading_after_last_heading_toplevel(self): + # add a heading after the last heading (top level heading) + self.assertEqual(len(self.document.headings), 3) + h = Heading() + h.title = u'Test heading' + h.body = u'Text, text\nmore text' + self.assertEqual(h.start, None) + #self.document.headings += h + self.document.headings.append(h) + self.assertEqual(h.start, 21) + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(len(self.document.headings), 4) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[-1].title, u'Test heading') + self.assertEqual(self.document.headings[-1].start, 21) + self.assertEqual(self.document.headings[-1]._orig_start, 21) + self.assertEqual(len(self.document.headings[-1]), 3) + self.assertEqual(self.document.headings[-2].title, u'Überschrift 3') + self.assertEqual(self.document.headings[-2].start, 18) + self.assertEqual(len(self.document.headings[-2]), 3) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(d.headings), 4) + self.assertEqual(d.headings[-1].title, u'Test heading') + self.assertEqual(d.headings[-1].start, 21) + self.assertEqual(d.headings[-1]._orig_start, 21) + self.assertEqual(len(d.headings[-1]), 3) + self.assertEqual(d.headings[-2].title, u'Überschrift 3') + self.assertEqual(d.headings[-2].start, 18) + self.assertEqual(len(d.headings[-2]), 3) + + def test_write_add_heading_after_last_heading_subheading(self): + # add a heading after the last heading (subheading) + self.assertEqual(len(self.document.headings), 3) + h = Heading() + h.title = u'Test heading' + h.level = 2 + h.body = u'Text, text\nmore text' + self.assertEqual(h.start, None) + # TODO make it work with += operator so far it works with append and + # extend so it seems that there is a problem in __iadd__ method in + # UserList from collection in python3 + #self.document.headings[-1].children += h + #self.document.headings[-1].children.extend([h]) + self.document.headings[-1].children.append(h) + self.assertEqual(h.start, 21) + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(len(self.document.headings), 3) + self.assertEqual(len(self.document.headings[-1]), 3) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[-1].children[-1].title, u'Test heading') + self.assertEqual(self.document.headings[-1].children[-1].start, 21) + self.assertEqual(self.document.headings[-1].children[-1]._orig_start, 21) + self.assertEqual(len(self.document.headings[-1].children[-1]), 3) + self.assertEqual(self.document.headings[-1].title, u'Überschrift 3') + self.assertEqual(self.document.headings[-1].start, 18) + self.assertEqual(len(self.document.headings[-1]), 3) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(d.headings), 3) + self.assertEqual(len(d.headings[-1]), 3) + self.assertEqual(d.headings[-1].children[-1].title, u'Test heading') + self.assertEqual(d.headings[-1].children[-1].start, 21) + self.assertEqual(d.headings[-1].children[-1]._orig_start, 21) + self.assertEqual(len(d.headings[-1].children[-1]), 3) + self.assertEqual(d.headings[-1].title, u'Überschrift 3') + self.assertEqual(d.headings[-1].start, 18) + self.assertEqual(len(d.headings[-1]), 3) + + def test_write_replace_one_heading(self): + # replace subheadings by a list of newly created headings (one item) + self.assertEqual(len(self.document.headings), 3) + h = Heading() + h.title = u'Test heading' + h.level = 3 + h.body = u'Text, text\nmore text\nanother text' + self.assertEqual(h.start, None) + self.document.headings[0].children[1].children[0] = h + self.assertEqual(h.start, 13) + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(len(self.document.headings), 3) + self.assertEqual(len(self.document.headings[0].children[1].children), 2) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].children[1].children[0].title, u'Test heading') + self.assertEqual(self.document.headings[0].children[1].children[0].start, 13) + self.assertEqual(self.document.headings[0].children[1].children[0]._orig_start, 13) + self.assertEqual(len(self.document.headings[0].children[1].children[0]), 4) + self.assertEqual(len(self.document.headings[0].children[1].children[0].children), 0) + self.assertEqual(len(self.document.headings[0].children[1]), 3) + self.assertEqual(len(self.document.headings[0].children[0].children), 0) + self.assertEqual(len(self.document.headings[1].children), 0) + self.assertEqual(self.document.headings[0].children[1].children[-1].title, u'Überschrift 1.2.1') + self.assertEqual(self.document.headings[0].children[1].children[-1].start, 17) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(d.headings), 3) + self.assertEqual(len(d.headings[0].children[1].children), 2) + self.assertEqual(d.headings[0].children[1].children[0].title, u'Test heading') + self.assertEqual(d.headings[0].children[1].children[0].start, 13) + self.assertEqual(d.headings[0].children[1].children[0]._orig_start, 13) + self.assertEqual(len(d.headings[0].children[1].children[0]), 4) + self.assertEqual(len(d.headings[0].children[1].children[0].children), 0) + self.assertEqual(len(d.headings[0].children[1]), 3) + self.assertEqual(len(d.headings[0].children[0].children), 0) + self.assertEqual(len(d.headings[1].children), 0) + self.assertEqual(d.headings[0].children[1].children[-1].title, u'Überschrift 1.2.1') + self.assertEqual(d.headings[0].children[1].children[-1].start, 17) + + def test_write_replace_multiple_headings_with_one_heading(self): + # replace subheadings by a list of newly created headings (one item) + self.assertEqual(len(self.document.headings), 3) + h = Heading() + h.title = u'Test heading' + h.level = 3 + h.body = u'Text, text\nmore text\nanother text' + + self.assertEqual(h.start, None) + self.assertEqual(len(self.document.headings[0].children[1].children), 2) + self.document.headings[0].children[1].children[:] = h + self.assertEqual(h.start, 13) + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.headings[0].children[1].is_dirty, False) + self.assertEqual(len(self.document.headings), 3) + self.assertEqual(len(self.document.headings[0].children[1].children), 1) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].children[1].title, u'Überschrift 1.2') + self.assertEqual(self.document.headings[0].children[1].children[0].title, u'Test heading') + self.assertEqual(self.document.headings[0].children[1].children[0].start, 13) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(d.headings[0].children[1].children), 1) + self.assertEqual(d.headings[0].children[1].title, u'Überschrift 1.2') + self.assertEqual(d.headings[0].children[1].children[0].title, u'Test heading') + self.assertEqual(d.headings[0].children[1].children[0].start, 13) + + def test_write_replace_multiple_headings_with_a_multiple_heading_structure(self): + # replace subheadings by a list of newly created headings (multiple items) + self.assertEqual(len(self.document.headings), 3) + h = Heading() + h.title = u'Test heading' + h.level = 3 + h.body = u'Text, text\nmore text\nanother text' + h1 = Heading() + h1.title = u'another heading' + h1.level = 4 + h1.body = u'This\nIs\nJust more\ntext' + h.children.append(h1) + h2 = Heading() + h2.title = u'yet another heading' + h2.level = 3 + h2.body = u'This\nis less text' + + self.assertEqual(h.start, None) + self.document.headings[0].children[1].children[:] = (h, h2) + self.assertEqual(h.start, 13) + self.assertEqual(h1.start, 17) + self.assertEqual(h2.start, 22) + self.assertEqual(self.document.is_dirty, True) + self.assertEqual(self.document.headings[0].children[1].is_dirty, False) + self.assertEqual(len(self.document.headings), 3) + self.assertEqual(len(self.document.headings[0].children[1].children), 2) + self.assertEqual(len(self.document.headings[0].children[1].children[0].children), 1) + self.assertEqual(len(self.document.headings[0].children[1].children[1].children), 0) + + self.assertEqual(self.document.write(), True) + self.assertEqual(self.document.is_dirty, False) + self.assertEqual(self.document.headings[0].children[1].title, u'Überschrift 1.2') + self.assertEqual(self.document.headings[0].children[1].children[0].title, u'Test heading') + self.assertEqual(self.document.headings[0].children[1].children[0].children[0].title, u'another heading') + self.assertEqual(self.document.headings[0].children[1].children[1].title, u'yet another heading') + self.assertEqual(self.document.headings[0].children[1].children[0].start, 13) + self.assertEqual(self.document.headings[0].children[1].children[0].children[0].start, 17) + self.assertEqual(self.document.headings[0].children[1].children[1].start, 22) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(d.headings[0].children[1].title, u'Überschrift 1.2') + self.assertEqual(d.headings[0].children[1].children[0].title, u'Test heading') + self.assertEqual(d.headings[0].children[1].children[0].children[0].title, u'another heading') + self.assertEqual(d.headings[0].children[1].children[1].title, u'yet another heading') + self.assertEqual(d.headings[0].children[1].children[0].start, 13) + self.assertEqual(d.headings[0].children[1].children[0].children[0].start, 17) + self.assertEqual(d.headings[0].children[1].children[1].start, 22) + + def test_dom(self): + self.assertEqual(len(self.document.headings), 3) + for h in self.document.headings: + self.assertEqual(h.level, 1) + self.assertEqual(len(self.document.headings[0].children), 2) + self.assertEqual(len(self.document.headings[0].children[0].children), 0) + self.assertEqual(len(self.document.headings[0].children[1].children), 2) + self.assertEqual(len(self.document.headings[0].children[1].children[0].children), 0) + self.assertEqual(len(self.document.headings[0].children[1].children[1].children), 0) + self.assertEqual(len(self.document.headings[1].children), 0) + self.assertEqual(len(self.document.headings[2].children), 0) + + # test no heading + vim.current.window.cursor = (1, 0) + h = self.document.current_heading() + self.assertEqual(h, None) + + def test_index_boundaries(self): + # test index boundaries + vim.current.window.cursor = (-1, 0) + h = self.document.current_heading() + self.assertEqual(h, None) + + vim.current.window.cursor = (21, 0) + h = self.document.current_heading() + self.assertNotEqual(h, None) + self.assertEqual(h.level, 1) + self.assertEqual(h.start, 18) + self.assertNotEqual(h.previous_sibling, None) + self.assertEqual(h.previous_sibling.level, 1) + self.assertEqual(h.parent, None) + self.assertEqual(h.next_sibling, None) + self.assertEqual(len(h.children), 0) + + vim.current.window.cursor = (999, 0) + h = self.document.current_heading() + self.assertEqual(h, None) + + def test_heading_start_and_end(self): + # test heading start and end + vim.current.window.cursor = (3, 0) + h = self.document.current_heading() + self.assertNotEqual(h, None) + self.assertEqual(h.start, 2) + self.assertEqual(h.end, 5) + self.assertEqual(h.end_of_last_child, 16) + + vim.current.window.cursor = (12, 0) + h = self.document.current_heading() + self.assertNotEqual(h, None) + self.assertEqual(h.start, 10) + self.assertEqual(h.end, 12) + self.assertEqual(h.end_of_last_child, 16) + + vim.current.window.cursor = (19, 0) + h = self.document.current_heading() + self.assertNotEqual(h, None) + self.assertEqual(h.start, 18) + self.assertEqual(h.end, 20) + self.assertEqual(h.end_of_last_child, 20) + + vim.current.buffer[:] = [ u_encode(i) for i in u""" +** Überschrift 1.2 +Text 3 + +**** Überschrift 1.2.1.falsch + +Bla Bla bla bla +*** Überschrift 1.2.1 +* Überschrift 2 +* Überschrift 3 + asdf sdf +""".split(u'\n') ] + self.document = VimBuffer().init_dom() + vim.current.window.cursor = (3, 0) + h = self.document.current_heading() + self.assertNotEqual(h, None) + self.assertEqual(h.parent, None) + self.assertEqual(h.level, 2) + self.assertEqual(h.title, u'Überschrift 1.2') + self.assertEqual(len(h.children), 2) + self.assertEqual(h.children[1].start, 7) + self.assertEqual(h.children[1].children, []) + self.assertEqual(h.children[1].next_sibling, None) + self.assertEqual(h.children[1].end, 7) + self.assertEqual(h.start, 1) + self.assertEqual(h.end, 3) + self.assertEqual(h.end_of_last_child, 7) + + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* Überschrift 2 +* Überschrift 3""".split(u'\n') ] + self.document = VimBuffer().init_dom() + vim.current.window.cursor = (3, 0) + h = self.document.current_heading() + self.assertNotEqual(h, None) + self.assertEqual(h.end, 2) + self.assertEqual(h.end_of_last_child, 2) + self.assertEqual(h.title, u'Überschrift 3') + + def test_first_heading(self): + # test first heading + vim.current.window.cursor = (3, 0) + h = self.document.current_heading() + + self.assertNotEqual(h, None) + self.assertEqual(h.parent, None) + self.assertEqual(h.level, 1) + self.assertEqual(len(h.children), 2) + self.assertEqual(h.previous_sibling, None) + + self.assertEqual(h.children[0].level, 2) + self.assertEqual(h.children[0].children, []) + self.assertEqual(h.children[1].level, 2) + self.assertEqual(len(h.children[1].children), 2) + self.assertEqual(h.children[1].children[0].level, 4) + self.assertEqual(h.children[1].children[1].level, 3) + + self.assertEqual(h.next_sibling.level, 1) + + self.assertEqual(h.next_sibling.next_sibling.level, 1) + + self.assertEqual(h.next_sibling.next_sibling.next_sibling, None) + self.assertEqual(h.next_sibling.next_sibling.parent, None) + + def test_heading_in_the_middle(self): + # test heading in the middle of the file + vim.current.window.cursor = (14, 0) + h = self.document.current_heading() + + self.assertNotEqual(h, None) + self.assertEqual(h.level, 4) + self.assertEqual(h.parent.level, 2) + self.assertNotEqual(h.next_sibling, None) + self.assertNotEqual(h.next_sibling.previous_sibling, None) + self.assertEqual(h.next_sibling.level, 3) + self.assertEqual(h.previous_sibling, None) + + def test_previous_headings(self): + # test previous headings + vim.current.window.cursor = (17, 0) + h = self.document.current_heading() + + self.assertNotEqual(h, None) + self.assertEqual(h.level, 3) + self.assertNotEqual(h.previous_sibling, None) + self.assertEqual(h.parent.level, 2) + self.assertNotEqual(h.parent.previous_sibling, None) + self.assertNotEqual(h.previous_sibling.parent, None) + self.assertEqual(h.previous_sibling.parent.start, 10) + + vim.current.window.cursor = (14, 0) + h = self.document.current_heading() + self.assertNotEqual(h.parent, None) + self.assertEqual(h.parent.start, 10) + + vim.current.window.cursor = (21, 0) + h = self.document.current_heading() + self.assertNotEqual(h, None) + self.assertEqual(h.level, 1) + self.assertNotEqual(h.previous_sibling, None) + self.assertEqual(h.previous_sibling.level, 1) + self.assertNotEqual(h.previous_sibling.previous_sibling, None) + self.assertEqual(h.previous_sibling.previous_sibling.level, 1) + self.assertEqual(h.previous_sibling.previous_sibling.previous_sibling, + None) + + vim.current.window.cursor = (77, 0) + h = self.document.current_heading() + self.assertEqual(h, None) + +class VimBufferTagsTestCase(unittest.TestCase): + def setUp(self): + global counter + counter += 1 + vim.CMDHISTORY = [] + vim.CMDRESULTS = {} + vim.EVALHISTORY = [] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), + u_encode(u'DONE'), u_encode(u'|')], + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u'b:changedtick'): u_encode(u'0'), + u_encode(u'&ts'): u_encode(u'8'), + u_encode(u'exists("g:org_tag_column")'): u_encode(u'0'), + u_encode(u'exists("b:org_tag_column")'): u_encode(u'0'), + u_encode(u"v:count"): u_encode(u'0')} + vim.current.buffer[:] = [ u_encode(i) for i in u"""#Meta information +#more meta information +* Überschrift 1 :testtag: +Text 1 + +Bla bla +** Überschrift 1.1 :multi:tags: +Text 2 + +Bla Bla bla +** Überschrift 1.2:notag: +Text 3 + +**** Überschrift 1.2.1.falsch :no tag: + +Bla Bla bla bla +*** Überschrift 1.2.1 :no tag +*** Überschrift 1.2.2 no tag: +* Überschrift 2 :more:tags: +* Überschrift 3 :lesser:tag: + asdf sdf +* Überschrift 4 super long long long long long long long long extremely long title :title:long: +* TODO Überschrift 5 super long long long long long long long long extremely long title :title_with_todo: +* oneword :with:tags: +* :noword:with:tags: +* TODO :todo:with:tags: +""".split(u'\n') ] + self.document = VimBuffer().init_dom() + + def test_tag_read_no_word_with_tags(self): + self.assertEqual(len(self.document.headings[6].tags), 3) + self.assertEqual(self.document.headings[6].tags[0], u'noword') + self.assertEqual(self.document.headings[6].title, u'') + self.assertEqual(self.document.headings[6].todo, None) + + def test_tag_read_one_word_with_tags(self): + self.assertEqual(len(self.document.headings[5].tags), 2) + self.assertEqual(self.document.headings[5].tags[0], u'with') + self.assertEqual(self.document.headings[5].title, u'oneword') + self.assertEqual(self.document.headings[5].todo, None) + + def test_tag_read_TODO_with_tags(self): + self.assertEqual(len(self.document.headings[7].tags), 3) + self.assertEqual(self.document.headings[7].tags[0], u'todo') + self.assertEqual(self.document.headings[7].title, u'') + self.assertEqual(self.document.headings[7].todo, u'TODO') + + def test_tag_read_one(self): + self.assertEqual(len(self.document.headings[0].tags), 1) + self.assertEqual(self.document.headings[0].tags[0], u'testtag') + self.assertEqual(unicode(self.document.headings[0]), u'* Überschrift 1 :testtag:') + + def test_tag_read_multiple(self): + self.assertEqual(len(self.document.headings[0].children[0].tags), 2) + self.assertEqual(self.document.headings[0].children[0].tags, [u'multi', 'tags']) + self.assertEqual(unicode(self.document.headings[0].children[0]), u'** Überschrift 1.1 :multi:tags:') + + def test_tag_no_tags(self): + self.assertEqual(len(self.document.headings[0].children[1].children), 3) + self.assertEqual(len(self.document.headings[0].children[1].tags), 0) + self.assertEqual(len(self.document.headings[0].children[1].children[0].tags), 0) + self.assertEqual(len(self.document.headings[0].children[1].children[1].tags), 0) + self.assertEqual(len(self.document.headings[0].children[1].children[2].tags), 0) + + def test_tag_read_space_and_tab_separated(self): + self.assertEqual(len(self.document.headings[1].children), 0) + self.assertEqual(len(self.document.headings[1].tags), 2) + self.assertEqual(self.document.headings[1].tags, [u'more', u'tags']) + + def test_tag_read_tab_separated(self): + self.assertEqual(len(self.document.headings[2].children), 0) + self.assertEqual(len(self.document.headings[2].tags), 2) + self.assertEqual(self.document.headings[2].tags, [u'lesser', u'tag']) + + def test_tag_read_long_title(self): + self.assertEqual(len(self.document.headings[3].children), 0) + self.assertEqual(len(self.document.headings[3].tags), 2) + self.assertEqual(self.document.headings[3].tags, [u'title', u'long']) + self.assertEqual(unicode(self.document.headings[3]), u'* Überschrift 4 super long long long long long long long long extremely long title :title:long:') + + def test_tag_read_long_title_plus_todo_state(self): + self.assertEqual(len(self.document.headings[4].children), 0) + self.assertEqual(len(self.document.headings[4].tags), 1) + self.assertEqual(self.document.headings[4].level, 1) + self.assertEqual(self.document.headings[4].todo, u'TODO') + self.assertEqual(self.document.headings[4].title, u'Überschrift 5 super long long long long long long long long extremely long title') + self.assertEqual(self.document.headings[4].tags, [u'title_with_todo']) + self.assertEqual(unicode(self.document.headings[4]), u'* TODO Überschrift 5 super long long long long long long long long extremely long title :title_with_todo:') + + def test_tag_del_tags(self): + self.assertEqual(len(self.document.headings[0].tags), 1) + del self.document.headings[0].tags + self.assertEqual(len(self.document.headings[0].tags), 0) + self.assertEqual(self.document.headings[0].is_dirty_heading, True) + self.assertEqual(self.document.headings[0].is_dirty_body, False) + self.assertEqual(unicode(self.document.headings[0]), u'* Überschrift 1') + self.assertEqual(self.document.write(), True) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(d.headings[0].tags), 0) + self.assertEqual(d.headings[0].title, u'Überschrift 1') + self.assertEqual(unicode(d.headings[0]), u'* Überschrift 1') + + def test_tag_replace_one_tag(self): + self.assertEqual(len(self.document.headings[0].tags), 1) + self.document.headings[0].tags = [u'justonetag'] + self.assertEqual(len(self.document.headings[0].tags), 1) + self.assertEqual(self.document.headings[0].is_dirty_heading, True) + self.assertEqual(self.document.headings[0].is_dirty_body, False) + self.assertEqual(unicode(self.document.headings[0]), u'* Überschrift 1 :justonetag:') + self.assertEqual(self.document.write(), True) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(d.headings[0].tags), 1) + self.assertEqual(d.headings[0].tags, [u'justonetag']) + self.assertEqual(d.headings[0].title, u'Überschrift 1') + self.assertEqual(unicode(d.headings[0]), u'* Überschrift 1 :justonetag:') + + def test_tag_replace_multiple_tags(self): + self.assertEqual(len(self.document.headings[1].tags), 2) + self.document.headings[1].tags = [u'justonetag', u'moretags', u'lesstags'] + self.assertEqual(len(self.document.headings[1].tags), 3) + self.assertEqual(self.document.headings[1].is_dirty_heading, True) + self.assertEqual(self.document.headings[1].is_dirty_body, False) + self.assertEqual(unicode(self.document.headings[1]), u'* Überschrift 2 :justonetag:moretags:lesstags:') + self.assertEqual(self.document.write(), True) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(len(d.headings[1].tags), 3) + self.assertEqual(d.headings[1].tags, [u'justonetag', u'moretags', u'lesstags']) + self.assertEqual(d.headings[1].title, u'Überschrift 2') + self.assertEqual(unicode(d.headings[1]), u'* Überschrift 2 :justonetag:moretags:lesstags:') + +class VimBufferTodoTestCase(unittest.TestCase): + def setUp(self): + global counter + counter += 1 + vim.CMDHISTORY = [] + vim.CMDRESULTS = {} + vim.EVALHISTORY = [] + vim.EVALRESULTS = { + # no org_todo_keywords for b + u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'), + # global values for org_todo_keywords + u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'), + u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), \ + u_encode(u'DONß'), u_encode(u'DONÉ'), \ + u_encode(u'DÖNE'), u_encode(u'WAITING'), \ + u_encode(u'DONE'), u_encode(u'|')], + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("g:org_debug")'): u_encode(u'0'), + u_encode(u'exists("*repeat#set()")'): u_encode(u'0'), + u_encode(u'b:changedtick'): u_encode(u'0'), + u_encode(u'&ts'): u_encode(u'8'), + u_encode(u'exists("g:org_tag_column")'): u_encode(u'0'), + u_encode(u'exists("b:org_tag_column")'): u_encode(u'0'), + u_encode(u"v:count"): u_encode(u'0')} + vim.current.buffer[:] = [ u_encode(i) for i in u"""#Meta information +#more meta information +* TODO Überschrift 1 :testtag: +Text 1 + +Bla bla +** TODO NOTODO Überschrift 1.1 :multi:tags: +Text 2 + +Bla Bla bla +** NO-TODO Überschrift 1.2:notag: +Text 3 + +**** NOTODOÜberschrift 1.2.1.falsch :no tag: + +Bla Bla bla bla +*** notodo Überschrift 1.2.1 :no tag +*** NOTODo Überschrift 1.2.2 no tag: +* WAITING Überschrift 2 :more:tags: +* DONE Überschrift 3 :lesser:tag: + asdf sdf +* DÖNE Überschrift 4 +* DONß Überschrift 5 +* DONÉ Überschrift 6 +* DONé Überschrift 7 +""".split(u'\n') ] + self.document = VimBuffer().init_dom() + + def test_no_space_after_upper_case_single_word_heading(self): + vim.current.buffer[:] = [ u_encode(i) for i in u""" +* TEST +** Text 1 +*** Text 2 +* Text 1 +** Text 1 + some text that is + no heading + +""".split(u'\n') ] + d = VimBuffer().init_dom() + self.assertEqual(unicode(d.headings[0]), u'* TEST') + + def test_todo_read_TODO(self): + self.assertEqual(self.document.headings[0].todo, u'TODO') + self.assertEqual(self.document.headings[0].title, u'Überschrift 1') + self.assertEqual(unicode(self.document.headings[0]), u'* TODO Überschrift 1 :testtag:') + + def test_todo_read_TODO_NOTODO(self): + self.assertEqual(self.document.headings[0].children[0].todo, u'TODO') + self.assertEqual(self.document.headings[0].children[0].title, u'NOTODO Überschrift 1.1') + self.assertEqual(unicode(self.document.headings[0].children[0]), u'** TODO NOTODO Überschrift 1.1 :multi:tags:') + + def test_todo_read_WAITING(self): + self.assertEqual(self.document.headings[1].todo, u'WAITING') + self.assertEqual(self.document.headings[1].title, u'Überschrift 2') + self.assertEqual(unicode(self.document.headings[1]), u'* WAITING Überschrift 2 :more:tags:') + + def test_todo_read_DONE(self): + self.assertEqual(self.document.headings[2].todo, u'DONE') + self.assertEqual(self.document.headings[2].title, u'Überschrift 3') + self.assertEqual(unicode(self.document.headings[2]), u'* DONE Überschrift 3 :lesser:tag:') + + def test_todo_read_special(self): + self.assertEqual(self.document.headings[3].todo, u'DÖNE') + self.assertEqual(self.document.headings[3].title, u'Überschrift 4') + + self.assertEqual(self.document.headings[4].todo, u'DONß') + self.assertEqual(self.document.headings[4].title, u'Überschrift 5') + + self.assertEqual(self.document.headings[5].todo, u'DONÉ') + self.assertEqual(self.document.headings[5].title, u'Überschrift 6') + + self.assertEqual(self.document.headings[6].todo, None) + self.assertEqual(self.document.headings[6].title, u'DONé Überschrift 7') + + def test_todo_del_todo(self): + self.assertEqual(self.document.headings[0].todo, u'TODO') + del self.document.headings[0].todo + self.assertEqual(self.document.headings[0].is_dirty_body, False) + self.assertEqual(self.document.headings[0].is_dirty_heading, True) + self.assertEqual(self.document.headings[0].todo, None) + self.assertEqual(self.document.headings[0].title, u'Überschrift 1') + self.assertEqual(unicode(self.document.headings[0]), u'* Überschrift 1 :testtag:') + self.assertEqual(self.document.write(), True) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(d.headings[0].todo, None) + self.assertEqual(d.headings[0].title, u'Überschrift 1') + self.assertEqual(unicode(d.headings[0]), u'* Überschrift 1 :testtag:') + + def test_todo_write_todo_uppercase(self): + self.assertEqual(self.document.headings[0].todo, u'TODO') + self.document.headings[0].todo = u'DONE' + self.assertEqual(self.document.headings[0].is_dirty_body, False) + self.assertEqual(self.document.headings[0].is_dirty_heading, True) + self.assertEqual(self.document.headings[0].todo, u'DONE') + self.assertEqual(self.document.headings[0].title, u'Überschrift 1') + self.assertEqual(unicode(self.document.headings[0]), u'* DONE Überschrift 1 :testtag:') + self.assertEqual(self.document.write(), True) + + # sanity check + d = VimBuffer().init_dom() + self.assertEqual(d.headings[0].todo, u'DONE') + self.assertEqual(d.headings[0].title, u'Überschrift 1') + self.assertEqual(unicode(d.headings[0]), u'* DONE Überschrift 1 :testtag:') + + def test_todo_set_illegal_todo(self): + def set_todo(todo): + self.document.headings[0].todo = todo + self.assertEqual(self.document.headings[0].todo, u'TODO') + self.assertRaises(ValueError, set_todo, u'DO NE') + self.assertRaises(ValueError, set_todo, u'DO\tNE') + self.assertRaises(ValueError, set_todo, u'D\nNE') + self.assertRaises(ValueError, set_todo, u'DO\rNE') + self.assertEqual(self.document.headings[0].todo, u'TODO') + +def suite(): + return ( \ + unittest.TestLoader().loadTestsFromTestCase(VimBufferTestCase), \ + unittest.TestLoader().loadTestsFromTestCase(VimBufferTagsTestCase), \ + unittest.TestLoader().loadTestsFromTestCase(VimBufferTodoTestCase), \ + ) diff --git a/pack/acp/start/vim-orgmode/tests/vim.py b/pack/acp/start/vim-orgmode/tests/vim.py new file mode 100644 index 0000000..95b6686 --- /dev/null +++ b/pack/acp/start/vim-orgmode/tests/vim.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + + +class VimWindow(object): + u""" Docstring for VimWindow """ + + def __init__(self, test): + object.__init__(self) + self._test = test + self.cursor = (1, 0) + + def buffer(): + def fget(self): + return self._test.buffer + + def fset(self, value): + self._test.buffer = value + return locals() + buffer = property(**buffer()) + + +class VimBuffer(list): + def __init__(self, iterable=None): + self.number = 0 + if iterable is not None: + list.__init__(self, iterable) + else: + list.__init__(self) + + def append(self, o): + u""" + mimic the specific behavior of vim.current.buffer + """ + if isinstance(o, list) or isinstance(o, tuple): + for i in o: + list.append(self, i) + else: + list.append(self, o) + + +class VimTest(object): + u""" Replacement for vim API """ + + def __init__(self): + object.__init__(self) + self._buffer = VimBuffer() + self.window = VimWindow(self) + + def buffer(): + def fget(self): + return self._buffer + + def fset(self, value): + self._buffer = VimBuffer(value) + return locals() + buffer = property(**buffer()) + + +EVALHISTORY = [] +EVALRESULTS = { + u'exists("g:org_debug")': 0, + u'exists("b:org_debug")': 0, + u'exists("*repeat#set()")': 0, + u'exists("b:org_plugins")': 0, + u'exists("g:org_plugins")': 0, + u'b:changedtick': 0, + } + + +def eval(cmd): + u""" evaluate command + + :returns: results stored in EVALRESULTS + """ + EVALHISTORY.append(cmd) + return EVALRESULTS.get(cmd, None) + + +CMDHISTORY = [] +CMDRESULTS = {} + + +def command(cmd): + CMDHISTORY.append(cmd) + return CMDRESULTS.get(cmd, None) + + +current = VimTest() diff --git a/pack/acp/start/vim-speeddating/.gitattributes b/pack/acp/start/vim-speeddating/.gitattributes new file mode 100644 index 0000000..0913cff --- /dev/null +++ b/pack/acp/start/vim-speeddating/.gitattributes @@ -0,0 +1 @@ +*.vim filter=vimdate diff --git a/pack/acp/start/vim-speeddating/.gitignore b/pack/acp/start/vim-speeddating/.gitignore new file mode 100644 index 0000000..0a56e3f --- /dev/null +++ b/pack/acp/start/vim-speeddating/.gitignore @@ -0,0 +1 @@ +/doc/tags diff --git a/pack/acp/start/vim-speeddating/README.markdown b/pack/acp/start/vim-speeddating/README.markdown new file mode 100644 index 0000000..797904c --- /dev/null +++ b/pack/acp/start/vim-speeddating/README.markdown @@ -0,0 +1,71 @@ +# speeddating.vim + +Take the following date: + + 1999-12-31 + +Because Vim treats the hyphen as a negative sign, pressing `<C-A>` on the 31 +would normally increment it to + + 1999-12-30 + +Compare this with what happens when speeddating.vim is installed: + + 2000-01-01 + +Pressing `5<C-X>` on the `03` in the first line below transforms it into the +second: + + Sat, 01 Jan 2000 00:00:03 +0000 + Fri, 31 Dec 1999 23:59:58 +0000 + +Several date, time, and datetime formats are included. Additional formats can +be defined in a strftime-like syntax with the `:SpeedDatingFormat` command. + +Existing Vim semantics are preserved. `<C-A>` and `<C-X>` accept a count, and +plain number incrementing is used if no date format is matched. + +Use of `<C-A>`/`<C-X>` in visual mode enables incrementing several lines at +once. Blank spots are filled by incrementing the match from the previous +line, allowing for creation of sequences (1, 2, 3; 2000-10-30, 2000-10-31, +2000-11-01). + +It can also increment roman numerals and ordinals (1st, 2nd, 3rd, ...). In +visual mode, letters of the alphabet are supported. + +`d<C-X>` sets the timestamp under the cursor to the current time. `d<C-A>` +does the same, but uses UTC rather than the local time. + +The `.` command will work as expected if you install +[repeat.vim](https://github.com/tpope/vim-repeat). + +## Installation + +If you don't have a preferred installation method, I recommend +installing [pathogen.vim](https://github.com/tpope/vim-pathogen), and +then simply copy and paste: + + cd ~/.vim/bundle + git clone git://github.com/tpope/vim-speeddating.git + +Once help tags have been generated, you can view the manual with +`:help speeddating`. + +## Contributing + +See the contribution guidelines for +[pathogen.vim](https://github.com/tpope/vim-pathogen#readme). + +## Self-Promotion + +Like speeddating.vim? Follow the repository on +[GitHub](https://github.com/tpope/vim-speeddating) and vote for it on +[vim.org](http://www.vim.org/scripts/script.php?script_id=2120). And if +you're feeling especially charitable, follow [tpope](http://tpo.pe/) on +[Twitter](http://twitter.com/tpope) and +[GitHub](https://github.com/tpope). + +## License + +Copyright © Tim Pope. Distributed under the same terms as Vim itself. +See `:help license`. diff --git a/pack/acp/start/vim-speeddating/autoload/speeddating.vim b/pack/acp/start/vim-speeddating/autoload/speeddating.vim new file mode 100644 index 0000000..bd425e2 --- /dev/null +++ b/pack/acp/start/vim-speeddating/autoload/speeddating.vim @@ -0,0 +1,808 @@ +" Location: autoload/speeddating.vim + +" Initialization {{{1 + +if !exists("g:loaded_speeddating") || &cp || v:version < 700 + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +let s:install_dir = expand("<sfile>:p:h:h") + +" }}}1 +" Utility Functions {{{1 + +function! s:function(name) + return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '.*\zs<SNR>\d\+_'),'')) +endfunction + +" In Vim, -4 % 3 == -1. Let's return 2 instead. +function! s:mod(a,b) + if (a:a < 0 && a:b > 0 || a:a > 0 && a:b < 0) && a:a % a:b != 0 + return (a:a % a:b) + a:b + else + return a:a % a:b + endif +endfunction + +" In Vim, -4 / 3 == -1. Let's return -2 instead. +function! s:div(a,b) + if a:a < 0 && a:b > 0 + return (a:a-a:b+1)/a:b + elseif a:a > 0 && a:b < 0 + return (a:a-a:b-1)/a:b + else + return a:a / a:b + endif +endfunction + +function! s:match(...) + let b = call("match",a:000) + let e = call("matchend",a:000) + let s = call("matchlist",a:000) + if s == [] + let s = ["","","","","","","","","",""] + endif + return [b,e] + s +endfunction + +function! s:findatoffset(string,pattern,offset) + let line = a:string + let curpos = 0 + let offset = a:offset + while strpart(line,offset,1) == " " + let offset += 1 + endwhile + let [start,end,string;caps] = s:match(line,a:pattern,curpos,0) + while start >= 0 + if offset >= start && offset < end + break + endif + let curpos = start + 1 + let [start,end,string;caps] = s:match(line,a:pattern,curpos,0) + endwhile + return [start,end,string] + caps +endfunction + +function! s:findinline(pattern) + return s:findatoffset(getline('.'),a:pattern,col('.')-1) +endfunction + +function! s:replaceinline(start,end,new) + let line = getline('.') + let before_text = strpart(line,0,a:start) + let after_text = strpart(line,a:end) + " If this generates a warning it will be attached to an ugly backtrace. + " No warning at all is preferable to that. + silent call setline('.',before_text.a:new.after_text) + call setpos("'[",[0,line('.'),strlen(before_text)+1,0]) + call setpos("']",[0,line('.'),a:start+strlen(a:new),0]) +endfunction + +" }}}1 +" Normal Mode {{{1 + +function! speeddating#increment(increment) + for handler in s:time_handlers + g:speeddating_handlers + let pattern = type(handler.regexp) == type(function('tr')) ? handler.regexp() : handler.regexp + let [start,end,string;caps] = s:findinline('\C'.pattern) + if string != "" + let [repl,offset] = handler.increment(string,col('.')-1-start,a:increment) + if offset < 0 + let offset += strlen(repl) + 1 + endif + if repl != "" + call s:replaceinline(start,end,repl) + call setpos('.',[0,line('.'),start+offset,0]) + silent! call repeat#set("\<Plug>SpeedDating" . (a:increment < 0 ? "Down" : "Up"),a:increment < 0 ? -a:increment : a:increment) + return + endif + endif + endfor + if a:increment > 0 + exe "norm! ". a:increment."\<C-A>" + else + exe "norm! ".-a:increment."\<C-X>" + endif + silent! call repeat#set("\<Plug>SpeedDating" . (a:increment < 0 ? "Down" : "Up"),a:increment < 0 ? -a:increment : a:increment) +endfunction + +" }}}1 +" Visual Mode {{{1 + +function! s:setvirtcol(line,col) + call setpos('.',[0,a:line,a:col,0]) + while virtcol('.') < a:col + call setpos('.',[0,a:line,col('.')+1,0]) + endwhile + while virtcol('.') > a:col + call setpos('.',[0,a:line,col('.')-1,0]) + endwhile + return col('.') + getpos('.')[3] +endfunction + +function! s:chars(string) + return strlen(substitute(a:string,'.','.','g')) +endfunction + +function! s:incrementstring(string,offset,count) + let repl = "" + let offset = -1 + for handler in s:time_handlers + g:speeddating_handlers + s:visual_handlers + let pattern = type(handler.regexp) == type(function('tr')) ? handler.regexp() : handler.regexp + let [start,end,string;caps] = s:findatoffset(a:string,'\C'.pattern,a:offset) + if string != "" + let [repl,offset] = handler.increment(string,a:offset,a:count) + if repl != "" + break + endif + endif + endfor + if offset < 0 + let offset += strlen(repl) + 1 + endif + + if repl != "" + let before_text = strpart(a:string,0,start) + let change = s:chars(repl) - s:chars(string) + if change < 0 && before_text !~ '\w$' + let offset -= change + let repl = repeat(' ',-change) . repl + elseif change > 0 && before_text =~ ' $' + let before_text = substitute(before_text,' \{1,'.change.'\}$','','') + let before_text = substitute(before_text,'\w$','& ','') + let start = strlen(before_text) + endif + let offset += start + let repl = before_text.repl.strpart(a:string,end) + endif + return [repl,offset,start,end] +endfunction + +function! speeddating#incrementvisual(count) + let ve = &ve + set virtualedit=all + exe "norm! gv\<Esc>" + if &selection ==# 'exclusive' && getpos('.') == getpos("'>") + normal! h + endif + let vcol = virtcol('.') + let lnum = line("'<") + let lastrepl = "" + call s:setvirtcol(lnum,vcol) + call setpos("'[",[0,line("'<"),1,0]) + while lnum <= line("'>") + call s:setvirtcol(lnum,vcol) + let [repl,offset,start,end] = s:incrementstring(getline('.'),col('.')-1,a:count) + if repl == "" && lastrepl != "" + call setpos(".",[0,lnum-1,laststart,0]) + let start = s:setvirtcol(lnum,virtcol('.')) + call setpos(".",[0,lnum-1,lastend,0]) + let end = s:setvirtcol(lnum,virtcol('.')) + call s:setvirtcol(lnum,vcol) + if strpart(getline('.'),start,end-start) =~ '^\s*$' + let before_padded = start == end ? '' : printf("%-".start."s",strpart(getline('.'),0,start)) + let tweaked_line = before_padded.strpart(lastrepl,laststart,lastend-laststart).strpart(getline('.'),end) + let [repl,offset,start,end] = s:incrementstring(tweaked_line,col('.')-1,a:count*(lnum-lastlnum)) + endif + elseif repl != "" + let [lastrepl,laststart,lastend,lastlnum] = [repl,start,end,lnum] + endif + if repl != "" + silent call setline('.',repl) + endif + let lnum += 1 + endwhile + let &ve = ve + call setpos("']",[0,line('.'),col('$'),0]) +endfunction + +" }}}1 +" Visual Mode Handlers {{{1 + +let s:visual_handlers = [] + +function! s:numberincrement(string,offset,increment) + let n = (a:string + a:increment) + if a:string =~# '^0x.*[A-F]' + return [printf("0x%X",n),-1] + elseif a:string =~# '^0x' + return [printf("0x%x",n),-1] + elseif a:string =~# '^00*[^0]' && &nrformats =~# 'octal' + return [printf("0%o",n),-1] + elseif a:string =~# '^00*[^0]' + return [printf("%0".strlen(a:string)."d",n),-1] + else + return [printf("%d",n),-1] + endif +endfunction + +let s:visual_handlers += [{'regexp': '-\=\<\%(0x\x\+\|\d\+\)\>', 'increment': s:function("s:numberincrement")}] + +function! s:letterincrement(string,offset,increment) + return [nr2char((char2nr(toupper(a:string)) - char2nr('A') + a:increment) % 26 + (a:string =~# '[A-Z]' ? char2nr('A') : char2nr('a'))),-1] +endfunction + +let s:visual_handlers += [{'regexp': '\<[A-Za-z]\>', 'increment': s:function("s:letterincrement")}] + +" }}}1 +" Ordinals {{{1 + +function! s:ordinalize(number) + let n = a:number + let a = n < 0 ? -n : +n + if a % 100 == 11 || a % 100 == 12 || a % 100 == 13 + return n."th" + elseif a % 10 == 1 + return n."st" + elseif a % 10 == 2 + return n."nd" + elseif a % 10 == 3 + return n."rd" + else + return n."th" + endif +endfunction + +function! s:ordinalincrement(string,offset,increment) + return [s:ordinalize(a:string+a:increment),-1] +endfunction + +let g:speeddating_handlers += [{'regexp': '-\=\<\d\+\%(st\|nd\|rd\|th\)\>', 'increment': s:function("s:ordinalincrement")}] + +" }}}1 +" Roman Numerals {{{1 + +" Based on similar functions from VisIncr.vim + +let s:a2r = [[1000, 'm'], [900, 'cm'], [500, 'd'], [400, 'cd'], [100, 'c'], + \ [90 , 'xc'], [50 , 'l'], [40 , 'xl'], [10 , 'x'], + \ [9 , 'ix'], [5 , 'v'], [4 , 'iv'], [1 , 'i']] + +function! s:roman2arabic(roman) + let roman = tolower(a:roman) + let sign = 1 + let arabic = 0 + while roman != '' + if roman =~ '^[-n]' + let sign = -sign + endif + for [numbers,letters] in s:a2r + if roman =~ '^'.letters + let arabic += sign * numbers + let roman = strpart(roman,strlen(letters)-1) + break + endif + endfor + let roman = strpart(roman,1) + endwhile + + return arabic +endfunction + +function! s:arabic2roman(arabic) + if a:arabic <= 0 + let arabic = -a:arabic + let roman = "n" + else + let arabic = a:arabic + let roman = "" + endif + for [numbers, letters] in s:a2r + let roman .= repeat(letters,arabic/numbers) + let arabic = arabic % numbers + endfor + return roman +endfunction + +" }}}1 +" Time Helpers {{{1 + +function! s:ary2pat(array) + return '\%('.join(a:array,'\|').'\)' + return '\%('.join(map(copy(a:array),'substitute(v:val,"[[:alpha:]]","[\\u&\\l&]","g")'),'\|').'\)' +endfunction + +function! s:initializetime(time) + call extend(a:time,{'y': '','b':1,'d':0,'h':0,'m':0,'s':0,'o':0,'k':0},"keep") + if get(a:time,'b','') !~ '^\d*$' + let full = index(s:months_full ,a:time.b,0,1) + 1 + let engl = index(s:months_engl ,a:time.b,0,1) + 1 + let abbr = index(s:months_abbr ,a:time.b,0,1) + 1 + if full + let a:time.b = full + elseif engl + let a:time.b = engl + elseif abbr + let a:time.b = abbr + else + let a:time.b = 1 + endif + endif + if has_key(a:time,'p') + let a:time.h = a:time.h % 12 + if a:time.p ==? "PM" + let a:time.h += 12 + endif + call remove(a:time,"p") + endif + if a:time.y !~ '^\d*$' + let a:time.y = s:roman2arabic(a:time.y) + elseif a:time.y =~ '^-\=0..' + let a:time.y = substitute(a:time.y,'0\+','','') + elseif a:time.y < 38 && a:time.y >= 0 && ''.a:time.y != '' + let a:time.y += 2000 + elseif a:time.y < 100 && a:time.y >= 38 + let a:time.y += 1900 + endif + if has_key(a:time,'w') + let full = index(s:days_full,a:time.w,0,1) + let engl = index(s:days_engl,a:time.w,0,1) + let abbr = index(s:days_abbr,a:time.w,0,1) + let a:time.w = full > 0 ? full : (engl > 0 ? engl : (abbr > 0 ? abbr : a:time.w)) + if a:time.d == 0 + let a:time.d = s:mod(a:time.w - s:jd(a:time.y,a:time.b,1),7) + elseif a:time.y == '' && a:time.b * a:time.d > 0 + let a:time.y = strftime("%Y")-2 + while s:mod(s:jd(a:time.y,a:time.b,a:time.d)+1,7) != a:time.w + let a:time.y += 1 + endwhile + endif + call remove(a:time,'w') + endif + if a:time.d == 0 + let a:time.d = 1 + endif + if ''.a:time.y == '' + let a:time.y = 2000 + endif + if a:time.o =~ '^[+-]\d\d:\=\d\d$' + let a:time.o = (a:time.o[0]=="-" ? -1 : 1)*(a:time.o[1:2]*60+matchstr(a:time.o,'\d\d$')) + elseif get(a:time,'z','') == g:speeddating_zone + let a:time.o = s:offset + elseif get(a:time,'z','') == g:speeddating_zone_dst + let a:time.o = s:offset_dst + endif + return a:time +endfunction + +" Julian day (always Gregorian calendar) +function! s:jd(year,mon,day) + let y = a:year + 4800 - (a:mon <= 2) + let m = a:mon + (a:mon <= 2 ? 9 : -3) + let jul = a:day + (153*m+2)/5 + s:div(1461*y,4) - 32083 + return jul - s:div(y,100) + s:div(y,400) + 38 +endfunction + +function! s:gregorian(jd) + let l = a:jd + 68569 + let n = s:div(4 * l, 146097) + let l = l - s:div(146097 * n + 3, 4) + let i = ( 4000 * ( l + 1 ) ) / 1461001 + let l = l - ( 1461 * i ) / 4 + 31 + let j = ( 80 * l ) / 2447 + let d = l - ( 2447 * j ) / 80 + let l = j / 11 + let m = j + 2 - ( 12 * l ) + let y = 100 * ( n - 49 ) + i + l + return {'y':y,'b':m,'d':d} +endfunction + +function! s:normalizetime(time) + let a:time.y += s:div(a:time.b-1,12) + let a:time.b = s:mod(a:time.b-1,12)+1 + let seconds = a:time.h * 3600 + a:time.m * 60 + a:time.s + s:div(a:time.k,1000) + let a:time.k = s:mod(a:time.k,1000) + let a:time.s = s:mod(seconds,60) + let a:time.m = s:mod(s:div(seconds,60),60) + let a:time.h = s:mod(s:div(seconds,3600),24) + if seconds != 0 || a:time.b != 1 || a:time.d != 1 + let day = s:gregorian(s:jd(a:time.y,a:time.b,a:time.d)+s:div(seconds,86400)) + return extend(a:time,day) + else + return a:time + endif +endfunction + +function! s:applymodifer(number,modifier,width) + if a:modifier == '-' + return substitute(a:number,'^0*','','') + elseif a:modifier == '_' + return printf('%'.a:width.'d',a:number) + elseif a:modifier == '^' + return toupper(a:number) + else + return printf('%0'.a:width.'s',a:number) + endif +endfunction + +function! s:modyear(y) + return printf('%02d',s:mod(a:y,100)) +endfunction + +function! s:strftime(pattern,time) + if type(a:time) == type({}) + let time = s:normalizetime(copy(a:time)) + else + let time = s:normalizetime(s:initializetime({'y':1970,'s':a:time})) + endif + let time.w = s:mod(s:jd(time.y,time.b,time.d)+1,7) + let time.p = time.h + let expanded = "" + let remaining = a:pattern + while remaining != "" + if remaining =~ '^%' + let modifier = matchstr(remaining,'%\zs[-_0^]\=\ze.') + let specifier = matchstr(remaining,'%[-_0^]\=\zs.') + let remaining = matchstr(remaining,'%[-_0^]\=.\zs.*') + if specifier == '%' + let expanded .= '%' + elseif has_key(s:strftime_items,specifier) + let item = s:strftime_items[specifier] + let number = time[item[1]] + if type(item[4]) == type([]) + let expanded .= s:applymodifer(item[4][number % len(item[4])],modifier,1) + elseif type(item[4]) == type(function('tr')) + let expanded .= s:applymodifer(call(item[4],[number]),modifier,1) + else + let expanded .= s:applymodifer(number,modifier,item[4]) + endif + else + let expanded .= '%'.modifier.specifier + endif + else + let expanded .= matchstr(remaining,'[^%]*') + let remaining = matchstr(remaining,'[^%]*\zs.*') + endif + endwhile + return expanded +endfunction + +function! s:localtime(...) + let ts = a:0 ? a:1 : has('unix') ? reltimestr(reltime()) : localtime().'.0' + let us = matchstr(ts,'\.\zs.\{0,6\}') + let us .= repeat(0,6-strlen(us)) + let us = +matchstr(us,'[1-9].*') + let time = { + \ 'y': +strftime('%Y',ts), + \ 'b': +strftime('%m',ts), + \ 'd': +strftime('%d',ts), + \ 'h': +strftime('%H',ts), + \ 'm': +strftime('%M',ts), + \ 's': +strftime('%S',ts), + \ 'k': us / 1000} + let jd = s:jd(time.y,time.b,time.d) - s:jd(1970,1,1) + let real_ts = jd * 86400 + time.h * 3600 + time.m * 60 + time.s + let time.o = (real_ts - ts) / 60 + return time +endfunction + +function! s:formattz(offset) + if a:offset < 0 + let offset = -a:offset + let sign = "-" + else + let offset = a:offset + let sign = "+" + endif + return printf("%s%02d%02d",sign,offset/60,offset%60) +endfunction + +" }}}1 +" Time Data {{{1 + +let s:offset = s:localtime(( 0+30*365)*86400).o +if !exists("g:speeddating_zone") + let g:speeddating_zone = strftime("%Z",30*365*86400) + if g:speeddating_zone == "" + let g:speeddating_zone = get({-8:'PST',-7:'MST',-6:'CST',-5:'EST',0:'WET',1:'CET',2:'EET'},s:offset/60,"XST") + endif +endif + +let s:offset_dst = s:localtime((180+30*365)*86400).o +if !exists("g:speeddating_zone_dst") + let g:speeddating_zone_dst = strftime("%Z",(180+30*365)*86400) + if g:speeddating_zone_dst == "" + if s:offset == s:offset_dst + let g:speeddating_zone_dst = g:speeddating_zone + else + let g:speeddating_zone_dst = get({-7:'PDT',-6:'MDT',-5:'CDT',-4:'EDT',1:'WEST',2:'CEST',3:'EEST'},s:offset_dst/60,"XDT") + endif + endif +endif + +let s:days_engl =["Sun","Mon","Tue","Wed","Thu","Fri","Sat"] +let s:days_abbr =map(range(86400*3+43200-s:offset*60,86400*12,86400),'strftime("%a",v:val)')[0:6] +let s:days_full =map(range(86400*3+43200-s:offset*60,86400*12,86400),'strftime("%A",v:val)')[0:6] + +let s:months_engl =["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"] +let s:months_abbr =map(range(86400*2,86400*365,86400*31),'strftime("%b",v:val)') +let s:months_full =map(range(86400*2,86400*365,86400*31),'strftime("%B",v:val)') + +let s:strftime_items = { + \ "a": ['d','w',s:ary2pat(s:days_abbr), 'weekday (abbreviation)',s:days_abbr], + \ "A": ['d','w',s:ary2pat(s:days_full), 'weekday (full name)',s:days_full], + \ "i": ['d','w',s:ary2pat(s:days_engl), 'weekday (English abbr)',s:days_engl], + \ "b": ['b','b',s:ary2pat(s:months_abbr), 'month (abbreviation)',[""]+s:months_abbr], + \ "B": ['b','b',s:ary2pat(s:months_full), 'month (full name)',[""]+s:months_full], + \ "h": ['b','b',s:ary2pat(s:months_engl), 'month (English abbr)',[""]+s:months_engl], + \ "d": ['d','d','[ 0-3]\=\d', 'day (01-31)',2], + \ "H": ['h','h','[ 0-2]\=\d', 'hour (00-23)',2], + \ "I": ['h','h','[ 0-2]\=\d', 'hour (01-12)',['12', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11']], + \ "m": ['b','b','[ 0-1]\=\d', 'month (01-12)',2], + \ "M": ['m','m','[ 0-5]\=\d', 'minutes',2], + \ "o": ['d','d','[ 0-3]\=\d\%(st\|nd\|rd\|th\)','day (1st-31st)',s:function("s:ordinalize")], + \ "P": ['h','p','[ap]m', 'am/pm',repeat(['am'],12) + repeat(['pm'],12)], + \ "S": ['s','s','[ 0-5]\=\d', 'seconds',2], + \ "v": ['y','y','[ivxlcdmn]\+','year (roman numerals)',s:function("s:arabic2roman")], + \ "y": ['y','y','\d\d','year (00-99)',s:function("s:modyear")], + \ "Y": ['y','y','-\=\d\d\d\=\d\=','year',4], + \ "k": ['k','k','\d\d\d','milliseconds',3], + \ "z": ['o','o','[+-]\d\d\d\d','timezone offset',s:function("s:formattz")], + \ "Z": [' ','z','[A-Z]\{3,5}','timezone (incomplete)',3]} + +" }}}1 +" Time Handler {{{1 + +function! speeddating#timestamp(utc,count) + for handler in s:time_handlers + let [start,end,string;caps] = s:findinline('\C'.join(handler.groups,'')) + if string != "" + let format = substitute(handler.strftime,'\\\([1-9]\)','\=caps[submatch(1)-1]','g') + if a:utc || a:count + let offset = (a:utc ? 1 : -1) * a:count * 15 + let time = s:initializetime({'y':1970,'s':localtime()+offset*60,'o':offset}) + else + let time = s:localtime() + endif + if a:utc && !a:count + let time.z = 'UTC' + elseif time.o == s:offset + let time.z = g:speeddating_zone + elseif time.o == s:offset_dst + let time.z = g:speeddating_zone_dst + elseif time.o == 0 + let time.z = 'UTC' + else + let time.z = 'XXT' + endif + let newstring = s:strftime(format,time) + call s:replaceinline(start,end,newstring) + call setpos('.',[0,line('.'),start+strlen(newstring),0]) + silent! call repeat#set("\<Plug>SpeedDatingNow".(a:utc ? "UTC" : "Local"),a:count) + return "" + endif + endfor + let [start,end,string;caps] = s:findinline('-\=\<\d\+\>') + if string != "" + let newstring = localtime() + (a:utc ? 1 : -1) * a:count * 60*15 + call s:replaceinline(start,end,newstring) + call setpos('.',[0,line('.'),start+strlen(newstring),0]) + silent! call repeat#set("\<Plug>SpeedDatingNow".(a:utc ? "UTC" : "Local"),a:count) + endif +endfunction + +function! s:dateincrement(string,offset,increment) dict + let [start,end,string;caps] = s:match(a:string,'\C'.join(self.groups,'')) + let string = a:string + let offset = a:offset + let cursor_capture = 1 + let idx = 0 + while idx < len(self.groups) + let partial_matchend = matchend(string,join(self.groups[0:idx],'')) + if partial_matchend > offset + break + endif + let idx += 1 + endwhile + while get(self.targets,idx,"") == " " + let idx += 1 + endwhile + while get(self.targets,idx," ") == " " + let idx -= 1 + endwhile + let partial_pattern = join(self.groups[0:idx],'') + let char = self.targets[idx] + let i = 0 + let time = {} + for cap in caps + if get(self.reader,i," ") !~ '^\s\=$' + let time[self.reader[i]] = substitute(cap,'^\s*','','') + endif + let i += 1 + endfor + call s:initializetime(time) + let inner_offset = 0 + if char == 'o' + let inner_offset = partial_matchend - offset - 1 + let factor = 15 + if inner_offset <= 0 + let inner_offset = 0 + let factor = 1 + elseif inner_offset > 1 + let factor = 60 + let inner_offset = 2 + endif + let time.o += factor * a:increment + let time.m += factor * a:increment + elseif char == 'b' + let time.b += a:increment + let goal = time.y*12 + time.b + call s:normalizetime(time) + while time.y*12 + time.b > goal + let time.d -= 1 + call s:normalizetime(time) + endwhile + else + let time[char] += a:increment + endif + let format = substitute(self.strftime,'\\\([1-9]\)','\=caps[submatch(1)-1]','g') + let time_string = s:strftime(format,time) + return [time_string, matchend(time_string,partial_pattern)-inner_offset] +endfunction + +function! s:timeregexp() dict + return join(self.groups,'') +endfunction + +function! s:createtimehandler(format) + let pattern = '^\%(%?\=\[.\{-\}\]\|%[-_0^]\=.\|[^%]*\)' + let regexp = ['\%(\<\|-\@=\)'] + let reader = [] + let targets = [' '] + let template = "" + let default = "" + let remaining = substitute(a:format,'\C%\@<!%p','%^P','g') + let group = 0 + let usergroups = [] + let userdefaults = [] + while remaining != "" + let fragment = matchstr(remaining,pattern) + let remaining = matchstr(remaining,pattern.'\zs.*') + if fragment =~ '^%\*\W' + let suffix = '*' + let fragment = '%' . strpart(fragment,2) + elseif fragment =~ '^%?\W' + let suffix = '\=' + let fragment = '%' . strpart(fragment,2) + else + let suffix = '' + endif + let targets += [' '] + if fragment =~ '^%' && has_key(s:strftime_items,matchstr(fragment,'.$')) + let item = s:strftime_items[matchstr(fragment,'.$')] + let modifier = matchstr(fragment,'^%\zs.\ze.$') + let targets[-1] = item[0] + let reader += [item[1]] + if modifier == '^' + let pat = substitute(item[2],'\C\\\@<![[:lower:]]','\u&','g') + elseif modifier == '0' + let pat = substitute(item[2],' \|-\@<!\\=','','g') + else + let pat = item[2] + endif + let regexp += ['\('.pat.'\)'] + let group += 1 + let template .= fragment + let default .= fragment + elseif fragment =~ '^%\[.*\]$' + let reader += [' '] + let regexp += ['\('.matchstr(fragment,'\[.*').suffix.'\)'] + let group += 1 + let usergroups += [group] + let template .= "\\".group + if suffix == "" + let default .= strpart(fragment,2,1) + let userdefaults += [strpart(fragment,2,1)] + else + let userdefaults += [""] + endif + elseif fragment =~ '^%\d' + let regexp += ["\\".usergroups[strpart(fragment,1)-1]] + let template .= regexp[-1] + let default .= userdefaults[strpart(fragment,1)-1] + elseif fragment == '%*' + if len(regexp) == 1 + let regexp = [] + let targets = [] + else + let regexp += ['\(.*\)'] + endif + else + let regexp += [escape(fragment,'.*^$[\]~')] + let template .= fragment + let default .= fragment + endif + endwhile + if regexp[-1] == '\(.*\)' + call remove(regexp,-1) + call remove(targets,-1) + else + let regexp += ['\>'] + endif + return {'source': a:format, 'strftime': template, 'groups': regexp, 'regexp': s:function('s:timeregexp'), 'reader': reader, 'targets': targets, 'default': default, 'increment': s:function('s:dateincrement')} +endfunction + +function! s:comparecase(i1, i2) + if a:i1 ==? a:i2 + return a:i1 ==# a:i2 ? 0 : a:i1 ># a:i2 ? 1 : -1 + else + return tolower(a:i1) > tolower(a:i2) ? 1 : -1 + endif +endfunction + +function! speeddating#loadformats() + if exists("g:speeddating_loaded_formats") + return + endif + + for fmt in g:speeddating_formats + call speeddating#adddate( fmt[0], fmt[1], fmt[2] ) + endfor + let g:speeddating_loaded_formats = 1 +endfunction + +function! speeddating#adddate(master,count,bang) + if a:master == "" + let time = s:initializetime({'y':1970,'s':localtime(),'z': 'UTC'}) + if a:bang && a:count + silent! call remove(s:time_handlers,a:count - 1) + elseif a:bang + echo "SpeedDatingFormat List defined formats" + echo "SpeedDatingFormat! This help" + echo "SpeedDatingFormat %Y-%m-%d Add a format" + echo "1SpeedDatingFormat %Y-%m-%d Add a format before first format" + echo "SpeedDatingFormat! %Y-%m-%d Remove a format" + echo "1SpeedDatingFormat! Remove first format" + echo " " + echo "Expansions:" + for key in sort(keys(s:strftime_items),s:function("s:comparecase")) + echo printf("%2s %-25s %s",'%'.key,s:strftime_items[key][3],s:strftime('%'.key,time)) + endfor + echo '%0x %x with mandatory leading zeros' + echo '%_x %x with spaces rather than leading zeros' + echo '%-x %x with no leading spaces or zeros' + echo '%^x %x in uppercase' + echo '%* at beginning/end, surpress \</\> respectively' + echo '%[..] any one character \([..]\)' + echo '%?[..] up to one character \([..]\=\)' + echo '%1 character from first collection match \1' + echo " " + echo "Examples:" + echo 'SpeedDatingFormat %m%[/-]%d%1%Y " American 12/25/2007' + echo 'SpeedDatingFormat %d%[/-]%m%1%Y " European 25/12/2007' + echo " " + echo "Define formats in ".s:install_dir."/after/plugin/speeddating.vim" + elseif a:count + echo get(s:time_handlers,a:count-1,{'source':''}).source + else + let i = 0 + for handler in s:time_handlers + let i += 1 + echo printf("%3d %-32s %-32s",i,handler.source,s:strftime(handler.default,time)) + endfor + endif + elseif a:bang + call filter(s:time_handlers,'v:val.source != a:master') + else + let handler = s:createtimehandler(a:master) + if a:count + call insert(s:time_handlers,handler,a:count - 1) + else + let s:time_handlers += [handler] + endif + endif +endfunction + +if !exists('s:time_handlers') + let s:time_handlers = [] + call speeddating#loadformats() +endif + +" }}}1 + +let &cpo = s:cpo_save + +" vim:set et sw=2 sts=2: diff --git a/pack/acp/start/vim-speeddating/doc/speeddating.txt b/pack/acp/start/vim-speeddating/doc/speeddating.txt new file mode 100644 index 0000000..044baf0 --- /dev/null +++ b/pack/acp/start/vim-speeddating/doc/speeddating.txt @@ -0,0 +1,102 @@ +*speeddating.txt* Use CTRL-A/CTRL-X to increment dates, times, and more + +Author: Tim Pope <http://tpo.pe/> +License: Same terms as Vim itself (see |license|) + +This plugin is only available if 'compatible' is not set. + +INTRODUCTION *speeddating* + +The easiest way to get a feel for this plugin is to copy the following lines +to a temp file and go to town on them with <C-A> and <C-X>. When you're done, +come back here and read about some of the more advanced features, like +incrementing lists and custom formats. > + + Fri, 31 Dec 1999 23:59:59 +0000 + Fri Dec 31 23:59:59 UTC 1999 + 2008-01-05T04:59:59Z + 1865-04-15 + 11/Sep/01 + January 14th, 1982 + 11:55 AM + 3rd + XXXVIII +< +MAPS *speeddating-maps* + +Here, "component" refers to any year, month, day, hour, minute, or second +written as either a number or a word ("January") in any recognized format, or +a number or ordinal ("1st") outside of a time. + + *speeddating-CTRL-A* +<C-A> Increment by [count] the component under the cursor. + + *speeddating-CTRL-X* +<C-X> Decrement by [count] the component under the cursor. + + *speeddating-d_CTRL-A* +d<C-A> Change the time under the cursor to the current time + in UTC. + + *speeddating-d_CTRL-X* +d<C-X> Change the time under the cursor to the current local + time. + + *speeddating-v_CTRL-A* +{Visual}<C-A> Increment by [count] the component under the cursor on + each line of the linewise visual selection. If a + component is absent on a line, it is filled in as + being [count] higher than on the line above it. This + can be used to create sequences. For example, place a + "0" on a line followed by 4 blank lines, visually + select all 5 lines, and press <C-A> to get a sequence + of 1 through 5. You can use letters in visual mode + too: make the first entry Z if you want a list + starting with A. + + *speeddating-v_CTRL-X* +{Visual}<C-X> Like |v_CTRL-A|, but decrement. + + *speeddating-.* +. If you want to use |.| to repeat a speeddating.vim + mapping, install repeat.vim. + +FORMATS *speeddating-formats* + +One can use the :SpeedDatingFormat command to list, add, and remove formats. +A good place to do this is in .vim/after/plugin/speeddating.vim. + + *:SpeedDatingFormat* +:SpeedDatingFormat List defined formats. + +:SpeedDatingFormat! Help for defining formats. + +:SpeedDatingFormat {format} + Define a new format. + +:{count}SpeedDatingFormat {format} + Define a new format with the specified priority. + +:SpeedDatingFormat! {format} + Remove an existing format. + +:{count}SpeedDatingFormat! + Remove an existing format by priority. + +Of note is that the built-in support for Roman numerals is actually +implemented with a Roman numeral year format and can be removed. + +CAVEATS *speeddating-caveats* + +Gregorian calendar always used. + +Time zone abbreviation support is limited to a few predefined codes on Windows +and other platforms without strftime("%Z") support. If your time zone +abbreviation is not correctly identified set the g:speeddating_zone and +g:speeddating_zone_dst variables. + +Beginning a format with a digit causes Vim to treat leading digits as a count +instead. To work around this escape it with %[] (e.g., %[2]0%0y%0m%0d%* is a +decent format for DNS serials). + + vim:tw=78:et:ft=help:norl: diff --git a/pack/acp/start/vim-speeddating/plugin/speeddating.vim b/pack/acp/start/vim-speeddating/plugin/speeddating.vim new file mode 100644 index 0000000..0982842 --- /dev/null +++ b/pack/acp/start/vim-speeddating/plugin/speeddating.vim @@ -0,0 +1,93 @@ +" speeddating.vim - Use CTRL-A/CTRL-X to increment dates, times, and more +" Maintainer: Tim Pope <http://tpo.pe/> +" Version: 20150124 +" GetLatestVimScripts: 2120 1 :AutoInstall: speeddating.vim + +" Initialization {{{1 + +if exists("g:loaded_speeddating") || &cp || v:version < 700 + finish +endif +let g:loaded_speeddating = 1 + +let s:cpo_save = &cpo +set cpo&vim + +let g:speeddating_handlers = [] + +" }}}1 +" Time Handler {{{1 + +function! s:add_format(master,count,bang) + " Calls with neither argument nor count are for information, + " and so should be handled immediately. + " Call loadformats to cause autoloading to happen + if a:master == "" && !a:count + call speeddating#loadformats() + endif + + if exists("g:speeddating_loaded_formats") + " Autoloading already done pass on request immediately + call speeddating#adddate(a:master,a:count,a:bang) + else + " Defer handling of format specifications until autoloading is done + let g:speeddating_formats += [[a:master,a:count,a:bang]] + endif +endfunction + +command! -bar -bang -count=0 -nargs=? SpeedDatingFormat :call s:add_format(<q-args>,<count>,<bang>0) + +" }}}1 +" Maps {{{1 + +nnoremap <silent> <Plug>SpeedDatingUp :<C-U>call speeddating#increment(v:count1)<CR> +nnoremap <silent> <Plug>SpeedDatingDown :<C-U>call speeddating#increment(-v:count1)<CR> +vnoremap <silent> <Plug>SpeedDatingUp :<C-U>call speeddating#incrementvisual(v:count1)<CR> +vnoremap <silent> <Plug>SpeedDatingDown :<C-U>call speeddating#incrementvisual(-v:count1)<CR> +nnoremap <silent> <Plug>SpeedDatingNowLocal :<C-U>call speeddating#timestamp(0,v:count)<CR> +nnoremap <silent> <Plug>SpeedDatingNowUTC :<C-U>call speeddating#timestamp(1,v:count)<CR> + +if !exists("g:speeddating_no_mappings") || !g:speeddating_no_mappings + nmap <C-A> <Plug>SpeedDatingUp + nmap <C-X> <Plug>SpeedDatingDown + xmap <C-A> <Plug>SpeedDatingUp + xmap <C-X> <Plug>SpeedDatingDown + nmap d<C-A> <Plug>SpeedDatingNowUTC + nmap d<C-X> <Plug>SpeedDatingNowLocal +endif + +" }}}1 +" Default Formats {{{1 + +if exists('g:speeddating_formats') + finish +endif +let g:speeddating_formats = [] +SpeedDatingFormat %i, %d %h %Y %H:%M:%S %z " RFC822 +SpeedDatingFormat %i, %h %d, %Y at %I:%M:%S%^P %z " mutt default date format +SpeedDatingFormat %a %b %_d %H:%M:%S %Z %Y " default date(1) format +SpeedDatingFormat %a %h %-d %H:%M:%S %Y %z " git +SpeedDatingFormat %h %_d %H:%M:%S " syslog +SpeedDatingFormat %Y-%m-%d%[ T_-]%H:%M:%S %z +SpeedDatingFormat %Y-%m-%d%[ T_-]%H:%M:%S%?[Z] " SQL, etc. +SpeedDatingFormat %Y-%m-%d%[ T_-]%H:%M%z " date -Im +SpeedDatingFormat %Y-%m-%d%[ T_-]%H:%M +SpeedDatingFormat %Y-%m-%d +SpeedDatingFormat %-I:%M:%S%?[ ]%^P +SpeedDatingFormat %-I:%M%?[ ]%^P +SpeedDatingFormat %-I%?[ ]%^P +SpeedDatingFormat %H:%M:%S,%k " SRT file +SpeedDatingFormat %H:%M:%S +SpeedDatingFormat %B %o, %Y +SpeedDatingFormat %d%[-/ ]%b%1%y +SpeedDatingFormat %d%[-/ ]%b%1%Y " These three are common in the +SpeedDatingFormat %Y %b %d " 'Last Change:' headers of +SpeedDatingFormat %b %d, %Y " Vim runtime files +SpeedDatingFormat %^v +SpeedDatingFormat %v + +" }}}1 + +let &cpo = s:cpo_save + +" vim:set et sw=2 sts=2: