Repository: jceb/vim-orgmode Branch: master Commit: e9506ee5cb49 Files: 81 Total size: 617.0 KB Directory structure: gitextract_x59pk6e5/ ├── .gitignore ├── .pylintrc ├── .travis.yml ├── CHANGELOG.org ├── LICENSE ├── Makefile ├── README.org ├── doc/ │ └── orgguide.txt ├── examples/ │ ├── mylife.org │ └── plugins/ │ └── PluginExample.py ├── ftdetect/ │ └── org.vim ├── ftplugin/ │ ├── org.cnf │ ├── org.vim │ └── orgmode/ │ ├── __init__.py │ ├── _vim.py │ ├── docs/ │ │ ├── Makefile │ │ ├── conf.py │ │ ├── index.rst │ │ ├── make.bat │ │ ├── orgmode.liborgmode.rst │ │ ├── orgmode.plugins.rst │ │ ├── orgmode.py3compat.rst │ │ └── orgmode.rst │ ├── exceptions.py │ ├── keybinding.py │ ├── liborgmode/ │ │ ├── __init__.py │ │ ├── agenda.py │ │ ├── agendafilter.py │ │ ├── base.py │ │ ├── checkboxes.py │ │ ├── documents.py │ │ ├── dom_obj.py │ │ ├── headings.py │ │ └── orgdate.py │ ├── menu.py │ ├── plugins/ │ │ ├── Agenda.py │ │ ├── Date.py │ │ ├── EditCheckbox.py │ │ ├── EditStructure.py │ │ ├── Export.py │ │ ├── Hyperlinks.py │ │ ├── LoggingWork.py │ │ ├── Misc.py │ │ ├── Navigator.py │ │ ├── ShowHide.py │ │ ├── TagsProperties.py │ │ ├── Todo.py │ │ └── __init__.py │ ├── py3compat/ │ │ ├── __init__.py │ │ ├── encode_compatibility.py │ │ ├── py_py3_string.py │ │ ├── unicode_compatibility.py │ │ └── xrange_compatibility.py │ ├── settings.py │ └── vimbuffer.py ├── indent/ │ └── org.vim ├── syntax/ │ ├── org.vim │ ├── orgagenda.vim │ └── orgtodo.vim └── tests/ ├── orgmode_testfile.org ├── run_tests.py ├── test_libagendafilter.py ├── test_libbase.py ├── test_libcheckbox.py ├── test_libheading.py ├── test_liborgdate.py ├── test_liborgdate_parsing.py ├── test_liborgdate_utf8.py ├── test_liborgdatetime.py ├── test_liborgtimerange.py ├── test_plugin_date.py ├── test_plugin_edit_checkbox.py ├── test_plugin_edit_structure.py ├── test_plugin_mappings.py ├── test_plugin_misc.py ├── test_plugin_navigator.py ├── test_plugin_show_hide.py ├── test_plugin_tags_properties.py ├── test_plugin_todo.py ├── test_vimbuffer.py └── vim.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.pyc *.swp tags .ropeproject .cover* cover* ================================================ FILE: .pylintrc ================================================ [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 [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 ================================================ FILE: .travis.yml ================================================ 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" - "3.7" - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" install: - pip install coverage - pip install codecov script: - cd tests - nosetests --with-coverage . after_success: - codecov ================================================ FILE: CHANGELOG.org ================================================ * 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 ================================================ FILE: LICENSE ================================================ --------------------------------------------------------------------- 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. ================================================ FILE: Makefile ================================================ 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 ================================================ FILE: README.org ================================================ * 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]] ================================================ FILE: doc/orgguide.txt ================================================ *orgguide.txt* For Vim version 7.3 Last change: 2019 December 13 _ _ ____ __ __ _____ ____ ___ __ __ _____ ____ ____ ( \/ )(_ _)( \/ ) ( _ )( _ \ / __)( \/ )( _ )( _ \( ___) \ / _)(_ ) ( )(_)( ) /( (_-. ) ( )(_)( )(_) ))__) \/ (____)(_/\/\_) (_____)(_)\_) \___/(_/\/\_)(_____)(____/(____) ============================================================================== 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| ============================================================================== 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 also. 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* Installation can be done with plugin managers, e.g. vim-plug (https://github.com/junegunn/vim-plug), dein.vim (https://github.com/Shougo/dein.vim), pathogen.vim (https://github.com/tpope/vim-pathogen) or if you're using Vim >= 8.0, you can use the built-in plugin manager. Add the following to your .vimrc, for *vim-plug* : > Plug 'jceb/vim-orgmode' < And for *dein.vim* : > call dein#add('jceb/vim-orgmode') < With pathogen, you need to run the following in your terminal: > cd ~/.vim/bundle && \ git clone https://github.com/jceb/vim-orgmode.git < With the package feature of Vim 8, it is a bit more involved. Run the following in your terminal > mkdir -p ~/.vim/pack/git-plugins/start cd ~/.vim/pack/git-plugins/start git clone https://github.com/jceb/vim-orgmode < Then, add this to your ~/.vimrc: > packloadall silent! helptags ALL < 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. < ------------------------------------------------------------------------------ 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/emacs" < *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 additionally 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 > ------------------------------------------------------------------------------ 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: ================================================ FILE: examples/mylife.org ================================================ * 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 ================================================ FILE: examples/plugins/PluginExample.py ================================================ # -*- 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]) ================================================ FILE: ftdetect/org.vim ================================================ autocmd BufNewFile,BufRead *.org setfiletype org "autocmd BufNewFile,BufReadPost org:todo* setfiletype orgtodo ================================================ FILE: ftplugin/org.cnf ================================================ --langdef=org --langmap=org:.org --regex-org=/^(\*+)[[:space:]]+(.*)([[:space:]]+:[^\t ]*:)?$/\1 \2/s,sections/ --regex-org=/\[\[([^][]+)\]\]/\1/h,hyperlinks/ --regex-org=/\[\[[^][]+\]\[([^][]+)\]\]/\1/h,hyperlinks/ ================================================ FILE: ftplugin/org.vim ================================================ " 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 aggressively 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 glob, vim, os, sys for p in vim.eval("&runtimepath").split(','): dname = os.path.join(p, "ftplugin") matches = glob.glob(dname) for match in matches: if os.path.exists(os.path.join(match, "orgmode")): if match not in sys.path: sys.path.append(match) break from orgmode._vim import ORGMODE, insert_at_cursor, get_user_input, date_to_str ORGMODE.start() 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) exe s:py_version . "selected_date = " . printf("datetime.date(%d, %d, %d)", a:year, a:month, a:day) exe s:py_version . "org_timestamp = '" . g:org_timestamp_template . "' % date_to_str(selected_date)" " get_user_input exe s:py_version . "modifier = get_user_input(org_timestamp)" " 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 . "if modifier != None: insert_at_cursor(timestamp)" " restore calendar_action let g:calendar_action = g:org_calendar_action_backup endf ================================================ FILE: ftplugin/orgmode/__init__.py ================================================ # -*- coding: utf-8 -*- ================================================ FILE: ftplugin/orgmode/_vim.py ================================================ # -*- coding: utf-8 -*- """ VIM ORGMODE ~~~~~~~~~~~~ TODO """ try: import importlib USE_DEPRECATED_IMP=False except: import imp USE_DEPRECATED_IMP=True 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. """ try: 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()')) 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 if USE_DEPRECATED_IMP: # 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: if USE_DEPRECATED_IMP: module = imp.load_module(plugin, *module) else: module = importlib.import_module(".plugins." + plugin, "orgmode") 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() ================================================ FILE: ftplugin/orgmode/docs/Makefile ================================================ # 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." ================================================ FILE: ftplugin/orgmode/docs/conf.py ================================================ #!/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 ================================================ FILE: ftplugin/orgmode/docs/index.rst ================================================ .. 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` ================================================ FILE: ftplugin/orgmode/docs/make.bat ================================================ @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 ================================================ FILE: ftplugin/orgmode/docs/orgmode.liborgmode.rst ================================================ 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: ================================================ FILE: ftplugin/orgmode/docs/orgmode.plugins.rst ================================================ 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: ================================================ FILE: ftplugin/orgmode/docs/orgmode.py3compat.rst ================================================ 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: ================================================ FILE: ftplugin/orgmode/docs/orgmode.rst ================================================ 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: ================================================ FILE: ftplugin/orgmode/exceptions.py ================================================ # -*- 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) ================================================ FILE: ftplugin/orgmode/keybinding.py ================================================ # -*- 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)) ================================================ FILE: ftplugin/orgmode/liborgmode/__init__.py ================================================ # -*- coding: utf-8 -*- ================================================ FILE: ftplugin/orgmode/liborgmode/agenda.py ================================================ # -*- 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 from orgmode.liborgmode.orgdate import OrgDateTime, OrgTimeRange import datetime def agenda_sorting_key(heading): orgtime = heading.active_date if orgtime is None or isinstance(orgtime, OrgDateTime): return orgtime if isinstance(orgtime, OrgTimeRange): return orgtime.start # It is an OrgDate. OrgDate cannot be compared with datetime-based Org* values by # default, so it will be converted in such a way that: # * OrgDate value of _today_ will be displayed after today's passed events and before # today's upcoming scheduled events. # * OrgDate value of a past day will be displayed after all other items of the same # day. # * OrgDate value of a future day will be displayed before all other items of the same # day. now = datetime.datetime.now() today = now.date() time_to_add = now.time() if today == orgtime else datetime.time(0, 0) if today < orgtime else datetime.time(23, 59) return datetime.datetime.combine(orgtime, time_to_add) 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, key=agenda_sorting_key) 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, key=agenda_sorting_key) 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, key=agenda_sorting_key) ================================================ FILE: ftplugin/orgmode/liborgmode/agendafilter.py ================================================ # -*- 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 criteria 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 within 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 dependency... 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) ================================================ FILE: ftplugin/orgmode/liborgmode/base.py ================================================ # -*- 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 try: from collections.abc import Iterable except ImportError: # preserve compatibility with python < 3.10 from collections import Iterable 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, 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 :position: 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) ================================================ FILE: ftplugin/orgmode/liborgmode/checkboxes.py ================================================ # -*- 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 current 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 current 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 ================================================ FILE: ftplugin/orgmode/liborgmode/documents.py ================================================ # -*- 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 implementation!') 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 instantiated 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) ================================================ FILE: ftplugin/orgmode/liborgmode/dom_obj.py ================================================ # -*- 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) ================================================ FILE: ftplugin/orgmode/liborgmode/headings.py ================================================ # -*- 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 :position: 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 instantiated :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, return 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 rewritten 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 documentation 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) ================================================ FILE: ftplugin/orgmode/liborgmode/orgdate.py ================================================ # -*- 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> r"<(\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> r"<(\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. That's 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 timestr(self): return '--:--' def date(self): return self 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. That's 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 timestr(self): return self.strftime('%H:%M') def date(self): return OrgDate(self.active, self.year, self.month, self.day) 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__()) def date(self): return OrgDate(self.active, self.start.year, self.start.month, self.start.day) ================================================ FILE: ftplugin/orgmode/menu.py ================================================ # -*- 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() ================================================ FILE: ftplugin/orgmode/plugins/Agenda.py ================================================ # -*- 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 = None final_agenda = [u'Week Agenda:'] for i, h in enumerate(raw_agenda): # insert date information for every new date (not datetime) active_date_no_time = h.active_date.date() if active_date_no_time != last_date: today = date.today() # insert additional "TODAY" string if active_date_no_time == today: section = unicode(active_date_no_time) + u" TODAY" today_row = len(final_agenda) + 1 else: section = unicode(active_date_no_time) final_agenda.append(section) # update last_date last_date = active_date_no_time p = h tags = [] while p is not None: tags += p.tags p = p.parent bufname = os.path.basename(vim.buffers[h.document.bufnr].name) bufname = bufname[:-4] if bufname.endswith(u'.org') else bufname formatted = u" %(bufname)s (%(bufnr)d) %(todo)s %(timestr)s %(title)s %(tags)s" % { 'bufname': bufname, 'bufnr': h.document.bufnr, 'todo': h.todo, 'timestr': h.active_date.timestr(), 'title': h.title, 'tags': ':' + ':'.join(tags) + ':' if tags else '' } final_agenda.append(formatted) 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 position 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 = fmt.format('{} {}', h.todo, h.title).lstrip().rstrip() 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' ) ================================================ FILE: ftplugin/orgmode/plugins/Date.py ================================================ # -*- 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, separated 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, separated 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, separated 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, separated 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 prompt 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>') ================================================ FILE: ftplugin/orgmode/plugins/EditCheckbox.py ================================================ # -*- 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' ) ================================================ FILE: ftplugin/orgmode/plugins/EditStructure.py ================================================ # -*- 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'<localleader><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))) ================================================ FILE: ftplugin/orgmode/plugins/Export.py ================================================ # -*- 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(map(lambda x: x.decode(), 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)' ) ================================================ FILE: ftplugin/orgmode/plugins/Hyperlinks.py ================================================ # -*- 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]) ================================================ FILE: ftplugin/orgmode/plugins/LoggingWork.py ================================================ # -*- 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]) ================================================ FILE: ftplugin/orgmode/plugins/Misc.py ================================================ # -*- 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)) ================================================ FILE: ftplugin/orgmode/plugins/Navigator.py ================================================ # -*- 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 performs 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))) ================================================ FILE: ftplugin/orgmode/plugins/ShowHide.py ================================================ # -*- 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 because 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 beyond 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)) ================================================ FILE: ftplugin/orgmode/plugins/TagsProperties.py ================================================ # -*- 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)\n" + sys.executable.split('/')[-1] + u""" << 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()")) ================================================ FILE: ftplugin/orgmode/plugins/Todo.py ================================================ # -*- coding: utf-8 -*- import vim import re 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 different 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(re.findall(r'\d+$',vim.current.buffer.name)[0]) 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 available 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/- (interactive)', 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)) ================================================ FILE: ftplugin/orgmode/plugins/__init__.py ================================================ # -*- coding: utf-8 -*- ================================================ FILE: ftplugin/orgmode/py3compat/__init__.py ================================================ # -*- coding: utf-8 -*- ================================================ FILE: ftplugin/orgmode/py3compat/encode_compatibility.py ================================================ 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 ================================================ FILE: ftplugin/orgmode/py3compat/py_py3_string.py ================================================ import sys from string import Formatter if sys.version_info < (3,): VIM_PY_CALL = u':py' else: VIM_PY_CALL = u':py3' class NoneAsEmptyFormatter(Formatter): def get_value(self, key, args, kwargs): v = super().get_value(key, args, kwargs) return '' if v is None else v fmt = NoneAsEmptyFormatter() ================================================ FILE: ftplugin/orgmode/py3compat/unicode_compatibility.py ================================================ try: unicode except NameError: basestring = unicode = str ================================================ FILE: ftplugin/orgmode/py3compat/xrange_compatibility.py ================================================ try: from __builtin__ import xrange as range except: pass ================================================ FILE: ftplugin/orgmode/settings.py ================================================ # -*- 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 ================================================ FILE: ftplugin/orgmode/vimbuffer.py ================================================ # -*- 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) ================================================ FILE: indent/org.vim ================================================ " 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 ================================================ FILE: syntax/org.vim ================================================ if exists("b:current_syntax") finish endif " Support org authoring markup as closely as possible " (we're adding two markdown-like variants for =code= and blockquotes) " ----------------------------------------------------------------------------- " " Do we use aggressive 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) " - 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 syntax region org_strikethrough matchgroup=org_border_strike 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 syntax region org_strikethrough 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 hi def org_strikethrough term=strikethrough cterm=strikethrough gui=strikethrough 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:safename = substitute(l:_i, "\\W", "\\=('u' . char2nr(submatch(0)))", "g") 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:safename call OrgExtendHighlightingGroup(l:default_group, l:group, OrgInterpretFaces(l:j[1])) break endif endfor silent! exec 'syntax match org_todo_keyword_' . l:safename . ' /\*\{1,\}\s\{1,\}\zs' . l:_i .'\(\s\|$\)/ ' . a:todo_headings . ' contains=@NoSpell' silent! exec 'hi def link org_todo_keyword_' . l:safename . ' ' . 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 " References: {{{1 syntax match reference '\\ref{.*}' transparent contains=referenceStart,referenceEnd syntax match referenceStart '\\ref{*' contained conceal cchar=[ syntax match referenceEnd '\(\\ref{\w\+\)\@<=\zs}' contained conceal cchar=] " 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 haskell', '#+END_SRC', 'haskell', 'comment') call SyntaxRange#Include('#+BEGIN_SRC ocaml', '#+END_SRC', 'ocaml', 'comment') call SyntaxRange#Include('#+BEGIN_SRC ruby', '#+END_SRC', 'ruby', 'comment') call SyntaxRange#Include('#+BEGIN_SRC rust', '#+END_SRC', 'rust', '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') call SyntaxRange#Include('\$[^$]', '\$', 'tex') endif let b:current_syntax = "org" " vi: ft=vim:tw=80:sw=4:ts=4:fdm=marker ================================================ FILE: syntax/orgagenda.vim ================================================ " TODO do we really need a separate syntax file for the agenda? " - Most of the stuff here is also in syntax.org " - DRY! if exists("b:current_syntax") finish endif syn match org_todo_key /\[\zs[^]]*\ze\]/ hi def link org_todo_key Identifier " Multi-colored tags in agenda syn match org_tag_1 /:[a-iA-I][^: ]*:/hs=s+1,me=e-1 syn match org_tag_2 /:[j-rJ-R][^: ]*:/hs=s+1,me=e-1 syn match org_tag_3 /:[s-zS-Z0][^: ]*:/hs=s+1,me=e-1 syn match org_tag_4 /:[1-9_][^: ]*:/hs=s+1,me=e-1 syn match org_tag_5 /:[\W][^: ]*:/hs=s+1,me=e-1 hi def link org_tag_1 Title hi def link org_tag_2 Constant hi def link org_tag_3 Statement hi def link org_tag_4 Type hi def link org_tag_5 Special 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 let b:current_syntax = "orgagenda" ================================================ FILE: syntax/orgtodo.vim ================================================ if exists("b:current_syntax") finish endif 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 . ' contains=@NoSpell' 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 let b:current_syntax = "orgtodo" ================================================ FILE: tests/orgmode_testfile.org ================================================ * 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*= ================================================ FILE: tests/run_tests.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import unittest 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 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) ================================================ FILE: tests/test_libagendafilter.py ================================================ # -*- 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 returned. """ 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) ================================================ FILE: tests/test_libbase.py ================================================ # -*- 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) ================================================ FILE: tests/test_libcheckbox.py ================================================ # -*- 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) ================================================ FILE: tests/test_libheading.py ================================================ # -*- 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) ================================================ FILE: tests/test_liborgdate.py ================================================ # -*- 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) ================================================ FILE: tests/test_liborgdate_parsing.py ================================================ # -*- 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) ================================================ FILE: tests/test_liborgdate_utf8.py ================================================ # -*- 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 = "fr_FR.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 sam.>' self.textinactive = u'[2016-05-07 sam.]' 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) ================================================ FILE: tests/test_liborgdatetime.py ================================================ # -*- 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) ================================================ FILE: tests/test_liborgtimerange.py ================================================ # -*- 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) ================================================ FILE: tests/test_plugin_date.py ================================================ # -*- 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) ================================================ FILE: tests/test_plugin_edit_checkbox.py ================================================ # -*- 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) ================================================ FILE: tests/test_plugin_edit_structure.py ================================================ # -*- 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) ================================================ FILE: tests/test_plugin_mappings.py ================================================ # -*- 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) ================================================ FILE: tests/test_plugin_misc.py ================================================ #!/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) ================================================ FILE: tests/test_plugin_navigator.py ================================================ # -*- 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 focused 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 position: | # << 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 position: | # << 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 position: | # 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) ================================================ FILE: tests/test_plugin_show_hide.py ================================================ # -*- 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) ================================================ FILE: tests/test_plugin_tags_properties.py ================================================ #!/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) ================================================ FILE: tests/test_plugin_todo.py ================================================ # -*- 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) ================================================ FILE: tests/test_vimbuffer.py ================================================ # -*- 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), ) ================================================ FILE: tests/vim.py ================================================ # -*- 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()