From 35456e0e6fe0b8222809065dd83e3797e4fe2ca7 Mon Sep 17 00:00:00 2001 From: Anthony Perkins Date: Tue, 11 Jun 2019 16:16:38 +0100 Subject: [PATCH] Add vim-orgmode 35e94218c12a0c063b4b3a9b48e7867578e1e13c and vim-speeddating v20151024 --- pack/acp/README.md | 2 + pack/acp/start/vim-orgmode/.gitignore | 6 + pack/acp/start/vim-orgmode/.pylintrc | 238 ++ pack/acp/start/vim-orgmode/.travis.yml | 21 + pack/acp/start/vim-orgmode/CHANGELOG.org | 214 ++ pack/acp/start/vim-orgmode/LICENSE | 60 + pack/acp/start/vim-orgmode/Makefile | 91 + pack/acp/start/vim-orgmode/README.org | 43 + pack/acp/start/vim-orgmode/build_vmb.vim | 4 + pack/acp/start/vim-orgmode/debian/changelog | 869 +++++ pack/acp/start/vim-orgmode/debian/compat | 1 + pack/acp/start/vim-orgmode/debian/control | 14 + pack/acp/start/vim-orgmode/debian/copyright | 28 + pack/acp/start/vim-orgmode/debian/dirs | 1 + pack/acp/start/vim-orgmode/debian/docs | 2 + pack/acp/start/vim-orgmode/debian/examples | 1 + pack/acp/start/vim-orgmode/debian/rules | 11 + pack/acp/start/vim-orgmode/doc/orgguide.txt | 1553 +++++++++ .../start/vim-orgmode/documentation/Makefile | 9 + .../vim-orgmode/documentation/diagram.txt | 75 + .../documentation/emacs_orgguide.org | 3087 +++++++++++++++++ .../documentation/emacs_orgguide.texi | 2689 ++++++++++++++ .../acp/start/vim-orgmode/examples/mylife.gif | Bin 0 -> 101565 bytes .../acp/start/vim-orgmode/examples/mylife.org | 26 + .../acp/start/vim-orgmode/examples/mylife.png | Bin 0 -> 67333 bytes .../examples/plugins/PluginExample.py | 53 + pack/acp/start/vim-orgmode/ftdetect/org.vim | 2 + pack/acp/start/vim-orgmode/ftplugin/org.cnf | 5 + pack/acp/start/vim-orgmode/ftplugin/org.vim | 169 + .../vim-orgmode/ftplugin/orgmode/__init__.py | 1 + .../vim-orgmode/ftplugin/orgmode/_vim.py | 411 +++ .../ftplugin/orgmode/docs/Makefile | 230 ++ .../vim-orgmode/ftplugin/orgmode/docs/conf.py | 387 +++ .../ftplugin/orgmode/docs/index.rst | 22 + .../ftplugin/orgmode/docs/make.bat | 281 ++ .../orgmode/docs/orgmode.liborgmode.rst | 78 + .../ftplugin/orgmode/docs/orgmode.plugins.rst | 110 + .../orgmode/docs/orgmode.py3compat.rst | 46 + .../ftplugin/orgmode/docs/orgmode.rst | 71 + .../ftplugin/orgmode/exceptions.py | 23 + .../ftplugin/orgmode/keybinding.py | 217 ++ .../ftplugin/orgmode/liborgmode/__init__.py | 1 + .../ftplugin/orgmode/liborgmode/agenda.py | 63 + .../orgmode/liborgmode/agendafilter.py | 93 + .../ftplugin/orgmode/liborgmode/base.py | 190 + .../ftplugin/orgmode/liborgmode/checkboxes.py | 406 +++ .../ftplugin/orgmode/liborgmode/documents.py | 315 ++ .../ftplugin/orgmode/liborgmode/dom_obj.py | 505 +++ .../ftplugin/orgmode/liborgmode/headings.py | 889 +++++ .../ftplugin/orgmode/liborgmode/orgdate.py | 296 ++ .../vim-orgmode/ftplugin/orgmode/menu.py | 173 + .../ftplugin/orgmode/plugins/Agenda.py | 314 ++ .../ftplugin/orgmode/plugins/Date.py | 318 ++ .../ftplugin/orgmode/plugins/EditCheckbox.py | 330 ++ .../ftplugin/orgmode/plugins/EditStructure.py | 430 +++ .../ftplugin/orgmode/plugins/Export.py | 183 + .../ftplugin/orgmode/plugins/Hyperlinks.py | 221 ++ .../ftplugin/orgmode/plugins/LoggingWork.py | 42 + .../ftplugin/orgmode/plugins/Misc.py | 173 + .../ftplugin/orgmode/plugins/Navigator.py | 326 ++ .../ftplugin/orgmode/plugins/ShowHide.py | 181 + .../orgmode/plugins/TagsProperties.py | 215 ++ .../ftplugin/orgmode/plugins/Todo.py | 345 ++ .../ftplugin/orgmode/plugins/__init__.py | 1 + .../ftplugin/orgmode/py3compat/__init__.py | 1 + .../orgmode/py3compat/encode_compatibility.py | 11 + .../orgmode/py3compat/py_py3_string.py | 7 + .../py3compat/unicode_compatibility.py | 4 + .../orgmode/py3compat/xrange_compatibility.py | 4 + .../vim-orgmode/ftplugin/orgmode/settings.py | 98 + .../vim-orgmode/ftplugin/orgmode/vimbuffer.py | 503 +++ pack/acp/start/vim-orgmode/indent/org.vim | 133 + pack/acp/start/vim-orgmode/install_vba.vim | 3 + pack/acp/start/vim-orgmode/install_vmb.vim | 3 + pack/acp/start/vim-orgmode/org | 54 + pack/acp/start/vim-orgmode/syntax/org.vim | 383 ++ .../start/vim-orgmode/syntax/orgagenda.vim | 79 + pack/acp/start/vim-orgmode/syntax/orgtodo.vim | 47 + .../vim-orgmode/tests/orgmode_testfile.org | 37 + pack/acp/start/vim-orgmode/tests/run_tests.py | 59 + .../vim-orgmode/tests/test_libagendafilter.py | 171 + .../start/vim-orgmode/tests/test_libbase.py | 34 + .../vim-orgmode/tests/test_libcheckbox.py | 132 + .../vim-orgmode/tests/test_libheading.py | 156 + .../vim-orgmode/tests/test_liborgdate.py | 53 + .../tests/test_liborgdate_parsing.py | 248 ++ .../vim-orgmode/tests/test_liborgdate_utf8.py | 52 + .../vim-orgmode/tests/test_liborgdatetime.py | 50 + .../vim-orgmode/tests/test_liborgtimerange.py | 86 + .../vim-orgmode/tests/test_plugin_date.py | 174 + .../tests/test_plugin_edit_checkbox.py | 241 ++ .../tests/test_plugin_edit_structure.py | 387 +++ .../vim-orgmode/tests/test_plugin_mappings.py | 68 + .../vim-orgmode/tests/test_plugin_misc.py | 164 + .../tests/test_plugin_navigator.py | 633 ++++ .../tests/test_plugin_show_hide.py | 385 ++ .../tests/test_plugin_tags_properties.py | 177 + .../vim-orgmode/tests/test_plugin_todo.py | 424 +++ .../start/vim-orgmode/tests/test_vimbuffer.py | 1257 +++++++ pack/acp/start/vim-orgmode/tests/vim.py | 88 + pack/acp/start/vim-speeddating/.gitattributes | 1 + pack/acp/start/vim-speeddating/.gitignore | 1 + .../acp/start/vim-speeddating/README.markdown | 71 + .../vim-speeddating/autoload/speeddating.vim | 808 +++++ .../start/vim-speeddating/doc/speeddating.txt | 102 + .../vim-speeddating/plugin/speeddating.vim | 93 + 106 files changed, 24643 insertions(+) create mode 100644 pack/acp/start/vim-orgmode/.gitignore create mode 100644 pack/acp/start/vim-orgmode/.pylintrc create mode 100644 pack/acp/start/vim-orgmode/.travis.yml create mode 100644 pack/acp/start/vim-orgmode/CHANGELOG.org create mode 100644 pack/acp/start/vim-orgmode/LICENSE create mode 100644 pack/acp/start/vim-orgmode/Makefile create mode 100644 pack/acp/start/vim-orgmode/README.org create mode 100644 pack/acp/start/vim-orgmode/build_vmb.vim create mode 100644 pack/acp/start/vim-orgmode/debian/changelog create mode 100644 pack/acp/start/vim-orgmode/debian/compat create mode 100644 pack/acp/start/vim-orgmode/debian/control create mode 100644 pack/acp/start/vim-orgmode/debian/copyright create mode 100644 pack/acp/start/vim-orgmode/debian/dirs create mode 100644 pack/acp/start/vim-orgmode/debian/docs create mode 100644 pack/acp/start/vim-orgmode/debian/examples create mode 100755 pack/acp/start/vim-orgmode/debian/rules create mode 100644 pack/acp/start/vim-orgmode/doc/orgguide.txt create mode 100644 pack/acp/start/vim-orgmode/documentation/Makefile create mode 100644 pack/acp/start/vim-orgmode/documentation/diagram.txt create mode 100644 pack/acp/start/vim-orgmode/documentation/emacs_orgguide.org create mode 100644 pack/acp/start/vim-orgmode/documentation/emacs_orgguide.texi create mode 100644 pack/acp/start/vim-orgmode/examples/mylife.gif create mode 100644 pack/acp/start/vim-orgmode/examples/mylife.org create mode 100644 pack/acp/start/vim-orgmode/examples/mylife.png create mode 100644 pack/acp/start/vim-orgmode/examples/plugins/PluginExample.py create mode 100644 pack/acp/start/vim-orgmode/ftdetect/org.vim create mode 100644 pack/acp/start/vim-orgmode/ftplugin/org.cnf create mode 100644 pack/acp/start/vim-orgmode/ftplugin/org.vim create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/__init__.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/_vim.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/Makefile create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/conf.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/index.rst create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/make.bat create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.liborgmode.rst create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.plugins.rst create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.py3compat.rst create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/docs/orgmode.rst create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/exceptions.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/keybinding.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/__init__.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/agenda.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/agendafilter.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/base.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/checkboxes.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/documents.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/dom_obj.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/headings.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/liborgmode/orgdate.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/menu.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Agenda.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Date.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/EditCheckbox.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/EditStructure.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Export.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Hyperlinks.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/LoggingWork.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Misc.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Navigator.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/ShowHide.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/TagsProperties.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/Todo.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/plugins/__init__.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/__init__.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/encode_compatibility.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/py_py3_string.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/unicode_compatibility.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/py3compat/xrange_compatibility.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/settings.py create mode 100644 pack/acp/start/vim-orgmode/ftplugin/orgmode/vimbuffer.py create mode 100644 pack/acp/start/vim-orgmode/indent/org.vim create mode 100644 pack/acp/start/vim-orgmode/install_vba.vim create mode 100644 pack/acp/start/vim-orgmode/install_vmb.vim create mode 100755 pack/acp/start/vim-orgmode/org create mode 100644 pack/acp/start/vim-orgmode/syntax/org.vim create mode 100644 pack/acp/start/vim-orgmode/syntax/orgagenda.vim create mode 100644 pack/acp/start/vim-orgmode/syntax/orgtodo.vim create mode 100644 pack/acp/start/vim-orgmode/tests/orgmode_testfile.org create mode 100755 pack/acp/start/vim-orgmode/tests/run_tests.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_libagendafilter.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_libbase.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_libcheckbox.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_libheading.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_liborgdate.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_liborgdate_parsing.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_liborgdate_utf8.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_liborgdatetime.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_liborgtimerange.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_plugin_date.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_plugin_edit_checkbox.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_plugin_edit_structure.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_plugin_mappings.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_plugin_misc.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_plugin_navigator.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_plugin_show_hide.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_plugin_tags_properties.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_plugin_todo.py create mode 100644 pack/acp/start/vim-orgmode/tests/test_vimbuffer.py create mode 100644 pack/acp/start/vim-orgmode/tests/vim.py create mode 100644 pack/acp/start/vim-speeddating/.gitattributes create mode 100644 pack/acp/start/vim-speeddating/.gitignore create mode 100644 pack/acp/start/vim-speeddating/README.markdown create mode 100644 pack/acp/start/vim-speeddating/autoload/speeddating.vim create mode 100644 pack/acp/start/vim-speeddating/doc/speeddating.txt create mode 100644 pack/acp/start/vim-speeddating/plugin/speeddating.vim 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 0000000000000000000000000000000000000000..71e7f654020101cddf2e184aac7e4dfb29d3c76e GIT binary patch literal 101565 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≫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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..70cca310274d1f1c0119143a915377b567e33b12 GIT binary patch literal 67333 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

44_<=#47neiIa4 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

^nU?UhkNUftjLIn2mDVYil1o&W#na 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 literal 0 HcmV?d00001 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: