Add vim-orgmode 35e94218c12a0c063b4b3a9b48e7867578e1e13c and vim-speeddating v20151024

This commit is contained in:
Anthony Rose 2019-06-11 16:16:38 +01:00
parent 15df158a9c
commit 35456e0e6f
106 changed files with 24643 additions and 0 deletions

View file

@ -11,5 +11,7 @@ Git submodules are slow, so handle this manually.
* [start/mom.vim](https://github.com/vim-scripts/mom.vim) * [start/mom.vim](https://github.com/vim-scripts/mom.vim)
* [start/rust.vim](https://github.com/rust-lang/rust.vim) * [start/rust.vim](https://github.com/rust-lang/rust.vim)
* [start/vim-go](https://github.com/fatih/vim-go) * [start/vim-go](https://github.com/fatih/vim-go)
* [start/vim-orgmode](https://github.com/jceb/vim-orgmode)
* [start/vim-ps1](https://github.com/PProvost/vim-ps1) * [start/vim-ps1](https://github.com/PProvost/vim-ps1)
* [start/vim-speeddating](https://github.com/tpope/vim-speeddating)
* [start/vim-surround](https://github.com/tpope/vim-surround) * [start/vim-surround](https://github.com/tpope/vim-surround)

6
pack/acp/start/vim-orgmode/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
*.pyc
*.swp
tags
.ropeproject
.cover*
cover*

View file

@ -0,0 +1,238 @@
[MASTER]
# Specify a configuration file.
#rcfile=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
init-hook=sys.path.append(os.path.abspath('ftplugin'))
# Profiled execution.
profile=no
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=.git
# Pickle collected data for later comparisons.
persistent=yes
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
[MESSAGES CONTROL]
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time.
#enable=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
#disable=
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html
output-format=parseable
# Include message's id in output
include-ids=no
# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells whether to display a full report or only the messages
reports=yes
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Add a comment according to your evaluation note. This is used by the global
# evaluation report (RP0004).
comment=no
[TYPECHECK]
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set).
ignored-classes=SQLObject
# When zope mode is activated, add a predefined set of Zope acquired attributes
# to generated-members.
zope=no
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E0201 when accessed. Python regular
# expressions are accepted.
generated-members=REQUEST,acl_users,aq_parent
[BASIC]
# Required attributes for module, separated by a comma
required-attributes=
# List of builtins function names that should not be used, separated by a comma
bad-functions=map,filter,apply,input
# Regular expression which should only match correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression which should only match correct module level names
const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$
# Regular expression which should only match correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct argument names
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct list comprehension /
# generator expression variable names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Regular expression which should only match functions or classes name which do
# not require a docstring
no-docstring-rgx=__.*__
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
[VARIABLES]
# Tells whether we should check for unused import in __init__ files.
init-import=no
# A regular expression matching the beginning of the name of dummy variables
# (i.e. not used).
dummy-variables-rgx=_|dummy
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=4
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=800
# Maximum number of lines in a module
max-module-lines=1000
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=\t
[CLASSES]
# List of interface methods to ignore, separated by a comma. This is used for
# instance to not check methods defines in Zope's Interface base class.
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
[IMPORTS]
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of branch for function / method body
max-branchs=12
# Maximum number of statements in function / method body
max-statements=50
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
# Maximum number of public methods for a class (see R0904).
max-public-methods=20

View file

@ -0,0 +1,21 @@
language: python
before_install:
- sudo apt-get update && sudo apt-get --reinstall install -qq language-pack-pt
python:
- "2.7"
- "3.4"
- "3.5"
- "3.6"
install:
- pip install coverage
- pip install codecov
script:
- cd tests
- nosetests --with-coverage .
after_success:
- codecov

View file

@ -0,0 +1,214 @@
* Changelog
All notable changes to this project will be documented in this file.
This log is kept according to the [[http://keepachangelog.com/][Keep a CHANGELOG]] manifesto
** 0.7.0 :unreleased:
*** Added
- Subtracting when entering dates (PR #276)
*** Fixed
- =ir= text object now works with most operations (PR #284, closes #273)
** 0.6.0 <2017-11-06 Mon> :released:
*** Added
- Introduced sphinx documentation to Python modules. (PR #237)
- Add =Python3= support. (PR #231, closes #226)
- Implementing agenda overview for current buffer. (PR #229)
- =g:org_aggressive_conceal=, if value =1=, will conceal all simple format
identifying characters, default =0=. (PR #188)
- (testing on `g:org_aggressive_conceal=1' mode) Add possibility to escape
format indicating characters from leading inline markup, by escaping with
"\".
- Add alternative behavior: refrain from entering insert mode after
heading/checkbox creation through keybindings. Activate by setting
=g:org_prefer_insert_mode= to 0. (closes #211)
- Add export as LaTeX beamer slides (PR #206)
- Keybinding to create plainlist item directly. (closes #190)
- Make % jump between < and >. (PR #251, closes #250)
*** Changed
- Changed default value for =g:org_indent= from =1= to =0=. (closes #243)
- Revamped TODO keyword cycling rules. (PR #237)
- In [[syntax/org.vim][syntax/org.vim]], changed `\@<=' with computational faster `\zs'
- Using =<localleader>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
[<localleader>|<leader>]<CR>
- 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 <LocalLeader> (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 <Esc> while editing a link (closes issue
#96)
- implement reverse visibility cycling using <S-Tab> (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

View file

@ -0,0 +1,60 @@
---------------------------------------------------------------------
All source code is licensed under the terms of the following license:
---------------------------------------------------------------------
Copyright (C) 2010,2011 Jan Christoph Ebersbach
http://www.e-jc.de/
All rights reserved.
The source code of this program is made available under the terms of the
GNU Affero General Public License version 3 (GNU AGPL V3) as published
by the Free Software Foundation.
Binary versions of this program provided by Univention to you as well as
other copyrighted, protected or trademarked materials like Logos,
graphics, fonts, specific documentations and configurations,
cryptographic keys etc. are subject to a license agreement between you
and Univention and not subject to the GNU AGPL V3.
In the case you use this program under the terms of the GNU AGPL V3, the
program is provided in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
License for more details.
You should have received a copy of the GNU Affero General Public License
with the Debian GNU/Linux or Univention distribution in file
/usr/share/common-licenses/AGPL-3; if not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------
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 FSFs 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.

View file

@ -0,0 +1,91 @@
PLUGIN = orgmode
PREFIX = /usr/local
VIMDIR = $(PREFIX)/share/vim
all: build
build:
# install plugin at destination
install: doc indent ftdetect ftplugin syntax
for i in doc indent ftdetect ftplugin syntax; do \
find $$i -type f -name \*.txt -o -type f -name \*.cnf -o -type f -name \*.py -o -type f -name \*.vim | while read f; do \
install -m 0755 -d $(DESTDIR)$(VIMDIR)/$$(dirname "$$f"); \
install -m 0644 $$f $(DESTDIR)$(VIMDIR)/$$f; \
done; \
done
# cleanup
clean: documentation
@find . -name \*.pyc -o -name \*.py,cover -exec rm {} \;
@rm -rf ${PLUGIN}.vmb ${PLUGIN}.vmb.gz tmp files
cd $< && $(MAKE) $@
# generate the vim ball package
${PLUGIN}.vmb: check build_vmb.vim clean
$(MAKE) DESTDIR=$(PWD)/tmp VIMDIR= install
find tmp -type f | sed -e 's/^tmp\///' > files
cp build_vmb.vim tmp
cd tmp && vim --cmd 'let g:plugin_name="${PLUGIN}"' -s build_vmb.vim
[ -e tmp/${PLUGIN}.vba ] && mv tmp/${PLUGIN}.vba tmp/$@ || true
mv tmp/$@ .
${PLUGIN}.vmb.gz: ${PLUGIN}.vmb
@rm -f ${PLUGIN}.vmb.gz
gzip $<
vmb: ${PLUGIN}.vmb
vmb.gz: ${PLUGIN}.vmb.gz
${PLUGIN}.vba: ${PLUGIN}.vmb
mv $< $@
${PLUGIN}.vba.gz: ${PLUGIN}.vba
@rm -f ${PLUGIN}.vba.gz
gzip $<
vba: ${PLUGIN}.vba
vba.gz: ${PLUGIN}.vba.gz
# run unit tests
test: check
check: tests/run_tests.py
cd tests && python2 run_tests.py
# generate documentation
docs: documentation
cd $< && $(MAKE)
# generate a test coverage report for all python files
coverage:
@echo ">>> Coverage depends on the package python-nose and python-coverage, make sure they are installed!"
cd tests && nosetests2 --with-coverage --cover-html .
# run a static code checker
lint:
@echo ">>> Lint depends on the package pylint make sure it's installed!"
pylint --rcfile .pylintrc --disable=C0301,C0103,C0111,C0322,C0323,C0324,W0703,W0612,W0603 orgmode
lintall:
@echo ">>> Lint depends on the package pylint make sure it's installed!"
pylint --rcfile .pylintrc orgmode
# install vim-orgmode in the .vim/bundle directory for test purposes
VIMPLUGINDIR = $(HOME)/.vim/bundle/orgmode
installvmb: ${PLUGIN}.vmb install_vmb.vim
rm -rvf ${VIMPLUGINDIR}
mkdir -p "${VIMPLUGINDIR}"
vim --cmd "let g:installdir='${VIMPLUGINDIR}'" -s install_vmb.vim $<
@echo "Plugin was installed in ${VIMPLUGINDIR}. Make sure you are using a plugin loader like pathegon, otherwise the ${PLUGIN} might not work properly."
installvba: ${PLUGIN}.vba install_vba.vim
rm -rvf ${VIMPLUGINDIR}
mkdir -p "${VIMPLUGINDIR}"
vim --cmd "let g:installdir='${VIMPLUGINDIR}'" -s install_vba.vim $<
@echo "Plugin was installed in ${VIMPLUGINDIR}. Make sure you are using a plugin loader like pathegon, otherwise the ${PLUGIN} might not work properly."
.PHONY: all build test check install clean vmb vmb.gz docs installvmb

View file

@ -0,0 +1,43 @@
* Vim-OrgMode
#+ATTR_HTML: title="Join the chat at https://gitter.im/jceb/vim-orgmode"
[[https://gitter.im/jceb/vim-orgmode?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge][file:https://badges.gitter.im/jceb/vim-orgmode.svg]]
[[https://travis-ci.org/jceb/vim-orgmode][file:https://travis-ci.org/jceb/vim-orgmode.svg]]
[[https://codecov.io/gh/jceb/vim-orgmode][file:https://codecov.io/gh/jceb/vim-orgmode/branch/master/graph/badge.svg]]
Text outlining and task management for Vim based on [[http://orgmode.org/][Emacs' Org-Mode]].
The idea for this plugin was born by listening to the
[[http://twit.tv/floss136][Floss Weekly podcast]] introducing Emacs Org-Mode.
Org-Mode has a lot of strong features like folding, views (sparse tree) and
scheduling of tasks. These are completed by hyperlinks, tags, todo states,
priorities aso.
vim-orgmode aims at providing the same functionality for Vim.
[[https://github.com/jceb/vim-orgmode/blob/master/examples/mylife.org][file:examples/mylife.gif]]
** Features
Currently vim-orgmode does not support all orgmode features but is quite
usable. Short list of the already supported features:
- Syntax highlighting
- Cycle visibility of headings (folding)
- Navigate between headings
- Edit the structure of the document: add, move, promote, denote headings
and more
- Hyperlinks within vim-orgmode and outside (files, webpages, etc.)
- TODO list management
- Tags for headings
- Lists in alphanumeric and bullet item notation and checkbox support
- Basic date handling
- Export to other formats (via Emacs' Org-Mode)
* Installation and Usage
Installation and usage instructions are found in the file [[doc/orgguide.txt][doc/orgguide.txt]].
* License
Information about the license is found in file [[LICENSE]].
* Changelog
All changes are found in file [[https://github.com/jceb/vim-orgmode/blob/master/CHANGELOG.org][CHANGELOG.org]]

View file

@ -0,0 +1,4 @@
:let g:vimball_home = "."
:e ../files
:execute '%MkVimball!' . g:plugin_name
:q!

View file

@ -0,0 +1,869 @@
vim-orgmode (0.3.0-2) unstable; urgency=low
* update documentation
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 09 Aug 2011 21:13:40 +0200
vim-orgmode (0.3.0-1) unstable; urgency=low
* update documentation
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 09 Aug 2011 08:37:25 +0200
vim-orgmode (0.3.0-0) unstable; urgency=low
* fix completion menu popup that disappeared because of the usage of
vim.command
* closes issue #48
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 09 Aug 2011 08:25:03 +0200
vim-orgmode (0.2.1-25) unstable; urgency=low
* playing around with ftdetect vs. setfiletype
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 08 Aug 2011 08:11:23 +0200
vim-orgmode (0.2.1-24) unstable; urgency=low
* improve implementation of todo state selection (issue #5)
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 07 Aug 2011 19:55:06 +0200
vim-orgmode (0.2.1-23) unstable; urgency=low
* more precise regex for dates - pull request by sotte
* closes issue #52
-- Roman Asendorf <roman.asendorf@gmail.com> Wed, 03 Aug 2011 22:40:48 +0200
vim-orgmode (0.2.1-22) unstable; urgency=low
* implement interactive todo state selection
* closes issue #5
-- Jan Christoph Ebersbach <jceb@e-jc.de> Wed, 03 Aug 2011 22:23:14 +0200
vim-orgmode (0.2.1-21) unstable; urgency=low
* added emacs as suggested package - pull request by sotte
* closes issue #54
-- Roman Asendorf <roman.asendorf@gmail.com> Wed, 03 Aug 2011 21:52:15 +0200
vim-orgmode (0.2.1-20) unstable; urgency=low
* simple export via emacs for pdf and html - pull request by sotte
* closes issue #54
-- Roman Asendorf <roman.asendorf@gmail.com> Wed, 03 Aug 2011 21:43:15 +0200
vim-orgmode (0.2.1-19) unstable; urgency=low
* add orgmode group to au commands in TagProperties plugin
* closes issue #53
-- Jan Christoph Ebersbach <jceb@e-jc.de> Wed, 03 Aug 2011 20:02:08 +0200
vim-orgmode (0.2.1-18) unstable; urgency=low
* allow demotion of first level headings
* closes issue #27
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 01 Aug 2011 21:59:32 +0200
vim-orgmode (0.2.1-17) unstable; urgency=low
* fix encoding issues in Date plugin
-- Jan Christoph Ebersbach <ebersbach@univention.de> Tue, 12 Jul 2011 17:59:41 +0200
vim-orgmode (0.2.1-16) unstable; urgency=low
* update installvba target to also work with older vim version
-- Jan Christoph Ebersbach <ebersbach@univention.de> Mon, 11 Jul 2011 08:23:18 +0200
vim-orgmode (0.2.1-15) unstable; urgency=low
* make switching to the next todo sequence more convenient
* fix issue in offset calculation when old and new state are None
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 09 Jul 2011 17:54:46 +0200
vim-orgmode (0.2.1-14) unstable; urgency=low
* make switching to the next todo sequence more consistent
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 09 Jul 2011 17:38:18 +0200
vim-orgmode (0.2.1-13) unstable; urgency=low
* fix minor issues related to switching to the next todo sequence
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 09 Jul 2011 17:33:56 +0200
vim-orgmode (0.2.1-12) unstable; urgency=low
* add general support for multiple todo sequences
* closes issue #46
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 09 Jul 2011 16:39:49 +0200
vim-orgmode (0.2.1-11) unstable; urgency=low
* update documentation
* update clean target
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 09 Jul 2011 11:29:57 +0200
vim-orgmode (0.2.1-10) unstable; urgency=low
* fix todo tests broken by version 0.2.1-8
* fix DONE result of VimBuffer.get_todo_states.parse_states
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 09 Jul 2011 09:49:07 +0200
vim-orgmode (0.2.1-9) unstable; urgency=low
* fix folded text for headings containing backslashes or double quotes
* closes issue #26
-- Jan Christoph Ebersbach <jceb@e-jc.de> Fri, 08 Jul 2011 23:21:07 +0200
vim-orgmode (0.2.1-8) unstable; urgency=low
* add Document.get_todo_states() and Document.get_all_todo_states()
* don't confuse upper case words at the beginning of a heading with a todo
state
* closes issue #28
-- Jan Christoph Ebersbach <jceb@e-jc.de> Fri, 08 Jul 2011 22:30:29 +0200
vim-orgmode (0.2.1-7) unstable; urgency=low
* fix error in setting tags
* closes issue #25
-- Jan Christoph Ebersbach <ebersbach@univention.de> Mon, 04 Jul 2011 12:56:27 +0200
vim-orgmode (0.2.1-6) unstable; urgency=low
* improve split of heading
* add variable g:org_improve_split_heading to enable/disable improve
the split of headings
* closes issue #24
* change Makefile to be more generic
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 04 Jul 2011 07:54:17 +0200
vim-orgmode (0.2.1-5) unstable; urgency=low
* implement shortcut for moving to the partent's next sibling
* closes issue #22
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 03 Jul 2011 13:21:04 +0200
vim-orgmode (0.2.1-4) unstable; urgency=low
* fix duplication of children when inserting a new heading
* closes issue #20
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 03 Jul 2011 12:43:32 +0200
vim-orgmode (0.2.1-3) unstable; urgency=low
* add support for new vimball file extension .vmb
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 03 Jul 2011 12:27:07 +0200
vim-orgmode (0.2.1-2) unstable; urgency=low
* always start insert mode when adding a new heading
* closes issue #21
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 03 Jul 2011 12:25:20 +0200
vim-orgmode (0.2.1-1) unstable; urgency=low
* make target installvba create the VIMPLUGINDIR before attempting the
installation
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 26 Jun 2011 21:07:54 +0200
vim-orgmode (0.2.1-0) unstable; urgency=low
* add documentation for customizing syntax highlighting
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 26 Jun 2011 19:08:59 +0200
vim-orgmode (0.2.0-6) unstable; urgency=low
* add dependency to clean target to orgmode.vba
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 26 Jun 2011 14:03:18 +0200
vim-orgmode (0.2.0-5) unstable; urgency=low
* add documentation for remapping shortcuts
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 26 Jun 2011 13:47:30 +0200
vim-orgmode (0.2.0-4) unstable; urgency=low
* fix <Plug> names for Todo plugin
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 26 Jun 2011 13:24:07 +0200
vim-orgmode (0.2.0-3) unstable; urgency=low
* fix encoding of todo states set by the Todo plugin (thanks to Daniel Carl
for pointing out the issue)
* add target installvba to locally install orgmode.vba
* cleanup Makefile
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 26 Jun 2011 13:15:34 +0200
vim-orgmode (0.2.0-2) unstable; urgency=low
* add hint for updateing vim-orgmode from a previous version
* move all documentation related to installation and usage to doc/org.txt
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 26 Jun 2011 12:46:55 +0200
vim-orgmode (0.2.0-1) unstable; urgency=low
* minor changes to the build files
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 25 Jun 2011 17:43:48 +0200
vim-orgmode (0.2.0-0) unstable; urgency=low
* remove unused echo command in Makefile
* add vim help file doc/org.txt
* first release
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 25 Jun 2011 17:35:31 +0200
vim-orgmode (0.1.0-89) unstable; urgency=low
* add separate target to generate the vba.gz archive
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 25 Jun 2011 16:47:23 +0200
vim-orgmode (0.1.0-88) unstable; urgency=low
* improve error message in case ditaa is not available
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 25 Jun 2011 16:42:36 +0200
vim-orgmode (0.1.0-87) unstable; urgency=low
* update README and diagram
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 25 Jun 2011 16:38:31 +0200
vim-orgmode (0.1.0-86) unstable; urgency=low
* fix issue in EditStructure._move_heading that caused the current heading
not to be removed from the list of heading
* improve error messages for non-allowed characters in tags or todo states
* allow \t and space in tags because of an automatic replacement by _ later
on
* fix issue when creating the debian package an not all path components
exist
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 25 Jun 2011 16:36:39 +0200
vim-orgmode (0.1.0-85) unstable; urgency=low
* add support and tests for splitting a headings title in insert mode
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 25 Jun 2011 15:55:16 +0200
vim-orgmode (0.1.0-84) unstable; urgency=low
* correct test cases for Navigator plugin
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 25 Jun 2011 14:05:17 +0200
vim-orgmode (0.1.0-83) unstable; urgency=low
* remove LoggingWork from loaded plugins
* fix a problem when positioning the cursor on a newly created heading
* fix a problem when positioning the cursor on the first character of a
non-heading line
* fix a problem when indenting non-heading lines in insert mode
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 25 Jun 2011 13:13:41 +0200
vim-orgmode (0.1.0-82) unstable; urgency=low
* change global function insert_at_cursor to append text after cursor
* add support for insert mode to insert_at_cursor
* fix traceback in Date plugin caused by conversion to unicode
* change insert date mappings to ,si and ,sa
* add submenu Change Date to Date plugin
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 25 Jun 2011 13:00:54 +0200
vim-orgmode (0.1.0-81) unstable; urgency=low
* fix error in TagsProperties.set_tags when None type object is returned
* change TagsProperties.realign_tags to use VimBuffer.write_heading instead
of VimBuffer.write method
* add convenience function VimBuffer.find_current_heading
* remove trailing spaces when a heading contains a single uppercase word
(pseudo TODO state)
* change Heading.copy to create a completely detached heading even without a
connection to a document
* change Heading.start to reture Heading._orig_start when the heading has no
connection to a document
* add parameter connect_with_document to Document.find_heading
* convert plugins and global functions to use VimBuffer.find_current_heading
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 21 Jun 2011 23:26:25 +0200
vim-orgmode (0.1.0-80) unstable; urgency=low
* update README
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 21 Jun 2011 07:46:04 +0200
vim-orgmode (0.1.0-79) unstable; urgency=low
* implement VimBuffer.write_heading to just update a single heading
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 19 Jun 2011 22:19:00 +0200
vim-orgmode (0.1.0-78) unstable; urgency=low
* fix assignment to vim.current.buffer in test cases
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 19 Jun 2011 15:21:39 +0200
vim-orgmode (0.1.0-77) unstable; urgency=low
* improve memory usage by removing unused document objects
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 19 Jun 2011 14:33:10 +0200
vim-orgmode (0.1.0-76) unstable; urgency=low
* improve speed by implementing a better reuse of document objects
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 19 Jun 2011 14:18:26 +0200
vim-orgmode (0.1.0-75) unstable; urgency=low
* fix issue with tag recognition in headings that contain zero or just one
word
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 19 Jun 2011 14:07:29 +0200
vim-orgmode (0.1.0-74) unstable; urgency=low
* change EditStructure mappings back to using the shift key
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 19 Jun 2011 13:29:50 +0200
vim-orgmode (0.1.0-73) unstable; urgency=low
* fix regression introduced in version 0.1.0-71
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 19 Jun 2011 13:28:43 +0200
vim-orgmode (0.1.0-72) unstable; urgency=low
* change default value of g:org_tag_completion_ignorecase to &ignorecase
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 19 Jun 2011 13:20:55 +0200
vim-orgmode (0.1.0-71) unstable; urgency=low
* convert vim.eval return values to unicode
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 19 Jun 2011 13:15:51 +0200
vim-orgmode (0.1.0-70) unstable; urgency=low
* update README
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 18 Jun 2011 19:21:04 +0200
vim-orgmode (0.1.0-69) unstable; urgency=low
* add support for tagbar plugin
* move ctags support to file ftplugin/org.cnf
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 18 Jun 2011 19:15:13 +0200
vim-orgmode (0.1.0-68) unstable; urgency=low
* fix encoding error for plugins that directly access vim.current.buffer
-- Jan Christoph Ebersbach <ebersbach@univention.de> Thu, 16 Jun 2011 12:44:06 +0200
vim-orgmode (0.1.0-67) unstable; urgency=low
* change regex strings to raw strings
-- Jan Christoph Ebersbach <jceb@e-jc.de> Fri, 17 Jun 2011 08:09:23 +0200
vim-orgmode (0.1.0-66) unstable; urgency=low
* remove highlighting group Question from title colors
* fix utf-8 encoding for vim.command calls
-- Jan Christoph Ebersbach <ebersbach@univention.de> Thu, 16 Jun 2011 12:39:46 +0200
vim-orgmode (0.1.0-65) unstable; urgency=low
* fix error when aktivating lazyredraw and hidden for the current buffer
* set commentstring to "# %s"
* fix error when switching buffers to get current changetick
* fix error when running get_document with bufnr 0 that opened to the first
document that was queried instead of return the current document
-- Jan Christoph Ebersbach <ebersbach@univention.de> Wed, 15 Jun 2011 12:42:03 +0200
vim-orgmode (0.1.0-64) unstable; urgency=low
* remove parameter mode from function change_visual_selection
* add more test cases for Navigator plugin, current this plugin is somehow
broken
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 14 Jun 2011 20:30:45 +0200
vim-orgmode (0.1.0-63) unstable; urgency=low
* convert LoggingWork plugin to unicode
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 14 Jun 2011 18:47:52 +0200
vim-orgmode (0.1.0-62) unstable; urgency=low
* move initialization of speeddating commands to __init__ method
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 14 Jun 2011 18:45:16 +0200
vim-orgmode (0.1.0-61) unstable; urgency=low
* convert Date plugin to unicode
* correct a typo in mapping for May
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 14 Jun 2011 18:36:18 +0200
vim-orgmode (0.1.0-60) unstable; urgency=low
* add missing counter to test_edit_structure.py
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 14 Jun 2011 18:34:51 +0200
vim-orgmode (0.1.0-59) unstable; urgency=low
* convert Todo plugin to unicode
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 14 Jun 2011 18:32:01 +0200
vim-orgmode (0.1.0-58) unstable; urgency=low
* fix indentation of docstring in VimBuffer
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 13 Jun 2011 21:51:16 +0200
vim-orgmode (0.1.0-57) unstable; urgency=low
* convert TagsProperties to liborgmode
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 13 Jun 2011 21:48:07 +0200
vim-orgmode (0.1.0-56) unstable; urgency=low
* add missing % when doing string formatting in EditStructure plugin
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 13 Jun 2011 19:27:38 +0200
vim-orgmode (0.1.0-55) unstable; urgency=low
* fix encoding error in keybinding.py when evaluation self.overwrite_exisiting
* convert Hyperlink plugin to unicode
* add hint to current plugin when a traceback occures during initialization
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 13 Jun 2011 19:23:13 +0200
vim-orgmode (0.1.0-54) unstable; urgency=low
* add copy method to Heading class to make a copy of the object
* track orig_start in Document.write method instead of the heading objects
* convert EditStructure plugin to liborgmode
* swap promote/demote meaning
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 13 Jun 2011 18:59:21 +0200
vim-orgmode (0.1.0-53) unstable; urgency=low
* fix performance issue when identing/folding text
* rename method load to init_dom
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 28 May 2011 15:21:58 +0200
vim-orgmode (0.1.0-52) unstable; urgency=low
* fix performance issue by introducing static computation of start value
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 28 May 2011 14:51:38 +0200
vim-orgmode (0.1.0-51) unstable; urgency=low
* add support for multiple color definitions for :foreground and :background
-- Jan Christoph Ebersbach <ebersbach@univention.de> Fri, 20 May 2011 08:29:59 +0200
vim-orgmode (0.1.0-50) unstable; urgency=low
* add support for org-todo-keyword-faces
* add support for nested org-todo-keyword lists
* add descriptions and examples for variables
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 15 May 2011 12:00:41 +0200
vim-orgmode (0.1.0-49) unstable; urgency=low
* general cleanup in syntax file
* add keyword default to all highlighting statements
* add syntax support for comments
* clear highlighting for org_shade_stars group if
g:org_heading_shade_leading_stars is not set
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 14 May 2011 23:33:50 +0200
vim-orgmode (0.1.0-48) unstable; urgency=low
* add syntax support for tables
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 14 May 2011 23:04:46 +0200
vim-orgmode (0.1.0-47) unstable; urgency=low
* add syntax support for DEADLINE and SCHEDULED
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 14 May 2011 21:27:07 +0200
vim-orgmode (0.1.0-46) unstable; urgency=low
* add syntax support for timestamps
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 14 May 2011 21:11:13 +0200
vim-orgmode (0.1.0-45) unstable; urgency=low
* add syntax support for properties
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sat, 14 May 2011 20:52:29 +0200
vim-orgmode (0.1.0-44) unstable; urgency=low
* convert tests to unicode
-- Jan Christoph Ebersbach <jceb@e-jc.de> Fri, 13 May 2011 23:39:38 +0200
vim-orgmode (0.1.0-43) unstable; urgency=low
* change comparison of None to "is" instead of "=="
* add function flatten_list to prevent sublists
* add convenience methods get_index_in_parent_list and get_parent_list to Heading class
* add __unicode__ and __str__ methods to Document
* started porting of EditStructure plugin
-- Jan Christoph Ebersbach <jceb@e-jc.de> Fri, 13 May 2011 22:59:41 +0200
vim-orgmode (0.1.0-42) unstable; urgency=low
* fix syntax error in syntax/org.vim
-- Jan Christoph Ebersbach <jceb@e-jc.de> Fri, 13 May 2011 19:30:44 +0200
vim-orgmode (0.1.0-41) unstable; urgency=low
* replace hbsitz's org syntax file
* change highlighting to integrate much better with colorschemes
* change license to AGPL 3
-- Jan Christoph Ebersbach <ebersbach@univention.de> Thu, 12 May 2011 18:46:13 +0200
vim-orgmode (0.1.0-40) unstable; urgency=low
* add tag_column and tabstop implementation to VimBuffer
* rename setting org_tags_column to org_tag_column
* rename setting org_tags_completion_ignorecase to
org_tag_completion_ignorecase
* fix unicode settings in test cases
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 10 May 2011 13:16:35 +0900
vim-orgmode (0.1.0-39) unstable; urgency=low
* fix bug in echo function - use echo command
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 21:17:50 +0900
vim-orgmode (0.1.0-38) unstable; urgency=low
* convert Navigator plugin to unicode and liborgmode
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 21:17:17 +0900
vim-orgmode (0.1.0-37) unstable; urgency=low
* change echo function to use vim echo function
* add missing unicode parameter to string in ShowHide keybinding
* enable Misc plugin
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 20:32:22 +0900
vim-orgmode (0.1.0-36) unstable; urgency=low
* convert Misc plugin to unicode and liborgmode
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 20:14:04 +0900
vim-orgmode (0.1.0-35) unstable; urgency=low
* fix a bug in ShowHide.toggle_folding when folding the first heading in
document when it doesn't have a child heading
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 19:55:52 +0900
vim-orgmode (0.1.0-34) unstable; urgency=low
* enable tests for ShowHide plugin
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 18:40:11 +0900
vim-orgmode (0.1.0-33) unstable; urgency=low
* optimize update_changedtick to not run through all but just the wanted
buffer
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 18:33:14 +0900
vim-orgmode (0.1.0-32) unstable; urgency=low
* remove debug infomation from liborgmode.py
* add __str__ method for for Heading class
* port base functionality of orgmode to unicode
* echo a message instead of an error when no plugins are definied
* add heading parameter to VimBuffer methods
* add functionality to reuse VimBuffer objects and recognise user changes
* add BufNotInSync exception
* disable all but ShowHide plugin
* port ShowHide plugin to unicode and liborgmode
* port PluginExample to unicode and liborgmode
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 18:20:59 +0900
vim-orgmode (0.1.0-31) unstable; urgency=low
* add vim swap files to gitignore list
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 14:29:41 +0900
vim-orgmode (0.1.0-30) unstable; urgency=low
* add support for unicode
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 14:27:37 +0900
vim-orgmode (0.1.0-29) unstable; urgency=low
* remove unsued import from liborgmode
* add heading parameter to VimBuffer.loag function
* update tests to use renamed liborgmode/document files
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 11:49:25 +0900
vim-orgmode (0.1.0-28) unstable; urgency=low
* rename ftplugin/orgmode/liborgmode.py to ftplugin/liborgmode.py
* rename ftplugin/orgmode/vimbuffer.py to ftplugin/orgmode/document.py
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 09 May 2011 11:46:39 +0900
vim-orgmode (0.1.0-27) unstable; urgency=low
* update README
-- Jan Christoph Ebersbach <jceb@e-jc.de> Fri, 06 May 2011 21:40:48 +0900
vim-orgmode (0.1.0-26) unstable; urgency=low
* add load method to documents for initializing the DOM
-- Jan Christoph Ebersbach <jceb@e-jc.de> Fri, 06 May 2011 21:21:39 +0900
vim-orgmode (0.1.0-25) unstable; urgency=low
* fix heading initialization (Heading.parse_heading_from_data) when orig_start == 0
* fix comparing of deleted headings
* fix overwriting a single item in a heading
-- Jan Christoph Ebersbach <jceb@e-jc.de> Fri, 06 May 2011 21:12:49 +0900
vim-orgmode (0.1.0-24) unstable; urgency=low
* add support for tags and todo
* externalize heading create to a separate classmethod
* add all relevant parameters to the heading constructor
-- Jan Christoph Ebersbach <jceb@e-jc.de> Fri, 06 May 2011 20:20:37 +0900
vim-orgmode (0.1.0-23) unstable; urgency=low
* make Document.is_dirty function test all headings not just the first two
levels
* add more tests for replacing headings
-- Jan Christoph Ebersbach <jceb@e-jc.de> Thu, 05 May 2011 21:37:27 +0900
vim-orgmode (0.1.0-22) unstable; urgency=low
* distinguish between heading and body changes
* make writing the VimBuffer more efficient
-- Jan Christoph Ebersbach <jceb@e-jc.de> Thu, 05 May 2011 21:03:28 +0900
vim-orgmode (0.1.0-21) unstable; urgency=low
* use functionality of UserList in MultiPurposeList
-- Jan Christoph Ebersbach <jceb@e-jc.de> Thu, 05 May 2011 20:13:26 +0900
vim-orgmode (0.1.0-20) unstable; urgency=low
* update README
-- Jan Christoph Ebersbach <jceb@e-jc.de> Thu, 05 May 2011 10:51:12 +0900
vim-orgmode (0.1.0-19) unstable; urgency=low
* major refactoring of liborgmode
* tests and plugins are currently broken
-- Jan Christoph Ebersbach <jceb@e-jc.de> Thu, 05 May 2011 10:48:44 +0900
vim-orgmode (0.1.0-18) unstable; urgency=low
* install -D doesn't work on BSD based systems; migrate commands to install -d
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 02 May 2011 15:41:17 +0900
vim-orgmode (0.1.0-17) unstable; urgency=low
* merge date-plugin from sotte
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 02 May 2011 15:40:40 +0900
vim-orgmode (0.1.0-16) unstable; urgency=low
* rename heading.py to liborgmode.py
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 26 Apr 2011 21:55:17 +0900
vim-orgmode (0.1.0-15) unstable; urgency=low
* add variable g:org_syntax_highlight_leading_stars to customize
highlighting of leading stars
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 26 Apr 2011 21:30:31 +0900
vim-orgmode (0.1.0-14) unstable; urgency=low
* merge todo_refactoring from sotte
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 26 Apr 2011 21:16:19 +0900
vim-orgmode (0.1.0-13) unstable; urgency=low
* add documentation in form of a diagram describing the functionality of
vim-orgmode, liborgmode and orgcmd
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 26 Apr 2011 20:41:20 +0900
vim-orgmode (0.1.0-12) unstable; urgency=low
* update todos
-- Jan Christoph Ebersbach <jceb@e-jc.de> Mon, 28 Mar 2011 00:07:37 +0200
vim-orgmode (0.1.0-11) unstable; urgency=low
* move initialization to start method
-- Jan Christoph Ebersbach <jceb@e-jc.de> Thu, 24 Mar 2011 20:39:49 +0100
vim-orgmode (0.1.0-10) unstable; urgency=low
* fix problem with the order of decorators (@classmethod must be first)
* remove silence from
-- Jan Christoph Ebersbach <ebersbach@univention.de> Thu, 24 Mar 2011 17:36:55 +0100
vim-orgmode (0.1.0-9) unstable; urgency=low
* rename update_tag_alignment
-- Jan Christoph Ebersbach <ebersbach@univention.de> Wed, 23 Mar 2011 19:40:44 +0100
vim-orgmode (0.1.0-8) unstable; urgency=low
* make edit tasks update tags alignment
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 22 Mar 2011 22:28:59 +0100
vim-orgmode (0.1.0-7) unstable; urgency=low
* clean up of unused imports
* change a bunch of methods into classmethods
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 22 Mar 2011 22:17:44 +0100
vim-orgmode (0.1.0-6) unstable; urgency=low
* add vim as build dependency
* update Installation and Building information in README
* update todos
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 22 Mar 2011 21:35:02 +0100
vim-orgmode (0.1.0-5) unstable; urgency=low
* fix passing of function arguments
* fix empty links creation
* update todos for Hyperlinks plugin
-- Jan Christoph Ebersbach <ebersbach@univention.de> Mon, 21 Mar 2011 12:35:14 +0100
vim-orgmode (0.1.0-4) unstable; urgency=low
* implement Hyperlinks plugin (closes: #11)
-- Jan Christoph Ebersbach <jceb@e-jc.de> Sun, 20 Mar 2011 00:07:04 +0100
vim-orgmode (0.1.0-3) unstable; urgency=low
* fix syntax issue in PluginExample.py
-- Jan Christoph Ebersbach <jceb@e-jc.de> Fri, 11 Mar 2011 08:47:34 +0100
vim-orgmode (0.1.0-2) unstable; urgency=low
* correct filename of PluginExample.py (closes: #3)
-- Jan Christoph Ebersbach <jceb@e-jc.de> Wed, 09 Mar 2011 22:02:49 +0100
vim-orgmode (0.1.0-1) unstable; urgency=low
* Initial release.
-- Jan Christoph Ebersbach <jceb@e-jc.de> Tue, 22 Feb 2011 22:14:01 +0100

View file

@ -0,0 +1 @@
5

View file

@ -0,0 +1,14 @@
Source: vim-orgmode
Priority: extra
Section: editors
Maintainer: Jan Christoph Ebersbach <jceb@e-jc.de>
Build-Depends: debhelper (>= 7), vim
Standards-Version: 3.7.3
Homepage: http://www.e-jc.de/
Package: vim-orgmode
Architecture: all
Depends: python, vim-common, vim
Suggests: emacs
Description: Clone of Org-mode for Vim
Clone of Org-mode for Vim

View file

@ -0,0 +1,28 @@
Copyright (C) 2010,2011 Jan Christoph Ebersbach
http://www.e-jc.de/
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3 as
published by the Free Software Foundation.
Binary versions of this file provided by Univention to you as
well as other copyrighted, protected or trademarked materials like
Logos, graphics, fonts, specific documentations and configurations,
cryptographic keys etc. are subject to a license agreement between
you and Univention.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
USA
Except file syntax/org.vim which was copied from Herbert Sitz and
changed later on by Jan Christoph Ebersbach.

View file

@ -0,0 +1 @@
usr/share/vim/addons

View file

@ -0,0 +1,2 @@
README.org
LICENSE

View file

@ -0,0 +1 @@
examples/plugins/PluginExample.py

View file

@ -0,0 +1,11 @@
#!/usr/bin/make -f
# -*- makefile -*-
DESTDIR=$(CURDIR)/debian/vim-orgmode
ADDONSDIR=/usr/share/vim/addons
override_dh_auto_install:
dh_auto_install -- DESTDIR=$(DESTDIR) VIMDIR=$(ADDONSDIR)
%:
dh $@

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
diagram: diagram.png
diagram.png: diagram.txt
if [ -n "$(shell which ditaa)" ]; then ditaa $^ $@; elif [ -f ditaa0_9.jar ]; then java -jar ditaa0_9.jar $^ $@; else echo "Unable to find ditaa, please install ditaa or download ditaa0_9.jar (http://ditaa.sf.net/)and place it in folder documentation"; exit 1 ; fi
clean:
@rm -f diagram.png
.PHONY: diagram clean

View file

@ -0,0 +1,75 @@
/------------------------------\ +-----------------------------------+
| Legend | | vim-orgmode |
| cCCC | | cBLU |
| | | o support for plugins |
| | | o plugins implement orgmode |
+--------------+---------------+ | functionality in vim |
| vim-orgmode | liborgmode | | o mainly plugins implement |
| cBLU | cYEL | | keybindings for interactively |
| | | | changing org-mode files |
| | | | |
+--------------+---------------+ | |
| orgcmd | doesn't exist | +---+-------------------------------+
| cRED | yet | |1
| | | |
| | | |
\--------------+-=-------------/ |x
v
+-----------------------------------+
| Plugins |
| cBLU |
| o manipulate headings |
| o change tags, todo states, lists |
| o integrate with orgcmd |
| o reusable functionality doesn't |
| belong here but into liborgmode!|
| o timer, time tracking |
| |
| |
+---+-------------------------------+ +------------+
| | VimBuffer |
| | {d} |
| /---------+ cBLU |
| | | |
v | +------------+
+-----------------------------------+ +---------------++
1 | Document | | Extend Document| +-=------------+
/-----+ cYEL |<-------+ cYEL | |+------------+|
| | o represents an org-mode document | | o abstraction | || file ||
| | o contains links to other | | of data/file | || {d} ||
| x | documents | | access +-------+| cYEL ||
\---->| o contains meta information | | o read | || ||
| o contains headings | | o write | |+------------+|
| | | | +--------------+
| | | |
| | +---------------++ +-=------------+
+----+------------------------------+ | |+------------+|
|1 ^ | || stdin/ ||
| | \--------+| stdout ||
| | || {d} ||
| \------------\ || cRED ||
|x | |+------------+|
v | +--------------+
+-----------------------------------+ +-+=--------------------------+
1 | Heading | |+---------------------------+|
/-----+ cYEL | || orgcmd ||
| | o represents a single heading | || cRED ||
| | o contains links to other headings| || o implement command line ||
| x | o parent heading | || tool for processing org ||
\---->| o siblings | || files ||
| o children | || o provide output filter ||
| | || o convert org-mode docs ||
| o title | || to other formats ||
| o level | |+---------------------------+|
| o body | +-----------------------------+
| o tags |
| o todo state |
| o closing date |
| o scheduled date |
| o priority |
| o item lists (class hierarchy) |
| |
| |
| |
+-----------------------------------+

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

View file

@ -0,0 +1,26 @@
* My Life in plain text
- [X] birth
- [-] life [50%]
- [X] use vim
- [ ] get everything else done
* Write minutes of last meeting <2014-08-08 Fri> :work:
** DONE John said
this
** TODO Mary said
that
** WAITING What did Mark say?
[[http://example.com/here/is/the/recording][1st recording]]
[[http://example.com/here/is/the/recording][2nd recording]]
* Some folding headline 1 :one:
** Folded
*** Even more folded
* Some folding headline 2
** Folded :two:
*** Even more folded
* Some folding headline 3
** Folded
*** Even more folded :three:
* Some folding headline 4
** Folded
*** Even more folded
completely unfolded

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View file

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
from orgmode import echo, echom, echoe, ORGMODE, apply_count, repeat
from orgmode.menu import Submenu, Separator, ActionEntry
from orgmode.keybinding import Keybinding, Plug, Command
import vim
class Example(object):
u"""
Example plugin.
TODO: Extend this doc!
"""
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'Example')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
# commands for this plugin
self.commands = []
@classmethod
def action(cls):
u"""
Some kind of action.
:returns: TODO
"""
pass
def register(self):
u"""
Registration of the plugin.
Key bindings and other initialization should be done here.
"""
# an Action menu entry which binds "keybinding" to action ":action"
self.commands.append(Command(u'OrgActionCommand',
u':py ORGMODE.plugins["Example"].action()'))
self.keybindings.append(Keybinding(u'keybinding',
Plug(u'OrgAction', self.commands[-1])))
self.menu + ActionEntry(u'&Action', self.keybindings[-1])
# vim: set noexpandtab:

View file

@ -0,0 +1,2 @@
autocmd BufNewFile,BufRead *.org setfiletype org
"autocmd BufNewFile,BufReadPost org:todo* setfiletype orgtodo

View file

@ -0,0 +1,5 @@
--langdef=org
--langmap=org:.org
--regex-org=/^(\*+)[[:space:]]+(.*)([[:space:]]+:[^\t ]*:)?$/\1 \2/s,sections/
--regex-org=/\[\[([^][]+)\]\]/\1/h,hyperlinks/
--regex-org=/\[\[[^][]+\]\[([^][]+)\]\]/\1/h,hyperlinks/

View file

@ -0,0 +1,169 @@
" org.vim -- Text outlining and task management for Vim based on Emacs' Org-Mode
" @Author : Jan Christoph Ebersbach (jceb@e-jc.de)
" @License : AGPL3 (see http://www.gnu.org/licenses/agpl.txt)
" @Created : 2010-10-03
" @Last Modified: Tue 13. Sep 2011 20:52:57 +0200 CEST
" @Revision : 0.4
" vi: ft=vim:tw=80:sw=4:ts=4:fdm=marker
if v:version > 702
if has('python3')
let s:py_version = 'python3 '
let s:py_env = 'python3 << EOF'
elseif has('python')
let s:py_version = 'python '
let s:py_env = 'python << EOF'
else
echoerr "Unable to start orgmode. Orgmode depends on Vim >= 7.3 with Python support complied in."
finish
endif
else
echoerr "Unable to start orgmode. Orgmode depends on Vim >= 7.3 with Python support complied in."
finish
endif
" Init buffer for file {{{1
if ! exists('b:did_ftplugin')
" default emacs settings
setlocal comments=fb:*,b:#,fb:-
setlocal commentstring=#\ %s
setlocal conceallevel=2 concealcursor=nc
" original emacs settings are: setlocal tabstop=6 shiftwidth=6, but because
" of checkbox indentation the following settings are used:
setlocal tabstop=6 shiftwidth=6
if exists('g:org_tag_column')
exe 'setlocal textwidth='.g:org_tag_column
else
setlocal textwidth=77
endif
" expand tab for counting level of checkbox
setlocal expandtab
" enable % for angle brackets < >
setlocal matchpairs+=<:>
" register keybindings if they don't have been registered before
if exists("g:loaded_org")
exe s:py_version . 'ORGMODE.register_keybindings()'
endif
endif
" Load orgmode just once {{{1
if &cp || exists("g:loaded_org")
finish
endif
let g:loaded_org = 1
" Default org plugins that will be loaded (in the given order) {{{2
if ! exists('g:org_plugins') && ! exists('b:org_plugins')
let g:org_plugins = ['ShowHide', '|', 'Navigator', 'EditStructure', 'EditCheckbox', '|', 'Hyperlinks', '|', 'Todo', 'TagsProperties', 'Date', 'Agenda', 'Misc', '|', 'Export']
endif
" Default org plugin settings {{{2
" What does this do?
if ! exists('g:org_syntax_highlight_leading_stars') && ! exists('b:org_syntax_highlight_leading_stars')
let g:org_syntax_highlight_leading_stars = 1
endif
" setting to conceal aggresively
if ! exists('g:org_aggressive_conceal') && ! exists('b:org_aggressive_conceal')
let g:org_aggressive_conceal = 0
endif
" Defined in separate plugins
" Adding Behavior preference:
" 1: go into insert-mode when new heading/checkbox/plainlist added
" 0: retain original mode when new heading/checkbox/plainlist added
if ! exists('g:org_prefer_insert_mode') && ! exists('b:org_prefer_insert_mode')
let g:org_prefer_insert_mode = 1
endif
" Menu and document handling {{{1
function! <SID>OrgRegisterMenu()
exe s:py_version . 'ORGMODE.register_menu()'
endfunction
function! <SID>OrgUnregisterMenu()
exe s:py_version . 'ORGMODE.unregister_menu()'
endfunction
function! <SID>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 <SID>OrgRegisterMenu() | endif
au BufLeave * :if &filetype == "org" | call <SID>OrgUnregisterMenu() | endif
au BufDelete * :call <SID>OrgDeleteUnusedDocument(expand('<abuf>'))
augroup END
" Start orgmode {{{1
" Expand our path
exec s:py_env
import vim, os, sys
for p in vim.eval("&runtimepath").split(','):
dname = os.path.join(p, "ftplugin")
if os.path.exists(os.path.join(dname, "orgmode")):
if dname not in sys.path:
sys.path.append(dname)
break
from orgmode._vim import ORGMODE, insert_at_cursor, get_user_input, date_to_str
ORGMODE.start()
from Date import Date
import datetime
EOF
" 3rd Party Plugin Integration {{{1
" * Repeat {{{2
try
call repeat#set()
catch
endtry
" * Tagbar {{{2
let g:tagbar_type_org = {
\ 'ctagstype' : 'org',
\ 'kinds' : [
\ 's:sections',
\ 'h:hyperlinks',
\ ],
\ 'sort' : 0,
\ 'deffile' : expand('<sfile>: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('<sfile>:p:h') . '/org.cnf '
endif
" * Calendar.vim {{{2
fun CalendarAction(day, month, year, week, dir)
let g:org_timestamp = printf("%04d-%02d-%02d Fri", a:year, a:month, a:day)
let datetime_date = printf("datetime.date(%d, %d, %d)", a:year, a:month, a:day)
exe s:py_version . "selected_date = " . datetime_date
" get_user_input
let msg = printf("Inserting %s | Modify date", g:org_timestamp)
exe s:py_version . "modifier = get_user_input('" . msg . "')"
" change date according to user input
exe s:py_version . "newdate = Date._modify_time(selected_date, modifier)"
exe s:py_version . "newdate = date_to_str(newdate)"
" close Calendar
exe "q"
" goto previous window
exe "wincmd p"
exe s:py_version . "timestamp = '" . g:org_timestamp_template . "' % newdate"
exe s:py_version . "insert_at_cursor(timestamp)"
" restore calendar_action
let g:calendar_action = g:org_calendar_action_backup
endf

View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View file

@ -0,0 +1,411 @@
# -*- coding: utf-8 -*-
"""
VIM ORGMODE
~~~~~~~~~~~~
TODO
"""
import imp
import re
import sys
import vim
from datetime import datetime
import orgmode.keybinding
import orgmode.menu
import orgmode.plugins
import orgmode.settings
from orgmode.exceptions import PluginError
from orgmode.vimbuffer import VimBuffer
from orgmode.liborgmode.agenda import AgendaManager
REPEAT_EXISTS = bool(int(vim.eval('exists("*repeat#set()")')))
TAGSPROPERTIES_EXISTS = False
cache_heading = None
from orgmode.py3compat.unicode_compatibility import *
from orgmode.py3compat.encode_compatibility import *
def realign_tags(f):
u"""
Update tag alignment, dependency to TagsProperties plugin!
"""
def r(*args, **kwargs):
global TAGSPROPERTIES_EXISTS
res = f(*args, **kwargs)
if not TAGSPROPERTIES_EXISTS and u'TagsProperties' in ORGMODE.plugins:
TAGSPROPERTIES_EXISTS = True
if TAGSPROPERTIES_EXISTS:
ORGMODE.plugins[u'TagsProperties'].realign_tags()
return res
return r
def repeat(f):
u"""
Integrate with the repeat plugin if available
The decorated function must return the name of the <Plug> 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("\\<Plug>%s")' % res))
return res
return r
def apply_count(f):
u"""
Decorator which executes function v:count or v:prevount (not implemented,
yet) times. The decorated function must return a value that evaluates to
True otherwise the function is not repeated.
"""
def r(*args, **kwargs):
count = 0
try:
count = int(vim.eval(u_encode(u'v:count')))
# visual count is not implemented yet
#if not count:
# count = int(vim.eval(u'v:prevcount'.encode(u'utf-8')))
except BaseException as e:
pass
res = f(*args, **kwargs)
count -= 1
while res and count > 0:
f(*args, **kwargs)
count -= 1
return res
return r
def echo(message):
u"""
Print a regular message that will not be visible to the user when
multiple lines are printed
"""
for m in message.split(u'\n'):
vim.command(u_encode(u':echo "%s"' % m))
def echom(message):
u"""
Print a regular message that will be visible to the user, even when
multiple lines are printed
"""
# probably some escaping is needed here
for m in message.split(u'\n'):
vim.command(u_encode(u':echomsg "%s"' % m))
def echoe(message):
u"""
Print an error message. This should only be used for serious errors!
"""
# probably some escaping is needed here
for m in message.split(u'\n'):
vim.command(u_encode(u':echoerr "%s"' % m))
def insert_at_cursor(text, move=True, start_insertmode=False):
u"""Insert text at the position of the cursor.
If move==True move the cursor with the inserted text.
"""
d = ORGMODE.get_document(allow_dirty=True)
line, col = vim.current.window.cursor
_text = d._content[line - 1]
d._content[line - 1] = _text[:col + 1] + text + _text[col + 1:]
if move:
vim.current.window.cursor = (line, col + len(text))
if start_insertmode:
vim.command(u_encode(u'startinsert'))
def get_user_input(message):
u"""Print the message and take input from the user.
Return the input or None if there is no input.
"""
vim.command(u_encode(u'call inputsave()'))
vim.command(u_encode(u"let user_input = input('" + message + u": ')"))
vim.command(u_encode(u'call inputrestore()'))
try:
return u_decode(vim.eval(u_encode(u'user_input')))
except:
return None
def get_bufnumber(bufname):
"""
Return the number of the buffer for the given bufname if it exist;
else None.
"""
for b in vim.buffers:
if b.name == bufname:
return int(b.number)
def get_bufname(bufnr):
"""
Return the name of the buffer for the given bufnr if it exist; else None.
"""
for b in vim.buffers:
if b.number == bufnr:
return b.name
def indent_orgmode():
u""" Set the indent value for the current line in the variable
b:indent_level
Vim prerequisites:
:setlocal indentexpr=Method-which-calls-indent_orgmode
:returns: None
"""
line = int(vim.eval(u_encode(u'v:lnum')))
d = ORGMODE.get_document()
heading = d.current_heading(line - 1)
if heading and line != heading.start_vim:
heading.init_checkboxes()
checkbox = heading.current_checkbox()
level = heading.level + 1
if checkbox:
if line != checkbox.start_vim:
# indent body up to the beginning of the checkbox' text
# if checkbox isn't indented to the proper location, the body
# won't be indented either
level = checkbox.level + len(checkbox.type) + 1 + \
(4 if checkbox.status else 0)
vim.command(u_encode((u'let b:indent_level = %d' % level)))
def fold_text(allow_dirty=False):
u""" Set the fold text
:setlocal foldtext=Method-which-calls-foldtext
:allow_dirty: Perform a query without (re)building the DOM if True
:returns: None
"""
line = int(vim.eval(u_encode(u'v:foldstart')))
d = ORGMODE.get_document(allow_dirty=allow_dirty)
heading = None
if allow_dirty:
heading = d.find_current_heading(line - 1)
else:
heading = d.current_heading(line - 1)
if heading:
str_heading = unicode(heading)
# expand tabs
ts = int(vim.eval(u_encode(u'&ts')))
idx = str_heading.find(u'\t')
if idx != -1:
tabs, spaces = divmod(idx, ts)
str_heading = str_heading.replace(u'\t', u' ' * (ts - spaces), 1)
str_heading = str_heading.replace(u'\t', u' ' * ts)
# Workaround for vim.command seems to break the completion menu
vim.eval(u_encode(u'SetOrgFoldtext("%s...")' % (re.sub(r'\[\[([^[\]]*\]\[)?([^[\]]+)\]\]', r'\2',
str_heading).replace( u'\\', u'\\\\').replace(u'"', u'\\"'), )))
def fold_orgmode(allow_dirty=False):
u""" Set the fold expression/value for the current line in the variable
b:fold_expr
Vim prerequisites:
:setlocal foldmethod=expr
:setlocal foldexpr=Method-which-calls-fold_orgmode
:allow_dirty: Perform a query without (re)building the DOM if True
:returns: None
"""
line = int(vim.eval(u_encode(u'v:lnum')))
d = ORGMODE.get_document(allow_dirty=allow_dirty)
heading = None
if allow_dirty:
heading = d.find_current_heading(line - 1)
else:
heading = d.current_heading(line - 1)
# if cache_heading != heading:
# heading.init_checkboxes()
# checkbox = heading.current_checkbox()
# cache_heading = heading
if heading:
# if checkbox:
# vim.command((u'let b:fold_expr = ">%d"' % heading.level + checkbox.level).encode(u'utf-8'))
if 0:
pass
elif line == heading.start_vim:
vim.command(u_encode(u'let b:fold_expr = ">%d"' % heading.level))
#elif line == heading.end_vim:
# vim.command((u'let b:fold_expr = "<%d"' % heading.level).encode(u'utf-8'))
# end_of_last_child_vim is a performance junky and is actually not needed
#elif line == heading.end_of_last_child_vim:
# vim.command((u'let b:fold_expr = "<%d"' % heading.level).encode(u'utf-8'))
else:
vim.command(u_encode(u'let b:fold_expr = %d' % heading.level))
def date_to_str(date):
if isinstance(date, datetime):
date = date.strftime(u_decode(u_encode(u'%Y-%m-%d %a %H:%M')))
else:
date = date.strftime(u_decode(u_encode(u'%Y-%m-%d %a')))
return date
class OrgMode(object):
u""" Vim Buffer """
def __init__(self):
object.__init__(self)
self.debug = bool(int(orgmode.settings.get(u'org_debug', False)))
self.orgmenu = orgmode.menu.Submenu(u'&Org')
self._plugins = {}
# list of vim buffer objects
self._documents = {}
# agenda manager
self.agenda_manager = AgendaManager()
def get_document(self, bufnr=0, allow_dirty=False):
""" Retrieve instance of vim buffer document. This Document should be
used for manipulating the vim buffer.
:bufnr: Retrieve document with bufnr
:allow_dirty: Allow the retrieved document to be dirty
:returns: vim buffer instance
"""
if bufnr == 0:
bufnr = vim.current.buffer.number
if bufnr in self._documents:
if allow_dirty or self._documents[bufnr].is_insync:
return self._documents[bufnr]
self._documents[bufnr] = VimBuffer(bufnr).init_dom()
return self._documents[bufnr]
@property
def plugins(self):
return self._plugins.copy()
@orgmode.keybinding.register_keybindings
@orgmode.keybinding.register_commands
@orgmode.menu.register_menu
def register_plugin(self, plugin):
if not isinstance(plugin, basestring):
raise ValueError(u'Parameter plugin is not of type string')
if plugin == u'|':
self.orgmenu + orgmode.menu.Separator()
self.orgmenu.children[-1].create()
return
if plugin in self._plugins:
raise PluginError(u'Plugin %s has already been loaded')
# a python module
module = None
# actual plugin class
_class = None
# locate module and initialize plugin class
try:
module = imp.find_module(plugin, orgmode.plugins.__path__)
except ImportError as e:
echom(u'Plugin not found: %s' % plugin)
if self.debug:
raise e
return
if not module:
echom(u'Plugin not found: %s' % plugin)
return
try:
module = imp.load_module(plugin, *module)
if not hasattr(module, plugin):
echoe(u'Unable to find plugin: %s' % plugin)
if self.debug:
raise PluginError(u'Unable to find class %s' % plugin)
return
_class = getattr(module, plugin)
self._plugins[plugin] = _class()
self._plugins[plugin].register()
if self.debug:
echo(u'Plugin registered: %s' % plugin)
return self._plugins[plugin]
except BaseException as e:
echoe(u'Unable to activate plugin: %s' % plugin)
echoe(u"%s" % e)
if self.debug:
import traceback
echoe(traceback.format_exc())
def register_keybindings(self):
@orgmode.keybinding.register_keybindings
def dummy(plugin):
return plugin
if sys.version_info < (3, ):
for p in self.plugins.itervalues():
dummy(p)
else:
for p in self.plugins.values():
dummy(p)
def register_menu(self):
self.orgmenu.create()
def unregister_menu(self):
vim.command(u_encode(u'silent! aunmenu Org'))
def start(self):
u""" Start orgmode and load all requested plugins
"""
plugins = orgmode.settings.get(u"org_plugins")
if not plugins:
echom(u'orgmode: No plugins registered.')
if isinstance(plugins, basestring):
try:
self.register_plugin(plugins)
except BaseException as e:
import traceback
traceback.print_exc()
elif isinstance(plugins, list) or \
isinstance(plugins, tuple):
for p in plugins:
try:
self.register_plugin(p)
except BaseException as e:
echoe('Error in %s plugin:' % p)
import traceback
traceback.print_exc()
return plugins
ORGMODE = OrgMode()
# vim: set noexpandtab:

View file

@ -0,0 +1,230 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help
help:
@echo "Please use \`make <target>' where <target> 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."

View file

@ -0,0 +1,387 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# orgmode documentation build configuration file, created by
# sphinx-quickstart on Sat May 21 15:51:55 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
import mock
# Mock vim
MOCK_MODULES = ['vim']
for m in MOCK_MODULES:
sys.modules[m] = mock.Mock()
import vim
vim.eval = mock.MagicMock(return_value=1)
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinx.ext.viewcode',
'sphinx.ext.doctest',
'sphinx.ext.coverage',
'sphinx.ext.viewcode',
'sphinx.ext.napoleon',
]
# Napoleon config
napoleon_google_docstring = True
napoleon_numpy_docstring = True
napoleon_include_private_with_doc = True
napoleon_include_special_with_doc = True
napoleon_use_admonition_for_examples = False
napoleon_use_admonition_for_notes = False
napoleon_use_admonition_for_references = False
napoleon_use_ivar = False
napoleon_use_param = True
napoleon_use_rtype = True
# Add any paths that contain templates here, relative to this directory.
#templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'orgmode'
copyright = '2016, Author'
author = 'Author'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = ''
# The full version, including alpha/beta/rc tags.
release = ''
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents.
# "<project> v<release> 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 <link> 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

View file

@ -0,0 +1,22 @@
.. orgmode documentation master file, created by
sphinx-quickstart on Sat May 21 16:35:00 2016.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to orgmode's documentation!
===================================
Contents:
.. toctree::
:maxdepth: 4
orgmode
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View file

@ -0,0 +1,281 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> 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

View file

@ -0,0 +1,78 @@
orgmode.liborgmode package
==========================
Submodules
----------
orgmode.liborgmode.agenda module
--------------------------------
.. automodule:: orgmode.liborgmode.agenda
:members:
:undoc-members:
:show-inheritance:
orgmode.liborgmode.agendafilter module
--------------------------------------
.. automodule:: orgmode.liborgmode.agendafilter
:members:
:undoc-members:
:show-inheritance:
orgmode.liborgmode.base module
------------------------------
.. automodule:: orgmode.liborgmode.base
:members:
:undoc-members:
:show-inheritance:
orgmode.liborgmode.checkboxes module
------------------------------------
.. automodule:: orgmode.liborgmode.checkboxes
:members:
:undoc-members:
:show-inheritance:
orgmode.liborgmode.documents module
-----------------------------------
.. automodule:: orgmode.liborgmode.documents
:members:
:undoc-members:
:show-inheritance:
orgmode.liborgmode.dom_obj module
---------------------------------
.. automodule:: orgmode.liborgmode.dom_obj
:members:
:undoc-members:
:show-inheritance:
orgmode.liborgmode.headings module
----------------------------------
.. automodule:: orgmode.liborgmode.headings
:members:
:undoc-members:
:show-inheritance:
orgmode.liborgmode.orgdate module
---------------------------------
.. automodule:: orgmode.liborgmode.orgdate
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: orgmode.liborgmode
:members:
:undoc-members:
:show-inheritance:

View file

@ -0,0 +1,110 @@
orgmode.plugins package
=======================
Submodules
----------
orgmode.plugins.Agenda module
-----------------------------
.. automodule:: orgmode.plugins.Agenda
:members:
:undoc-members:
:show-inheritance:
orgmode.plugins.Date module
---------------------------
.. automodule:: orgmode.plugins.Date
:members:
:undoc-members:
:show-inheritance:
orgmode.plugins.EditCheckbox module
-----------------------------------
.. automodule:: orgmode.plugins.EditCheckbox
:members:
:undoc-members:
:show-inheritance:
orgmode.plugins.EditStructure module
------------------------------------
.. automodule:: orgmode.plugins.EditStructure
:members:
:undoc-members:
:show-inheritance:
orgmode.plugins.Export module
-----------------------------
.. automodule:: orgmode.plugins.Export
:members:
:undoc-members:
:show-inheritance:
orgmode.plugins.Hyperlinks module
---------------------------------
.. automodule:: orgmode.plugins.Hyperlinks
:members:
:undoc-members:
:show-inheritance:
orgmode.plugins.LoggingWork module
----------------------------------
.. automodule:: orgmode.plugins.LoggingWork
:members:
:undoc-members:
:show-inheritance:
orgmode.plugins.Misc module
---------------------------
.. automodule:: orgmode.plugins.Misc
:members:
:undoc-members:
:show-inheritance:
orgmode.plugins.Navigator module
--------------------------------
.. automodule:: orgmode.plugins.Navigator
:members:
:undoc-members:
:show-inheritance:
orgmode.plugins.ShowHide module
-------------------------------
.. automodule:: orgmode.plugins.ShowHide
:members:
:undoc-members:
:show-inheritance:
orgmode.plugins.TagsProperties module
-------------------------------------
.. automodule:: orgmode.plugins.TagsProperties
:members:
:undoc-members:
:show-inheritance:
orgmode.plugins.Todo module
---------------------------
.. automodule:: orgmode.plugins.Todo
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: orgmode.plugins
:members:
:undoc-members:
:show-inheritance:

View file

@ -0,0 +1,46 @@
orgmode.py3compat package
=========================
Submodules
----------
orgmode.py3compat.encode_compatibility module
---------------------------------------------
.. automodule:: orgmode.py3compat.encode_compatibility
:members:
:undoc-members:
:show-inheritance:
orgmode.py3compat.py_py3_string module
--------------------------------------
.. automodule:: orgmode.py3compat.py_py3_string
:members:
:undoc-members:
:show-inheritance:
orgmode.py3compat.unicode_compatibility module
----------------------------------------------
.. automodule:: orgmode.py3compat.unicode_compatibility
:members:
:undoc-members:
:show-inheritance:
orgmode.py3compat.xrange_compatibility module
---------------------------------------------
.. automodule:: orgmode.py3compat.xrange_compatibility
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: orgmode.py3compat
:members:
:undoc-members:
:show-inheritance:

View file

@ -0,0 +1,71 @@
orgmode package
===============
Subpackages
-----------
.. toctree::
orgmode.liborgmode
orgmode.plugins
orgmode.py3compat
Submodules
----------
orgmode._vim module
-------------------
.. automodule:: orgmode._vim
:members:
:undoc-members:
:show-inheritance:
orgmode.exceptions module
-------------------------
.. automodule:: orgmode.exceptions
:members:
:undoc-members:
:show-inheritance:
orgmode.keybinding module
-------------------------
.. automodule:: orgmode.keybinding
:members:
:undoc-members:
:show-inheritance:
orgmode.menu module
-------------------
.. automodule:: orgmode.menu
:members:
:undoc-members:
:show-inheritance:
orgmode.settings module
-----------------------
.. automodule:: orgmode.settings
:members:
:undoc-members:
:show-inheritance:
orgmode.vimbuffer module
------------------------
.. automodule:: orgmode.vimbuffer
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: orgmode
:members:
:undoc-members:
:show-inheritance:

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
class PluginError(BaseException):
def __init__(self, message):
BaseException.__init__(self, message)
class BufferNotFound(BaseException):
def __init__(self, message):
BaseException.__init__(self, message)
class BufferNotInSync(BaseException):
def __init__(self, message):
BaseException.__init__(self, message)
class HeadingDomError(BaseException):
def __init__(self, message):
BaseException.__init__(self, message)
# vim: set noexpandtab:

View file

@ -0,0 +1,217 @@
# -*- coding: utf-8 -*-
import vim
MODE_ALL = u'a'
MODE_NORMAL = u'n'
MODE_VISUAL = u'v'
MODE_INSERT = u'i'
MODE_OPERATOR = u'o'
OPTION_BUFFER_ONLY = u'<buffer>'
OPTION_SLIENT = u'<silent>'
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<CR>' % 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 <Plug> to an abitrary command """
def __init__(self, name, command, mode=MODE_NORMAL):
u"""
:name: the name of the <Plug> 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'<Plug>%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 <silent>, <buffer> ...
:repmap: allow or disallow nested mapping
:buffer_only: define the key binding only for the current buffer
"""
object.__init__(self)
self._key = key
self._action = action
# grab mode from plug if not set otherwise
if isinstance(self._action, Plug) and not mode:
mode = self._action.mode
if mode not in (MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT, MODE_OPERATOR):
raise ValueError(u'Parameter mode not in MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT, MODE_OPERATOR')
self._mode = mode
self._options = options
if self._options is None:
self._options = []
self._remap = remap
self._buffer_only = buffer_only
self._silent = silent
if self._buffer_only and OPTION_BUFFER_ONLY not in self._options:
self._options.append(OPTION_BUFFER_ONLY)
if self._silent and OPTION_SLIENT not in self._options:
self._options.append(OPTION_SLIENT)
@property
def key(self):
return self._key
@property
def action(self):
return str(self._action)
@property
def mode(self):
return self._mode
@property
def options(self):
return self._options[:]
@property
def remap(self):
return self._remap
@property
def buffer_only(self):
return self._buffer_only
@property
def silent(self):
return self._silent
def create(self):
from orgmode._vim import ORGMODE, echom
cmd = self._mode
if cmd == MODE_ALL:
cmd = u''
if not self._remap:
cmd += u'nore'
try:
create_mapping = True
if isinstance(self._action, Plug):
# create plug
self._action.create()
if int(vim.eval(u_encode(u'hasmapto("%s")' % (self._action, )))):
create_mapping = False
if isinstance(self._action, Command):
# create command
self._action.create()
if create_mapping:
vim.command(u_encode(u':%smap %s %s %s' % (cmd, u' '.join(self._options), self._key, self._action)))
except BaseException as e:
if ORGMODE.debug:
echom(u'Failed to register key binding %s %s' % (self._key, self._action))
# vim: set noexpandtab:

View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View file

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
u"""
Agenda
~~~~~~~~~~~~~~~~~~
The agenda is one of the main concepts of orgmode. It allows to
collect TODO items from multiple org documents in an agenda view.
Features:
* filtering
* sorting
"""
from orgmode.liborgmode.agendafilter import filter_items
from orgmode.liborgmode.agendafilter import is_within_week_and_active_todo
from orgmode.liborgmode.agendafilter import contains_active_todo
from orgmode.liborgmode.agendafilter import contains_active_date
class AgendaManager(object):
u"""Simple parsing of Documents to create an agenda."""
# TODO Move filters in this file, they do the same thing
def __init__(self):
super(AgendaManager, self).__init__()
def get_todo(self, documents):
u"""
Get the todo agenda for the given documents (list of document).
"""
filtered = []
for document in iter(documents):
# filter and return headings
filtered.extend(filter_items(document.all_headings(),
[contains_active_todo]))
return sorted(filtered)
def get_next_week_and_active_todo(self, documents):
u"""
Get the agenda for next week for the given documents (list of
document).
"""
filtered = []
for document in iter(documents):
# filter and return headings
filtered.extend(filter_items(document.all_headings(),
[is_within_week_and_active_todo]))
return sorted(filtered)
def get_timestamped_items(self, documents):
u"""
Get all time-stamped items in a time-sorted way for the given
documents (list of document).
"""
filtered = []
for document in iter(documents):
# filter and return headings
filtered.extend(filter_items(document.all_headings(),
[contains_active_date]))
return sorted(filtered)
# vim: set noexpandtab:

View file

@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
u"""
agendafilter
~~~~~~~~~~~~~~~~
AgendaFilter contains all the filters that can be applied to create the
agenda.
All functions except filter_items() in the module are filters. Given a
heading they return if the heading meets the critera of the filter.
The function filter_items() can combine different filters and only returns
the filtered headings.
"""
from datetime import datetime
from datetime import timedelta
try:
from itertools import ifilter as filter
except:
pass
def filter_items(headings, filters):
u""" Filter the given headings.
Args:
headings (list): Contains headings
filters (list): Filters that will be applied. All functions in
this module (except this function) are filters.
Returns:
filter iterator: Headings which were not filtered.
Examples:
>>> filtered = filter_items(headings, [contains_active_date,
contains_active_todo])
"""
filtered = headings
for f in filters:
filtered = filter(f, filtered)
return filtered
def is_within_week(heading):
u""" Test if headings date is withing a week
Returns:
bool: True if the date in the deading is within a week in the future (or
older False otherwise.
"""
if contains_active_date(heading):
next_week = datetime.today() + timedelta(days=7)
if heading.active_date < next_week:
return True
def is_within_week_and_active_todo(heading):
u"""
Returns:
bool: True if heading contains an active TODO and the date is within a
week.
"""
return is_within_week(heading) and contains_active_todo(heading)
def contains_active_todo(heading):
u"""
Returns:
bool: True if heading contains an active TODO.
"""
# TODO make this more efficient by checking some val and not calling the
# function
# TODO why is this import failing at top level? circular dependecy...
from orgmode._vim import ORGMODE
active = []
for act in ORGMODE.get_document().get_todo_states():
active.extend(act[0])
return heading.todo in active
def contains_active_date(heading):
u"""
Returns:
bool: True if heading contains an active date.
"""
return not(heading.active_date is None)
# vim: set noexpandtab:

View file

@ -0,0 +1,190 @@
# -*- coding: utf-8 -*-
"""
base
~~~~~~~~~~
Here are some really basic data structures that are used throughout
the liborgmode.
"""
try:
from collections import UserList
except:
from UserList import UserList
import collections
import sys
from orgmode.py3compat.unicode_compatibility import *
def flatten_list(lst):
""" Flattens a list
Args:
lst (iterable): An iterable that will is non-flat
Returns:
list: Flat list
"""
# TODO write tests
def gen_lst(item):
if isinstance(item, basestring) or isinstance(item, bytes):
yield item
elif isinstance(item, collections.Iterable):
# yield from would be so nice... but c'est la vie
for val in item:
for final in gen_lst(val):
yield final
else:
yield item
return [i for i in gen_lst(lst)]
class Direction():
u"""
Direction is used to indicate the direction of certain actions.
Example: it defines the direction headings get parted in.
"""
FORWARD = 1
BACKWARD = 2
class MultiPurposeList(UserList):
u"""
A Multi Purpose List is a list that calls a user defined hook on
change. The implementation is very basic - the hook is called without any
parameters. Otherwise the Multi Purpose List can be used like any other
list.
The member element "data" can be used to fill the list without causing the
list to be marked dirty. This should only be used during initialization!
"""
def __init__(self, initlist=None, on_change=None):
UserList.__init__(self, initlist)
self._on_change = on_change
def _changed(self):
u""" Call hook """
if callable(self._on_change):
self._on_change()
def __setitem__(self, i, item):
if sys.version_info < (3, ) and isinstance(i, slice):
start, stop, _ = i.indices(len(self))
UserList.__setslice__(self, start, stop, item)
else:
UserList.__setitem__(self, i, item)
self._changed()
def __delitem__(self, i):
if sys.version_info < (3, ) and isinstance(i, slice):
start, stop, _ = i.indices(len(self))
UserList.__delslice__(self, start, stop)
else:
UserList.__delitem__(self, i)
self._changed()
def __getitem__(self, i):
if sys.version_info < (3, ):
if isinstance(i, slice):
# TODO Return just a list. Why?
return [self[i] for i in range(*i.indices(len(self)))]
# return UserList([self[i] for i in range(*i.indices(len(self)))])
return UserList.__getitem__(self, i)
# NOTE: These wrappers are necessary because of python 2
def __setslice__(self, i, j, other):
self.__setitem__(slice(i, j), other)
def __delslice__(self, i, j):
self.__delitem__(slice(i, j))
def __getslice__(self, i, j):
return self.__getitem__(slice(i, j))
def __iadd__(self, other):
res = UserList.__iadd__(self, other)
self._changed()
return res
def __imul__(self, n):
res = UserList.__imul__(self, n)
self._changed()
return res
def append(self, item):
UserList.append(self, item)
self._changed()
def insert(self, i, item):
UserList.insert(self, i, item)
self._changed()
def pop(self, i=-1):
item = self[i]
del self[i]
return item
def remove(self, item):
self.__delitem__(self.index(item))
def reverse(self):
UserList.reverse(self)
self._changed()
def sort(self, *args, **kwds):
UserList.sort(self, *args, **kwds)
self._changed()
def extend(self, other):
UserList.extend(self, other)
self._changed()
def get_domobj_range(content=[], position=0, direction=Direction.FORWARD, identify_fun=None):
u"""
Get the start and end line number of the dom obj lines from content.
:content: String to be recognized dom obj
:positon: Line number in content
:direction: Search direction
:identify_fun: A identify function to recognize dom obj(Heading, Checkbox) title string.
:return: Start and end line number for the recognized dom obj.
"""
len_cb = len(content)
if position < 0 or position > len_cb:
return (None, None)
tmp_line = position
start = None
end = None
if direction == Direction.FORWARD:
while tmp_line < len_cb:
if identify_fun(content[tmp_line]) is not None:
if start is None:
start = tmp_line
elif end is None:
end = tmp_line - 1
if start is not None and end is not None:
break
tmp_line += 1
else:
while tmp_line >= 0 and tmp_line < len_cb:
if identify_fun(content[tmp_line]) is not None:
if start is None:
start = tmp_line
elif end is None:
end = tmp_line - 1
if start is not None and end is not None:
break
tmp_line -= 1 if start is None else -1
return (start, end)
# vim: set noexpandtab:

View file

@ -0,0 +1,406 @@
# -*- coding: utf-8 -*-
"""
checkboxes
~~~~~~~~~~
TODO: explain this :)
"""
import re
try:
from collections import UserList
except:
from UserList import UserList
import vim
from orgmode.liborgmode.base import MultiPurposeList, flatten_list
from orgmode.liborgmode.orgdate import OrgTimeRange
from orgmode.liborgmode.orgdate import get_orgdate
from orgmode.liborgmode.dom_obj import DomObj, DomObjList, REGEX_SUBTASK, REGEX_SUBTASK_PERCENT, REGEX_HEADING, REGEX_CHECKBOX
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.unicode_compatibility import *
class Checkbox(DomObj):
u""" Structural checkbox object """
STATUS_ON = u'[X]'
STATUS_OFF = u'[ ]'
# intermediate status
STATUS_INT = u'[-]'
def __init__(self, level=1, type=u'-', title=u'', status=u'[ ]', body=None):
u"""
:level: Indent level of the checkbox
:type: Type of the checkbox list (-, +, *)
:title: Title of the checkbox
:status: Status of the checkbox ([ ], [X], [-])
:body: Body of the checkbox
"""
DomObj.__init__(self, level=level, title=title, body=body)
# heading
self._heading = None
self._children = CheckboxList(obj=self)
self._dirty_checkbox = False
# list type
self._type = u'-'
if type:
self.type = type
# status
self._status = Checkbox.STATUS_OFF
if status:
self.status = status
def __unicode__(self):
return u' ' * self.level + self.type + u' ' + \
(self.status + u' ' if self.status else u'') + self.title
def __str__(self):
return u_encode(self.__unicode__())
def __len__(self):
# 1 is for the heading's title
return 1 + len(self.body)
def copy(self, including_children=True, parent=None):
u"""
Create a copy of the current checkbox. The checkbox will be completely
detached and not even belong to a document anymore.
:including_children: If True a copy of all children is create as
well. If False the returned checkbox doesn't
have any children.
:parent: Don't use this parameter. It's set
automatically.
"""
checkbox = self.__class__(
level=self.level, title=self.title,
body=self.body[:])
if parent:
parent.children.append(checkbox)
if including_children and self.children:
for item in self.children:
item.copy(
including_children=including_children,
parent=checkbox)
checkbox._orig_start = self._orig_start
checkbox._orig_len = self._orig_len
checkbox._dirty_heading = self.is_dirty_checkbox
return checkbox
@classmethod
def parse_checkbox_from_data(cls, data, heading=None, orig_start=None):
u""" Construct a new checkbox from the provided data
:data: List of lines
:heading: The heading object this checkbox belongs to
:orig_start: The original start of the heading in case it was read
from a document. If orig_start is provided, the
resulting heading will not be marked dirty.
:returns: The newly created checkbox
"""
def parse_title(heading_line):
# checkbox is not heading
if REGEX_HEADING.match(heading_line) is not None:
return None
m = REGEX_CHECKBOX.match(heading_line)
if m:
r = m.groupdict()
return (len(r[u'level']), r[u'type'], r[u'status'], r[u'title'])
return None
if not data:
raise ValueError(u'Unable to create checkbox, no data provided.')
# create new checkbox
nc = cls()
nc.level, nc.type, nc.status, nc.title = parse_title(data[0])
nc.body = data[1:]
if orig_start is not None:
nc._dirty_heading = False
nc._dirty_body = False
nc._orig_start = orig_start
nc._orig_len = len(nc)
if heading:
nc._heading = heading
return nc
def update_subtasks(self, total=0, on=0):
if total != 0:
percent = (on * 100) / total
else:
percent = 0
count = "%d/%d" % (on, total)
self.title = REGEX_SUBTASK.sub("[%s]" % (count), self.title)
self.title = REGEX_SUBTASK_PERCENT.sub("[%d%%]" % (percent), self.title)
d = self._heading.document.write_checkbox(self, including_children=False)
@classmethod
def identify_checkbox(cls, line):
u""" Test if a certain line is a checkbox or not.
:line: the line to check
:returns: indent_level
"""
# checkbox is not heading
if REGEX_HEADING.match(line) is not None:
return None
m = REGEX_CHECKBOX.match(line)
if m:
r = m.groupdict()
return len(r[u'level'])
return None
@property
def is_dirty(self):
u""" Return True if the heading's body is marked dirty """
return self._dirty_checkbox or self._dirty_body
@property
def is_dirty_checkbox(self):
u""" Return True if the heading is marked dirty """
return self._dirty_checkbox
def get_index_in_parent_list(self):
""" Retrieve the index value of current checkbox in the parents list of
checkboxes. This works also for top level checkboxes.
:returns: Index value or None if heading doesn't have a
parent/document or is not in the list of checkboxes
"""
if self.parent:
return super(Checkbox, self).get_index_in_parent_list()
elif self.document:
l = self.get_parent_list()
if l:
return l.index(self)
def get_parent_list(self):
""" Retrieve the parents' list of headings. This works also for top
level headings.
:returns: List of headings or None if heading doesn't have a
parent/document or is not in the list of headings
"""
if self.parent:
return super(Checkbox, self).get_parent_list()
elif self.document:
if self in self.document.checkboxes:
return self.document.checkboxes
def set_dirty(self):
u""" Mark the heading and body dirty so that it will be rewritten when
saving the document """
self._dirty_checkbox = True
self._dirty_body = True
if self._document:
self._document.set_dirty_document()
def set_dirty_checkbox(self):
u""" Mark the checkbox dirty so that it will be rewritten when saving the
document """
self._dirty_checkbox = True
if self._document:
self._document.set_dirty_document()
@property
def previous_checkbox(self):
u""" Serialized access to the previous checkbox """
return super(Checkbox, self).previous_item
@property
def next_checkbox(self):
u""" Serialized access to the next checkbox """
return super(Checkbox, self).next_item
@property
def first_checkbox(self):
u""" Access to the first child heading or None if no children exist """
if self.children:
return self.children[0]
@property
def start(self):
u""" Access to the starting line of the checkbox """
return super(Checkbox, self).start
def toggle(self):
u""" Toggle status of this checkbox """
if self.status == Checkbox.STATUS_OFF or self.status is None:
self.status = Checkbox.STATUS_ON
else:
self.status = Checkbox.STATUS_OFF
self.set_dirty()
def all_siblings(self):
if not self.parent:
p = self._heading
else:
p = self.parent
if not p.children:
return
c = p.first_checkbox
while c:
yield c
c = c.next_sibling
return
def all_children(self):
if not self.children:
return
c = self.first_checkbox
while c:
yield c
for d in c.all_children():
yield d
c = c.next_sibling
return
def all_children_status(self):
u""" Return checkboxes status for currnet checkbox's all children
:return: (total, on)
total: total # of checkboxes
on: # of checkboxes which are on
"""
total, on = 0, 0
for c in self.all_children():
if c.status is not None:
total += 1
if c.status == Checkbox.STATUS_ON:
on += 1
return (total, on)
def all_siblings_status(self):
u""" Return checkboxes status for currnet checkbox's all siblings
:return: (total, on)
total: total # of checkboxes
on: # of checkboxes which are on
"""
total, on = 0, 0
for c in self.all_siblings():
if c.status is not None:
total += 1
if c.status == Checkbox.STATUS_ON:
on += 1
return (total, on)
def are_children_all(self, status):
u""" Check all children checkboxes status """
clen = len(self.children)
for i in range(clen):
if self.children[i].status != status:
return False
# recursively check children's status
if not self.children[i].are_children_all(status):
return False
return True
def is_child_one(self, status):
u""" Return true, if there is one child with given status """
clen = len(self.children)
for i in range(clen):
if self.children[i].status == status:
return True
return False
def are_siblings_all(self, status):
u""" Check all sibling checkboxes status """
for c in self.all_siblings():
if c.status != status:
return False
return True
@DomObj.level.setter
def level(self, value):
u""" Set the checkbox level and mark the checkbox and the document
dirty """
self._level = int(value)
self.set_dirty_checkbox()
@DomObj.title.setter
def title(self, value):
u""" Set the title and mark the document and the checkbox dirty """
if type(value) not in (unicode, str):
raise ValueError(u'Title must be a string.')
v = value
if type(v) == str:
v = u_decode(v)
self._title = v.strip()
self.set_dirty_checkbox()
@property
def status(self):
u""" status of current checkbox """
return self._status
@status.setter
def status(self, value):
self._status = value
self.set_dirty()
@status.deleter
def status(self):
self._status = u''
@property
def type(self):
u""" type of current checkbox list type """
return self._type
@type.setter
def type(self, value):
self._type = value
@type.deleter
def type(self):
self._type = u''
class CheckboxList(DomObjList):
u"""
Checkbox List
"""
def __init__(self, initlist=None, obj=None):
"""
:initlist: Initial data
:obj: Link to a concrete Checkbox or Document object
"""
# it's not necessary to register a on_change hook because the heading
# list will itself take care of marking headings dirty or adding
# headings to the deleted headings list
DomObjList.__init__(self, initlist, obj)
@classmethod
def is_checkbox(cls, obj):
return CheckboxList.is_domobj(obj)
def _get_heading(self):
if self.__class__.is_checkbox(self._obj):
return self._obj._document
return self._obj
# vim: set noexpandtab:

View file

@ -0,0 +1,315 @@
# -*- coding: utf-8 -*-
"""
documents
~~~~~~~~~
TODO: explain this :)
"""
try:
from collections import UserList
except:
from UserList import UserList
from orgmode.liborgmode.base import MultiPurposeList, flatten_list, Direction, get_domobj_range
from orgmode.liborgmode.headings import Heading, HeadingList
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.unicode_compatibility import *
class Document(object):
u"""
Representation of a whole org-mode document.
A Document consists basically of headings (see Headings) and some metadata.
TODO: explain the 'dirty' mechanism
"""
def __init__(self):
u"""
Don't call this constructor directly but use one of the concrete
implementations.
TODO: what are the concrete implementatiions?
"""
object.__init__(self)
# is a list - only the Document methods should work on this list!
self._content = None
self._dirty_meta_information = False
self._dirty_document = False
self._meta_information = MultiPurposeList(
on_change=self.set_dirty_meta_information)
self._orig_meta_information_len = None
self._headings = HeadingList(obj=self)
self._deleted_headings = []
# settings needed to align tags properly
self._tabstop = 8
self._tag_column = 77
# TODO this doesn't differentiate between ACTIVE and FINISHED todo's
self.todo_states = [u'TODO', u'DONE']
def __unicode__(self):
if self.meta_information is None:
return u'\n'.join(self.all_headings())
return u'\n'.join(self.meta_information) + u'\n' + u'\n'.join([u'\n'.join([unicode(i)] + i.body) for i in self.all_headings()])
def __str__(self):
return u_encode(self.__unicode__())
def get_all_todo_states(self):
u""" Convenience function that returns all todo and done states and
sequences in one big list.
Returns:
list: [all todo/done states]
"""
# TODO This is not necessary remove
return flatten_list(self.get_todo_states())
def get_todo_states(self):
u""" Returns a list containing a tuple of two lists of allowed todo
states split by todo and done states. Multiple todo-done state
sequences can be defined.
Returns:
list: [([todo states], [done states]), ..]
"""
# TODO this should be made into property so todo states can be set like
# this too.. or there was also some todo property around... oh well..
# TODO there is the same method in vimbuffer
return self.todo_states
@property
def tabstop(self):
u""" Tabstop for this document """
return self._tabstop
@tabstop.setter
def tabstop(self, value):
self._tabstop = value
@property
def tag_column(self):
u""" The column all tags are right-aligned to """
return self._tag_column
@tag_column.setter
def tag_column(self, value):
self._tag_column = value
def init_dom(self, heading=Heading):
u""" Initialize all headings in document - build DOM. This method
should be call prior to accessing the document.
Returns:
self
"""
def init_heading(_h):
u"""
:returns the initialized heading
"""
start = _h.end + 1
prev_heading = None
while True:
new_heading = self.find_heading(start, heading=heading)
# * Heading 1 <- heading
# * Heading 1 <- sibling
# or
# * Heading 2 <- heading
# * Heading 1 <- parent's sibling
if not new_heading or \
new_heading.level <= _h.level:
break
# * Heading 1 <- heading
# * Heading 2 <- first child
# * Heading 2 <- another child
new_heading._parent = _h
if prev_heading:
prev_heading._next_sibling = new_heading
new_heading._previous_sibling = prev_heading
_h.children.data.append(new_heading)
# the start and end computation is only
# possible when the new heading was properly
# added to the document structure
init_heading(new_heading)
if new_heading.children:
# skip children
start = new_heading.end_of_last_child + 1
else:
start = new_heading.end + 1
prev_heading = new_heading
return _h
h = self.find_heading(heading=heading)
# initialize meta information
if h:
self._meta_information.data.extend(self._content[:h._orig_start])
else:
self._meta_information.data.extend(self._content[:])
self._orig_meta_information_len = len(self.meta_information)
# initialize dom tree
prev_h = None
while h:
if prev_h:
prev_h._next_sibling = h
h._previous_sibling = prev_h
self.headings.data.append(h)
init_heading(h)
prev_h = h
h = self.find_heading(h.end_of_last_child + 1, heading=heading)
return self
@property
def meta_information(self):
u""" Meta information is text that precedes all headings in an org-mode
document. It might contain additional information about the document,
e.g. author
"""
return self._meta_information
@meta_information.setter
def meta_information(self, value):
if self._orig_meta_information_len is None:
self._orig_meta_information_len = len(self.meta_information)
if type(value) in (list, tuple) or isinstance(value, UserList):
self._meta_information[:] = flatten_list(value)
elif type(value) in (str, ):
self._meta_information[:] = u_decode(value).split(u'\n')
elif type(value) in (unicode, ):
self._meta_information[:] = value.split(u'\n')
self.set_dirty_meta_information()
@meta_information.deleter
def meta_information(self):
self.meta_information = u''
@property
def headings(self):
u""" List of top level headings """
return self._headings
@headings.setter
def headings(self, value):
self._headings[:] = value
@headings.deleter
def headings(self):
del self.headings[:]
def write(self):
u""" Write the document
Returns:
bool: True if something was written, otherwise False
"""
raise NotImplementedError(u'Abstract method, please use concrete impelementation!')
def set_dirty_meta_information(self):
u""" Mark the meta information dirty.
Note:
Causes meta information to be rewritten when saving the document
"""
self._dirty_meta_information = True
def set_dirty_document(self):
u""" Mark the whole document dirty.
Note:
When changing a heading this method must be executed in order to
changed computation of start and end positions from a static to a
dynamic computation
"""
self._dirty_document = True
@property
def is_dirty(self):
u""" Return information about unsaved changes for the document and all
related headings.
Returns:
bool: True if document contains unsaved changes.
"""
if self.is_dirty_meta_information:
return True
if self.is_dirty_document:
return True
if self._deleted_headings:
return True
return False
@property
def is_dirty_meta_information(self):
u""" Return True if the meta information is marked dirty """
return self._dirty_meta_information
@property
def is_dirty_document(self):
u""" Return True if the document is marked dirty """
return self._dirty_document
def all_headings(self):
u""" Iterate over all headings of the current document in serialized
order
:returns: Returns an iterator object which returns all headings of
the current file in serialized order
"""
if not self.headings:
return
h = self.headings[0]
while h:
yield h
h = h.next_heading
return
def find_heading(
self, position=0, direction=Direction.FORWARD, heading=Heading,
connect_with_document=True):
u""" Find heading in the given direction
Args:
position (int): starting line, counting from 0 (in vim you start
counting from 1, don't forget)
direction: downwards == Direction.FORWARD,
upwards == Direction.BACKWARD
heading: Heading class from which new heading objects will be
instanciated
connect_with_document: if True, the newly created heading will be
connected with the document, otherwise not
Returns:
heading or None: New heading
"""
start, end = get_domobj_range(
content=self._content, position=position, direction=direction,
identify_fun=heading.identify_heading)
if start is None:
return None
if end is None:
end = len(self._content) - 1
document = self if connect_with_document else None
return heading.parse_heading_from_data(
self._content[start:end + 1], self.get_all_todo_states(),
document=document, orig_start=start)
# vim: set noexpandtab:

View file

@ -0,0 +1,505 @@
# -*- coding: utf-8 -*-
"""
dom object
~~~~~~~~~~
TODO: explain this :)
"""
import re
from orgmode.liborgmode.base import MultiPurposeList, flatten_list
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.unicode_compatibility import *
try:
from collections import UserList
except:
from UserList import UserList
# breaking down tasks regex
REGEX_SUBTASK = re.compile(r'\[(\d*)/(\d*)\]')
REGEX_SUBTASK_PERCENT = re.compile(r'\[(\d*)%\]')
# heading regex
REGEX_HEADING = re.compile(
r'^(?P<level>\*+)(\s+(?P<title>.*?))?\s*(\s(?P<tags>:[\w_:@]+:))?$',
flags=re.U)
REGEX_TAG = re.compile(
r'^\s*((?P<title>[^\s]*?)\s+)?(?P<tags>:[\w_:@]+:)$',
flags=re.U)
REGEX_TODO = re.compile(r'^[^\s]*$')
# checkbox regex:
# - [ ] checkbox item
# - [X] checkbox item
# - [ ]
# - no status checkbox
UnOrderListType = [u'-', u'+', u'*']
OrderListType = [u'.', u')']
REGEX_CHECKBOX = re.compile(
r'^(?P<level>\s*)(?P<type>[%s]|([a-zA-Z]|[\d]+)[%s])(\s+(?P<status>\[.\]))?\s*(?P<title>.*)$'
% (''.join(UnOrderListType), ''.join(OrderListType)), flags=re.U)
class DomObj(object):
u"""
A DomObj is DOM structure element, like Heading and Checkbox.
Its purpose is to abstract the same parts of Heading and Checkbox objects,
and make code reusable.
All methods and properties are extracted from Heading object.
Heading and Checkbox objects inherit from DomObj, and override some specific
methods in their own objects.
Normally, we don't intend to use DomObj directly. However, we can add some more
DOM structure element based on this class to make code more concise.
"""
# TODO should this and DomObj_list be abstract methods? If so use ABC to
# force abstract methods
def __init__(self, level=1, title=u'', body=None):
u"""
:level: Level of the dom object
:title: Title of the dom object
:body: Body of the dom object
"""
object.__init__(self)
self._document = None
self._parent = None
self._previous_sibling = None
self._next_sibling = None
self._children = MultiPurposeList()
self._orig_start = None
self._orig_len = 0
self._level = level
# title
self._title = u''
if title:
self.title = title
# body
self._dirty_body = False
self._body = MultiPurposeList(on_change=self.set_dirty_body)
if body:
self.body = body
def __unicode__(self):
return u'<dom obj level=%s, title=%s>' % (level, title)
def __str__(self):
return u_encode(self.__unicode__())
def __len__(self):
# 1 is for the heading's title
return 1 + len(self.body)
@property
def is_dirty(self):
u""" Return True if the dom obj body is marked dirty """
return self._dirty_body
@property
def is_dirty_body(self):
u""" Return True if the dom obj body is marked dirty """
return self._dirty_body
def get_index_in_parent_list(self):
""" Retrieve the index value of current dom obj in the parents list of
dom objs. This works also for top level dom objs.
:returns: Index value or None if dom obj doesn't have a
parent/document or is not in the list of dom objs
"""
l = self.get_parent_list()
if l:
return l.index(self)
def get_parent_list(self):
""" Retrieve the parents list of dom objs. This works also for top
level dom objs.
:returns: List of dom objs or None if dom objs doesn't have a
parent/document or is not in the list of dom objs
"""
if self.parent:
if self in self.parent.children:
return self.parent.children
def set_dirty(self):
u""" Mark the dom objs and body dirty so that it will be rewritten when
saving the document """
if self._document:
self._document.set_dirty_document()
def set_dirty_body(self):
u""" Mark the dom objs' body dirty so that it will be rewritten when
saving the document """
self._dirty_body = True
if self._document:
self._document.set_dirty_document()
@property
def document(self):
u""" Read only access to the document. If you want to change the
document, just assign the dom obj to another document """
return self._document
@property
def parent(self):
u""" Access to the parent dom obj """
return self._parent
@property
def number_of_parents(self):
u""" Access to the number of parent dom objs before reaching the root
document """
def count_parents(h):
if h.parent:
return 1 + count_parents(h.parent)
else:
return 0
return count_parents(self)
@property
def previous_sibling(self):
u""" Access to the previous dom obj that's a sibling of the current one
"""
return self._previous_sibling
@property
def next_sibling(self):
u""" Access to the next dom obj that's a sibling of the current one """
return self._next_sibling
@property
def previous_item(self):
u""" Serialized access to the previous dom obj """
if self.previous_sibling:
h = self.previous_sibling
while h.children:
h = h.children[-1]
return h
elif self.parent:
return self.parent
@property
def next_item(self):
u""" Serialized access to the next dom obj """
if self.children:
return self.children[0]
elif self.next_sibling:
return self.next_sibling
else:
h = self.parent
while h:
if h.next_sibling:
return h.next_sibling
else:
h = h.parent
@property
def start(self):
u""" Access to the starting line of the dom obj """
if self.document is None or not self.document.is_dirty:
return self._orig_start
def item_len_generator(h):
while h:
yield len(h)
h = h.previous_item
return sum(item for item in item_len_generator(self.previous_item))
@property
def start_vim(self):
if self.start is not None:
return self.start + 1
@property
def end(self):
u""" Access to the ending line of the dom obj """
if self.start is not None:
return self.start + len(self.body)
@property
def end_vim(self):
if self.end is not None:
return self.end + 1
@property
def end_of_last_child(self):
u""" Access to end of the last child """
if self.children:
child = self.children[-1]
while child.children:
child = child.children[-1]
return child.end
return self.end
@property
def end_of_last_child_vim(self):
return self.end_of_last_child + 1
@property
def children(self):
u""" MultiPurposeList[dom_objects??]: subheadings of the current DomObj
Setter method takes list, tuple or userlist with DOMObjects
"""
return self._children
@children.setter
def children(self, value):
v = value
if type(v) in (list, tuple) or isinstance(v, UserList):
v = flatten_list(v)
self._children[:] = v
@children.deleter
def children(self):
del self.children[:]
@property
def first_child(self):
u""" Access to the first child dom obj or None if no children exist """
if self.children:
return self.children[0]
@property
def last_child(self):
u""" Access to the last child dom obj or None if no children exist """
if self.children:
return self.children[-1]
@property
def level(self):
u""" int: Access the the dom obj level
Setter sets the DOM object and the document as dirty if invoked.
"""
return self._level
@level.setter
def level(self, value):
# TODO Shouldn't there be and error when values is not int?
self._level = int(value)
self.set_dirty()
@level.deleter
def level(self):
self.level = None
@property
def title(self):
u""" str: Get the title of current dom object
Setter sets the DOM object and the document as dirty if invoked.
"""
return self._title.strip()
@title.setter
def title(self, value):
if type(value) not in (unicode, str):
raise ValueError(u'Title must be a string.')
v = value
if type(v) == str:
v = u_decode(v)
self._title = v.strip()
self.set_dirty()
@title.deleter
def title(self):
self._title = u''
@property
def body(self):
u""" MultiPurposeList[]: Holds the content belonging to the heading """
return self._body
@body.setter
def body(self, value):
if type(value) in (list, tuple) or isinstance(value, UserList):
self._body[:] = flatten_list(value)
elif type(value) in (str, ):
self._body[:] = u_decode(value).split(u'\n')
elif type(value) in (unicode, ):
self._body[:] = value.split(u'\n')
else:
self.body = list(unicode(value))
@body.deleter
def body(self):
# TODO write this as del self._body[:] because there is no reason to
# call so much code for deleting a list
self.body = []
class DomObjList(MultiPurposeList):
u"""
A Dom Obj List
"""
def __init__(self, initlist=None, obj=None):
"""
:initlist: Initial data
:obj: Link to a concrete Heading or Document object
"""
# it's not necessary to register a on_change hook because the heading
# list will itself take care of marking headings dirty or adding
# headings to the deleted headings list
MultiPurposeList.__init__(self)
self._obj = obj
# initialization must be done here, because
# self._document is not initialized when the
# constructor of MultiPurposeList is called
if initlist:
self.extend(initlist)
@classmethod
def is_domobj(cls, obj):
# TODO no reason for it to be class method. Does it even need to exist
# because it is quite clear what isinstance does and in derived methods
# isinstance(Heading, DomObj) would return True anyway.
return isinstance(obj, DomObj)
# TODO this should be made into a property
def _get_document(self):
if self.__class__.is_domobj(self._obj):
return self._obj._document
return self._obj
def __setitem__(self, i, item):
if isinstance(i, slice):
o = item
if self.__class__.is_domobj(o):
o = (o, )
o = flatten_list(o)
for item in o:
if not self.__class__.is_domobj(item):
raise ValueError(u'List contains items that are not a Dom obj!')
# self._add_to_deleted_domobjs(self[i:j])
# self._associate_domobj(o, \
# self[i - 1] if i - 1 >= 0 and i < len(self) else None, \
# self[j] if j >= 0 and j < len(self) else None)
MultiPurposeList.__setitem__(self, i, o)
else:
if not self.__class__.is_domobj(item):
raise ValueError(u'Item is not a Dom obj!')
if item in self:
raise ValueError(u'Dom obj is already part of this list!')
# self._add_to_deleted_domobjs(self[i])
# self._associate_domobj(item, \
# self[i - 1] if i - 1 >= 0 else None, \
# self[i + 1] if i + 1 < len(self) else None)
MultiPurposeList.__setitem__(self, i, item)
def __delitem__(self, i, taint=True):
if isinstance(i, slice):
items = self[i]
if items:
first = items[0]
last = items[-1]
if first.previous_sibling:
first.previous_sibling._next_sibling = last.next_sibling
if last.next_sibling:
last.next_sibling._previous_sibling = first.previous_sibling
# if taint:
# self._add_to_deleted_domobjs(items)
else:
item = self[i]
if item.previous_sibling:
item.previous_sibling._next_sibling = item.next_sibling
if item.next_sibling:
item.next_sibling._previous_sibling = item.previous_sibling
# if taint:
# self._add_to_deleted_domobjs(item)
MultiPurposeList.__delitem__(self, i)
def __setslice__(self, i, j, other):
self.__setitem__(slice(i, j), other)
def __delslice__(self, i, j, taint=True):
self.__delitem__(slice(i, j), taint=taint)
def __iadd__(self, other):
o = other
if self.__class__.is_domobj(o):
o = (o, )
for item in flatten_list(o):
if not self.__class__.is_domobj(item):
raise ValueError(u'List contains items that are not a Dom obj!')
# self._associate_domobj(o, self[-1] if len(self) > 0 else None, None)
return MultiPurposeList.__iadd__(self, o)
def __imul__(self, n):
# TODO das müsste eigentlich ein klonen von objekten zur Folge haben
return MultiPurposeList.__imul__(self, n)
def append(self, item, taint=True):
if not self.__class__.is_domobj(item):
raise ValueError(u'Item is not a heading!')
if item in self:
raise ValueError(u'Heading is already part of this list!')
# self._associate_domobj(
# item, self[-1] if len(self) > 0 else None,
# None, taint=taint)
MultiPurposeList.append(self, item)
def insert(self, i, item, taint=True):
# self._associate_domobj(
# item,
# self[i - 1] if i - 1 >= 0 and i - 1 < len(self) else None,
# self[i] if i >= 0 and i < len(self) else None, taint=taint)
MultiPurposeList.insert(self, i, item)
def pop(self, i=-1):
item = self[i]
# self._add_to_deleted_domobjs(item)
del self[i]
return item
def remove_slice(self, i, j, taint=True):
self.__delitem__(slice(i, j), taint=taint)
def remove(self, item, taint=True):
self.__delitem__(self.index(item), taint=taint)
def reverse(self):
MultiPurposeList.reverse(self)
prev_h = None
for h in self:
h._previous_sibling = prev_h
h._next_sibling = None
prev_h._next_sibling = h
h.set_dirty()
prev_h = h
def sort(self, *args, **kwds):
MultiPurposeList.sort(*args, **kwds)
prev_h = None
for h in self:
h._previous_sibling = prev_h
h._next_sibling = None
prev_h._next_sibling = h
h.set_dirty()
prev_h = h
def extend(self, other):
o = other
if self.__class__.is_domobj(o):
o = (o, )
for item in o:
if not self.__class__.is_domobj(item):
raise ValueError(u'List contains items that are not a heading!')
# self._associate_domobj(o, self[-1] if len(self) > 0 else None, None)
MultiPurposeList.extend(self, o)
# vim: set noexpandtab:

View file

@ -0,0 +1,889 @@
# -*- coding: utf-8 -*-
"""
headings
~~~~~~~~~
TODO: explain this :)
"""
import re
import vim
from orgmode.liborgmode.base import MultiPurposeList, flatten_list, Direction, get_domobj_range
from orgmode.liborgmode.orgdate import OrgTimeRange
from orgmode.liborgmode.orgdate import get_orgdate
from orgmode.liborgmode.checkboxes import Checkbox, CheckboxList
from orgmode.liborgmode.dom_obj import DomObj, DomObjList, REGEX_SUBTASK, REGEX_SUBTASK_PERCENT, REGEX_HEADING, REGEX_TAG, REGEX_TODO
from orgmode.py3compat.xrange_compatibility import *
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.unicode_compatibility import *
try:
from collections import UserList
except:
from UserList import UserList
from itertools import ifilter as filter
class Heading(DomObj):
u""" Structural heading object """
def __init__(self, level=1, title=u'', tags=None, todo=None, body=None, active_date=None):
u"""
:level: Level of the heading
:title: Title of the heading
:tags: Tags of the heading
:todo: Todo state of the heading
:body: Body of the heading
:active_date: active date that is used in the agenda
"""
DomObj.__init__(self, level=level, title=title, body=body)
self._children = HeadingList(obj=self)
self._dirty_heading = False
# todo
self._todo = None
if todo:
self.todo = todo
# tags
self._tags = MultiPurposeList(on_change=self.set_dirty_heading)
if tags:
self.tags = tags
# active date
self._active_date = active_date
if active_date:
self.active_date = active_date
# checkboxes
self._checkboxes = CheckboxList(obj=self)
self._cached_checkbox = None
def __unicode__(self):
res = u'*' * self.level
if self.todo:
res = u' '.join((res, self.todo))
if self.title:
res = u' '.join((res, self.title))
# compute position of tags
if self.tags:
tabs = 0
spaces = 2
tags = u':%s:' % (u':'.join(self.tags), )
# FIXME this is broken because of missing associations for headings
ts = 6
tag_column = 77
if self.document:
ts = self.document.tabstop
tag_column = self.document.tag_column
len_heading = len(res)
len_tags = len(tags)
if len_heading + spaces + len_tags < tag_column:
spaces_to_next_tabstop = ts - divmod(len_heading, ts)[1]
if len_heading + spaces_to_next_tabstop + len_tags < tag_column:
tabs, spaces = divmod(
tag_column - (len_heading + spaces_to_next_tabstop + len_tags),
ts)
if spaces_to_next_tabstop:
tabs += 1
else:
spaces = tag_column - (len_heading + len_tags)
res += u'\t' * tabs + u' ' * spaces + tags
# append a trailing space when there are just * and no text
if len(res) == self.level:
res += u' '
return res
def __str__(self):
return u_encode(self.__unicode__())
def __len__(self):
# 1 is for the heading's title
return 1 + len(self.body)
def __lt__(self, other):
"""
Headings can be sorted by date.
"""
try:
if self.active_date < other.active_date:
return True
elif self.active_date == other.active_date:
return False
elif self.active_date > other.active_date:
return False
except:
if self.active_date and not other.active_date:
return True
elif not self.active_date and other.active_date:
return False
elif not self.active_date and not other.active_date:
return False
def __le__(self, other):
"""
Headings can be sorted by date.
"""
try:
if self.active_date < other.active_date:
return True
elif self.active_date == other.active_date:
return True
elif self.active_date > other.active_date:
return False
except:
if self.active_date and not other.active_date:
return True
elif not self.active_date and other.active_date:
return False
elif not self.active_date and not other.active:
return True
def __ge__(self, other):
"""
Headings can be sorted by date.
"""
try:
if self.active_date > other.active_date:
return True
elif self.active_date == other.active_date:
return True
elif self.active_date < other.active_date:
return False
except:
if not self.active_date and other.active_date:
return True
elif self.active_date and not other.active_date:
return False
elif not self.active_date and not other.active:
return True
def __gt__(self, other):
"""
Headings can be sorted by date.
"""
try:
if self.active_date > other.active_date:
return True
elif self.active_date == other.active_date:
return False
elif self.active_date < other.active_date:
return False
except:
if not self.active_date and other.active_date:
return True
elif self.active_date and not other.active_date:
return False
elif not self.active_date and not other.active:
return False
def copy(self, including_children=True, parent=None):
u"""
Create a copy of the current heading. The heading will be completely
detached and not even belong to a document anymore.
:including_children: If True a copy of all children is create as
well. If False the returned heading doesn't
have any children.
:parent: Don't use this parameter. It's set
automatically.
"""
heading = self.__class__(
level=self.level, title=self.title,
tags=self.tags, todo=self.todo, body=self.body[:])
if parent:
parent.children.append(heading)
if including_children and self.children:
for item in self.children:
item.copy(
including_children=including_children,
parent=heading)
heading._orig_start = self._orig_start
heading._orig_len = self._orig_len
heading._dirty_heading = self.is_dirty_heading
return heading
def all_checkboxes(self):
u""" Iterate over all checkboxes of the current heading in serialized
order
:returns: Returns an iterator object which returns all checkboxes of
the current heading in serialized order
"""
if not self.checkboxes:
return
c = self.first_checkbox
while c:
yield c
c = c.next_checkbox
return
def all_toplevel_checkboxes(self):
u""" return all top level checkboxes for current heading """
if not self.checkboxes:
return
c = self.first_checkbox
while c:
yield c
c = c.next_sibling
return
def find_checkbox(self, position=0, direction=Direction.FORWARD,
checkbox=Checkbox, connect_with_heading=True):
u""" Find checkbox in the given direction
:postition: starting line, counting from 0 (in vim you start
counting from 1, don't forget)
:direction: downwards == Direction.FORWARD,
upwards == Direction.BACKWARD
:checkbox: Checkbox class from which new checkbox objects will be
instanciated
:connect_with_heading: if True, the newly created checkbox will be
connected with the heading, otherwise not
:returns: New checkbox object or None
"""
doc = self.document
(start, end) = get_domobj_range(content=doc._content, position=position, direction=direction, identify_fun=checkbox.identify_checkbox)
# if out of current headinig range, reutrn None
heading_end = self.start + len(self) - 1
if start is not None and start > heading_end:
return None
if end is not None and end > heading_end:
end = heading_end
if start is not None and end is None:
end = heading_end
if start is not None and end is not None:
return checkbox.parse_checkbox_from_data(
doc._content[start:end + 1],
heading=self if connect_with_heading else None, orig_start=start)
def init_checkboxes(self, checkbox=Checkbox):
u""" Initialize all checkboxes in current heading - build DOM.
:returns: self
"""
def init_checkbox(_c):
u"""
:returns the initialized checkbox
"""
start = _c.end + 1
prev_checkbox = None
while True:
new_checkbox = self.find_checkbox(start, checkbox=checkbox)
# * Checkbox 1 <- checkbox
# * Checkbox 1 <- sibling
# or
# * Checkbox 2 <- checkbox
# * Checkbox 1 <- parent's sibling
if not new_checkbox or \
new_checkbox.level <= _c.level:
break
# * Checkbox 1 <- heading
# * Checkbox 2 <- first child
# * Checkbox 2 <- another child
new_checkbox._parent = _c
if prev_checkbox:
prev_checkbox._next_sibling = new_checkbox
new_checkbox._previous_sibling = prev_checkbox
_c.children.data.append(new_checkbox)
# the start and end computation is only
# possible when the new checkbox was properly
# added to the document structure
init_checkbox(new_checkbox)
if new_checkbox.children:
# skip children
start = new_checkbox.end_of_last_child + 1
else:
start = new_checkbox.end + 1
prev_checkbox = new_checkbox
return _c
c = self.find_checkbox(checkbox=checkbox, position=self.start)
# initialize dom tree
prev_c = None
while c:
if prev_c and prev_c.level == c.level:
prev_c._next_sibling = c
c._previous_sibling = prev_c
self.checkboxes.data.append(c)
init_checkbox(c)
prev_c = c
c = self.find_checkbox(c.end_of_last_child + 1, checkbox=checkbox)
return self
def current_checkbox(self, position=None):
u""" Find the current checkbox (search backward) and return the related object
:returns: Checkbox object or None
"""
if position is None:
position = vim.current.window.cursor[0] - 1
if not self.checkboxes:
return
def binaryFindInHeading():
hi = len(self.checkboxes)
lo = 0
while lo < hi:
mid = (lo + hi) // 2
c = self.checkboxes[mid]
if c.end_of_last_child < position:
lo = mid + 1
elif c.start > position:
hi = mid
else:
return binaryFindCheckbox(c)
def binaryFindCheckbox(checkbox):
if not checkbox.children or checkbox.end >= position:
return checkbox
hi = len(checkbox.children)
lo = 0
while lo < hi:
mid = (lo + hi) // 2
c = checkbox.children[mid]
if c.end_of_last_child < position:
lo = mid + 1
elif c.start > position:
hi = mid
else:
return binaryFindCheckbox(c)
# look at the cache to find the heading
c_tmp = self._cached_checkbox
if c_tmp is not None:
if c_tmp.end_of_last_child > position and \
c_tmp.start < position:
if c_tmp.end < position:
self._cached_checkbox = binaryFindCheckbox(c_tmp)
return self._cached_checkbox
self._cached_checkbox = binaryFindInHeading()
return self._cached_checkbox
@property
def first_checkbox(self):
u""" Access to the first child checkbox or None if no children exist """
if self.checkboxes:
return self.checkboxes[0]
@classmethod
def parse_heading_from_data(
cls, data, allowed_todo_states, document=None,
orig_start=None):
u""" Construct a new heading from the provided data
:data: List of lines
:allowed_todo_states: TODO???
:document: The document object this heading belongs to
:orig_start: The original start of the heading in case it was read
from a document. If orig_start is provided, the
resulting heading will not be marked dirty.
:returns: The newly created heading
"""
test_not_empty = lambda x: x != u''
def parse_title(heading_line):
# WARNING this regular expression fails if there is just one or no
# word in the heading but a tag!
m = REGEX_HEADING.match(heading_line)
if m:
r = m.groupdict()
level = len(r[u'level'])
todo = None
title = u''
tags = filter(test_not_empty, r[u'tags'].split(u':')) if r[u'tags'] else []
tags = list(tags)
# if there is just one or no word in the heading, redo the parsing
mt = REGEX_TAG.match(r[u'title'])
if not tags and mt:
r = mt.groupdict()
tags = filter(test_not_empty, r[u'tags'].split(u':')) if r[u'tags'] else []
tags = list(tags)
if r[u'title'] is not None:
_todo_title = [i.strip() for i in r[u'title'].split(None, 1)]
if _todo_title and _todo_title[0] in allowed_todo_states:
todo = _todo_title[0]
if len(_todo_title) > 1:
title = _todo_title[1]
else:
title = r[u'title'].strip()
return (level, todo, title, tags)
raise ValueError(u'Data doesn\'t start with a heading definition.')
if not data:
raise ValueError(u'Unable to create heading, no data provided.')
# create new heaing
new_heading = cls()
new_heading.level, new_heading.todo, new_heading.title, new_heading.tags = parse_title(data[0])
new_heading.body = data[1:]
if orig_start is not None:
new_heading._dirty_heading = False
new_heading._dirty_body = False
new_heading._orig_start = orig_start
new_heading._orig_len = len(new_heading)
if document:
new_heading._document = document
# try to find active dates
tmp_orgdate = get_orgdate(data)
if tmp_orgdate and tmp_orgdate.active \
and not isinstance(tmp_orgdate, OrgTimeRange):
new_heading.active_date = tmp_orgdate
else:
new_heading.active_date = None
return new_heading
def update_subtasks(self, total=0, on=0):
u""" Update subtask information for current heading
:total: total # of top level checkboxes
:on: # of top level checkboxes which are on
"""
if total != 0:
percent = (on * 100) / total
else:
percent = 0
count = "%d/%d" % (on, total)
self.title = REGEX_SUBTASK.sub("[%s]" % (count), self.title)
self.title = REGEX_SUBTASK_PERCENT.sub("[%d%%]" % (percent), self.title)
self.document.write_heading(self, including_children=False)
@staticmethod
def identify_heading(line):
u""" Test if a certain line is a heading or not.
Args:
line (str): the line to check
Returns:
int or None: level of heading or None if line is not heading
"""
# TODO would it make sense to return 0 for heading level?
# TODO add tests e.g. '*** abc', '**', '', '* ', '*\t', '*'
for i, item in enumerate(line):
if item == '*':
continue
elif i and item in ('\t', ' '):
return i
break
return None
@property
def is_dirty(self):
u""" Return True if the heading's body is marked dirty """
return self._dirty_heading or self._dirty_body
@property
def is_dirty_heading(self):
u""" Return True if the heading is marked dirty """
return self._dirty_heading
def get_index_in_parent_list(self):
""" Retrieve the index value of current heading in the parents list of
headings. This works also for top level headings.
:returns: Index value or None if heading doesn't have a
parent/document or is not in the list of headings
"""
if self.parent:
return super(Heading, self).get_index_in_parent_list()
elif self.document:
l = self.get_parent_list()
if l:
return l.index(self)
def get_parent_list(self):
""" Retrieve the parents' list of headings. This works also for top
level headings.
:returns: List of headings or None if heading doesn't have a
parent/document or is not in the list of headings
"""
if self.parent:
return super(Heading, self).get_parent_list()
elif self.document:
if self in self.document.headings:
return self.document.headings
def set_dirty(self):
u""" Mark the heading and body dirty so that it will be rewritten when
saving the document """
self._dirty_heading = True
self._dirty_body = True
if self._document:
self._document.set_dirty_document()
def set_dirty_heading(self):
u""" Mark the heading dirty so that it will be rewritten when saving the
document """
self._dirty_heading = True
if self._document:
self._document.set_dirty_document()
@property
def previous_heading(self):
u""" Serialized access to the previous heading """
return super(Heading, self).previous_item
@property
def next_heading(self):
u""" Serialized access to the next heading """
return super(Heading, self).next_item
@property
def start(self):
u""" Access to the starting line of the heading """
if self.document is None or not self.document.is_dirty:
return self._orig_start
meta_len = len(self.document.meta_information) if \
self.document.meta_information else 0
return super(Heading, self).start + meta_len
@DomObj.level.setter
def level(self, value):
u""" Set the heading level and mark the heading and the document dirty """
self._level = int(value)
self.set_dirty_heading()
@property
def todo(self):
u""" Todo state of current heading. When todo state is set"""
# extract todo state from heading
return self._todo
@todo.setter
def todo(self, value):
# update todo state
if type(value) not in (unicode, str, type(None)):
raise ValueError(u'Todo state must be a string or None.')
if value and not REGEX_TODO.match(value):
raise ValueError(u'Found non allowed character in todo state! %s' % value)
if not value:
self._todo = None
else:
v = value
if type(v) == str:
v = u_decode(v)
self._todo = v
self.set_dirty_heading()
@todo.deleter
def todo(self):
self.todo = None
@property
def active_date(self):
u"""
active date of the hearing.
active dates are used in the agenda view. they can be part of the
heading and/or the body.
"""
return self._active_date
@active_date.setter
def active_date(self, value):
self._active_date = value
@active_date.deleter
def active_date(self):
self._active_date = None
@DomObj.title.setter
def title(self, value):
u""" Set the title and mark the document and the heading dirty """
# TODO these setter should be rewriten to also reuse code from DOM OBJ
if type(value) not in (unicode, str):
raise ValueError(u'Title must be a string.')
v = value
if type(v) == str:
v = u_decode(v)
self._title = v.strip()
self.set_dirty_heading()
@property
def tags(self):
u""" Tags of the current heading """
return self._tags
@tags.setter
def tags(self, value):
v = value
if type(v) in (unicode, str):
v = list(unicode(v))
if type(v) not in (list, tuple) and not isinstance(v, UserList):
v = list(unicode(v))
v = flatten_list(v)
v_decoded = []
for i in v:
if type(i) not in (unicode, str):
raise ValueError(u'Found non string value in tags! %s' % unicode(i))
if u':' in i:
raise ValueError(u'Found non allowed character in tag! %s' % i)
i_tmp = i.strip().replace(' ', '_').replace('\t', '_')
if type(i) == str:
i_tmp = u_decode(i)
v_decoded.append(i_tmp)
self._tags[:] = v_decoded
@tags.deleter
def tags(self):
self.tags = []
@property
def checkboxes(self):
u""" All checkboxes in current heading """
return self._checkboxes
@checkboxes.setter
def checkboxes(self, value):
self._checkboxes[:] = value
@checkboxes.deleter
def checkboxes(self):
del self.checkboxes[:]
class HeadingList(DomObjList):
u"""
A Heading List just contains headings. It's used for documents to store top
level headings and for headings to store subheadings.
A Heading List must be linked to a Document or Heading!
See documenatation of MultiPurposeList for more information.
"""
def __init__(self, initlist=None, obj=None):
"""
:initlist: Initial data
:obj: Link to a concrete Heading or Document object
"""
# it's not necessary to register a on_change hook because the heading
# list will itself take care of marking headings dirty or adding
# headings to the deleted headings list
DomObjList.__init__(self, initlist, obj)
@classmethod
def is_heading(cls, obj):
# TODO no need to make this or is_domobj a class methods
return HeadingList.is_domobj(obj)
def _get_document(self):
if self.__class__.is_heading(self._obj):
return self._obj._document
return self._obj
def _add_to_deleted_headings(self, item):
u"""
Serialize headings so that all subheadings are also marked for deletion
"""
if not self._get_document():
# HeadingList has not yet been associated
return
if type(item) in (list, tuple) or isinstance(item, UserList):
for i in flatten_list(item):
self._add_to_deleted_headings(i)
else:
self._get_document()._deleted_headings.append(
item.copy(including_children=False))
self._add_to_deleted_headings(item.children)
self._get_document().set_dirty_document()
def _associate_heading(
self, heading, previous_sibling, next_sibling,
children=False, taint=True):
"""
:heading: The heading or list to associate with the current heading
:previous_sibling: The previous sibling of the current heading. If
heading is a list the first heading will be
connected with the previous sibling and the last
heading with the next sibling. The items in between
will be linked with one another.
:next_sibling: The next sibling of the current heading. If
heading is a list the first heading will be
connected with the previous sibling and the last
heading with the next sibling. The items in between
will be linked with one another.
:children: Marks whether children are processed in the current
iteration or not (should not be use, it's set
automatically)
:taint: If not True, the heading is not marked dirty at the end
of the association process and its orig_start and
orig_len values are not updated.
"""
# TODO this method should be externalized and moved to the Heading class
# TODO should this method work with slice?
if type(heading) in (list, tuple) or isinstance(heading, UserList):
prev = previous_sibling
current = None
for _next in flatten_list(heading):
if current:
self._associate_heading(
current, prev, _next,
children=children, taint=taint)
prev = current
current = _next
if current:
self._associate_heading(
current, prev, next_sibling,
children=children, taint=taint)
else:
if taint:
heading._orig_start = None
heading._orig_len = None
d = self._get_document()
if heading._document != d:
heading._document = d
if not children:
# connect heading with previous and next headings
heading._previous_sibling = previous_sibling
if previous_sibling:
previous_sibling._next_sibling = heading
heading._next_sibling = next_sibling
if next_sibling:
next_sibling._previous_sibling = heading
if d == self._obj:
# self._obj is a Document
heading._parent = None
elif heading._parent != self._obj:
# self._obj is a Heading
heading._parent = self._obj
if taint:
heading.set_dirty()
self._associate_heading(
heading.children, None, None,
children=True, taint=taint)
def __setitem__(self, i, item):
if isinstance(i, slice):
start, stop, step = i.indices(len(self))
items = item
if self.__class__.is_heading(items):
items = (items, )
items = flatten_list(items)
for head in items:
if not self.__class__.is_heading(head):
raise ValueError(u'List contains items that are not a heading!')
self._add_to_deleted_headings(self[i])
self._associate_heading(
items,
self[start - 1] if start - 1 >= 0 else None,
self[stop] if stop < len(self) else None)
MultiPurposeList.__setitem__(self, i, items)
else:
if not self.__class__.is_heading(item):
raise ValueError(u'Item is not a heading!')
if item in self:
raise ValueError(u'Heading is already part of this list!')
self._add_to_deleted_headings(self[i])
self._associate_heading(
item,
self[i - 1] if i - 1 >= 0 else None,
self[i + 1] if i + 1 < len(self) else None)
MultiPurposeList.__setitem__(self, i, item)
def __delitem__(self, i, taint=True):
# TODO refactor this item, it works the same in dom_obj except taint?
if isinstance(i, slice):
items = self[i]
if items:
first = items[0]
last = items[-1]
if first.previous_sibling:
first.previous_sibling._next_sibling = last.next_sibling
if last.next_sibling:
last.next_sibling._previous_sibling = first.previous_sibling
if taint:
self._add_to_deleted_headings(items)
MultiPurposeList.__delitem__(self, i)
else:
item = self[i]
if item.previous_sibling:
item.previous_sibling._next_sibling = item.next_sibling
if item.next_sibling:
item.next_sibling._previous_sibling = item.previous_sibling
if taint:
self._add_to_deleted_headings(item)
MultiPurposeList.__delitem__(self, i)
def __iadd__(self, other):
o = other
if self.__class__.is_heading(o):
o = (o, )
for item in flatten_list(o):
if not self.__class__.is_heading(item):
raise ValueError(u'List contains items that are not a heading!')
self._associate_heading(o, self[-1] if len(self) > 0 else None, None)
return MultiPurposeList.__iadd__(self, o)
def append(self, item, taint=True):
if not self.__class__.is_heading(item):
raise ValueError(u'Item is not a heading!')
if item in self:
raise ValueError(u'Heading is already part of this list!')
self._associate_heading(
item, self[-1] if len(self) > 0 else None,
None, taint=taint)
MultiPurposeList.append(self, item)
def insert(self, i, item, taint=True):
self._associate_heading(
item,
self[i - 1] if i - 1 >= 0 and i - 1 < len(self) else None,
self[i] if i >= 0 and i < len(self) else None, taint=taint)
MultiPurposeList.insert(self, i, item)
def pop(self, i=-1):
item = self[i]
self._add_to_deleted_headings(item)
del self[i]
return item
def extend(self, other):
o = other
if self.__class__.is_heading(o):
o = (o, )
for item in o:
if not self.__class__.is_heading(item):
raise ValueError(u'List contains items that are not a heading!')
self._associate_heading(o, self[-1] if len(self) > 0 else None, None)
MultiPurposeList.extend(self, o)
# vim: set noexpandtab:

View file

@ -0,0 +1,296 @@
# -*- coding: utf-8 -*-
u"""
OrgDate
~~~~~~~~~~~~~~~~~~
This module contains all date/time/timerange representations that exist in
orgmode.
There exist three different kinds:
* OrgDate: is similar to a date object in python and it looks like
'2011-09-07 Wed'.
* OrgDateTime: is similar to a datetime object in python and looks like
'2011-09-07 Wed 10:30'
* OrgTimeRange: indicates a range of time. It has a start and and end date:
* <2011-09-07 Wed>--<2011-09-08 Fri>
* <2011-09-07 Wed 10:00-13:00>
All OrgTime oblects can be active or inactive.
"""
import datetime
import re
from orgmode.py3compat.encode_compatibility import *
# <2011-09-12 Mon>
_DATE_REGEX = re.compile(r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>", re.UNICODE)
# [2011-09-12 Mon]
_DATE_PASSIVE_REGEX = re.compile(r"\[(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w\]", re.UNICODE)
# <2011-09-12 Mon 10:20>
_DATETIME_REGEX = re.compile(
r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d{1,2}):(\d\d)>", re.UNICODE)
# [2011-09-12 Mon 10:20]
_DATETIME_PASSIVE_REGEX = re.compile(
r"\[(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d{1,2}):(\d\d)\]", re.UNICODE)
# <2011-09-12 Mon>--<2011-09-13 Tue>
_DATERANGE_REGEX = re.compile(
# <2011-09-12 Mon>--
r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>--"
# <2011-09-13 Tue>
"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>", re.UNICODE)
# <2011-09-12 Mon 10:00>--<2011-09-12 Mon 11:00>
_DATETIMERANGE_REGEX = re.compile(
# <2011-09-12 Mon 10:00>--
r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)>--"
# <2011-09-12 Mon 11:00>
"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)>", re.UNICODE)
# <2011-09-12 Mon 10:00--12:00>
_DATETIMERANGE_SAME_DAY_REGEX = re.compile(
r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)-(\d\d):(\d\d)>", re.UNICODE)
def get_orgdate(data):
u"""
Parse the given data (can be a string or list). Return an OrgDate if data
contains a string representation of an OrgDate; otherwise return None.
data can be a string or a list containing strings.
"""
# TODO maybe it should be checked just for iterable? Does it affect here if
# in base __getitem__(slice(i,j)) doesn't return a list but userlist...
if isinstance(data, list):
return _findfirst(_text2orgdate, data)
else:
return _text2orgdate(data)
# if no dates found
return None
def _findfirst(f, seq):
u"""
Return first item in sequence seq where f(item) == True.
TODO: this is a general help function and it should be moved somewhere
else; preferably into the standard lib :)
"""
for found in (f(item) for item in seq if f(item)):
return found
def _text2orgdate(string):
u"""
Transform the given string into an OrgDate.
Return an OrgDate if data contains a string representation of an OrgDate;
otherwise return None.
"""
# handle active datetime with same day
result = _DATETIMERANGE_SAME_DAY_REGEX.search(string)
if result:
try:
(syear, smonth, sday, shour, smin, ehour, emin) = \
[int(m) for m in result.groups()]
start = datetime.datetime(syear, smonth, sday, shour, smin)
end = datetime.datetime(syear, smonth, sday, ehour, emin)
return OrgTimeRange(True, start, end)
except BaseException:
return None
# handle active datetime
result = _DATETIMERANGE_REGEX.search(string)
if result:
try:
tmp = [int(m) for m in result.groups()]
(syear, smonth, sday, shour, smin, eyear, emonth, eday, ehour, emin) = tmp
start = datetime.datetime(syear, smonth, sday, shour, smin)
end = datetime.datetime(eyear, emonth, eday, ehour, emin)
return OrgTimeRange(True, start, end)
except BaseException:
return None
# handle active datetime
result = _DATERANGE_REGEX.search(string)
if result:
try:
tmp = [int(m) for m in result.groups()]
syear, smonth, sday, eyear, emonth, ehour = tmp
start = datetime.date(syear, smonth, sday)
end = datetime.date(eyear, emonth, ehour)
return OrgTimeRange(True, start, end)
except BaseException:
return None
# handle active datetime
result = _DATETIME_REGEX.search(string)
if result:
try:
year, month, day, hour, minutes = [int(m) for m in result.groups()]
return OrgDateTime(True, year, month, day, hour, minutes)
except BaseException:
return None
# handle passive datetime
result = _DATETIME_PASSIVE_REGEX.search(string)
if result:
try:
year, month, day, hour, minutes = [int(m) for m in result.groups()]
return OrgDateTime(False, year, month, day, hour, minutes)
except BaseException:
return None
# handle passive dates
result = _DATE_PASSIVE_REGEX.search(string)
if result:
try:
year, month, day = [int(m) for m in result.groups()]
return OrgDate(False, year, month, day)
except BaseException:
return None
# handle active dates
result = _DATE_REGEX.search(string)
if result:
try:
year, month, day = [int(m) for m in result.groups()]
return OrgDate(True, year, month, day)
except BaseException:
return None
class OrgDate(datetime.date):
u"""
OrgDate represents a normal date like '2011-08-29 Mon'.
OrgDates can be active or inactive.
NOTE: date is immutable. Thats why there needs to be __new__().
See: http://docs.python.org/reference/datamodel.html#object.__new__
"""
def __init__(self, active, year, month, day):
self.active = active
pass
def __new__(cls, active, year, month, day):
return datetime.date.__new__(cls, year, month, day)
def __unicode__(self):
u"""
Return a string representation.
"""
if self.active:
return self.strftime(u'<%Y-%m-%d %a>')
else:
return self.strftime(u'[%Y-%m-%d %a]')
def __str__(self):
return u_encode(self.__unicode__())
def strftime(self, fmt):
return u_decode(datetime.date.strftime(self, u_encode(fmt)))
class OrgDateTime(datetime.datetime):
u"""
OrgDateTime represents a normal date like '2011-08-29 Mon'.
OrgDateTime can be active or inactive.
NOTE: date is immutable. Thats why there needs to be __new__().
See: http://docs.python.org/reference/datamodel.html#object.__new__
"""
def __init__(self, active, year, month, day, hour, mins):
self.active = active
def __new__(cls, active, year, month, day, hour, minute):
return datetime.datetime.__new__(cls, year, month, day, hour, minute)
def __unicode__(self):
u"""
Return a string representation.
"""
if self.active:
return self.strftime(u'<%Y-%m-%d %a %H:%M>')
else:
return self.strftime(u'[%Y-%m-%d %a %H:%M]')
def __str__(self):
return u_encode(self.__unicode__())
def strftime(self, fmt):
return u_decode(datetime.datetime.strftime(self, u_encode(fmt)))
class OrgTimeRange(object):
u"""
OrgTimeRange objects have a start and an end. Start and ent can be date
or datetime. Start and end have to be the same type.
OrgTimeRange objects look like this:
* <2011-09-07 Wed>--<2011-09-08 Fri>
* <2011-09-07 Wed 20:00>--<2011-09-08 Fri 10:00>
* <2011-09-07 Wed 10:00-13:00>
"""
def __init__(self, active, start, end):
u"""
stat and end must be datetime.date or datetime.datetime (both of the
same type).
"""
super(OrgTimeRange, self).__init__()
self.start = start
self.end = end
self.active = active
def __unicode__(self):
u"""
Return a string representation.
"""
# active
if self.active:
# datetime
if isinstance(self.start, datetime.datetime):
# if start and end are on same the day
if self.start.year == self.end.year and\
self.start.month == self.end.month and\
self.start.day == self.end.day:
return u"<%s-%s>" % (
self.start.strftime(u'%Y-%m-%d %a %H:%M'),
self.end.strftime(u'%H:%M'))
else:
return u"<%s>--<%s>" % (
self.start.strftime(u'%Y-%m-%d %a %H:%M'),
self.end.strftime(u'%Y-%m-%d %a %H:%M'))
# date
if isinstance(self.start, datetime.date):
return u"<%s>--<%s>" % (
self.start.strftime(u'%Y-%m-%d %a'),
self.end.strftime(u'%Y-%m-%d %a'))
# inactive
else:
if isinstance(self.start, datetime.datetime):
# if start and end are on same the day
if self.start.year == self.end.year and\
self.start.month == self.end.month and\
self.start.day == self.end.day:
return u"[%s-%s]" % (
self.start.strftime(u'%Y-%m-%d %a %H:%M'),
self.end.strftime(u'%H:%M'))
else:
return u"[%s]--[%s]" % (
self.start.strftime(u'%Y-%m-%d %a %H:%M'),
self.end.strftime(u'%Y-%m-%d %a %H:%M'))
if isinstance(self.start, datetime.date):
return u"[%s]--[%s]" % (
self.start.strftime(u'%Y-%m-%d %a'),
self.end.strftime(u'%Y-%m-%d %a'))
def __str__(self):
return u_encode(self.__unicode__())
# vim: set noexpandtab:

View file

@ -0,0 +1,173 @@
# -*- coding: utf-8 -*-
import vim
from orgmode.keybinding import Command, Plug, Keybinding
from orgmode.keybinding import MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT
from orgmode.py3compat.encode_compatibility import *
def register_menu(f):
def r(*args, **kwargs):
p = f(*args, **kwargs)
def create(entry):
if isinstance(entry, Submenu) or isinstance(entry, Separator) \
or isinstance(entry, ActionEntry):
entry.create()
if hasattr(p, u'menu'):
if isinstance(p.menu, list) or isinstance(p.menu, tuple):
for e in p.menu:
create(e)
else:
create(p.menu)
return p
return r
def add_cmd_mapping_menu(plugin, name, function, key_mapping, menu_desrc):
u"""A helper function to create a vim command and keybinding and add these
to the menu for a given plugin.
:plugin: the plugin to operate on.
:name: the name of the vim command (and the name of the Plug)
:function: the actual python function which is called when executing the
vim command.
:key_mapping: the keymapping to execute the command.
:menu_desrc: the text which appears in the menu.
"""
cmd = Command(name, function)
keybinding = Keybinding(key_mapping, Plug(name, cmd))
plugin.commands.append(cmd)
plugin.keybindings.append(keybinding)
plugin.menu + ActionEntry(menu_desrc, keybinding)
class Submenu(object):
u""" Submenu entry """
def __init__(self, name, parent=None):
object.__init__(self)
self.name = name
self.parent = parent
self._children = []
def __add__(self, entry):
if entry not in self._children:
self._children.append(entry)
entry.parent = self
return entry
def __sub__(self, entry):
if entry in self._children:
idx = self._children.index(entry)
del self._children[idx]
@property
def children(self):
return self._children[:]
def get_menu(self):
n = self.name.replace(u' ', u'\\ ')
if self.parent:
return u'%s.%s' % (self.parent.get_menu(), n)
return n
def create(self):
for c in self.children:
c.create()
def __str__(self):
res = self.name
for c in self.children:
res += str(c)
return res
class Separator(object):
u""" Menu entry for a Separator """
def __init__(self, parent=None):
object.__init__(self)
self.parent = parent
def __unicode__(self):
return u'-----'
def __str__(self):
return u_encode(self.__unicode__())
def create(self):
if self.parent:
menu = self.parent.get_menu()
vim.command(u_encode(u'menu %s.-%s- :' % (menu, id(self))))
class ActionEntry(object):
u""" ActionEntry entry """
def __init__(self, lname, action, rname=None, mode=MODE_NORMAL, parent=None):
u"""
:lname: menu title on the left hand side of the menu entry
:action: could be a vim command sequence or an actual Keybinding
:rname: menu title that appears on the right hand side of the menu
entry. If action is a Keybinding this value ignored and is
taken from the Keybinding
:mode: defines when the menu entry/action is executable
:parent: the parent instance of this object. The only valid parent is Submenu
"""
object.__init__(self)
self._lname = lname
self._action = action
self._rname = rname
if mode not in (MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT):
raise ValueError(u'Parameter mode not in MODE_ALL, MODE_NORMAL, MODE_VISUAL, MODE_INSERT')
self._mode = mode
self.parent = parent
def __str__(self):
return u'%s\t%s' % (self.lname, self.rname)
@property
def lname(self):
return self._lname.replace(u' ', u'\\ ')
@property
def action(self):
if isinstance(self._action, Keybinding):
return self._action.action
return self._action
@property
def rname(self):
if isinstance(self._action, Keybinding):
return self._action.key.replace(u'<Tab>', u'Tab')
return self._rname
@property
def mode(self):
if isinstance(self._action, Keybinding):
return self._action.mode
return self._mode
def create(self):
menucmd = u':%smenu ' % self.mode
menu = u''
cmd = u''
if self.parent:
menu = self.parent.get_menu()
menu += u'.%s' % self.lname
if self.rname:
cmd = u'%s %s<Tab>%s %s' % (menucmd, menu, self.rname, self.action)
else:
cmd = u'%s %s %s' % (menucmd, menu, self.action)
vim.command(u_encode(cmd))
# keybindings should be stored in the plugin.keybindings property and be registered by the appropriate keybinding registrar
#if isinstance(self._action, Keybinding):
# self._action.create()
# vim: set noexpandtab:

View file

@ -0,0 +1,314 @@
# -*- coding: utf-8 -*-
from datetime import date
import os
import glob
import vim
from orgmode._vim import ORGMODE, get_bufnumber, get_bufname, echoe
from orgmode import settings
from orgmode.keybinding import Keybinding, Plug, Command
from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.unicode_compatibility import *
from orgmode.py3compat.py_py3_string import *
class Agenda(object):
u"""
The Agenda Plugin uses liborgmode.agenda to display the agenda views.
The main task is to format the agenda from liborgmode.agenda.
Also all the mappings: jump from agenda to todo, etc are realized here.
"""
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'Agenda')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
# commands for this plugin
self.commands = []
@classmethod
def _switch_to(cls, bufname, vim_commands=None):
u"""
Swicht to the buffer with bufname.
A list of vim.commands (if given) gets executed as well.
TODO: this should be extracted and imporved to create an easy to use
way to create buffers/jump to buffers. Otherwise there are going to be
quite a few ways to open buffers in vimorgmode.
"""
cmds = [
u'botright split org:%s' % bufname,
u'setlocal buftype=nofile',
u'setlocal modifiable',
u'setlocal nonumber',
# call opendoc() on enter the original todo item
u'nnoremap <silent> <buffer> <CR> :exec "%s ORGMODE.plugins[u\'Agenda\'].opendoc()"<CR>' % VIM_PY_CALL,
u'nnoremap <silent> <buffer> <TAB> :exec "%s ORGMODE.plugins[u\'Agenda\'].opendoc(switch=True)"<CR>' % VIM_PY_CALL,
u'nnoremap <silent> <buffer> <S-CR> :exec "%s ORGMODE.plugins[u\'Agenda\'].opendoc(split=True)"<CR>' % VIM_PY_CALL,
# statusline
u'setlocal statusline=Org\\ %s' % bufname]
if vim_commands:
cmds.extend(vim_commands)
for cmd in cmds:
vim.command(u_encode(cmd))
@classmethod
def _get_agendadocuments(self):
u"""
Return the org documents of the agenda files; return None if no
agenda documents are defined.
TODO: maybe turn this into an decorator?
"""
# load org files of agenda
agenda_files = settings.get(u'org_agenda_files', u',')
if not agenda_files or agenda_files == ',':
echoe(
u"No org_agenda_files defined. Use :let "
u"g:org_agenda_files=['~/org/index.org'] to add "
u"files to the agenda view.")
return
return self._load_agendafiles(agenda_files)
@classmethod
def _load_agendafiles(self, agenda_files):
# glob for files in agenda_files
resolved_files = []
for f in agenda_files:
f = glob.glob(os.path.join(
os.path.expanduser(os.path.dirname(f)),
os.path.basename(f)))
resolved_files.extend(f)
agenda_files = [os.path.realpath(f) for f in resolved_files]
# load the agenda files into buffers
for agenda_file in agenda_files:
vim.command(u_encode(u'badd %s' % agenda_file.replace(" ", "\ ")))
# determine the buffer nr of the agenda files
agenda_nums = [get_bufnumber(fn) for fn in agenda_files]
# collect all documents of the agenda files and create the agenda
return [ORGMODE.get_document(i) for i in agenda_nums if i is not None]
@classmethod
def opendoc(cls, split=False, switch=False):
u"""
If you are in the agenda view jump to the document the item in the
current line belongs to. cls.line2doc is used for that.
:split: if True, open the document in a new split window.
:switch: if True, switch to another window and open the the document
there.
"""
row, _ = vim.current.window.cursor
try:
bufname, bufnr, destrow = cls.line2doc[row]
except:
return
# reload source file if it is not loaded
if get_bufname(bufnr) is None:
vim.command(u_encode(u'badd %s' % bufname))
bufnr = get_bufnumber(bufname)
tmp = cls.line2doc[row]
cls.line2doc[bufnr] = tmp
# delete old endry
del cls.line2doc[row]
if split:
vim.command(u_encode(u"sbuffer %s" % bufnr))
elif switch:
vim.command(u_encode(u"wincmd w"))
vim.command(u_encode(u"buffer %d" % bufnr))
else:
vim.command(u_encode(u"buffer %s" % bufnr))
vim.command(u_encode(u"normal! %dgg <CR>" % (destrow + 1)))
@classmethod
def list_next_week(cls):
agenda_documents = cls._get_agendadocuments()
if not agenda_documents:
return
cls.list_next_week_for(agenda_documents)
@classmethod
def list_next_week_for_buffer(cls):
agenda_documents = vim.current.buffer.name
loaded_agendafiles = cls._load_agendafiles([agenda_documents])
cls.list_next_week_for(loaded_agendafiles)
@classmethod
def list_next_week_for(cls, agenda_documents):
raw_agenda = ORGMODE.agenda_manager.get_next_week_and_active_todo(
agenda_documents)
# if raw_agenda is empty, return directly
if not raw_agenda:
vim.command('echom "All caught-up. No agenda or active todo next week."')
return
# create buffer at bottom
cmd = [u'setlocal filetype=orgagenda', ]
cls._switch_to(u'AGENDA', cmd)
# line2doc is a dic with the mapping:
# line in agenda buffer --> source document
# It's easy to jump to the right document this way
cls.line2doc = {}
# format text for agenda
last_date = raw_agenda[0].active_date
final_agenda = [u'Week Agenda:', unicode(last_date)]
for i, h in enumerate(raw_agenda):
# insert date information for every new date (not datetime)
if unicode(h.active_date)[1:11] != unicode(last_date)[1:11]:
today = date.today()
# insert additional "TODAY" string
if h.active_date.year == today.year and \
h.active_date.month == today.month and \
h.active_date.day == today.day:
section = unicode(h.active_date) + u" TODAY"
today_row = len(final_agenda) + 1
else:
section = unicode(h.active_date)
final_agenda.append(section)
# update last_date
last_date = h.active_date
bufname = os.path.basename(vim.buffers[h.document.bufnr].name)
bufname = bufname[:-4] if bufname.endswith(u'.org') else bufname
formated = u" %(bufname)s (%(bufnr)d) %(todo)s %(title)s" % {
'bufname': bufname,
'bufnr': h.document.bufnr,
'todo': h.todo,
'title': h.title
}
final_agenda.append(formated)
cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
# show agenda
vim.current.buffer[:] = [u_encode(i) for i in final_agenda]
vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'))
# try to jump to the positon of today
try:
vim.command(u_encode(u'normal! %sgg<CR>' % today_row))
except:
pass
@classmethod
def list_all_todos(cls, current_buffer=False):
u""" List all todos in one buffer.
Args:
current_buffer (bool):
False: all agenda files
True: current org_file
"""
if current_buffer:
agenda_documents = vim.current.buffer.name
loaded_agendafiles = cls._load_agendafiles([agenda_documents])
else:
loaded_agendafiles = cls._get_agendadocuments()
if not loaded_agendafiles:
return
raw_agenda = ORGMODE.agenda_manager.get_todo(loaded_agendafiles)
cls.line2doc = {}
# create buffer at bottom
cmd = [u'setlocal filetype=orgagenda']
cls._switch_to(u'AGENDA', cmd)
# format text of agenda
final_agenda = []
for i, h in enumerate(raw_agenda):
tmp = u"%s %s" % (h.todo, h.title)
final_agenda.append(tmp)
cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
# show agenda
vim.current.buffer[:] = [u_encode(i) for i in final_agenda]
vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'))
@classmethod
def list_timeline(cls):
"""
List a timeline of the current buffer to get an overview of the
current file.
"""
raw_agenda = ORGMODE.agenda_manager.get_timestamped_items(
[ORGMODE.get_document()])
# create buffer at bottom
cmd = [u'setlocal filetype=orgagenda']
cls._switch_to(u'AGENDA', cmd)
cls.line2doc = {}
# format text of agenda
final_agenda = []
for i, h in enumerate(raw_agenda):
tmp = u"%s %s" % (h.todo, h.title)
final_agenda.append(tmp)
cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
# show agenda
vim.current.buffer[:] = [u_encode(i) for i in final_agenda]
vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'))
def register(self):
u"""
Registration of the plugin.
Key bindings and other initialization should be done here.
"""
add_cmd_mapping_menu(
self,
name=u"OrgAgendaTodo",
function=u'%s ORGMODE.plugins[u"Agenda"].list_all_todos()' % VIM_PY_CALL,
key_mapping=u'<localleader>cat',
menu_desrc=u'Agenda for all TODOs'
)
add_cmd_mapping_menu(
self,
name=u"OrgBufferAgendaTodo",
function=u'%s ORGMODE.plugins[u"Agenda"].list_all_todos(current_buffer=True)' % VIM_PY_CALL,
key_mapping=u'<localleader>caT',
menu_desrc=u'Agenda for all TODOs based on current buffer'
)
add_cmd_mapping_menu(
self,
name=u"OrgAgendaWeek",
function=u'%s ORGMODE.plugins[u"Agenda"].list_next_week()' % VIM_PY_CALL,
key_mapping=u'<localleader>caa',
menu_desrc=u'Agenda for the week'
)
add_cmd_mapping_menu(
self,
name=u"OrgBufferAgendaWeek",
function=u'%s ORGMODE.plugins[u"Agenda"].list_next_week_for_buffer()' % VIM_PY_CALL,
key_mapping=u'<localleader>caA',
menu_desrc=u'Agenda for the week based on current buffer'
)
add_cmd_mapping_menu(
self,
name=u'OrgAgendaTimeline',
function=u'%s ORGMODE.plugins[u"Agenda"].list_timeline()' % VIM_PY_CALL,
key_mapping=u'<localleader>caL',
menu_desrc=u'Timeline for this buffer'
)
# vim: set noexpandtab:

View file

@ -0,0 +1,318 @@
# -*- coding: utf-8 -*-
import re
from datetime import timedelta, date, datetime
import operator
import vim
from orgmode._vim import ORGMODE, echom, insert_at_cursor, get_user_input
from orgmode import settings
from orgmode.keybinding import Keybinding, Plug
from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.unicode_compatibility import *
from orgmode.py3compat.py_py3_string import *
class Date(object):
u"""
Handles all date and timestamp related tasks.
TODO: extend functionality (calendar, repetitions, ranges). See
http://orgmode.org/guide/Dates-and-Times.html#Dates-and-Times
"""
date_regex = r"\d\d\d\d-\d\d-\d\d"
datetime_regex = r"[A-Z]\w\w \d\d\d\d-\d\d-\d\d \d\d:\d\d>"
month_mapping = {
u'jan': 1, u'feb': 2, u'mar': 3, u'apr': 4, u'may': 5,
u'jun': 6, u'jul': 7, u'aug': 8, u'sep': 9, u'oct': 10, u'nov': 11,
u'dec': 12}
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'Dates and Scheduling')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
# commands for this plugin
self.commands = []
# set speeddating format that is compatible with orgmode
try:
if int(vim.eval(u_encode(u'exists(":SpeedDatingFormat")'))) == 2:
vim.command(u_encode(u':1SpeedDatingFormat %Y-%m-%d %a'))
vim.command(u_encode(u':1SpeedDatingFormat %Y-%m-%d %a %H:%M'))
else:
echom(u'Speeddating plugin not installed. Please install it.')
except:
echom(u'Speeddating plugin not installed. Please install it.')
@classmethod
def _modify_time(cls, startdate, modifier):
u"""Modify the given startdate according to modifier. Return the new
date or datetime.
See http://orgmode.org/manual/The-date_002ftime-prompt.html
"""
if modifier is None or modifier == '' or modifier == '.':
return startdate
# rm crap from modifier
modifier = modifier.strip()
ops = {'-': operator.sub, '+': operator.add}
# check real date
date_regex = r"(\d\d\d\d)-(\d\d)-(\d\d)"
match = re.search(date_regex, modifier)
if match:
year, month, day = match.groups()
newdate = date(int(year), int(month), int(day))
# check abbreviated date, seperated with '-'
date_regex = u"(\d{1,2})-(\d+)-(\d+)"
match = re.search(date_regex, modifier)
if match:
year, month, day = match.groups()
newdate = date(2000 + int(year), int(month), int(day))
# check abbreviated date, seperated with '/'
# month/day
date_regex = u"(\d{1,2})/(\d{1,2})"
match = re.search(date_regex, modifier)
if match:
month, day = match.groups()
newdate = date(startdate.year, int(month), int(day))
# date should be always in the future
if newdate < startdate:
newdate = date(startdate.year + 1, int(month), int(day))
# check full date, seperated with 'space'
# month day year
# 'sep 12 9' --> 2009 9 12
date_regex = u"(\w\w\w) (\d{1,2}) (\d{1,2})"
match = re.search(date_regex, modifier)
if match:
gr = match.groups()
day = int(gr[1])
month = int(cls.month_mapping[gr[0]])
year = 2000 + int(gr[2])
newdate = date(year, int(month), int(day))
# check days as integers
date_regex = u"^(\d{1,2})$"
match = re.search(date_regex, modifier)
if match:
newday, = match.groups()
newday = int(newday)
if newday > startdate.day:
newdate = date(startdate.year, startdate.month, newday)
else:
# TODO: DIRTY, fix this
# this does NOT cover all edge cases
newdate = startdate + timedelta(days=28)
newdate = date(newdate.year, newdate.month, newday)
# check for full days: Mon, Tue, Wed, Thu, Fri, Sat, Sun
modifier_lc = modifier.lower()
match = re.search(u'mon|tue|wed|thu|fri|sat|sun', modifier_lc)
if match:
weekday_mapping = {
u'mon': 0, u'tue': 1, u'wed': 2, u'thu': 3,
u'fri': 4, u'sat': 5, u'sun': 6}
diff = (weekday_mapping[modifier_lc] - startdate.weekday()) % 7
# use next weeks weekday if current weekday is the same as modifier
if diff == 0:
diff = 7
newdate = startdate + timedelta(days=diff)
# check for days modifier with appended d
match = re.search(u'^(\+|-)(\d*)d', modifier)
if match:
op, days = match.groups()
newdate = ops[op](startdate, timedelta(days=int(days)))
# check for days modifier without appended d
match = re.search(u'^(\+|-)(\d*) |^(\+|-)(\d*)$', modifier)
if match:
groups = match.groups()
try:
op = groups[0]
days = int(groups[1])
except:
op = groups[2]
days = int(groups[3])
newdate = ops[op](startdate, timedelta(days=days))
# check for week modifier
match = re.search(u'^(\+|-)(\d+)w', modifier)
if match:
op, weeks = match.groups()
newdate = ops[op](startdate, timedelta(weeks=int(weeks)))
# check for month modifier
match = re.search(u'^(\+|-)(\d+)m', modifier)
if match:
op, months = match.groups()
newdate = date(startdate.year, ops[op](startdate.month, int(months)),
startdate.day)
# check for year modifier
match = re.search(u'^(\+|-)(\d*)y', modifier)
if match:
op, years = match.groups()
newdate = date(ops[op](startdate.year, int(years)), startdate.month,
startdate.day)
# check for month day
match = re.search(
u'(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) (\d{1,2})',
modifier.lower())
if match:
month = cls.month_mapping[match.groups()[0]]
day = int(match.groups()[1])
newdate = date(startdate.year, int(month), int(day))
# date should be always in the future
if newdate < startdate:
newdate = date(startdate.year + 1, int(month), int(day))
# check abbreviated date, seperated with '/'
# month/day/year
date_regex = u"(\d{1,2})/(\d+)/(\d+)"
match = re.search(date_regex, modifier)
if match:
month, day, year = match.groups()
newdate = date(2000 + int(year), int(month), int(day))
# check for month day year
# sep 12 2011
match = re.search(
u'(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) (\d{1,2}) (\d{1,4})',
modifier.lower())
if match:
month = int(cls.month_mapping[match.groups()[0]])
day = int(match.groups()[1])
if len(match.groups()[2]) < 4:
year = 2000 + int(match.groups()[2])
else:
year = int(match.groups()[2])
newdate = date(year, month, day)
# check for time: HH:MM
# '12:45' --> datetime(2006, 06, 13, 12, 45))
match = re.search(u'(\d{1,2}):(\d\d)$', modifier)
if match:
try:
startdate = newdate
except:
pass
return datetime(
startdate.year, startdate.month, startdate.day,
int(match.groups()[0]), int(match.groups()[1]))
try:
return newdate
except:
return startdate
@classmethod
def insert_timestamp(cls, active=True):
u"""
Insert a timestamp at the cursor position.
TODO: show fancy calendar to pick the date from.
TODO: add all modifier of orgmode.
"""
today = date.today()
msg = u''.join([
u'Inserting ',
unicode(u_decode(today.strftime(u'%Y-%m-%d %a'))),
u' | Modify date'])
modifier = get_user_input(msg)
# abort if the user canceled the input promt
if modifier is None:
return
newdate = cls._modify_time(today, modifier)
# format
if isinstance(newdate, datetime):
newdate = newdate.strftime(
u_decode(u_encode(u'%Y-%m-%d %a %H:%M')))
else:
newdate = newdate.strftime(
u_decode(u_encode(u'%Y-%m-%d %a')))
timestamp = u'<%s>' % newdate if active else u'[%s]' % newdate
insert_at_cursor(timestamp)
@classmethod
def insert_timestamp_with_calendar(cls, active=True):
u"""
Insert a timestamp at the cursor position.
Show fancy calendar to pick the date from.
TODO: add all modifier of orgmode.
"""
if int(vim.eval(u_encode(u'exists(":CalendarH")'))) != 2:
vim.command("echo 'Please install plugin Calendar to enable this function'")
return
vim.command("CalendarH")
# backup calendar_action
calendar_action = vim.eval("g:calendar_action")
vim.command("let g:org_calendar_action_backup = '" + calendar_action + "'")
vim.command("let g:calendar_action = 'CalendarAction'")
timestamp_template = u'<%s>' if active else u'[%s]'
# timestamp template
vim.command("let g:org_timestamp_template = '" + timestamp_template + "'")
def register(self):
u"""
Registration of the plugin.
Key bindings and other initialization should be done here.
"""
add_cmd_mapping_menu(
self,
name=u'OrgDateInsertTimestampActiveCmdLine',
key_mapping=u'<localleader>sa',
function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp()' % VIM_PY_CALL,
menu_desrc=u'Timest&amp'
)
add_cmd_mapping_menu(
self,
name=u'OrgDateInsertTimestampInactiveCmdLine',
key_mapping='<localleader>si',
function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp(False)' % VIM_PY_CALL,
menu_desrc=u'Timestamp (&inactive)'
)
add_cmd_mapping_menu(
self,
name=u'OrgDateInsertTimestampActiveWithCalendar',
key_mapping=u'<localleader>pa',
function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp_with_calendar()' % VIM_PY_CALL,
menu_desrc=u'Timestamp with Calendar'
)
add_cmd_mapping_menu(
self,
name=u'OrgDateInsertTimestampInactiveWithCalendar',
key_mapping=u'<localleader>pi',
function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp_with_calendar(False)' % VIM_PY_CALL,
menu_desrc=u'Timestamp with Calendar(inactive)'
)
submenu = self.menu + Submenu(u'Change &Date')
submenu + ActionEntry(u'Day &Earlier', u'<C-x>', u'<C-x>')
submenu + ActionEntry(u'Day &Later', u'<C-a>', u'<C-a>')
# vim: set noexpandtab:

View file

@ -0,0 +1,330 @@
# -*- coding: utf-8 -*-
import vim
from orgmode._vim import echo, echom, echoe, ORGMODE, apply_count, repeat, insert_at_cursor, indent_orgmode
from orgmode import settings
from orgmode.menu import Submenu, Separator, ActionEntry, add_cmd_mapping_menu
from orgmode.keybinding import Keybinding, Plug, Command
from orgmode.liborgmode.checkboxes import Checkbox
from orgmode.liborgmode.dom_obj import OrderListType
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.py_py3_string import *
from orgmode.py3compat.unicode_compatibility import *
class EditCheckbox(object):
u"""
Checkbox plugin.
"""
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'Edit Checkbox')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
# commands for this plugin
self.commands = []
@classmethod
def new_checkbox(cls, below=None, plain=None):
'''
if below is:
True -> create new list below current line
False/None -> create new list above current line
if plain is:
True -> create a plainlist item
False/None -> create an empty checkbox
'''
d = ORGMODE.get_document()
h = d.current_heading()
if h is None:
return
# init checkboxes for current heading
h.init_checkboxes()
c = h.current_checkbox()
nc = Checkbox()
nc._heading = h
# default checkbox level
level = h.level + 1
start = vim.current.window.cursor[0] - 1
# if no checkbox is found, insert at current line with indent level=1
if c is None:
h.checkboxes.append(nc)
else:
l = c.get_parent_list()
idx = c.get_index_in_parent_list()
if l is not None and idx is not None:
l.insert(idx + (1 if below else 0), nc)
# workaround for broken associations, Issue #165
nc._parent = c.parent
if below:
if c.next_sibling:
c.next_sibling._previous_sibling = nc
nc._next_sibling = c.next_sibling
c._next_sibling = nc
nc._previous_sibling = c
else:
if c.previous_sibling:
c.previous_sibling._next_sibling = nc
nc._next_sibling = c
nc._previous_sibling = c.previous_sibling
c._previous_sibling = nc
t = c.type
# increase key for ordered lists
if t[-1] in OrderListType:
try:
num = int(t[:-1]) + (1 if below else -1)
if num < 0:
# don't decrease to numbers below zero
echom(u"Can't decrement further than '0'")
return
t = '%d%s' % (num, t[-1])
except ValueError:
try:
char = ord(t[:-1]) + (1 if below else -1)
if below:
if char == 91:
# stop incrementing at Z (90)
echom(u"Can't increment further than 'Z'")
return
elif char == 123:
# increment from z (122) to A
char = 65
else:
if char == 96:
# stop decrementing at a (97)
echom(u"Can't decrement further than 'a'")
return
elif char == 64:
# decrement from A (65) to z
char = 122
t = u'%s%s' % (chr(char), t[-1])
except ValueError:
pass
nc.type = t
level = c.level
if below:
start = c.end_of_last_child
else:
start = c.start
if plain: # only create plainlist item when requested
nc.status = None
nc.level = level
if below:
start += 1
# vim's buffer behave just opposite to Python's list when inserting a
# new item. The new entry is appended in vim put prepended in Python!
vim.current.buffer.append("") # workaround for neovim
vim.current.buffer[start:start] = [unicode(nc)]
del vim.current.buffer[-1] # restore from workaround for neovim
# update checkboxes status
cls.update_checkboxes_status()
# do not start insert upon adding new checkbox, Issue #211
if int(settings.get(u'org_prefer_insert_mode', u'1')):
vim.command(u_encode(u'exe "normal %dgg"|startinsert!' % (start + 1, )))
else:
vim.command(u_encode(u'exe "normal %dgg$"' % (start + 1, )))
@classmethod
def toggle(cls, checkbox=None):
u"""
Toggle the checkbox given in the parameter.
If the checkbox is not given, it will toggle the current checkbox.
"""
d = ORGMODE.get_document()
current_heading = d.current_heading()
# init checkboxes for current heading
if current_heading is None:
return
current_heading = current_heading.init_checkboxes()
if checkbox is None:
# get current_checkbox
c = current_heading.current_checkbox()
# no checkbox found
if c is None:
cls.update_checkboxes_status()
return
else:
c = checkbox
if c.status == Checkbox.STATUS_OFF or c.status is None:
# set checkbox status on if all children are on
if c.all_children_status()[0] == 0 or c.are_children_all(Checkbox.STATUS_ON):
c.toggle()
d.write_checkbox(c)
elif c.status is None:
c.status = Checkbox.STATUS_OFF
d.write_checkbox(c)
elif c.status == Checkbox.STATUS_ON:
if c.all_children_status()[0] == 0 or c.is_child_one(Checkbox.STATUS_OFF):
c.toggle()
d.write_checkbox(c)
elif c.status == Checkbox.STATUS_INT:
# can't toggle intermediate state directly according to emacs orgmode
pass
# update checkboxes status
cls.update_checkboxes_status()
@classmethod
def _update_subtasks(cls):
d = ORGMODE.get_document()
h = d.current_heading()
# init checkboxes for current heading
h.init_checkboxes()
# update heading subtask info
c = h.first_checkbox
if c is None:
return
total, on = c.all_siblings_status()
h.update_subtasks(total, on)
# update all checkboxes under current heading
cls._update_checkboxes_subtasks(c)
@classmethod
def _update_checkboxes_subtasks(cls, checkbox):
# update checkboxes
for c in checkbox.all_siblings():
if c.children:
total, on = c.first_child.all_siblings_status()
c.update_subtasks(total, on)
cls._update_checkboxes_subtasks(c.first_child)
@classmethod
def update_checkboxes_status(cls):
d = ORGMODE.get_document()
h = d.current_heading()
if h is None:
return
# init checkboxes for current heading
h.init_checkboxes()
cls._update_checkboxes_status(h.first_checkbox)
cls._update_subtasks()
@classmethod
def _update_checkboxes_status(cls, checkbox=None):
u""" helper function for update checkboxes status
:checkbox: The first checkbox of this indent level
:return: The status of the parent checkbox
"""
if checkbox is None:
return
status_off, status_on, status_int, total = 0, 0, 0, 0
# update all top level checkboxes' status
for c in checkbox.all_siblings():
current_status = c.status
# if this checkbox is not leaf, its status should determine by all its children
if c.all_children_status()[0] > 0:
current_status = cls._update_checkboxes_status(c.first_child)
# don't update status if the checkbox has no status
if c.status is None:
current_status = None
# the checkbox needs to have status
else:
total += 1
# count number of status in this checkbox level
if current_status == Checkbox.STATUS_OFF:
status_off += 1
elif current_status == Checkbox.STATUS_ON:
status_on += 1
elif current_status == Checkbox.STATUS_INT:
status_int += 1
# write status if any update
if current_status is not None and c.status != current_status:
c.status = current_status
d = ORGMODE.get_document()
d.write_checkbox(c)
parent_status = Checkbox.STATUS_INT
# all silbing checkboxes are off status
if total == 0:
pass
elif status_off == total:
parent_status = Checkbox.STATUS_OFF
# all silbing checkboxes are on status
elif status_on == total:
parent_status = Checkbox.STATUS_ON
# one silbing checkbox is on or int status
elif status_on != 0 or status_int != 0:
parent_status = Checkbox.STATUS_INT
# other cases
else:
parent_status = None
return parent_status
def register(self):
u"""
Registration of the plugin.
Key bindings and other initialization should be done here.
"""
# default setting if it is not already set.
# checkbox related operation
add_cmd_mapping_menu(
self,
name=u'OrgCheckBoxNewAbove',
function=u'%s ORGMODE.plugins[u"EditCheckbox"].new_checkbox()<CR>' % VIM_PY_CALL,
key_mapping=u'<localleader>cN',
menu_desrc=u'New CheckBox Above'
)
add_cmd_mapping_menu(
self,
name=u'OrgCheckBoxNewBelow',
function=u'%s ORGMODE.plugins[u"EditCheckbox"].new_checkbox(below=True)<CR>' % VIM_PY_CALL,
key_mapping=u'<localleader>cn',
menu_desrc=u'New CheckBox Below'
)
add_cmd_mapping_menu(
self,
name=u'OrgCheckBoxToggle',
function=u':silent! %s ORGMODE.plugins[u"EditCheckbox"].toggle()<CR>' % VIM_PY_CALL,
key_mapping=u'<localleader>cc',
menu_desrc=u'Toggle Checkbox'
)
add_cmd_mapping_menu(
self,
name=u'OrgCheckBoxUpdate',
function=u':silent! %s ORGMODE.plugins[u"EditCheckbox"].update_checkboxes_status()<CR>' % VIM_PY_CALL,
key_mapping=u'<localleader>c#',
menu_desrc=u'Update Subtasks'
)
# plainlist related operation
add_cmd_mapping_menu(
self,
name=u'OrgPlainListItemNewAbove',
function=u'%s ORGMODE.plugins[u"EditCheckbox"].new_checkbox(plain=True)<CR>' % VIM_PY_CALL,
key_mapping=u'<localleader>cL',
menu_desrc=u'New PlainList Item Above'
)
add_cmd_mapping_menu(
self,
name=u'OrgPlainListItemNewBelow',
function=u'%s ORGMODE.plugins[u"EditCheckbox"].new_checkbox(below=True, plain=True)<CR>' % VIM_PY_CALL,
key_mapping=u'<localleader>cl',
menu_desrc=u'New PlainList Item Below'
)
# vim: set noexpandtab:

View file

@ -0,0 +1,430 @@
# -*- coding: utf-8 -*-
import vim
from orgmode._vim import ORGMODE, apply_count, repeat, realign_tags
from orgmode import settings
from orgmode.exceptions import HeadingDomError
from orgmode.keybinding import Keybinding, Plug, MODE_INSERT, MODE_NORMAL
from orgmode.menu import Submenu, Separator, ActionEntry
from orgmode.liborgmode.base import Direction
from orgmode.liborgmode.headings import Heading
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.py_py3_string import *
class EditStructure(object):
u""" EditStructure plugin """
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'&Edit Structure')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
@classmethod
def new_heading(cls, below=None, insert_mode=False, end_of_last_child=False):
u"""
:below: True, insert heading below current heading, False,
insert heading above current heading, None, special
behavior for insert mode, use the current text as
heading
:insert_mode: True, if action is performed in insert mode
:end_of_last_child: True, insert heading at the end of last child,
otherwise the newly created heading will "take
over" the current heading's children
"""
d = ORGMODE.get_document()
current_heading = d.current_heading()
cursor = vim.current.window.cursor[:]
if not current_heading:
# the user is in meta data region
pos = cursor[0] - 1
heading = Heading(title=d.meta_information[pos], body=d.meta_information[pos + 1:])
d.headings.insert(0, heading)
del d.meta_information[pos:]
d.write()
vim.command(u_encode(u'exe "normal %dgg"|startinsert!' % (heading.start_vim, )))
return heading
# check for plain list(checkbox)
current_heading.init_checkboxes()
c = current_heading.current_checkbox()
if c is not None:
ORGMODE.plugins[u"EditCheckbox"].new_checkbox(below, not c.status)
return
heading = Heading(level=current_heading.level)
# it's weird but this is the behavior of original orgmode
if below is None:
below = cursor[1] != 0 or end_of_last_child
# insert newly created heading
l = current_heading.get_parent_list()
idx = current_heading.get_index_in_parent_list()
if l is not None and idx is not None:
l.insert(idx + (1 if below else 0), heading)
else:
raise HeadingDomError(u'Current heading is not properly linked in DOM')
if below and not end_of_last_child:
# append heading at the end of current heading and also take
# over the children of current heading
for child in current_heading.children:
heading.children.append(child, taint=False)
current_heading.children.remove_slice(
0, len(current_heading.children),
taint=False)
# if cursor is currently on a heading, insert parts of it into the
# newly created heading
if insert_mode and cursor[1] != 0 and cursor[0] == current_heading.start_vim:
offset = cursor[1] - current_heading.level - 1 - (
len(current_heading.todo) + 1 if current_heading.todo else 0)
if offset < 0:
offset = 0
if int(settings.get(u'org_improve_split_heading', u'1')) and \
offset > 0 and len(current_heading.title) == offset + 1 \
and current_heading.title[offset - 1] not in (u' ', u'\t'):
offset += 1
heading.title = current_heading.title[offset:]
current_heading.title = current_heading.title[:offset]
heading.body = current_heading.body[:]
current_heading.body = []
d.write()
# do not start insert upon adding new headings, unless already in insert mode. Issue #211
if int(settings.get(u'org_prefer_insert_mode', u'1')) or insert_mode:
vim.command(u_encode(u'exe "normal %dgg"|startinsert!' % (heading.start_vim, )))
else:
vim.command(u_encode(u'exe "normal %dgg$"' % (heading.start_vim, )))
# return newly created heading
return heading
@classmethod
def _append_heading(cls, heading, parent):
if heading.level <= parent.level:
raise ValueError('Heading level not is lower than parent level: %d ! > %d' % (heading.level, parent.level))
if parent.children and parent.children[-1].level < heading.level:
cls._append_heading(heading, parent.children[-1])
else:
parent.children.append(heading, taint=False)
@classmethod
def _change_heading_level(cls, level, including_children=True, on_heading=False, insert_mode=False):
u"""
Change level of heading realtively with or without including children.
:level: the number of levels to promote/demote heading
:including_children: True if should should be included in promoting/demoting
:on_heading: True if promoting/demoting should only happen when the cursor is on the heading
:insert_mode: True if vim is in insert mode
"""
# TODO : current promote and demote works for only headings. Since
# checkboxes also have tree structure. We should think of
# expanding the functionality of promoting and demoting to
# checkboxes as well
d = ORGMODE.get_document()
current_heading = d.current_heading()
if not current_heading or on_heading and current_heading.start_vim != vim.current.window.cursor[0]:
# TODO figure out the actually pressed keybinding and feed these
# keys instead of making keys up like this
if level > 0:
if insert_mode:
vim.eval(u_encode(u'feedkeys("\<C-t>", "n")'))
elif including_children:
vim.eval(u_encode(u'feedkeys(">]]", "n")'))
elif on_heading:
vim.eval(u_encode(u'feedkeys(">>", "n")'))
else:
vim.eval(u_encode(u'feedkeys(">}", "n")'))
else:
if insert_mode:
vim.eval(u_encode(u'feedkeys("\<C-d>", "n")'))
elif including_children:
vim.eval(u_encode(u'feedkeys("<]]", "n")'))
elif on_heading:
vim.eval(u_encode(u'feedkeys("<<", "n")'))
else:
vim.eval(u_encode(u'feedkeys("<}", "n")'))
# return True because otherwise apply_count will not work
return True
# don't allow demotion below level 1
if current_heading.level == 1 and level < 1:
return False
# reduce level of demotion to a minimum heading level of 1
if (current_heading.level + level) < 1:
level = 1
def indent(heading, ic):
if not heading:
return
heading.level += level
if ic:
for child in heading.children:
indent(child, ic)
# save cursor position
c = vim.current.window.cursor[:]
# indent the promoted/demoted heading
indent_end_vim = current_heading.end_of_last_child_vim if including_children else current_heading.end_vim
indent(current_heading, including_children)
# when changing the level of a heading, its position in the DOM
# needs to be updated. It's likely that the heading gets a new
# parent and new children when demoted or promoted
# find new parent
p = current_heading.parent
pl = current_heading.get_parent_list()
ps = current_heading.previous_sibling
nhl = current_heading.level
if level > 0:
# demotion
# subheading or top level heading
if ps and nhl > ps.level:
pl.remove(current_heading, taint=False)
# find heading that is the new parent heading
oh = ps
h = ps
while nhl > h.level:
oh = h
if h.children:
h = h.children[-1]
else:
break
np = h if nhl > h.level else oh
# append current heading to new heading
np.children.append(current_heading, taint=False)
# if children are not included, distribute them among the
# parent heading and it's siblings
if not including_children:
for h in current_heading.children[:]:
if h and h.level <= nhl:
cls._append_heading(h, np)
current_heading.children.remove(h, taint=False)
else:
# promotion
if p and nhl <= p.level:
idx = current_heading.get_index_in_parent_list() + 1
# find the new parent heading
oh = p
h = p
while nhl <= h.level:
# append new children to current heading
for child in h.children[idx:]:
cls._append_heading(child, current_heading)
h.children.remove_slice(idx, len(h.children), taint=False)
idx = h.get_index_in_parent_list() + 1
if h.parent:
h = h.parent
else:
break
ns = oh.next_sibling
while ns and ns.level > current_heading.level:
nns = ns.next_sibling
cls._append_heading(ns, current_heading)
ns = nns
# append current heading to new parent heading / document
pl.remove(current_heading, taint=False)
if nhl > h.level:
h.children.insert(idx, current_heading, taint=False)
else:
d.headings.insert(idx, current_heading, taint=False)
d.write()
# restore cursor position
vim.current.window.cursor = (c[0], c[1] + level)
return True
@classmethod
@realign_tags
@repeat
@apply_count
def demote_heading(cls, including_children=True, on_heading=False, insert_mode=False):
if cls._change_heading_level(1, including_children=including_children, on_heading=on_heading, insert_mode=insert_mode):
if including_children:
return u'OrgDemoteSubtree'
return u'OrgDemoteHeading'
@classmethod
@realign_tags
@repeat
@apply_count
def promote_heading(cls, including_children=True, on_heading=False, insert_mode=False):
if cls._change_heading_level(-1, including_children=including_children, on_heading=on_heading, insert_mode=insert_mode):
if including_children:
return u'OrgPromoteSubtreeNormal'
return u'OrgPromoteHeadingNormal'
@classmethod
def _move_heading(cls, direction=Direction.FORWARD, including_children=True):
u""" Move heading up or down
:returns: heading or None
"""
d = ORGMODE.get_document()
current_heading = d.current_heading()
if not current_heading or \
(direction == Direction.FORWARD and not current_heading.next_sibling) or \
(direction == Direction.BACKWARD and not current_heading.previous_sibling):
return None
cursor_offset = vim.current.window.cursor[0] - (current_heading._orig_start + 1)
l = current_heading.get_parent_list()
if l is None:
raise HeadingDomError(u'Current heading is not properly linked in DOM')
if not including_children:
if current_heading.previous_sibling:
npl = current_heading.previous_sibling.children
for child in current_heading.children:
npl.append(child, taint=False)
elif current_heading.parent:
# if the current heading doesn't have a previous sibling it
# must be the first heading
np = current_heading.parent
for child in current_heading.children:
cls._append_heading(child, np)
else:
# if the current heading doesn't have a parent, its children
# must be added as top level headings to the document
npl = l
for child in current_heading.children[::-1]:
npl.insert(0, child, taint=False)
current_heading.children.remove_slice(0, len(current_heading.children), taint=False)
idx = current_heading.get_index_in_parent_list()
if idx is None:
raise HeadingDomError(u'Current heading is not properly linked in DOM')
offset = 1 if direction == Direction.FORWARD else -1
del l[idx]
l.insert(idx + offset, current_heading)
d.write()
vim.current.window.cursor = (
current_heading.start_vim + cursor_offset,
vim.current.window.cursor[1])
return True
@classmethod
@repeat
@apply_count
def move_heading_upward(cls, including_children=True):
if cls._move_heading(direction=Direction.BACKWARD, including_children=including_children):
if including_children:
return u'OrgMoveSubtreeUpward'
return u'OrgMoveHeadingUpward'
@classmethod
@repeat
@apply_count
def move_heading_downward(cls, including_children=True):
if cls._move_heading(direction=Direction.FORWARD, including_children=including_children):
if including_children:
return u'OrgMoveSubtreeDownward'
return u'OrgMoveHeadingDownward'
def register(self):
u"""
Registration of plugin. Key bindings and other initialization should be done.
"""
# EditStructure related default settings
settings.set(u'org_improve_split_heading', u'1')
# EditStructure related keybindings
self.keybindings.append(Keybinding(u'<C-S-CR>',
Plug(u'OrgNewHeadingAboveNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=False)<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'New Heading &above', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<localleader>hN', u'<Plug>OrgNewHeadingAboveNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'<localleader><CR>', u'<Plug>OrgNewHeadingAboveNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'<S-CR>',
Plug(u'OrgNewHeadingBelowNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=True)<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'New Heading &below', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<localleader>hh', u'<Plug>OrgNewHeadingBelowNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'<leader><CR>', u'<Plug>OrgNewHeadingBelowNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'<C-CR>', Plug(u'OrgNewHeadingBelowAfterChildrenNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=True, end_of_last_child=True)<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'New Heading below, after &children', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<localleader>hn', u'<Plug>OrgNewHeadingBelowAfterChildrenNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'<CR>', u'<Plug>OrgNewHeadingBelowAfterChildrenNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'<C-S-CR>', Plug(u'OrgNewHeadingAboveInsert', u'<C-o>:<C-u>silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=False, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT)))
self.keybindings.append(Keybinding(u'<S-CR>', Plug(u'OrgNewHeadingBelowInsert', u'<C-o>:<C-u>silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=True, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT)))
self.keybindings.append(Keybinding(u'<C-CR>', Plug(u'OrgNewHeadingBelowAfterChildrenInsert', u'<C-o>:<C-u>silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(insert_mode=True, end_of_last_child=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT)))
self.menu + Separator()
self.keybindings.append(Keybinding(u'm{', Plug(u'OrgMoveHeadingUpward',
u'%s ORGMODE.plugins[u"EditStructure"].move_heading_upward(including_children=False)<CR>' % VIM_PY_CALL)))
self.keybindings.append(Keybinding(u'm[[',
Plug(u'OrgMoveSubtreeUpward', u'%s ORGMODE.plugins[u"EditStructure"].move_heading_upward()<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'Move Subtree &Up', self.keybindings[-1])
self.keybindings.append(Keybinding(u'm}',
Plug(u'OrgMoveHeadingDownward', u'%s ORGMODE.plugins[u"EditStructure"].move_heading_downward(including_children=False)<CR>' % VIM_PY_CALL)))
self.keybindings.append(Keybinding(u'm]]',
Plug(u'OrgMoveSubtreeDownward', u'%s ORGMODE.plugins[u"EditStructure"].move_heading_downward()<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'Move Subtree &Down', self.keybindings[-1])
self.menu + Separator()
self.menu + ActionEntry(u'&Copy Heading', u'yah', u'yah')
self.menu + ActionEntry(u'C&ut Heading', u'dah', u'dah')
self.menu + Separator()
self.menu + ActionEntry(u'&Copy Subtree', u'yar', u'yar')
self.menu + ActionEntry(u'C&ut Subtree', u'dar', u'dar')
self.menu + ActionEntry(u'&Paste Subtree', u'p', u'p')
self.menu + Separator()
self.keybindings.append(Keybinding(u'<ah', Plug(u'OrgPromoteHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False)<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'&Promote Heading', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<<', Plug(u'OrgPromoteOnHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False, on_heading=True)<CR>' % VIM_PY_CALL)))
self.keybindings.append(Keybinding(u'<{', u'<Plug>OrgPromoteHeadingNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'<ih', u'<Plug>OrgPromoteHeadingNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'<ar', Plug(u'OrgPromoteSubtreeNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading()<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'&Promote Subtree', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<[[', u'<Plug>OrgPromoteSubtreeNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'<ir', u'<Plug>OrgPromoteSubtreeNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'>ah', Plug(u'OrgDemoteHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False)<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'&Demote Heading', self.keybindings[-1])
self.keybindings.append(Keybinding(u'>>', Plug(u'OrgDemoteOnHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False, on_heading=True)<CR>' % VIM_PY_CALL)))
self.keybindings.append(Keybinding(u'>}', u'<Plug>OrgDemoteHeadingNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'>ih', u'<Plug>OrgDemoteHeadingNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'>ar', Plug(u'OrgDemoteSubtreeNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading()<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'&Demote Subtree', self.keybindings[-1])
self.keybindings.append(Keybinding(u'>]]', u'<Plug>OrgDemoteSubtreeNormal', mode=MODE_NORMAL))
self.keybindings.append(Keybinding(u'>ir', u'<Plug>OrgDemoteSubtreeNormal', mode=MODE_NORMAL))
# other keybindings
self.keybindings.append(Keybinding(u'<C-d>', Plug(u'OrgPromoteOnHeadingInsert', u'<C-o>:silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False, on_heading=True, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT)))
self.keybindings.append(Keybinding(u'<C-t>', Plug(u'OrgDemoteOnHeadingInsert', u'<C-o>:silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False, on_heading=True, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT)))
# vim: set noexpandtab:

View file

@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
import os
import subprocess
import vim
from orgmode._vim import ORGMODE, echoe, echom
from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu
from orgmode.keybinding import Keybinding, Plug, Command
from orgmode import settings
from orgmode.py3compat.py_py3_string import *
class Export(object):
u"""
Export a orgmode file using emacs orgmode.
This is a *very simple* wrapper of the emacs/orgmode export. emacs and
orgmode need to be installed. We simply call emacs with some options to
export the .org.
TODO: Offer export options in vim. Don't use the menu.
TODO: Maybe use a native implementation.
"""
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'Export')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
# commands for this plugin
self.commands = []
@classmethod
def _get_init_script(cls):
init_script = settings.get(u'org_export_init_script', u'')
if init_script:
init_script = os.path.expandvars(os.path.expanduser(init_script))
if os.path.exists(init_script):
return init_script
else:
echoe(u'Unable to find init script %s' % init_script)
@classmethod
def _export(cls, format_):
"""Export current file to format.
Args:
format_: pdf or html
Returns:
return code
"""
emacsbin = os.path.expandvars(os.path.expanduser(
settings.get(u'org_export_emacs', u'/usr/bin/emacs')))
if not os.path.exists(emacsbin):
echoe(u'Unable to find emacs binary %s' % emacsbin)
# build the export command
cmd = [
emacsbin,
u'-nw',
u'--batch',
u'--visit=%s' % vim.eval(u'expand("%:p")'),
u'--funcall=%s' % format_
]
# source init script as well
init_script = cls._get_init_script()
if init_script:
cmd.extend(['--script', init_script])
# export
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.wait()
if p.returncode != 0 or settings.get(u'org_export_verbose') == 1:
echom('\n'.join(p.communicate()))
return p.returncode
@classmethod
def topdf(cls):
u"""Export the current buffer as pdf using emacs orgmode."""
ret = cls._export(u'org-latex-export-to-pdf')
if ret != 0:
echoe(u'PDF export failed.')
else:
echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'pdf'))
@classmethod
def tobeamer(cls):
u"""Export the current buffer as beamer pdf using emacs orgmode."""
ret = cls._export(u'org-beamer-export-to-pdf')
if ret != 0:
echoe(u'PDF export failed.')
else:
echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'pdf'))
@classmethod
def tohtml(cls):
u"""Export the current buffer as html using emacs orgmode."""
ret = cls._export(u'org-html-export-to-html')
if ret != 0:
echoe(u'HTML export failed.')
else:
echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'html'))
@classmethod
def tolatex(cls):
u"""Export the current buffer as latex using emacs orgmode."""
ret = cls._export(u'org-latex-export-to-latex')
if ret != 0:
echoe(u'latex export failed.')
else:
echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'tex'))
@classmethod
def tomarkdown(cls):
u"""Export the current buffer as markdown using emacs orgmode."""
ret = cls._export(u'org-md-export-to-markdown')
if ret != 0:
echoe('Markdown export failed. Make sure org-md-export-to-markdown is loaded in emacs, see the manual for details.')
else:
echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'md'))
def register(self):
u"""Registration and keybindings."""
# path to emacs executable
settings.set(u'org_export_emacs', u'/usr/bin/emacs')
# verbose output for export
settings.set(u'org_export_verbose', 0)
# allow the user to define an initialization script
settings.set(u'org_export_init_script', u'')
# to PDF
add_cmd_mapping_menu(
self,
name=u'OrgExportToPDF',
function=u':%s ORGMODE.plugins[u"Export"].topdf()<CR>' % VIM_PY_CALL,
key_mapping=u'<localleader>ep',
menu_desrc=u'To PDF (via Emacs)'
)
# to Beamer PDF
add_cmd_mapping_menu(
self,
name=u'OrgExportToBeamerPDF',
function=u':%s ORGMODE.plugins[u"Export"].tobeamer()<CR>' % VIM_PY_CALL,
key_mapping=u'<localleader>eb',
menu_desrc=u'To Beamer PDF (via Emacs)'
)
# to latex
add_cmd_mapping_menu(
self,
name=u'OrgExportToLaTeX',
function=u':%s ORGMODE.plugins[u"Export"].tolatex()<CR>' % VIM_PY_CALL,
key_mapping=u'<localleader>el',
menu_desrc=u'To LaTeX (via Emacs)'
)
# to HTML
add_cmd_mapping_menu(
self,
name=u'OrgExportToHTML',
function=u':%s ORGMODE.plugins[u"Export"].tohtml()<CR>' % VIM_PY_CALL,
key_mapping=u'<localleader>eh',
menu_desrc=u'To HTML (via Emacs)'
)
# to Markdown
add_cmd_mapping_menu(
self,
name=u'OrgExportToMarkdown',
function=u':%s ORGMODE.plugins[u"Export"].tomarkdown()<CR>' % VIM_PY_CALL,
key_mapping=u'<localleader>em',
menu_desrc=u'To Markdown (via Emacs)'
)
# vim: set noexpandtab:

View file

@ -0,0 +1,221 @@
# -*- coding: utf-8 -*-
import re
import vim
from orgmode._vim import echom, ORGMODE, realign_tags
from orgmode.menu import Submenu, Separator, ActionEntry
from orgmode.keybinding import Keybinding, Plug, Command
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.py_py3_string import *
class Hyperlinks(object):
u""" Hyperlinks plugin """
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'Hyperlinks')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
# commands for this plugin
self.commands = []
uri_match = re.compile(
r'^\[{2}(?P<uri>[^][]*)(\]\[(?P<description>[^][]*))?\]{2}')
@classmethod
def _get_link(cls, cursor=None):
u"""
Get the link the cursor is on and return it's URI and description
:cursor: None or (Line, Column)
:returns: None if no link was found, otherwise {uri:URI,
description:DESCRIPTION, line:LINE, start:START, end:END}
or uri and description could be None if not set
"""
cursor = cursor if cursor else vim.current.window.cursor
line = u_decode(vim.current.buffer[cursor[0] - 1])
# if the cursor is on the last bracket, it's not recognized as a hyperlink
start = line.rfind(u'[[', 0, cursor[1])
if start == -1:
start = line.rfind(u'[[', 0, cursor[1] + 2)
end = line.find(u']]', cursor[1])
if end == -1:
end = line.find(u']]', cursor[1] - 1)
# extract link
if start != -1 and end != -1:
end += 2
match = Hyperlinks.uri_match.match(line[start:end])
res = {
u'line': line,
u'start': start,
u'end': end,
u'uri': None,
u'description': None}
if match:
res.update(match.groupdict())
# reverse character escaping(partly done due to matching)
res[u'uri'] = res[u'uri'].replace(u'\\\\', u'\\')
return res
@classmethod
def follow(cls, action=u'openLink', visual=u''):
u""" Follow hyperlink. If called on a regular string UTL determines the
outcome. Normally a file with that name will be opened.
:action: "copy" if the link should be copied to clipboard, otherwise
the link will be opened
:visual: "visual" if Universal Text Linking should be triggered in
visual mode
:returns: URI or None
"""
if not int(vim.eval(u'exists(":Utl")')):
echom(u'Universal Text Linking plugin not installed, unable to proceed.')
return
action = u'copyLink' \
if (action and action.startswith(u'copy')) \
else u'openLink'
visual = u'visual' if visual and visual.startswith(u'visual') else u''
link = Hyperlinks._get_link()
if link and link[u'uri'] is not None:
# call UTL with the URI
vim.command(u_encode(u'Utl %s %s %s' % (action, visual, link[u'uri'])))
return link[u'uri']
else:
# call UTL and let it decide what to do
vim.command(u_encode(u'Utl %s %s' % (action, visual)))
@classmethod
@realign_tags
def insert(cls, uri=None, description=None):
u""" Inserts a hyperlink. If no arguments are provided, an interactive
query will be started.
:uri: The URI that will be opened
:description: An optional description that will be displayed instead of
the URI
:returns: (URI, description)
"""
link = Hyperlinks._get_link()
if link:
if uri is None and link[u'uri'] is not None:
uri = link[u'uri']
if description is None and link[u'description'] is not None:
description = link[u'description']
if uri is None:
uri = vim.eval(u'input("Link: ", "", "file")')
elif link:
uri = vim.eval(u'input("Link: ", "%s", "file")' % link[u'uri'])
if uri is None:
return
else:
uri = u_decode(uri)
# character escaping
uri = uri.replace(u'\\', u'\\\\\\\\')
uri = uri.replace(u' ', u'\ ')
if description is None:
description = u_decode(vim.eval(u'input("Description: ")'))
elif link:
description = vim.eval(
u'input("Description: ", "%s")' %
u_decode(link[u'description']))
if description is None:
return
cursor = vim.current.window.cursor
cl = u_decode(vim.current.buffer[cursor[0] - 1])
head = cl[:cursor[1] + 1] if not link else cl[:link[u'start']]
tail = cl[cursor[1] + 1:] if not link else cl[link[u'end']:]
separator = u''
if description:
separator = u']['
if uri or description:
vim.current.buffer[cursor[0] - 1] = \
u_encode(u''.join((head, u'[[%s%s%s]]' % (uri, separator, description), tail)))
elif link:
vim.current.buffer[cursor[0] - 1] = \
u_encode(u''.join((head, tail)))
def register(self):
u"""
Registration of plugin. Key bindings and other initialization should be done.
"""
cmd = Command(
u'OrgHyperlinkFollow',
u'%s ORGMODE.plugins[u"Hyperlinks"].follow()' % VIM_PY_CALL)
self.commands.append(cmd)
self.keybindings.append(
Keybinding(u'gl', Plug(u'OrgHyperlinkFollow', self.commands[-1])))
self.menu + ActionEntry(u'&Follow Link', self.keybindings[-1])
cmd = Command(
u'OrgHyperlinkCopy',
u'%s ORGMODE.plugins[u"Hyperlinks"].follow(action=u"copy")' % VIM_PY_CALL)
self.commands.append(cmd)
self.keybindings.append(
Keybinding(u'gyl', Plug(u'OrgHyperlinkCopy', self.commands[-1])))
self.menu + ActionEntry(u'&Copy Link', self.keybindings[-1])
cmd = Command(
u'OrgHyperlinkInsert',
u'%s ORGMODE.plugins[u"Hyperlinks"].insert(<f-args>)' % VIM_PY_CALL,
arguments=u'*')
self.commands.append(cmd)
self.keybindings.append(
Keybinding(u'gil', Plug(u'OrgHyperlinkInsert', self.commands[-1])))
self.menu + ActionEntry(u'&Insert Link', self.keybindings[-1])
self.menu + Separator()
# find next link
cmd = Command(
u'OrgHyperlinkNextLink',
u":if search('\[\{2}\zs[^][]*\(\]\[[^][]*\)\?\ze\]\{2}', 's') == 0 | echo 'No further link found.' | endif")
self.commands.append(cmd)
self.keybindings.append(
Keybinding(u'gn', Plug(u'OrgHyperlinkNextLink', self.commands[-1])))
self.menu + ActionEntry(u'&Next Link', self.keybindings[-1])
# find previous link
cmd = Command(
u'OrgHyperlinkPreviousLink',
u":if search('\[\{2}\zs[^][]*\(\]\[[^][]*\)\?\ze\]\{2}', 'bs') == 0 | echo 'No further link found.' | endif")
self.commands.append(cmd)
self.keybindings.append(
Keybinding(u'go', Plug(u'OrgHyperlinkPreviousLink', self.commands[-1])))
self.menu + ActionEntry(u'&Previous Link', self.keybindings[-1])
self.menu + Separator()
# Descriptive Links
cmd = Command(u'OrgHyperlinkDescriptiveLinks', u':setlocal cole=2')
self.commands.append(cmd)
self.menu + ActionEntry(u'&Descriptive Links', self.commands[-1])
# Literal Links
cmd = Command(u'OrgHyperlinkLiteralLinks', u':setlocal cole=0')
self.commands.append(cmd)
self.menu + ActionEntry(u'&Literal Links', self.commands[-1])
# vim: set noexpandtab:

View file

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
import vim
from orgmode._vim import echo, echom, echoe, ORGMODE, apply_count, repeat
from orgmode.menu import Submenu, Separator, ActionEntry
from orgmode.keybinding import Keybinding, Plug, Command
from orgmode.py3compat.py_py3_string import *
class LoggingWork(object):
u""" LoggingWork plugin """
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'&Logging work')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
# commands for this plugin
self.commands = []
@classmethod
def action(cls):
u""" Some kind of action
:returns: TODO
"""
pass
def register(self):
u"""
Registration of plugin. Key bindings and other initialization should be done.
"""
# an Action menu entry which binds "keybinding" to action ":action"
self.commands.append(Command(u'OrgLoggingRecordDoneTime', u'%s ORGMODE.plugins[u"LoggingWork"].action()' % VIM_PY_CALL))
self.menu + ActionEntry(u'&Record DONE time', self.commands[-1])

View file

@ -0,0 +1,173 @@
# -*- coding: utf-8 -*-
import vim
from orgmode._vim import ORGMODE, apply_count
from orgmode.menu import Submenu
from orgmode.keybinding import Keybinding, Plug, MODE_VISUAL, MODE_OPERATOR
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.py_py3_string import *
class Misc(object):
u""" Miscellaneous functionality """
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'Misc')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
@classmethod
def jump_to_first_character(cls):
heading = ORGMODE.get_document().current_heading()
if not heading or heading.start_vim != vim.current.window.cursor[0]:
vim.eval(u_encode(u'feedkeys("^", "n")'))
return
vim.current.window.cursor = (vim.current.window.cursor[0], heading.level + 1)
@classmethod
def edit_at_first_character(cls):
heading = ORGMODE.get_document().current_heading()
if not heading or heading.start_vim != vim.current.window.cursor[0]:
vim.eval(u_encode(u'feedkeys("I", "n")'))
return
vim.current.window.cursor = (vim.current.window.cursor[0], heading.level + 1)
vim.command(u_encode(u'startinsert'))
# @repeat
@classmethod
@apply_count
def i_heading(cls, mode=u'visual', selection=u'inner', skip_children=False):
u"""
inner heading text object
"""
heading = ORGMODE.get_document().current_heading()
if heading:
if selection != u'inner':
heading = heading if not heading.parent else heading.parent
line_start, col_start = [int(i) for i in vim.eval(u_encode(u'getpos("\'<")'))[1:3]]
line_end, col_end = [int(i) for i in vim.eval(u_encode(u'getpos("\'>")'))[1:3]]
if mode != u'visual':
line_start = vim.current.window.cursor[0]
line_end = line_start
start = line_start
end = line_end
move_one_character_back = u'' if mode == u'visual' else u'h'
if heading.start_vim < line_start:
start = heading.start_vim
if heading.end_vim > line_end and not skip_children:
end = heading.end_vim
elif heading.end_of_last_child_vim > line_end and skip_children:
end = heading.end_of_last_child_vim
if mode != u'visual' and not vim.current.buffer[end - 1]:
end -= 1
move_one_character_back = u''
swap_cursor = u'o' if vim.current.window.cursor[0] == line_start else u''
if selection == u'inner' and vim.current.window.cursor[0] != line_start:
h = ORGMODE.get_document().current_heading()
if h:
heading = h
visualmode = u_decode(vim.eval(u'visualmode()')) if mode == u'visual' else u'v'
if line_start == start and line_start != heading.start_vim:
if col_start in (0, 1):
vim.command(u_encode(u'normal! %dgg0%s%dgg$%s%s' % (start, visualmode, end, move_one_character_back, swap_cursor)))
else:
vim.command(u_encode(u'normal! %dgg0%dl%s%dgg$%s%s' % (start, col_start - 1, visualmode, end, move_one_character_back, swap_cursor)))
else:
vim.command(u_encode(u'normal! %dgg0%dl%s%dgg$%s%s' % (start, heading.level + 1, visualmode, end, move_one_character_back, swap_cursor)))
if selection == u'inner':
if mode == u'visual':
return u'OrgInnerHeadingVisual' if not skip_children else u'OrgInnerTreeVisual'
else:
return u'OrgInnerHeadingOperator' if not skip_children else u'OrgInnerTreeOperator'
else:
if mode == u'visual':
return u'OrgOuterHeadingVisual' if not skip_children else u'OrgOuterTreeVisual'
else:
return u'OrgOuterHeadingOperator' if not skip_children else u'OrgOuterTreeOperator'
elif mode == u'visual':
vim.command(u_encode(u'normal! gv'))
# @repeat
@classmethod
@apply_count
def a_heading(cls, selection=u'inner', skip_children=False):
u"""
a heading text object
"""
heading = ORGMODE.get_document().current_heading()
if heading:
if selection != u'inner':
heading = heading if not heading.parent else heading.parent
line_start, col_start = [int(i) for i in vim.eval(u_encode(u'getpos("\'<")'))[1:3]]
line_end, col_end = [int(i) for i in vim.eval(u_encode(u'getpos("\'>")'))[1:3]]
start = line_start
end = line_end
if heading.start_vim < line_start:
start = heading.start_vim
if heading.end_vim > line_end and not skip_children:
end = heading.end_vim
elif heading.end_of_last_child_vim > line_end and skip_children:
end = heading.end_of_last_child_vim
swap_cursor = u'o' if vim.current.window.cursor[0] == line_start else u''
vim.command(u_encode(u'normal! %dgg%s%dgg$%s' % (start, vim.eval(u_encode(u'visualmode()')), end, swap_cursor)))
if selection == u'inner':
return u'OrgAInnerHeadingVisual' if not skip_children else u'OrgAInnerTreeVisual'
else:
return u'OrgAOuterHeadingVisual' if not skip_children else u'OrgAOuterTreeVisual'
else:
vim.command(u_encode(u'normal! gv'))
def register(self):
u"""
Registration of plugin. Key bindings and other initialization should be done.
"""
self.keybindings.append(Keybinding(u'^',
Plug(u'OrgJumpToFirstCharacter', u'%s ORGMODE.plugins[u"Misc"].jump_to_first_character()<CR>' % VIM_PY_CALL)))
self.keybindings.append(Keybinding(u'I',
Plug(u'OrgEditAtFirstCharacter', u'%s ORGMODE.plugins[u"Misc"].edit_at_first_character()<CR>' % VIM_PY_CALL)))
self.keybindings.append(Keybinding(u'ih', Plug(u'OrgInnerHeadingVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading()<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u'ah', Plug(u'OrgAInnerHeadingVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].a_heading()<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u'Oh', Plug(u'OrgOuterHeadingVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(selection=u"outer")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u'OH', Plug(u'OrgAOuterHeadingVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].a_heading(selection=u"outer")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u'ih', Plug(u'OrgInnerHeadingOperator', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
self.keybindings.append(Keybinding(u'ah', u':normal Vah<CR>', mode=MODE_OPERATOR))
self.keybindings.append(Keybinding(u'Oh', Plug(u'OrgOuterHeadingOperator', ':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator", selection=u"outer")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
self.keybindings.append(Keybinding(u'OH', u':normal VOH<CR>', mode=MODE_OPERATOR))
self.keybindings.append(Keybinding(u'ir', Plug(u'OrgInnerTreeVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u'ar', Plug(u'OrgAInnerTreeVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].a_heading(skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u'Or', Plug(u'OrgOuterTreeVisual', u'<:<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(selection=u"outer", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u'OR', Plug(u'OrgAOuterTreeVisual', u':<C-u>%s ORGMODE.plugins[u"Misc"].a_heading(selection=u"outer", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u'ir', Plug(u'OrgInnerTreeOperator', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
self.keybindings.append(Keybinding(u'ar', u':normal Var<CR>', mode=MODE_OPERATOR))
self.keybindings.append(Keybinding(u'Or', Plug(u'OrgOuterTreeOperator', u':<C-u>%s ORGMODE.plugins[u"Misc"].i_heading(mode=u"operator", selection=u"outer", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
self.keybindings.append(Keybinding(u'OR', u':normal VOR<CR>', mode=MODE_OPERATOR))
# vim: set noexpandtab:

View file

@ -0,0 +1,326 @@
# -*- coding: utf-8 -*-
import vim
from orgmode._vim import echo, ORGMODE, apply_count
from orgmode.menu import Submenu, ActionEntry
from orgmode.keybinding import Keybinding, MODE_VISUAL, MODE_OPERATOR, Plug
from orgmode.liborgmode.documents import Direction
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.py_py3_string import *
class Navigator(object):
u""" Implement navigation in org-mode documents """
def __init__(self):
object.__init__(self)
self.menu = ORGMODE.orgmenu + Submenu(u'&Navigate Headings')
self.keybindings = []
@classmethod
@apply_count
def parent(cls, mode):
u"""
Focus parent heading
:returns: parent heading or None
"""
heading = ORGMODE.get_document().current_heading()
if not heading:
if mode == u'visual':
vim.command(u_encode(u'normal! gv'))
else:
echo(u'No heading found')
return
if not heading.parent:
if mode == u'visual':
vim.command(u_encode(u'normal! gv'))
else:
echo(u'No parent heading found')
return
p = heading.parent
if mode == u'visual':
cls._change_visual_selection(heading, p, direction=Direction.BACKWARD, parent=True)
else:
vim.current.window.cursor = (p.start_vim, p.level + 1)
return p
@classmethod
@apply_count
def parent_next_sibling(cls, mode):
u"""
Focus the parent's next sibling
:returns: parent's next sibling heading or None
"""
heading = ORGMODE.get_document().current_heading()
if not heading:
if mode == u'visual':
vim.command(u_encode(u'normal! gv'))
else:
echo(u'No heading found')
return
if not heading.parent or not heading.parent.next_sibling:
if mode == u'visual':
vim.command(u_encode(u'normal! gv'))
else:
echo(u'No parent heading found')
return
ns = heading.parent.next_sibling
if mode == u'visual':
cls._change_visual_selection(heading, ns, direction=Direction.FORWARD, parent=False)
elif mode == u'operator':
vim.current.window.cursor = (ns.start_vim, 0)
else:
vim.current.window.cursor = (ns.start_vim, ns.level + 1)
return ns
@classmethod
def _change_visual_selection(cls, current_heading, heading, direction=Direction.FORWARD, noheadingfound=False, parent=False):
current = vim.current.window.cursor[0]
line_start, col_start = [int(i) for i in vim.eval(u_encode(u'getpos("\'<")'))[1:3]]
line_end, col_end = [int(i) for i in vim.eval(u_encode(u'getpos("\'>")'))[1:3]]
f_start = heading.start_vim
f_end = heading.end_vim
swap_cursor = True
# << |visual start
# selection end >>
if current == line_start:
if (direction == Direction.FORWARD and line_end < f_start) or noheadingfound and not direction == Direction.BACKWARD:
swap_cursor = False
# focus heading HERE
# << |visual start
# selection end >>
# << |visual start
# focus heading HERE
# selection end >>
if f_start < line_start and direction == Direction.BACKWARD:
if current_heading.start_vim < line_start and not parent:
line_start = current_heading.start_vim
else:
line_start = f_start
elif (f_start < line_start or f_start < line_end) and not noheadingfound:
line_start = f_start
# << |visual start
# selection end >>
# focus heading HERE
else:
if direction == Direction.FORWARD:
if line_end < f_start and not line_start == f_start - 1 and current_heading:
# focus end of previous heading instead of beginning of next heading
line_start = line_end
line_end = f_start - 1
else:
# focus end of next heading
line_start = line_end
line_end = f_end
elif direction == Direction.BACKWARD:
if line_end < f_end:
pass
else:
line_start = line_end
line_end = f_end
# << visual start
# selection end| >>
else:
# focus heading HERE
# << visual start
# selection end| >>
if line_start > f_start and line_end > f_end and not parent:
line_end = f_end
swap_cursor = False
elif (line_start > f_start or line_start == f_start) and \
line_end <= f_end and direction == Direction.BACKWARD:
line_end = line_start
line_start = f_start
# << visual start
# selection end and focus heading end HERE| >>
# << visual start
# focus heading HERE
# selection end| >>
# << visual start
# selection end| >>
# focus heading HERE
else:
if direction == Direction.FORWARD:
if line_end < f_start - 1:
# focus end of previous heading instead of beginning of next heading
line_end = f_start - 1
else:
# focus end of next heading
line_end = f_end
else:
line_end = f_end
swap_cursor = False
move_col_start = u'%dl' % (col_start - 1) if (col_start - 1) > 0 and (col_start - 1) < 2000000000 else u''
move_col_end = u'%dl' % (col_end - 1) if (col_end - 1) > 0 and (col_end - 1) < 2000000000 else u''
swap = u'o' if swap_cursor else u''
vim.command(u_encode(u'normal! %dgg%s%s%dgg%s%s' % (line_start, move_col_start, vim.eval(u_encode(u'visualmode()')), line_end, move_col_end, swap)))
@classmethod
def _focus_heading(cls, mode, direction=Direction.FORWARD, skip_children=False):
u"""
Focus next or previous heading in the given direction
:direction: True for next heading, False for previous heading
:returns: next heading or None
"""
d = ORGMODE.get_document()
current_heading = d.current_heading()
heading = current_heading
focus_heading = None
# FIXME this is just a piece of really ugly and unmaintainable code. It
# should be rewritten
if not heading:
if direction == Direction.FORWARD and d.headings \
and vim.current.window.cursor[0] < d.headings[0].start_vim:
# the cursor is in the meta information are, therefore focus
# first heading
focus_heading = d.headings[0]
if not (heading or focus_heading):
if mode == u'visual':
# restore visual selection when no heading was found
vim.command(u_encode(u'normal! gv'))
else:
echo(u'No heading found')
return
elif direction == Direction.BACKWARD:
if vim.current.window.cursor[0] != heading.start_vim:
# the cursor is in the body of the current heading, therefore
# the current heading will be focused
if mode == u'visual':
line_start, col_start = [int(i) for i in
vim.eval(u_encode(u'getpos("\'<")'))[1:3]]
line_end, col_end = [int(i) for i in vim.eval(u_encode(u'getpos("\'>")'))[1:3]]
if line_start >= heading.start_vim and line_end > heading.start_vim:
focus_heading = heading
else:
focus_heading = heading
# so far no heading has been found that the next focus should be on
if not focus_heading:
if not skip_children and direction == Direction.FORWARD and heading.children:
focus_heading = heading.children[0]
elif direction == Direction.FORWARD and heading.next_sibling:
focus_heading = heading.next_sibling
elif direction == Direction.BACKWARD and heading.previous_sibling:
focus_heading = heading.previous_sibling
if not skip_children:
while focus_heading.children:
focus_heading = focus_heading.children[-1]
else:
if direction == Direction.FORWARD:
focus_heading = current_heading.next_heading
else:
focus_heading = current_heading.previous_heading
noheadingfound = False
if not focus_heading:
if mode in (u'visual', u'operator'):
# the cursor seems to be on the last or first heading of this
# document and performes another next/previous operation
focus_heading = heading
noheadingfound = True
else:
if direction == Direction.FORWARD:
echo(u'Already focussing last heading')
else:
echo(u'Already focussing first heading')
return
if mode == u'visual':
cls._change_visual_selection(current_heading, focus_heading, direction=direction, noheadingfound=noheadingfound)
elif mode == u'operator':
if direction == Direction.FORWARD and vim.current.window.cursor[0] >= focus_heading.start_vim:
vim.current.window.cursor = (focus_heading.end_vim, len(u_decode(vim.current.buffer[focus_heading.end])))
else:
vim.current.window.cursor = (focus_heading.start_vim, 0)
else:
vim.current.window.cursor = (focus_heading.start_vim, focus_heading.level + 1)
if noheadingfound:
return
return focus_heading
@classmethod
@apply_count
def previous(cls, mode, skip_children=False):
u"""
Focus previous heading
"""
return cls._focus_heading(mode, direction=Direction.BACKWARD, skip_children=skip_children)
@classmethod
@apply_count
def next(cls, mode, skip_children=False):
u"""
Focus next heading
"""
return cls._focus_heading(mode, direction=Direction.FORWARD, skip_children=skip_children)
def register(self):
# normal mode
self.keybindings.append(Keybinding(u'g{', Plug('OrgJumpToParentNormal',
u'%s ORGMODE.plugins[u"Navigator"].parent(mode=u"normal")<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'&Up', self.keybindings[-1])
self.keybindings.append(Keybinding(u'g}',
Plug('OrgJumpToParentsSiblingNormal', u'%s ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"normal")<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'&Down', self.keybindings[-1])
self.keybindings.append(Keybinding(u'{',
Plug(u'OrgJumpToPreviousNormal', u'%s ORGMODE.plugins[u"Navigator"].previous(mode=u"normal")<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'&Previous', self.keybindings[-1])
self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextNormal',
u'%s ORGMODE.plugins[u"Navigator"].next(mode=u"normal")<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'&Next', self.keybindings[-1])
# visual mode
self.keybindings.append(Keybinding(u'g{', Plug(u'OrgJumpToParentVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].parent(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u'g}', Plug('OrgJumpToParentsSiblingVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u'{', Plug(u'OrgJumpToPreviousVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
# operator-pending mode
self.keybindings.append(Keybinding(u'g{', Plug(u'OrgJumpToParentOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].parent(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
self.keybindings.append(Keybinding(u'g}', Plug('OrgJumpToParentsSiblingOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
self.keybindings.append(Keybinding(u'{', Plug(u'OrgJumpToPreviousOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
# section wise movement (skip children)
# normal mode
self.keybindings.append(Keybinding(u'[[',
Plug(u'OrgJumpToPreviousSkipChildrenNormal',
u'%s ORGMODE.plugins[u"Navigator"].previous(mode=u"normal", skip_children=True)<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'Ne&xt Same Level', self.keybindings[-1])
self.keybindings.append(Keybinding(u']]',
Plug(u'OrgJumpToNextSkipChildrenNormal',
u'%s ORGMODE.plugins[u"Navigator"].next(mode=u"normal", skip_children=True)<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'Pre&vious Same Level', self.keybindings[-1])
# visual mode
self.keybindings.append(Keybinding(u'[[', Plug(u'OrgJumpToPreviousSkipChildrenVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"visual", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
self.keybindings.append(Keybinding(u']]', Plug(u'OrgJumpToNextSkipChildrenVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"visual", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
# operator-pending mode
self.keybindings.append(Keybinding(u'[[', Plug(u'OrgJumpToPreviousSkipChildrenOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"operator", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
self.keybindings.append(Keybinding(u']]', Plug(u'OrgJumpToNextSkipChildrenOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"operator", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
# vim: set noexpandtab:

View file

@ -0,0 +1,181 @@
# -*- coding: utf-8 -*-
import vim
from orgmode.liborgmode.headings import Heading
from orgmode._vim import ORGMODE, apply_count
from orgmode import settings
from orgmode.menu import Submenu, ActionEntry
from orgmode.keybinding import Keybinding, Plug, MODE_NORMAL
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.xrange_compatibility import *
from orgmode.py3compat.py_py3_string import *
class ShowHide(object):
u""" Show Hide plugin """
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'&Show Hide')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
@classmethod
def _fold_depth(cls, h):
""" Find the deepest level of open folds
:h: Heading
:returns: Tuple (int - level of open folds, boolean - found fold) or None if h is not a Heading
"""
if not isinstance(h, Heading):
return
if int(vim.eval(u_encode(u'foldclosed(%d)' % h.start_vim))) != -1:
return (h.number_of_parents, True)
res = [h.number_of_parents + 1]
found = False
for c in h.children:
d, f = cls._fold_depth(c)
res.append(d)
found |= f
return (max(res), found)
@classmethod
@apply_count
def toggle_folding(cls, reverse=False):
u""" Toggle folding similar to the way orgmode does
This is just a convenience function, don't hesitate to use the z*
keybindings vim offers to deal with folding!
:reverse: If False open folding by one level otherwise close it by one.
"""
d = ORGMODE.get_document()
heading = d.current_heading()
if not heading:
vim.eval(u_encode(u'feedkeys("<Tab>", "n")'))
return
cursor = vim.current.window.cursor[:]
if int(vim.eval(u_encode(u'foldclosed(%d)' % heading.start_vim))) != -1:
if not reverse:
# open closed fold
p = heading.number_of_parents
if not p:
p = heading.level
vim.command(u_encode(u'normal! %dzo' % p))
else:
# reverse folding opens all folds under the cursor
vim.command(u_encode(u'%d,%dfoldopen!' % (heading.start_vim, heading.end_of_last_child_vim)))
vim.current.window.cursor = cursor
return heading
def open_fold(h):
if h.number_of_parents <= open_depth:
vim.command(u_encode(u'normal! %dgg%dzo' % (h.start_vim, open_depth)))
for c in h.children:
open_fold(c)
def close_fold(h):
for c in h.children:
close_fold(c)
if h.number_of_parents >= open_depth - 1 and \
int(vim.eval(u_encode(u'foldclosed(%d)' % h.start_vim))) == -1:
vim.command(u_encode(u'normal! %dggzc' % (h.start_vim, )))
# find deepest fold
open_depth, found_fold = cls._fold_depth(heading)
if not reverse:
# recursively open folds
if found_fold:
for child in heading.children:
open_fold(child)
else:
vim.command(u_encode(u'%d,%dfoldclose!' % (heading.start_vim, heading.end_of_last_child_vim)))
if heading.number_of_parents:
# restore cursor position, it might have been changed by open_fold
vim.current.window.cursor = cursor
p = heading.number_of_parents
if not p:
p = heading.level
# reopen fold again beacause the former closing of the fold closed all levels, including parents!
vim.command(u_encode(u'normal! %dzo' % (p, )))
else:
# close the last level of folds
close_fold(heading)
# restore cursor position
vim.current.window.cursor = cursor
return heading
@classmethod
@apply_count
def global_toggle_folding(cls, reverse=False):
""" Toggle folding globally
:reverse: If False open folding by one level otherwise close it by one.
"""
d = ORGMODE.get_document()
if reverse:
foldlevel = int(vim.eval(u_encode(u'&foldlevel')))
if foldlevel == 0:
# open all folds because the user tries to close folds beyound 0
vim.eval(u_encode(u'feedkeys("zR", "n")'))
else:
# vim can reduce the foldlevel on its own
vim.eval(u_encode(u'feedkeys("zm", "n")'))
else:
found = False
for h in d.headings:
res = cls._fold_depth(h)
if res:
found = res[1]
if found:
break
if not found:
# no fold found and the user tries to advance the fold level
# beyond maximum so close everything
vim.eval(u_encode(u'feedkeys("zM", "n")'))
else:
# fold found, vim can increase the foldlevel on its own
vim.eval(u_encode(u'feedkeys("zr", "n")'))
return d
def register(self):
u"""
Registration of plugin. Key bindings and other initialization should be done.
"""
# register plug
self.keybindings.append(Keybinding(u'<Tab>',
Plug(u'OrgToggleFoldingNormal', u'%s ORGMODE.plugins[u"ShowHide"].toggle_folding()<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'&Cycle Visibility', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<S-Tab>',
Plug(u'OrgToggleFoldingReverse', u'%s ORGMODE.plugins[u"ShowHide"].toggle_folding(reverse=True)<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'Cycle Visibility &Reverse', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<localleader>.',
Plug(u'OrgGlobalToggleFoldingNormal', u'%s ORGMODE.plugins[u"ShowHide"].global_toggle_folding()<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'Cycle Visibility &Globally', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<localleader>,',
Plug(u'OrgGlobalToggleFoldingReverse',
u'%s ORGMODE.plugins[u"ShowHide"].global_toggle_folding(reverse=True)<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'Cycle Visibility Reverse G&lobally', self.keybindings[-1])
for i in range(0, 10):
self.keybindings.append(Keybinding(u'<localleader>%d' % (i, ), u'zM:set fdl=%d<CR>' % i, mode=MODE_NORMAL))

View file

@ -0,0 +1,215 @@
# -*- coding: utf-8 -*-
import vim
from orgmode._vim import ORGMODE, repeat
from orgmode.menu import Submenu, ActionEntry
from orgmode.keybinding import Keybinding, Plug, Command
from orgmode import settings
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.py_py3_string import *
class TagsProperties(object):
u""" TagsProperties plugin """
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'&TAGS and Properties')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
# commands for this plugin
self.commands = []
@classmethod
def complete_tags(cls):
u""" build a list of tags and store it in variable b:org_tag_completion
"""
d = ORGMODE.get_document()
heading = d.current_heading()
if not heading:
return
leading_portion = u_decode(vim.eval(u'a:ArgLead'))
cursor = int(vim.eval(u'a:CursorPos'))
# extract currently completed tag
idx_orig = leading_portion.rfind(u':', 0, cursor)
if idx_orig == -1:
idx = 0
else:
idx = idx_orig
current_tag = leading_portion[idx: cursor].lstrip(u':')
head = leading_portion[:idx + 1]
if idx_orig == -1:
head = u''
tail = leading_portion[cursor:]
# extract all tags of the current file
all_tags = set()
for h in d.all_headings():
for t in h.tags:
all_tags.add(t)
ignorecase = bool(int(settings.get(u'org_tag_completion_ignorecase', int(vim.eval(u'&ignorecase')))))
possible_tags = []
# TODO current tags never used...
current_tags = heading.tags
for t in all_tags:
if ignorecase:
if t.lower().startswith(current_tag.lower()):
possible_tags.append(t)
elif t.startswith(current_tag):
possible_tags.append(t)
vim.command(u_encode(u'let b:org_complete_tags = [%s]' % u', '.join([u'"%s%s:%s"' % (head, i, tail) for i in possible_tags])))
@classmethod
@repeat
def set_tags(cls):
u""" Set tags for current heading
"""
d = ORGMODE.get_document()
heading = d.current_heading()
if not heading:
return
# retrieve tags
res = None
if heading.tags:
res = vim.eval(u'input("Tags: ", ":%s:", "customlist,Org_complete_tags")' % u':'.join(heading.tags))
else:
res = vim.eval(u'input("Tags: ", "", "customlist,Org_complete_tags")')
if res is None:
# user pressed <Esc> abort any further processing
return
# remove empty tags
heading.tags = [x for x in u_decode(res).strip().strip(u':').split(u':') if x.strip() != u'']
d.write()
return u'OrgSetTags'
@classmethod
def find_tags(cls):
""" Find tags in current file
"""
tags = vim.eval(u'input("Find Tags: ", "", "customlist,Org_complete_tags")')
if tags is None:
# user pressed <Esc> abort any further processing
return
tags = [x for x in u_decode(tags).strip().strip(u':').split(u':') if x.strip() != u'']
if tags:
searchstring = u'\\('
first = True
for t1 in tags:
if first:
first = False
searchstring += u'%s' % t1
else:
searchstring += u'\\|%s' % t1
for t2 in tags:
if t1 == t2:
continue
searchstring += u'\\(:[a-zA-Z:]*\\)\?:%s' % t2
searchstring += u'\\)'
vim.command(u'/\\zs:%s:\\ze' % searchstring)
return u'OrgFindTags'
@classmethod
def realign_tags(cls):
u"""
Updates tags when user finished editing a heading
"""
d = ORGMODE.get_document(allow_dirty=True)
heading = d.find_current_heading()
if not heading:
return
if vim.current.window.cursor[0] == heading.start_vim:
heading.set_dirty_heading()
d.write_heading(heading, including_children=False)
@classmethod
def realign_all_tags(cls):
u"""
Updates tags when user finishes editing a heading
"""
d = ORGMODE.get_document()
for heading in d.all_headings():
heading.set_dirty_heading()
d.write()
def register(self):
u"""
Registration of plugin. Key bindings and other initialization should be done.
"""
# an Action menu entry which binds "keybinding" to action ":action"
settings.set(u'org_tag_column', vim.eval(u'&textwidth'))
settings.set(u'org_tag_completion_ignorecase', int(vim.eval(u'&ignorecase')))
cmd = Command(
u'OrgSetTags',
u'%s ORGMODE.plugins[u"TagsProperties"].set_tags()' % VIM_PY_CALL)
self.commands.append(cmd)
keybinding = Keybinding(
u'<localleader>st',
Plug(u'OrgSetTags', cmd))
self.keybindings.append(keybinding)
self.menu + ActionEntry(u'Set &Tags', keybinding)
cmd = Command(
u'OrgFindTags',
u'%s ORGMODE.plugins[u"TagsProperties"].find_tags()' % VIM_PY_CALL)
self.commands.append(cmd)
keybinding = Keybinding(
u'<localleader>ft',
Plug(u'OrgFindTags', cmd))
self.keybindings.append(keybinding)
self.menu + ActionEntry(u'&Find Tags', keybinding)
cmd = Command(
u'OrgTagsRealign',
u"%s ORGMODE.plugins[u'TagsProperties'].realign_all_tags()" % VIM_PY_CALL)
self.commands.append(cmd)
# workaround to align tags when user is leaving insert mode
vim.command(u_encode(u"""function Org_complete_tags(ArgLead, CmdLine, CursorPos)
python << EOF
ORGMODE.plugins[u'TagsProperties'].complete_tags()
EOF
if exists('b:org_complete_tags')
let tmp = b:org_complete_tags
unlet b:org_complete_tags
return tmp
else
return []
endif
endfunction"""))
vim.command(u_encode(u"""function Org_realign_tags_on_insert_leave()
if !exists('b:org_complete_tag_on_insertleave_au')
:au orgmode InsertLeave <buffer> %s ORGMODE.plugins[u'TagsProperties'].realign_tags()
let b:org_complete_tag_on_insertleave_au = 1
endif
endfunction""" % VIM_PY_CALL))
# this is for all org files opened after this file
vim.command(u_encode(u"au orgmode FileType org call Org_realign_tags_on_insert_leave()"))
# this is for the current file
vim.command(u_encode(u"call Org_realign_tags_on_insert_leave()"))
# vim: set noexpandtab:

View file

@ -0,0 +1,345 @@
# -*- coding: utf-8 -*-
import vim
import itertools as it
from orgmode._vim import echom, ORGMODE, apply_count, repeat, realign_tags
from orgmode import settings
from orgmode.liborgmode.base import Direction
from orgmode.menu import Submenu, ActionEntry
from orgmode.keybinding import Keybinding, Plug
from orgmode.exceptions import PluginError
# temporary todo states for differnent orgmode buffers
ORGTODOSTATES = {}
from orgmode.py3compat.xrange_compatibility import *
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.unicode_compatibility import *
from orgmode.py3compat.py_py3_string import *
def split_access_key(t, sub=None):
u""" Split access key
Args:
t (str): Todo state
sub: A value that will be returned instead of access key if there was
not access key
Returns:
tuple: Todo state and access key separated (TODO, ACCESS_KEY)
Example:
>>> split_access_key('TODO(t)')
>>> ('TODO', '(t)')
>>> split_access_key('WANT', sub='(hi)')
>>> ('WANT', '(hi)')
"""
if type(t) != unicode:
echom("String must be unicode")
return (None, None)
idx = t.find(u'(')
v, k = (t, sub)
if idx != -1 and t[idx + 1:-1]:
v, k = (t[:idx], t[idx + 1:-1])
return (v, k)
class Todo(object):
u"""
Todo plugin.
Description taken from orgmode.org:
You can use TODO keywords to indicate different sequential states in the
process of working on an item, for example:
["TODO", "FEEDBACK", "VERIFY", "|", "DONE", "DELEGATED"]
The vertical bar separates the TODO keywords (states that need action) from
the DONE states (which need no further action). If you don't provide the
separator bar, the last state is used as the DONE state. With this setup,
the command ``,d`` will cycle an entry from TODO to FEEDBACK, then to
VERIFY, and finally to DONE and DELEGATED.
"""
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'&TODO Lists')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
@classmethod
def _process_all_states(cls, all_states):
u""" verify if states defined by user is valid.
Return cleaned_todo and flattened if is. Raise Exception if not.
Valid checking:
* no two state share a same name
"""
# TODO Write tests. -- Ron89
cleaned_todos = [[
split_access_key(todo)[0] for todo in it.chain.from_iterable(x)]
for x in all_states] + [[None]]
flattened_todos = list(it.chain.from_iterable(cleaned_todos))
if len(flattened_todos) != len(set(flattened_todos)):
raise PluginError(u"Duplicate names detected in TODO keyword list. Please examine `g/b:org_todo_keywords`")
# TODO This is the case when there are 2 todo states with the same
# name. It should be handled by making a simple class to hold TODO
# states, which would avoid mixing 2 todo states with the same name
# since they would have a different reference (but same content),
# albeit this can fail because python optimizes short strings (i.e.
# they hold the same ref) so care should be taken in implementation
return (cleaned_todos, flattened_todos)
@classmethod
def _get_next_state(
cls, current_state, all_states, direction=Direction.FORWARD,
next_set=False):
u""" Get the next todo state
Args:
current_state (str): The current todo state
all_states (list): A list containing all todo states within
sublists. The todo states may contain access keys
direction: Direction of state or keyword set change (forward or
backward)
next_set: Advance to the next keyword set in defined direction.
Returns:
str or None: next todo state, or None if there is no next state.
Note: all_states should have the form of:
[(['TODO(t)'], ['DONE(d)']),
(['REPORT(r)', 'BUG(b)', 'KNOWNCAUSE(k)'], ['FIXED(f)']),
([], ['CANCELED(c)'])]
"""
cleaned_todos, flattened_todos = cls._process_all_states(all_states)
# backward direction should really be -1 not 2
next_dir = -1 if direction == Direction.BACKWARD else 1
# work only with top level index
if next_set:
top_set = next((
todo_set[0] for todo_set in enumerate(cleaned_todos)
if current_state in todo_set[1]), -1)
ind = (top_set + next_dir) % len(cleaned_todos)
if ind != len(cleaned_todos) - 1:
echom("Using set: %s" % str(all_states[ind]))
else:
echom("Keyword removed.")
return cleaned_todos[ind][0]
# No next set, cycle around everything
else:
ind = next((
todo_iter[0] for todo_iter in enumerate(flattened_todos)
if todo_iter[1] == current_state), -1)
return flattened_todos[(ind + next_dir) % len(flattened_todos)]
@classmethod
@realign_tags
@repeat
@apply_count
def toggle_todo_state(
cls, direction=Direction.FORWARD, interactive=False, next_set=False):
u""" Toggle state of TODO item
:returns: The changed heading
"""
d = ORGMODE.get_document(allow_dirty=True)
# get heading
heading = d.find_current_heading()
if not heading:
vim.eval(u'feedkeys("^", "n")')
return
todo_states = d.get_todo_states(strip_access_key=False)
# get todo states
if not todo_states:
echom(u'No todo keywords configured.')
return
current_state = heading.todo
# get new state interactively
if interactive:
# determine position of the interactive prompt
prompt_pos = settings.get(u'org_todo_prompt_position', u'botright')
if prompt_pos not in [u'botright', u'topleft']:
prompt_pos = u'botright'
# pass todo states to new window
ORGTODOSTATES[d.bufnr] = todo_states
settings.set(
u'org_current_state_%d' % d.bufnr,
current_state if current_state is not None else u'', overwrite=True)
todo_buffer_exists = bool(int(vim.eval(u_encode(
u'bufexists("org:todo/%d")' % (d.bufnr, )))))
if todo_buffer_exists:
# if the buffer already exists, reuse it
vim.command(u_encode(
u'%s sbuffer org:todo/%d' % (prompt_pos, d.bufnr, )))
else:
# create a new window
vim.command(u_encode(
u'keepalt %s %dsplit org:todo/%d' % (prompt_pos, len(todo_states), d.bufnr)))
else:
new_state = Todo._get_next_state(
current_state, todo_states, direction=direction,
next_set=next_set)
cls.set_todo_state(new_state)
# plug
plug = u'OrgTodoForward'
if direction == Direction.BACKWARD:
plug = u'OrgTodoBackward'
return plug
@classmethod
def set_todo_state(cls, state):
u""" Set todo state for buffer.
:bufnr: Number of buffer the todo state should be updated for
:state: The new todo state
"""
lineno, colno = vim.current.window.cursor
d = ORGMODE.get_document(allow_dirty=True)
heading = d.find_current_heading()
if not heading:
return
current_state = heading.todo
# set new headline
heading.todo = state
d.write_heading(heading)
# move cursor along with the inserted state only when current position
# is in the heading; otherwite do nothing
if heading.start_vim == lineno and colno > heading.level:
if current_state is not None and \
colno <= heading.level + len(current_state):
# the cursor is actually on the todo keyword
# move it back to the beginning of the keyword in that case
vim.current.window.cursor = (lineno, heading.level + 1)
else:
# the cursor is somewhere in the text, move it along
if current_state is None and state is None:
offset = 0
elif current_state is None and state is not None:
offset = len(state) + 1
elif current_state is not None and state is None:
offset = -len(current_state) - 1
else:
offset = len(state) - len(current_state)
vim.current.window.cursor = (lineno, colno + offset)
@classmethod
def init_org_todo(cls):
u""" Initialize org todo selection window.
"""
bufnr = int(vim.current.buffer.name.split('/')[-1])
all_states = ORGTODOSTATES.get(bufnr, None)
vim_commands = [
u'let g:org_sav_timeoutlen=&timeoutlen',
u'au orgmode BufEnter <buffer> :if ! exists("g:org_sav_timeoutlen")|let g:org_sav_timeoutlen=&timeoutlen|set timeoutlen=1|endif',
u'au orgmode BufLeave <buffer> :if exists("g:org_sav_timeoutlen")|let &timeoutlen=g:org_sav_timeoutlen|unlet g:org_sav_timeoutlen|endif',
u'setlocal nolist tabstop=16 buftype=nofile timeout timeoutlen=1 winfixheight',
u'setlocal statusline=Org\\ todo\\ (%s)' % vim.eval(u_encode(u'fnameescape(fnamemodify(bufname(%d), ":t"))' % bufnr)),
u'nnoremap <silent> <buffer> <Esc> :%sbw<CR>' % vim.eval(u_encode(u'bufnr("%")')),
u'nnoremap <silent> <buffer> <CR> :let g:org_state = fnameescape(expand("<cword>"))<Bar>bw<Bar>exec "%s ORGMODE.plugins[u\'Todo\'].set_todo_state(\'".g:org_state."\')"<Bar>unlet! g:org_state<CR>' % VIM_PY_CALL,
]
# because timeoutlen can only be set globally it needs to be stored and
# restored later
# make window a scratch window and set the statusline differently
for cmd in vim_commands:
vim.command(u_encode(cmd))
if all_states is None:
vim.command(u_encode(u'bw'))
echom(u'No todo states avaiable for buffer %s' % vim.current.buffer.name)
for idx, state in enumerate(all_states):
pairs = [split_access_key(x, sub=u' ') for x in it.chain(*state)]
line = u'\t'.join(u''.join((u'[%s] ' % x[1], x[0])) for x in pairs)
vim.current.buffer.append(u_encode(line))
for todo, key in pairs:
# FIXME if double key is used for access modified this doesn't work
vim.command(u_encode(u'nnoremap <silent> <buffer> %s :bw<CR><c-w><c-p>%s ORGMODE.plugins[u"Todo"].set_todo_state("%s")<CR>' % (key, VIM_PY_CALL, u_decode(todo))))
# position the cursor of the current todo item
vim.command(u_encode(u'normal! G'))
current_state = settings.unset(u'org_current_state_%d' % bufnr)
if current_state is not None and current_state != '':
for i, buf in enumerate(vim.current.buffer):
idx = buf.find(current_state)
if idx != -1:
vim.current.window.cursor = (i + 1, idx)
break
else:
vim.current.window.cursor = (2, 4)
# finally make buffer non modifiable
vim.command(u_encode(u'setfiletype orgtodo'))
vim.command(u_encode(u'setlocal nomodifiable'))
# remove temporary todo states for the current buffer
del ORGTODOSTATES[bufnr]
def register(self):
u"""
Registration of plugin. Key bindings and other initialization should be done.
"""
self.keybindings.append(Keybinding(u'<localleader>ct', Plug(
u'OrgTodoToggleNonInteractive',
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(interactive=False)<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'&TODO/DONE/-', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<localleader>d', Plug(
u'OrgTodoToggleInteractive',
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(interactive=True)<CR>' % VIM_PY_CALL)))
self.menu + ActionEntry(u'&TODO/DONE/- (interactiv)', self.keybindings[-1])
# add submenu
submenu = self.menu + Submenu(u'Select &keyword')
self.keybindings.append(Keybinding(u'<S-Right>', Plug(
u'OrgTodoForward',
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state()<CR>' % VIM_PY_CALL)))
submenu + ActionEntry(u'&Next keyword', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<S-Left>', Plug(
u'OrgTodoBackward',
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(direction=2)<CR>' % VIM_PY_CALL)))
submenu + ActionEntry(u'&Previous keyword', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<C-S-Right>', Plug(
u'OrgTodoSetForward',
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(next_set=True)<CR>' % VIM_PY_CALL)))
submenu + ActionEntry(u'Next keyword &set', self.keybindings[-1])
self.keybindings.append(Keybinding(u'<C-S-Left>', Plug(
u'OrgTodoSetBackward',
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(direction=2, next_set=True)<CR>' % VIM_PY_CALL)))
submenu + ActionEntry(u'Previous &keyword set', self.keybindings[-1])
settings.set(u'org_todo_keywords', [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')])
settings.set(u'org_todo_prompt_position', u'botright')
vim.command(u_encode(u'au orgmode BufReadCmd org:todo/* %s ORGMODE.plugins[u"Todo"].init_org_todo()' % VIM_PY_CALL))
# vim: set noexpandtab:

View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View file

@ -0,0 +1,11 @@
import sys
if sys.version_info < (3,):
def u_encode(string):
return string.encode('utf8')
def u_decode(string):
return string.decode('utf8')
else:
def u_encode(string):
return string
def u_decode(string):
return string

View file

@ -0,0 +1,7 @@
import sys
if sys.version_info < (3,):
VIM_PY_CALL = u':py'
else:
VIM_PY_CALL = u':py3'

View file

@ -0,0 +1,4 @@
try:
unicode
except NameError:
basestring = unicode = str

View file

@ -0,0 +1,4 @@
try:
from __builtin__ import xrange as range
except:
pass

View file

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
import vim
import sys
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.unicode_compatibility import *
SCOPE_ALL = 1
# for all vim-orgmode buffers
SCOPE_GLOBAL = 2
# just for the current buffer - has priority before the global settings
SCOPE_BUFFER = 4
VARIABLE_LEADER = {SCOPE_GLOBAL: u'g', SCOPE_BUFFER: u'b'}
u""" Evaluate and store settings """
def get(setting, default=None, scope=SCOPE_ALL):
u""" Evaluate setting in scope of the current buffer,
globally and also from the contents of the current buffer
WARNING: Only string values are converted to unicode. If a different value
is received, e.g. a list or dict, no conversion is done.
:setting: name of the variable to evaluate
:default: default value in case the variable is empty
:returns: variable value
"""
# TODO first read setting from org file which take precedence over vim
# variable settings
if (scope & SCOPE_ALL | SCOPE_BUFFER) and \
int(vim.eval(u_encode(u'exists("b:%s")' % setting))):
res = vim.eval(u_encode(u"b:%s" % setting))
if type(res) in (unicode, str):
return u_decode(res)
return res
elif (scope & SCOPE_ALL | SCOPE_GLOBAL) and \
int(vim.eval(u_encode(u'exists("g:%s")' % setting))):
res = vim.eval(u_encode(u"g:%s" % setting))
if type(res) in (unicode, str):
return u_decode(res)
return res
return default
def set(setting, value, scope=SCOPE_GLOBAL, overwrite=False):
u""" Store setting in the defined scope
WARNING: For the return value, only string are converted to unicode. If a
different value is received by vim.eval, e.g. a list or dict, no conversion
is done.
:setting: name of the setting
:value: the actual value, repr is called on the value to create a string
representation
:scope: the scope o the setting/variable
:overwrite: overwrite existing settings (probably user defined settings)
:returns: the new value in case of overwrite==False the current value
"""
if (not overwrite) and (
int(vim.eval(u_encode(u'exists("%s:%s")' % \
(VARIABLE_LEADER[scope], setting))))):
res = vim.eval(
u_encode(u'%s:%s' % (VARIABLE_LEADER[scope], setting)))
if type(res) in (unicode, str):
return u_decode(res)
return res
v = repr(value)
if type(value) == unicode and sys.version_info < (3,):
# strip leading u of unicode string representations
v = v[1:]
cmd = u'let %s:%s = %s' % (VARIABLE_LEADER[scope], setting, v)
vim.command(u_encode(cmd))
return value
def unset(setting, scope=SCOPE_GLOBAL):
u""" Unset setting in the defined scope
:setting: name of the setting
:scope: the scope o the setting/variable
:returns: last value of setting
"""
value = get(setting, scope=scope)
cmd = u'unlet! %s:%s' % (VARIABLE_LEADER[scope], setting)
vim.command(u_encode(cmd))
return value
# vim: set noexpandtab:

View file

@ -0,0 +1,503 @@
# -*- coding: utf-8 -*-
"""
vimbuffer
~~~~~~~~~~
VimBuffer and VimBufferContent are the interface between liborgmode and
vim.
VimBuffer extends the liborgmode.document.Document().
Document() is just a general implementation for loading an org file. It
has no interface to an actual file or vim buffer. This is the task of
vimbuffer.VimBuffer(). It is the interfaces to vim. The main tasks for
VimBuffer are to provide read and write access to a real vim buffer.
VimBufferContent is a helper class for VimBuffer. Basically, it hides the
details of encoding - everything read from or written to VimBufferContent
is UTF-8.
"""
try:
from collections import UserList
except:
from UserList import UserList
import vim
from orgmode import settings
from orgmode.exceptions import BufferNotFound, BufferNotInSync
from orgmode.liborgmode.documents import Document, MultiPurposeList, Direction
from orgmode.liborgmode.headings import Heading
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.unicode_compatibility import *
class VimBuffer(Document):
def __init__(self, bufnr=0):
u"""
:bufnr: 0: current buffer, every other number refers to another buffer
"""
Document.__init__(self)
self._bufnr = vim.current.buffer.number if bufnr == 0 else bufnr
self._changedtick = -1
self._cached_heading = None
if self._bufnr == vim.current.buffer.number:
self._content = VimBufferContent(vim.current.buffer)
else:
_buffer = None
for b in vim.buffers:
if self._bufnr == b.number:
_buffer = b
break
if not _buffer:
raise BufferNotFound(u'Unable to locate buffer number #%d' % self._bufnr)
self._content = VimBufferContent(_buffer)
self.update_changedtick()
self._orig_changedtick = self._changedtick
@property
def tabstop(self):
return int(vim.eval(u_encode(u'&ts')))
@property
def tag_column(self):
return int(settings.get(u'org_tag_column', u'77'))
@property
def is_insync(self):
if self._changedtick == self._orig_changedtick:
self.update_changedtick()
return self._changedtick == self._orig_changedtick
@property
def bufnr(self):
u"""
:returns: The buffer's number for the current document
"""
return self._bufnr
@property
def changedtick(self):
u""" Number of changes in vimbuffer """
return self._changedtick
@changedtick.setter
def changedtick(self, value):
self._changedtick = value
def get_todo_states(self, strip_access_key=True):
u""" Returns a list containing a tuple of two lists of allowed todo
states split by todo and done states. Multiple todo-done state
sequences can be defined.
:returns: [([todo states], [done states]), ..]
"""
states = settings.get(u'org_todo_keywords', [])
# TODO this function gets called too many times when change of state of
# one todo is triggered, check with:
# print(states)
# this should be changed by saving todo states into some var and only
# if new states are set hook should be called to register them again
# into a property
# TODO move this to documents.py, it is all tangled up like this, no
# structure...
if type(states) not in (list, tuple):
return []
def parse_states(s, stop=0):
res = []
if not s:
return res
if type(s[0]) in (unicode, str):
r = []
for i in s:
_i = i
if type(_i) == str:
_i = u_decode(_i)
if type(_i) == unicode and _i:
if strip_access_key and u'(' in _i:
_i = _i[:_i.index(u'(')]
if _i:
r.append(_i)
else:
r.append(_i)
if not u'|' in r:
if not stop:
res.append((r[:-1], [r[-1]]))
else:
res = (r[:-1], [r[-1]])
else:
seperator_pos = r.index(u'|')
if not stop:
res.append((r[0:seperator_pos], r[seperator_pos + 1:]))
else:
res = (r[0:seperator_pos], r[seperator_pos + 1:])
elif type(s) in (list, tuple) and not stop:
for i in s:
r = parse_states(i, stop=1)
if r:
res.append(r)
return res
return parse_states(states)
def update_changedtick(self):
if self.bufnr == vim.current.buffer.number:
self._changedtick = int(vim.eval(u_encode(u'b:changedtick')))
else:
vim.command(u_encode(u'unlet! g:org_changedtick | let g:org_lz = &lz | let g:org_hidden = &hidden | set lz hidden'))
# TODO is this likely to fail? maybe some error hangling should be added
vim.command(u_encode(u'keepalt buffer %d | let g:org_changedtick = b:changedtick | buffer %d' % \
(self.bufnr, vim.current.buffer.number)))
vim.command(u_encode(u'let &lz = g:org_lz | let &hidden = g:org_hidden | unlet! g:org_lz g:org_hidden | redraw'))
self._changedtick = int(vim.eval(u_encode(u'g:org_changedtick')))
def write(self):
u""" write the changes to the vim buffer
:returns: True if something was written, otherwise False
"""
if not self.is_dirty:
return False
self.update_changedtick()
if not self.is_insync:
raise BufferNotInSync(u'Buffer is not in sync with vim!')
# write meta information
if self.is_dirty_meta_information:
meta_end = 0 if self._orig_meta_information_len is None else self._orig_meta_information_len
self._content[:meta_end] = self.meta_information
self._orig_meta_information_len = len(self.meta_information)
# remove deleted headings
already_deleted = []
for h in sorted(self._deleted_headings, key=lambda x: x._orig_start, reverse=True):
if h._orig_start is not None and h._orig_start not in already_deleted:
# this is a heading that actually exists on the buffer and it
# needs to be removed
del self._content[h._orig_start:h._orig_start + h._orig_len]
already_deleted.append(h._orig_start)
del self._deleted_headings[:]
del already_deleted
# update changed headings and add new headings
for h in self.all_headings():
if h.is_dirty:
vim.current.buffer.append("") # workaround for neovim bug
if h._orig_start is not None:
# this is a heading that existed before and was changed. It
# needs to be replaced
if h.is_dirty_heading:
self._content[h.start:h.start + 1] = [unicode(h)]
if h.is_dirty_body:
self._content[h.start + 1:h.start + h._orig_len] = h.body
else:
# this is a new heading. It needs to be inserted
self._content[h.start:h.start] = [unicode(h)] + h.body
del vim.current.buffer[-1] # restore workaround for neovim bug
h._dirty_heading = False
h._dirty_body = False
# for all headings the length and start offset needs to be updated
h._orig_start = h.start
h._orig_len = len(h)
self._dirty_meta_information = False
self._dirty_document = False
self.update_changedtick()
self._orig_changedtick = self._changedtick
return True
def write_heading(self, heading, including_children=True):
""" WARNING: use this function only when you know what you are doing!
This function writes a heading to the vim buffer. It offers performance
advantages over the regular write() function. This advantage is
combined with no sanity checks! Whenever you use this function, make
sure the heading you are writing contains the right offsets
(Heading._orig_start, Heading._orig_len).
Usage example:
# Retrieve a potentially dirty document
d = ORGMODE.get_document(allow_dirty=True)
# Don't rely on the DOM, retrieve the heading afresh
h = d.find_heading(direction=Direction.FORWARD, position=100)
# Update tags
h.tags = ['tag1', 'tag2']
# Write the heading
d.write_heading(h)
This function can't be used to delete a heading!
:heading: Write this heading with to the vim buffer
:including_children: Also include children in the update
:returns The written heading
"""
if including_children and heading.children:
for child in heading.children[::-1]:
self.write_heading(child, including_children)
if heading.is_dirty:
if heading._orig_start is not None:
# this is a heading that existed before and was changed. It
# needs to be replaced
if heading.is_dirty_heading:
self._content[heading._orig_start:heading._orig_start + 1] = [unicode(heading)]
if heading.is_dirty_body:
self._content[heading._orig_start + 1:heading._orig_start + heading._orig_len] = heading.body
else:
# this is a new heading. It needs to be inserted
raise ValueError('Heading must contain the attribute _orig_start! %s' % heading)
heading._dirty_heading = False
heading._dirty_body = False
# for all headings the length offset needs to be updated
heading._orig_len = len(heading)
return heading
def write_checkbox(self, checkbox, including_children=True):
if including_children and checkbox.children:
for child in checkbox.children[::-1]:
self.write_checkbox(child, including_children)
if checkbox.is_dirty:
if checkbox._orig_start is not None:
# this is a heading that existed before and was changed. It
# needs to be replaced
# print "checkbox is dirty? " + str(checkbox.is_dirty_checkbox)
# print checkbox
if checkbox.is_dirty_checkbox:
self._content[checkbox._orig_start:checkbox._orig_start + 1] = [unicode(checkbox)]
if checkbox.is_dirty_body:
self._content[checkbox._orig_start + 1:checkbox._orig_start + checkbox._orig_len] = checkbox.body
else:
# this is a new checkbox. It needs to be inserted
raise ValueError('Checkbox must contain the attribute _orig_start! %s' % checkbox)
checkbox._dirty_checkbox = False
checkbox._dirty_body = False
# for all headings the length offset needs to be updated
checkbox._orig_len = len(checkbox)
return checkbox
def write_checkboxes(self, checkboxes):
pass
def previous_heading(self, position=None):
u""" Find the next heading (search forward) and return the related object
:returns: Heading object or None
"""
h = self.current_heading(position=position)
if h:
return h.previous_heading
def current_heading(self, position=None):
u""" Find the current heading (search backward) and return the related object
:returns: Heading object or None
"""
if position is None:
position = vim.current.window.cursor[0] - 1
if not self.headings:
return
def binaryFindInDocument():
hi = len(self.headings)
lo = 0
while lo < hi:
mid = (lo+hi)//2
h = self.headings[mid]
if h.end_of_last_child < position:
lo = mid + 1
elif h.start > position:
hi = mid
else:
return binaryFindHeading(h)
def binaryFindHeading(heading):
if not heading.children or heading.end >= position:
return heading
hi = len(heading.children)
lo = 0
while lo < hi:
mid = (lo+hi)//2
h = heading.children[mid]
if h.end_of_last_child < position:
lo = mid + 1
elif h.start > position:
hi = mid
else:
return binaryFindHeading(h)
# look at the cache to find the heading
h_tmp = self._cached_heading
if h_tmp is not None:
if h_tmp.end_of_last_child > position and \
h_tmp.start < position:
if h_tmp.end < position:
self._cached_heading = binaryFindHeading(h_tmp)
return self._cached_heading
self._cached_heading = binaryFindInDocument()
return self._cached_heading
def next_heading(self, position=None):
u""" Find the next heading (search forward) and return the related object
:returns: Heading object or None
"""
h = self.current_heading(position=position)
if h:
return h.next_heading
def find_current_heading(self, position=None, heading=Heading):
u""" Find the next heading backwards from the position of the cursor.
The difference to the function current_heading is that the returned
object is not built into the DOM. In case the DOM doesn't exist or is
out of sync this function is much faster in fetching the current
heading.
:position: The position to start the search from
:heading: The base class for the returned heading
:returns: Heading object or None
"""
return self.find_heading(vim.current.window.cursor[0] - 1 \
if position is None else position, \
direction=Direction.BACKWARD, heading=heading, \
connect_with_document=False)
class VimBufferContent(MultiPurposeList):
u""" Vim Buffer Content is a UTF-8 wrapper around a vim buffer. When
retrieving or setting items in the buffer an automatic conversion is
performed.
This ensures UTF-8 usage on the side of liborgmode and the vim plugin
vim-orgmode.
"""
def __init__(self, vimbuffer, on_change=None):
MultiPurposeList.__init__(self, on_change=on_change)
# replace data with vimbuffer to make operations change the actual
# buffer
self.data = vimbuffer
def __contains__(self, item):
i = item
if type(i) is unicode:
i = u_encode(item)
return MultiPurposeList.__contains__(self, i)
def __getitem__(self, i):
if isinstance(i, slice):
return [u_decode(item) if type(item) is str else item \
for item in MultiPurposeList.__getitem__(self, i)]
else:
item = MultiPurposeList.__getitem__(self, i)
if type(item) is str:
return u_decode(item)
return item
def __setitem__(self, i, item):
if isinstance(i, slice):
o = []
o_tmp = item
if type(o_tmp) not in (list, tuple) and not isinstance(o_tmp, UserList):
o_tmp = list(o_tmp)
for item in o_tmp:
if type(item) == unicode:
o.append(u_encode(item))
else:
o.append(item)
MultiPurposeList.__setitem__(self, i, o)
else:
_i = item
if type(_i) is unicode:
_i = u_encode(item)
# TODO: fix this bug properly, it is really strange that it fails on
# python3 without it. Problem is that when _i = ['* '] it fails in
# UserList.__setitem__() but if it is changed in debuggr in __setitem__
# like item[0] = '* ' it works, hence this is some quirk with unicode
# stuff but very likely vim 7.4 BUG too.
if isinstance(_i, UserList) and sys.version_info > (3, ):
_i = [s.encode('utf8').decode('utf8') for s in _i]
MultiPurposeList.__setitem__(self, i, _i)
def __add__(self, other):
raise NotImplementedError()
# TODO: implement me
if isinstance(other, UserList):
return self.__class__(self.data + other.data)
elif isinstance(other, type(self.data)):
return self.__class__(self.data + other)
else:
return self.__class__(self.data + list(other))
def __radd__(self, other):
raise NotImplementedError()
# TODO: implement me
if isinstance(other, UserList):
return self.__class__(other.data + self.data)
elif isinstance(other, type(self.data)):
return self.__class__(other + self.data)
else:
return self.__class__(list(other) + self.data)
def __iadd__(self, other):
o = []
o_tmp = other
if type(o_tmp) not in (list, tuple) and not isinstance(o_tmp, UserList):
o_tmp = list(o_tmp)
for i in o_tmp:
if type(i) is unicode:
o.append(u_encode(i))
else:
o.append(i)
return MultiPurposeList.__iadd__(self, o)
def append(self, item):
i = item
if type(item) is str:
i = u_encode(item)
MultiPurposeList.append(self, i)
def insert(self, i, item):
_i = item
if type(_i) is str:
_i = u_encode(item)
MultiPurposeList.insert(self, i, _i)
def index(self, item, *args):
i = item
if type(i) is unicode:
i = u_encode(item)
MultiPurposeList.index(self, i, *args)
def pop(self, i=-1):
return u_decode(MultiPurposeList.pop(self, i))
def extend(self, other):
o = []
o_tmp = other
if type(o_tmp) not in (list, tuple) and not isinstance(o_tmp, UserList):
o_tmp = list(o_tmp)
for i in o_tmp:
if type(i) is unicode:
o.append(u_encode(i))
else:
o.append(i)
MultiPurposeList.extend(self, o)
# vim: set noexpandtab:

View file

@ -0,0 +1,133 @@
" Delete the next line to avoid the special indention of items
if !exists("g:org_indent")
let g:org_indent = 0
endif
setlocal foldtext=GetOrgFoldtext()
setlocal fillchars-=fold:-
setlocal fillchars+=fold:\
setlocal foldexpr=GetOrgFolding()
setlocal foldmethod=expr
setlocal indentexpr=GetOrgIndent()
setlocal nolisp
setlocal nosmartindent
setlocal autoindent
if has('python3')
let s:py_env = 'python3 << EOF'
else
let s:py_env = 'python << EOF'
endif
function! GetOrgIndent()
if g:org_indent == 0
return -1
endif
exe s:py_env
from orgmode._vim import indent_orgmode
indent_orgmode()
EOF
if exists('b:indent_level')
let l:tmp = b:indent_level
unlet b:indent_level
return l:tmp
else
return -1
endif
endfunction
function! GetOrgFolding()
let l:mode = mode()
if l:mode == 'i'
" the cache size is limited to 3, because vim queries the current and
" both surrounding lines when the user is typing in insert mode. The
" cache is shared between GetOrgFolding and GetOrgFoldtext
if ! exists('b:org_folding_cache')
let b:org_folding_cache = {}
endif
if has_key(b:org_folding_cache, v:lnum)
if match(b:org_folding_cache[v:lnum], '^>') == 0 &&
\ match(getline(v:lnum), '^\*\+\s') != 0
" when the user pastes text or presses enter, it happens that
" the cache starts to confuse vim's folding abilities
" these entries can safely be removed
unlet b:org_folding_cache[v:lnum]
" the fold text cache is probably also damaged, delete it as
" well
unlet! b:org_foldtext_cache
else
return b:org_folding_cache[v:lnum]
endif
endif
exe s:py_env
from orgmode._vim import fold_orgmode
fold_orgmode(allow_dirty=True)
EOF
else
exe s:py_env
from orgmode._vim import fold_orgmode
fold_orgmode()
EOF
endif
if exists('b:fold_expr')
let l:tmp = b:fold_expr
unlet b:fold_expr
if l:mode == 'i'
if ! has_key(b:org_folding_cache, v:lnum)
if len(b:org_folding_cache) > 3
let b:org_folding_cache = {}
endif
let b:org_folding_cache[v:lnum] = l:tmp
endif
endif
return l:tmp
else
return -1
endif
endfunction
function! SetOrgFoldtext(text)
let b:foldtext = a:text
endfunction
function! GetOrgFoldtext()
let l:mode = mode()
if l:mode == 'i'
" add a separate cache for fold text
if ! exists('b:org_foldtext_cache') ||
\ ! has_key(b:org_foldtext_cache, 'timestamp') ||
\ b:org_foldtext_cache['timestamp'] > (localtime() + 10)
let b:org_foldtext_cache = {'timestamp': localtime()}
endif
if has_key(b:org_foldtext_cache, v:foldstart)
return b:org_foldtext_cache[v:foldstart]
endif
exe s:py_env
from orgmode._vim import fold_text
fold_text(allow_dirty=True)
EOF
else
unlet! b:org_foldtext_cache
exec s:py_env
from orgmode._vim import fold_text
fold_text()
EOF
endif
if exists('b:foldtext')
let l:tmp = b:foldtext
unlet b:foldtext
if l:mode == 'i'
let b:org_foldtext_cache[v:foldstart] = l:tmp
endif
return l:tmp
endif
endfunction

View file

@ -0,0 +1,3 @@
:exec 'set rtp='.g:installdir
:so orgmode.vba
:q!

View file

@ -0,0 +1,3 @@
:exec 'set rtp='.g:installdir
:so orgmode.vmb
:q!

54
pack/acp/start/vim-orgmode/org Executable file
View file

@ -0,0 +1,54 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# org
# Command line utility for working with orgmode documents
#
# Depends: python-liborgmode
#
# Copyright (C) 2012 Jan Christoph Ebersbach
#
# http://www.e-jc.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <http://www.gnu.org/licenses/>.
import sys
sys.path.append('ftplugin')
from orgmode.liborgmode.documents import Document
class OrgFile(Document):
def __init__(self, filename):
u"""
:filename: Name of the file that shall be opened
"""
Document.__init__(self)
with file(filename) as f:
self._content = [line.decode(u'utf-8') for line in f.readlines()]
if __name__ == '__main__':
import cProfile
for filename in sys.argv[1:]:
cProfile.run('OrgFile(filename).init_dom()')

View file

@ -0,0 +1,383 @@
" Support org authoring markup as closely as possible
" (we're adding two markdown-like variants for =code= and blockquotes)
" -----------------------------------------------------------------------------
"
" Do we use aggresive conceal?
if exists("b:org_aggressive_conceal")
let s:conceal_aggressively=b:org_aggressive_conceal
elseif exists("g:org_aggressive_conceal")
let s:conceal_aggressively=g:org_aggressive_conceal
else
let s:conceal_aggressively=0
endif
" Inline markup {{{1
" *bold*, /italic/, _underline_, +strike-through+, =code=, ~verbatim~
" Note:
" - /italic/ is rendered as reverse in most terms (works fine in gVim, though)
" - +strike-through+ doesn't work on Vim / gVim
" - the non-standard `code' markup is also supported
" - =code= and ~verbatim~ are also supported as block-level markup, see below.
" Ref: http://orgmode.org/manual/Emphasis-and-monospace.html
"syntax match org_bold /\*[^ ]*\*/
" FIXME: Always make org_bold syntax define before org_heading syntax
" to make sure that org_heading syntax got higher priority(help :syn-priority) than org_bold.
" If there is any other good solution, please help fix it.
" \\\\*sinuate*
if (s:conceal_aggressively == 1)
syntax region org_bold matchgroup=org_border_bold start="[^ \\]\zs\*\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\*\|\(^\|[^\\]\)\@<=\*\S\@=" end="[^ \\]\zs\*\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\*\|[^\\]\zs\*\S\@=" concealends oneline
syntax region org_italic matchgroup=org_border_ital start="[^ \\]\zs\/\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\/\|\(^\|[^\\]\)\@<=\/\S\@=" end="[^ \\]\zs\/\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\/\|[^\\]\zs\/\S\@=" concealends oneline
syntax region org_underline matchgroup=org_border_undl start="[^ \\]\zs_\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs_\|\(^\|[^\\]\)\@<=_\S\@=" end="[^ \\]\zs_\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs_\|[^\\]\zs_\S\@=" concealends oneline
syntax region org_code matchgroup=org_border_code start="[^ \\]\zs=\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs=\|\(^\|[^\\]\)\@<==\S\@=" end="[^ \\]\zs=\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs=\|[^\\]\zs=\S\@=" concealends oneline
syntax region org_code matchgroup=org_border_code start="[^ \\]\zs`\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs`\|\(^\|[^\\]\)\@<=`\S\@=" end="[^ \\]\zs'\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs'\|[^\\]\zs'\S\@=" concealends oneline
syntax region org_verbatim matchgroup=org_border_verb start="[^ \\]\zs\~\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\~\|\(^\|[^\\]\)\@<=\~\S\@=" end="[^ \\]\zs\~\|\(\(^\|[^\\]\)\zs\(\\\\\)\+\)\zs\~\|[^\\]\zs\~\S\@=" concealends oneline
else
syntax region org_bold start="\S\zs\*\|\*\S\@=" end="\S\zs\*\|\*\S\@=" keepend oneline
syntax region org_italic start="\S\zs\/\|\/\S\@=" end="\S\zs\/\|\/\S\@=" keepend oneline
syntax region org_underline start="\S\zs_\|_\S\@=" end="\S\zs_\|_\S\@=" keepend oneline
syntax region org_code start="\S\zs=\|=\S\@=" end="\S\zs=\|=\S\@=" keepend oneline
syntax region org_code start="\S\zs`\|`\S\@=" end="\S\zs'\|'\S\@=" keepend oneline
syntax region org_verbatim start="\S\zs\~\|\~\S\@=" end="\S\zs\~\|\~\S\@=" keepend oneline
endif
hi def org_bold term=bold cterm=bold gui=bold
hi def org_italic term=italic cterm=italic gui=italic
hi def org_underline term=underline cterm=underline gui=underline
if (s:conceal_aggressively == 1)
hi link org_border_bold org_bold
hi link org_border_ital org_italic
hi link org_border_undl org_underline
endif
" Headings: {{{1
" Load Settings: {{{2
if !exists('g:org_heading_highlight_colors')
let g:org_heading_highlight_colors = ['Title', 'Constant', 'Identifier', 'Statement', 'PreProc', 'Type', 'Special']
endif
if !exists('g:org_heading_highlight_levels')
let g:org_heading_highlight_levels = len(g:org_heading_highlight_colors)
endif
if !exists('g:org_heading_shade_leading_stars')
let g:org_heading_shade_leading_stars = 1
endif
" Enable Syntax HL: {{{2
unlet! s:i s:j s:contains
let s:i = 1
let s:j = len(g:org_heading_highlight_colors)
let s:contains = ' contains=org_timestamp,org_timestamp_inactive,org_subtask_percent,org_subtask_number,org_subtask_percent_100,org_subtask_number_all,org_list_checkbox,org_bold,org_italic,org_underline,org_code,org_verbatim'
if g:org_heading_shade_leading_stars == 1
let s:contains = s:contains . ',org_shade_stars'
syntax match org_shade_stars /^\*\{2,\}/me=e-1 contained
hi def link org_shade_stars Ignore
else
hi clear org_shade_stars
endif
while s:i <= g:org_heading_highlight_levels
exec 'syntax match org_heading' . s:i . ' /^\*\{' . s:i . '\}\s.*/' . s:contains
exec 'hi def link org_heading' . s:i . ' ' . g:org_heading_highlight_colors[(s:i - 1) % s:j]
let s:i += 1
endwhile
unlet! s:i s:j s:contains
" Todo Keywords: {{{1
" Load Settings: {{{2
if !exists('g:org_todo_keywords')
let g:org_todo_keywords = ['TODO', '|', 'DONE']
endif
if !exists('g:org_todo_keyword_faces')
let g:org_todo_keyword_faces = []
endif
" Enable Syntax HL: {{{2
let s:todo_headings = ''
let s:i = 1
while s:i <= g:org_heading_highlight_levels
if s:todo_headings == ''
let s:todo_headings = 'containedin=org_heading' . s:i
else
let s:todo_headings = s:todo_headings . ',org_heading' . s:i
endif
let s:i += 1
endwhile
unlet! s:i
if !exists('g:loaded_org_syntax')
let g:loaded_org_syntax = 1
function! OrgExtendHighlightingGroup(base_group, new_group, settings)
let l:base_hi = ''
redir => l:base_hi
silent execute 'highlight ' . a:base_group
redir END
let l:group_hi = substitute(split(l:base_hi, '\n')[0], '^' . a:base_group . '\s\+xxx', '', '')
execute 'highlight ' . a:new_group . l:group_hi . ' ' . a:settings
endfunction
function! OrgInterpretFaces(faces)
let l:res_faces = ''
if type(a:faces) == 3
let l:style = []
for l:f in a:faces
let l:_f = [l:f]
if type(l:f) == 3
let l:_f = l:f
endif
for l:g in l:_f
if type(l:g) == 1 && l:g =~ '^:'
if l:g !~ '[\t ]'
continue
endif
let l:k_v = split(l:g)
if l:k_v[0] == ':foreground'
let l:gui_color = ''
let l:found_gui_color = 0
for l:color in split(l:k_v[1], ',')
if l:color =~ '^#'
let l:found_gui_color = 1
let l:res_faces = l:res_faces . ' guifg=' . l:color
elseif l:color != ''
let l:gui_color = l:color
let l:res_faces = l:res_faces . ' ctermfg=' . l:color
endif
endfor
if ! l:found_gui_color && l:gui_color != ''
let l:res_faces = l:res_faces . ' guifg=' . l:gui_color
endif
elseif l:k_v[0] == ':background'
let l:gui_color = ''
let l:found_gui_color = 0
for l:color in split(l:k_v[1], ',')
if l:color =~ '^#'
let l:found_gui_color = 1
let l:res_faces = l:res_faces . ' guibg=' . l:color
elseif l:color != ''
let l:gui_color = l:color
let l:res_faces = l:res_faces . ' ctermbg=' . l:color
endif
endfor
if ! l:found_gui_color && l:gui_color != ''
let l:res_faces = l:res_faces . ' guibg=' . l:gui_color
endif
elseif l:k_v[0] == ':weight' || l:k_v[0] == ':slant' || l:k_v[0] == ':decoration'
if index(l:style, l:k_v[1]) == -1
call add(l:style, l:k_v[1])
endif
endif
elseif type(l:g) == 1
" TODO emacs interprets the color and automatically determines
" whether it should be set as foreground or background color
let l:res_faces = l:res_faces . ' ctermfg=' . l:k_v[1] . ' guifg=' . l:k_v[1]
endif
endfor
endfor
let l:s = ''
for l:i in l:style
if l:s == ''
let l:s = l:i
else
let l:s = l:s . ','. l:i
endif
endfor
if l:s != ''
let l:res_faces = l:res_faces . ' term=' . l:s . ' cterm=' . l:s . ' gui=' . l:s
endif
elseif type(a:faces) == 1
" TODO emacs interprets the color and automatically determines
" whether it should be set as foreground or background color
let l:res_faces = l:res_faces . ' ctermfg=' . a:faces . ' guifg=' . a:faces
endif
return l:res_faces
endfunction
function! s:ReadTodoKeywords(keywords, todo_headings)
let l:default_group = 'Todo'
for l:i in a:keywords
if type(l:i) == 3
call s:ReadTodoKeywords(l:i, a:todo_headings)
continue
endif
if l:i == '|'
let l:default_group = 'Question'
continue
endif
" strip access key
let l:_i = substitute(l:i, "\(.*$", "", "")
let l:group = l:default_group
for l:j in g:org_todo_keyword_faces
if l:j[0] == l:_i
let l:group = 'org_todo_keyword_face_' . l:_i
call OrgExtendHighlightingGroup(l:default_group, l:group, OrgInterpretFaces(l:j[1]))
break
endif
endfor
silent! exec 'syntax match org_todo_keyword_' . l:_i . ' /\*\{1,\}\s\{1,\}\zs' . l:_i .'\(\s\|$\)/ ' . a:todo_headings
silent! exec 'hi def link org_todo_keyword_' . l:_i . ' ' . l:group
endfor
endfunction
endif
call s:ReadTodoKeywords(g:org_todo_keywords, s:todo_headings)
unlet! s:todo_headings
" Timestamps: {{{1
"<2003-09-16 Tue>
"<2003-09-16 Sáb>
syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k>\)/
"<2003-09-16 Tue 12:00>
syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>\)/
"<2003-09-16 Tue 12:00-12:30>
syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d-\d\d:\d\d>\)/
"<2003-09-16 Tue>--<2003-09-16 Tue>
syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k>--<\d\d\d\d-\d\d-\d\d \k\k\k>\)/
"<2003-09-16 Tue 12:00>--<2003-09-16 Tue 12:00>
syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>--<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>\)/
syn match org_timestamp /\(<%%(diary-float.\+>\)/
"[2003-09-16 Tue]
syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \k\k\k\]\)/
"[2003-09-16 Tue 12:00]
syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d\]\)/
"[2003-09-16 Tue]--[2003-09-16 Tue]
syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \k\k\k\]--\[\d\d\d\d-\d\d-\d\d \k\k\k\]\)/
"[2003-09-16 Tue 12:00]--[2003-09-16 Tue 12:00]
syn match org_timestamp_inactive /\(\[\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d\]--\[\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d\]\)/
syn match org_timestamp_inactive /\(\[%%(diary-float.\+\]\)/
hi def link org_timestamp PreProc
hi def link org_timestamp_inactive Comment
" Deadline And Schedule: {{{1
syn match org_deadline_scheduled /^\s*\(DEADLINE\|SCHEDULED\):/
hi def link org_deadline_scheduled PreProc
" Tables: {{{1
syn match org_table /^\s*|.*/ contains=org_timestamp,org_timestamp_inactive,hyperlink,org_table_separator,org_table_horizontal_line
syn match org_table_separator /\(^\s*|[-+]\+|\?\||\)/ contained
hi def link org_table_separator Type
" Hyperlinks: {{{1
syntax match hyperlink "\[\{2}[^][]*\(\]\[[^][]*\)\?\]\{2}" contains=hyperlinkBracketsLeft,hyperlinkURL,hyperlinkBracketsRight containedin=ALL
if (s:conceal_aggressively == 1)
syntax match hyperlinkBracketsLeft contained "\[\{2}#\?" conceal
else
syntax match hyperlinkBracketsLeft contained "\[\{2}" conceal
endif
syntax match hyperlinkURL contained "[^][]*\]\[" conceal
syntax match hyperlinkBracketsRight contained "\]\{2}" conceal
hi def link hyperlink Underlined
" Comments: {{{1
syntax match org_comment /^#.*/
hi def link org_comment Comment
" Bullet Lists: {{{1
" Ordered Lists:
" 1. list item
" 1) list item
" a. list item
" a) list item
syn match org_list_ordered "^\s*\(\a\|\d\+\)[.)]\(\s\|$\)" nextgroup=org_list_item
hi def link org_list_ordered Identifier
" Unordered Lists:
" - list item
" * list item
" + list item
" + and - don't need a whitespace prefix
syn match org_list_unordered "^\(\s*[-+]\|\s\+\*\)\(\s\|$\)" nextgroup=org_list_item
hi def link org_list_unordered Identifier
" Definition Lists:
" - Term :: expl.
" 1) Term :: expl.
syntax match org_list_def /.*\s\+::/ contained
hi def link org_list_def PreProc
syntax match org_list_item /.*$/ contained contains=org_subtask_percent,org_subtask_number,org_subtask_percent_100,org_subtask_number_all,org_list_checkbox,org_bold,org_italic,org_underline,org_code,org_verbatim,org_timestamp,org_timestamp_inactive,org_list_def
syntax match org_list_checkbox /\[[ X-]]/ contained
hi def link org_list_bullet Identifier
hi def link org_list_checkbox PreProc
" Block Delimiters: {{{1
syntax case ignore
syntax match org_block_delimiter /^#+BEGIN_.*/
syntax match org_block_delimiter /^#+END_.*/
syntax match org_key_identifier /^#+[^ ]*:/
syntax match org_title /^#+TITLE:.*/ contains=org_key_identifier
hi def link org_block_delimiter Comment
hi def link org_key_identifier Comment
hi def link org_title Title
" Block Markup: {{{1
" we consider all BEGIN/END sections as 'verbatim' blocks (inc. 'quote', 'verse', 'center')
" except 'example' and 'src' which are treated as 'code' blocks.
" Note: the non-standard '>' prefix is supported for quotation lines.
" Note: the '^:.*" rule must be defined before the ':PROPERTIES:' one below.
" TODO: http://vim.wikia.com/wiki/Different_syntax_highlighting_within_regions_of_a_file
syntax match org_verbatim /^\s*>.*/
syntax match org_code /^\s*:.*/
syntax region org_verbatim start="^\s*#+BEGIN_.*" end="^\s*#+END_.*" keepend contains=org_block_delimiter
syntax region org_code start="^\s*#+BEGIN_SRC" end="^\s*#+END_SRC" keepend contains=org_block_delimiter
syntax region org_code start="^\s*#+BEGIN_EXAMPLE" end="^\s*#+END_EXAMPLE" keepend contains=org_block_delimiter
hi def link org_code String
hi def link org_verbatim String
if (s:conceal_aggressively==1)
hi link org_border_code org_code
hi link org_border_verb org_verbatim
endif
" Properties: {{{1
syn region Error matchgroup=org_properties_delimiter start=/^\s*:PROPERTIES:\s*$/ end=/^\s*:END:\s*$/ contains=org_property keepend
syn match org_property /^\s*:[^\t :]\+:\s\+[^\t ]/ contained contains=org_property_value
syn match org_property_value /:\s\zs.*/ contained
hi def link org_properties_delimiter PreProc
hi def link org_property Statement
hi def link org_property_value Constant
" Break down subtasks
syntax match org_subtask_number /\[\d*\/\d*]/ contained
syntax match org_subtask_percent /\[\d*%\]/ contained
syntax match org_subtask_number_all /\[\(\d\+\)\/\1\]/ contained
syntax match org_subtask_percent_100 /\[100%\]/ contained
hi def link org_subtask_number String
hi def link org_subtask_percent String
hi def link org_subtask_percent_100 Identifier
hi def link org_subtask_number_all Identifier
" Plugin SyntaxRange: {{{1
" This only works if you have SyntaxRange installed:
" https://github.com/vim-scripts/SyntaxRange
" BEGIN_SRC
if exists('g:loaded_SyntaxRange')
call SyntaxRange#Include('#+BEGIN_SRC vim', '#+END_SRC', 'vim', 'comment')
call SyntaxRange#Include('#+BEGIN_SRC python', '#+END_SRC', 'python', 'comment')
call SyntaxRange#Include('#+BEGIN_SRC c', '#+END_SRC', 'c', 'comment')
" cpp must be below c, otherwise you get c syntax hl for cpp files
call SyntaxRange#Include('#+BEGIN_SRC cpp', '#+END_SRC', 'cpp', 'comment')
call SyntaxRange#Include('#+BEGIN_SRC ruby', '#+END_SRC', 'ruby', 'comment')
" call SyntaxRange#Include('#+BEGIN_SRC lua', '#+END_SRC', 'lua', 'comment')
" call SyntaxRange#Include('#+BEGIN_SRC lisp', '#+END_SRC', 'lisp', 'comment')
" LaTeX
call SyntaxRange#Include('\\begin[.*]{.*}', '\\end{.*}', 'tex')
call SyntaxRange#Include('\\begin{.*}', '\\end{.*}', 'tex')
call SyntaxRange#Include('\\\[', '\\\]', 'tex')
endif
" vi: ft=vim:tw=80:sw=4:ts=4:fdm=marker

View file

@ -0,0 +1,79 @@
" TODO do we really need a separate syntax file for the agenda?
" - Most of the stuff here is also in syntax.org
" - DRY!
syn match org_todo_key /\[\zs[^]]*\ze\]/
hi def link org_todo_key Identifier
let s:todo_headings = ''
let s:i = 1
while s:i <= g:org_heading_highlight_levels
if s:todo_headings == ''
let s:todo_headings = 'containedin=org_heading' . s:i
else
let s:todo_headings = s:todo_headings . ',org_heading' . s:i
endif
let s:i += 1
endwhile
unlet! s:i
if !exists('g:loaded_orgagenda_syntax')
let g:loaded_orgagenda_syntax = 1
function! s:ReadTodoKeywords(keywords, todo_headings)
let l:default_group = 'Todo'
for l:i in a:keywords
if type(l:i) == 3
call s:ReadTodoKeywords(l:i, a:todo_headings)
continue
endif
if l:i == '|'
let l:default_group = 'Question'
continue
endif
" strip access key
let l:_i = substitute(l:i, "\(.*$", "", "")
let l:group = l:default_group
for l:j in g:org_todo_keyword_faces
if l:j[0] == l:_i
let l:group = 'orgtodo_todo_keyword_face_' . l:_i
call OrgExtendHighlightingGroup(l:default_group, l:group, OrgInterpretFaces(l:j[1]))
break
endif
endfor
silent! exec 'syntax match orgtodo_todo_keyword_' . l:_i . ' /' . l:_i .'/ ' . a:todo_headings
silent! exec 'hi def link orgtodo_todo_keyword_' . l:_i . ' ' . l:group
endfor
endfunction
endif
call s:ReadTodoKeywords(g:org_todo_keywords, s:todo_headings)
unlet! s:todo_headings
" Timestamps
"<2003-09-16 Tue>
syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k>\)/
"<2003-09-16 Tue 12:00>
syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>\)/
"<2003-09-16 Tue 12:00-12:30>
syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d-\d\d:\d\d>\)/
"<2003-09-16 Tue>--<2003-09-16 Tue>
syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k>--<\d\d\d\d-\d\d-\d\d \k\k\k>\)/
"<2003-09-16 Tue 12:00>--<2003-09-16 Tue 12:00>
syn match org_timestamp /\(<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>--<\d\d\d\d-\d\d-\d\d \k\k\k \d\d:\d\d>\)/
syn match org_timestamp /\(<%%(diary-float.\+>\)/
hi def link org_timestamp PreProc
" special words
syn match today /TODAY$/
hi def link today PreProc
syn match week_agenda /^Week Agenda:$/
hi def link week_agenda PreProc
" Hyperlinks
syntax match hyperlink "\[\{2}[^][]*\(\]\[[^][]*\)\?\]\{2}" contains=hyperlinkBracketsLeft,hyperlinkURL,hyperlinkBracketsRight containedin=ALL
syntax match hyperlinkBracketsLeft contained "\[\{2}" conceal
syntax match hyperlinkURL contained "[^][]*\]\[" conceal
syntax match hyperlinkBracketsRight contained "\]\{2}" conceal
hi def link hyperlink Underlined

View file

@ -0,0 +1,47 @@
syn match org_todo_key /\[\zs[^]]*\ze\]/
hi def link org_todo_key Identifier
let s:todo_headings = ''
let s:i = 1
while s:i <= g:org_heading_highlight_levels
if s:todo_headings == ''
let s:todo_headings = 'containedin=org_heading' . s:i
else
let s:todo_headings = s:todo_headings . ',org_heading' . s:i
endif
let s:i += 1
endwhile
unlet! s:i
if !exists('g:loaded_orgtodo_syntax')
let g:loaded_orgtodo_syntax = 1
function! s:ReadTodoKeywords(keywords, todo_headings)
let l:default_group = 'Todo'
for l:i in a:keywords
if type(l:i) == 3
call s:ReadTodoKeywords(l:i, a:todo_headings)
continue
endif
if l:i == '|'
let l:default_group = 'Question'
continue
endif
" strip access key
let l:_i = substitute(l:i, "\(.*$", "", "")
let l:group = l:default_group
for l:j in g:org_todo_keyword_faces
if l:j[0] == l:_i
let l:group = 'orgtodo_todo_keyword_face_' . l:_i
call OrgExtendHighlightingGroup(l:default_group, l:group, OrgInterpretFaces(l:j[1]))
break
endif
endfor
silent! exec 'syntax match orgtodo_todo_keyword_' . l:_i . ' /' . l:_i .'/ ' . a:todo_headings
silent! exec 'hi def link orgtodo_todo_keyword_' . l:_i . ' ' . l:group
endfor
endfunction
endif
call s:ReadTodoKeywords(g:org_todo_keywords, s:todo_headings)
unlet! s:todo_headings

View file

@ -0,0 +1,37 @@
* bold, italics and underline syntax matching
** Should match:
*foo* *foo*
*Really, quite long sentence*.
_foo_ _foo_
_really, quite long sentence._.
*Übermensch á* *eä* *ý€*
_Ÿ ï_
*sdf l.*
*sdfsdf ,.*
*foo_ sdf /*
/sdf sdf sdf ./
/google.com/
*[sdf]*
*a* /a/ =b= ~b~ `d`
*abc* /abc/ =bde= ~bde~ `def`
*=*a*=*
** Should not match
http://google.com/
//google.com/
* sdf* _ sdf_
*sdfsdf sdf,*
*foo *
foo_not underlined_bar
*.sdf*[
[*.sdf*
[*sdf*]
*=*a*=

View file

@ -0,0 +1,59 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import test_vimbuffer
import test_libagendafilter
import test_libcheckbox
import test_libbase
import test_libheading
import test_liborgdate
import test_liborgdate_utf8
import test_liborgdate_parsing
import test_liborgdatetime
import test_liborgtimerange
import test_plugin_date
import test_plugin_edit_structure
import test_plugin_edit_checkbox
import test_plugin_misc
import test_plugin_navigator
import test_plugin_show_hide
import test_plugin_tags_properties
import test_plugin_todo
import test_plugin_mappings
import unittest
if __name__ == '__main__':
tests = unittest.TestSuite()
tests.addTests(test_vimbuffer.suite())
# lib
tests.addTests(test_libbase.suite())
tests.addTests(test_libcheckbox.suite())
tests.addTests(test_libagendafilter.suite())
tests.addTests(test_libheading.suite())
tests.addTests(test_liborgdate.suite())
tests.addTests(test_liborgdate_utf8.suite())
tests.addTests(test_liborgdate_parsing.suite())
tests.addTests(test_liborgdatetime.suite())
tests.addTests(test_liborgtimerange.suite())
# plugins
tests.addTests(test_plugin_date.suite())
tests.addTests(test_plugin_edit_structure.suite())
tests.addTests(test_plugin_edit_checkbox.suite())
tests.addTests(test_plugin_misc.suite())
tests.addTests(test_plugin_navigator.suite())
tests.addTests(test_plugin_show_hide.suite())
tests.addTests(test_plugin_tags_properties.suite())
tests.addTests(test_plugin_todo.suite())
tests.addTests(test_plugin_mappings.suite())
runner = unittest.TextTestRunner()
runner.run(tests)
# vim: set noexpandtab:

View file

@ -0,0 +1,171 @@
# -*- coding: utf-8 -*-
import sys
sys.path.append(u'../ftplugin')
import unittest
from datetime import date
from datetime import timedelta
from orgmode.liborgmode.headings import Heading
from orgmode.liborgmode.orgdate import OrgDate
from orgmode.liborgmode.agendafilter import contains_active_todo
from orgmode.liborgmode.agendafilter import contains_active_date
from orgmode.liborgmode.orgdate import OrgDateTime
from orgmode.liborgmode.agendafilter import is_within_week
from orgmode.liborgmode.agendafilter import is_within_week_and_active_todo
from orgmode.liborgmode.agendafilter import filter_items
import vim
from orgmode.py3compat.encode_compatibility import *
counter = 0
class AgendaFilterTestCase(unittest.TestCase):
u"""Tests all the functionality of the Agenda filter module."""
def setUp(self):
global counter
counter += 1
vim.EVALHISTORY = []
vim.EVALRESULTS = {
# no org_todo_keywords for b
u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'),
# global values for org_todo_keywords
u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'),
u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')],
u_encode(u'exists("g:org_debug")'): u_encode(u'0'),
u_encode(u'exists("b:org_debug")'): u_encode(u'0'),
u_encode(u'exists("*repeat#set()")'): u_encode(u'0'),
u_encode(u'b:changedtick'): u_encode(u'%d' % counter),
u_encode(u"v:count"): u_encode(u'0')
}
vim.current.buffer[:] = [u_encode(i) for i in u"""
* TODO Heading 1
some text
""".split(u'\n')]
def test_contains_active_todo(self):
heading = Heading(title=u'Refactor the code', todo='TODO')
self.assertTrue(contains_active_todo(heading))
heading = Heading(title=u'Refactor the code', todo='DONE')
self.assertFalse(contains_active_todo(heading))
heading = Heading(title=u'Refactor the code', todo=None)
self.assertFalse(contains_active_todo(heading))
def test_contains_active_date(self):
heading = Heading(title=u'Refactor the code', active_date=None)
self.assertFalse(contains_active_date(heading))
odate = OrgDate(True, 2011, 11, 1)
heading = Heading(title=u'Refactor the code', active_date=odate)
self.assertTrue(contains_active_date(heading))
def test_is_within_week_with_orgdate(self):
# to far in the future
tmpdate = date.today() + timedelta(days=8)
odate = OrgDate(True, tmpdate.year, tmpdate.month, tmpdate.day)
heading = Heading(title=u'Refactor the code', active_date=odate)
self.assertFalse(is_within_week(heading))
# within a week
tmpdate = date.today() + timedelta(days=5)
odate = OrgDate(True, tmpdate.year, tmpdate.month, tmpdate.day)
heading = Heading(title=u'Refactor the code', active_date=odate)
self.assertTrue(is_within_week(heading))
# in the past
tmpdate = date.today() - timedelta(days=105)
odate = OrgDate(True, tmpdate.year, tmpdate.month, tmpdate.day)
heading = Heading(title=u'Refactor the code', active_date=odate)
self.assertTrue(is_within_week(heading))
def test_is_within_week_with_orgdatetime(self):
# to far in the future
tmp = date.today() + timedelta(days=1000)
odate = OrgDateTime(True, tmp.year, tmp.month, tmp.day, 10, 10)
heading = Heading(title=u'Refactor the code', active_date=odate)
self.assertFalse(is_within_week(heading))
# within a week
tmpdate = date.today() + timedelta(days=5)
odate = OrgDateTime(True, tmpdate.year, tmpdate.month, tmpdate.day, 1, 0)
heading = Heading(title=u'Refactor the code', active_date=odate)
self.assertTrue(is_within_week(heading))
# in the past
tmpdate = date.today() - timedelta(days=5)
odate = OrgDateTime(True, tmpdate.year, tmpdate.month, tmpdate.day, 1, 0)
heading = Heading(title=u'Refactor the code', active_date=odate)
self.assertTrue(is_within_week(heading))
def test_filter_items(self):
# only headings with date and todo should be returned
vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = \
[u_encode(u'TODO'), u_encode(u'STARTED'), u_encode(u'|'), u_encode(u'DONE')]
tmpdate = date.today()
odate = OrgDate(True, tmpdate.year, tmpdate.month, tmpdate.day)
tmp_head = Heading(title=u'Refactor the code', todo=u'TODO', active_date=odate)
tmp_head_01 = Heading(title=u'Refactor the code', todo=u'STARTED', active_date=odate)
# TODO add more tests
headings = [tmp_head, tmp_head_01]
filtered = list(filter_items(headings,
[contains_active_date, contains_active_todo]))
self.assertEqual(len(filtered), 2)
self.assertEqual(filtered, headings)
# try a longer list
headings = headings * 3
filtered = list(filter_items(headings,
[contains_active_date, contains_active_todo]))
self.assertEqual(len(filtered), 6)
self.assertEqual(filtered, headings)
# date does not contain all needed fields thus gets ignored
tmpdate = date.today()
odate = OrgDate(True, tmpdate.year, tmpdate.month, tmpdate.day)
tmp_head = Heading(title=u'Refactor the code', active_date=odate)
headings = [tmp_head]
filtered = list(filter_items(headings, [contains_active_date,
contains_active_todo]))
self.assertEqual([], filtered)
def test_filter_items_with_some_todos_and_dates(self):
u"""
Only the headings with todo and dates should be retunrned.
"""
tmp = [u"* TODO OrgMode Demo und Tests"
u"<2011-08-22 Mon>"]
headings = [Heading.parse_heading_from_data(tmp, [u'TODO'])]
filtered = list(filter_items(headings,
[is_within_week_and_active_todo]))
self.assertEqual(len(filtered), 1)
self.assertEqual(headings, filtered)
tmp = [Heading.parse_heading_from_data([u"** DONE something <2011-08-10 Wed>"], [u'TODO']),
Heading.parse_heading_from_data([u"*** TODO rsitenaoritns more <2011-08-25 Thu>"], [u'TODO']),
Heading.parse_heading_from_data([u"*** DONE some more <2011-08-25 Thu>"], [u'TODO']),
Heading.parse_heading_from_data([u"*** TODO some more <2011-08-25 Thu>"], [u'TODO']),
Heading.parse_heading_from_data([u"** DONE something2 <2011-08-10 Wed>"], [u'TODO'])
]
for h in tmp:
headings.append(h)
filtered = list(filter_items(headings,
[is_within_week_and_active_todo]))
self.assertEqual(len(filtered), 3)
self.assertEqual(filtered, [headings[0], headings[2], headings[4]])
def suite():
return unittest.TestLoader().loadTestsFromTestCase(AgendaFilterTestCase)
# vim: set noexpandtab:

View file

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
import unittest
import sys
sys.path.append(u'../ftplugin')
from orgmode.liborgmode.base import Direction, get_domobj_range
from orgmode.liborgmode.headings import Heading
class LibBaseTestCase(unittest.TestCase):
def setUp(self):
self.case1 = """
* head1
heading body
for testing
* head2
** head3
""".split("\n")
def test_base_functions(self):
# direction FORWARD
(start, end) = get_domobj_range(content=self.case1, position=1, identify_fun=Heading.identify_heading)
self.assertEqual((start, end), (1, 3))
(start, end) = get_domobj_range(content=self.case1, position=3, direction=Direction.BACKWARD, \
identify_fun=Heading.identify_heading)
self.assertEqual((start, end), (1, 3))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(
LibBaseTestCase)
# vim: set noexpandtab:

View file

@ -0,0 +1,132 @@
# -*- coding: utf-8 -*-
import unittest
import sys
sys.path.append(u'../ftplugin')
import vim
from orgmode.liborgmode.checkboxes import Checkbox
from orgmode._vim import ORGMODE
from orgmode.py3compat.encode_compatibility import *
def set_vim_buffer(buf=None, cursor=(2, 0), bufnr=0):
if buf is None:
buf = []
vim.current.buffer[:] = buf
vim.current.window.cursor = cursor
vim.current.buffer.number = bufnr
class CheckboxTestCase(unittest.TestCase):
def setUp(self):
counter = 0
vim.CMDHISTORY = []
vim.CMDRESULTS = {}
vim.EVALHISTORY = []
vim.EVALRESULTS = {
# no org_todo_keywords for b
u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'),
# global values for org_todo_keywords
u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'),
u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')],
u_encode(u'exists("g:org_improve_split_heading")'): u_encode(u'0'),
u_encode(u'exists("b:org_improve_split_heading")'): u_encode(u'0'),
u_encode(u'exists("g:org_debug")'): u_encode(u'0'),
u_encode(u'exists("b:org_debug")'): u_encode(u'0'),
u_encode(u'exists("*repeat#set()")'): u_encode(u'0'),
u_encode(u'b:changedtick'): u_encode(u'%d' % counter),
u_encode(u'&ts'): u_encode(u'8'),
u_encode(u'exists("g:org_tag_column")'): u_encode(u'0'),
u_encode(u'exists("b:org_tag_column")'): u_encode(u'0'),
u_encode(u"v:count"): u_encode(u'0')}
self.c1 = """
* heading1 [/]
- [-] checkbox1 [%]
- [X] checkbox2
- [ ] checkbox3
- [X] checkbox4
""".split("\n")
self.c2 = """
* heading1
- [ ] checkbox1
- [ ] checkbox2
- [ ] checkbox3
- [ ] checkbox4
- [ ] checkbox5
- [ ] checkbox6
""".split("\n")
def test_init(self):
# test initialize Checkbox
c = Checkbox(level=1, title="checkbox1")
self.assertEqual(str(c), " - [ ] checkbox1")
c = Checkbox(level=3, title="checkbox2", status="[X]")
self.assertEqual(str(c), " - [X] checkbox2")
def test_basic(self):
bufnr = 1
set_vim_buffer(buf=self.c1, bufnr=bufnr)
h = ORGMODE.get_document(bufnr=bufnr).current_heading()
h.init_checkboxes()
c = h.current_checkbox(position=2)
self.assertEqual(str(c), self.c1[2])
self.assertFalse(c.are_children_all(Checkbox.STATUS_ON))
self.assertTrue(c.is_child_one(Checkbox.STATUS_OFF))
self.assertFalse(c.are_siblings_all(Checkbox.STATUS_ON))
for child in c.all_children():
pass
for sibling in c.all_siblings():
pass
c = h.current_checkbox(position=3)
new_checkbox = c.copy()
self.assertEqual(str(c), self.c1[3])
c.get_parent_list()
c.get_index_in_parent_list()
def test_identify(self):
# test identify_checkbox
self.assertEqual(Checkbox.identify_checkbox(self.c1[2]), 2)
self.assertEqual(Checkbox.identify_checkbox(self.c1[3]), 8)
# check for corner case
self.assertEqual(Checkbox.identify_checkbox(" - [ ]"), 1)
def test_toggle(self):
bufnr = 2
# test init_checkboxes
set_vim_buffer(buf=self.c1, bufnr=bufnr)
h = ORGMODE.get_document(bufnr=bufnr).current_heading()
h.init_checkboxes()
# toggle checkbox
c = h.current_checkbox(position=4)
c.toggle()
self.assertEqual(str(c), " - [X] checkbox3")
c.toggle()
self.assertEqual(str(c), " - [ ] checkbox3")
(total, on) = c.all_siblings_status()
self.assertEqual((total, on), (2, 1))
def test_subtasks(self):
bufnr = 3
set_vim_buffer(buf=self.c1, bufnr=bufnr)
h = ORGMODE.get_document(bufnr=bufnr).current_heading()
h.init_checkboxes()
c = h.current_checkbox(position=3)
c.toggle()
c = h.current_checkbox(position=2)
(total, on) = c.all_siblings_status()
c.update_subtasks(total=total, on=on)
self.assertEqual(str(c), " - [-] checkbox1 [50%]")
def suite():
return unittest.TestLoader().loadTestsFromTestCase(CheckboxTestCase)
# vim: set noexpandtab:

View file

@ -0,0 +1,156 @@
# -*- coding: utf-8 -*-
import unittest
import sys
sys.path.append(u'../ftplugin')
from orgmode.liborgmode.headings import Heading
from orgmode.liborgmode.orgdate import OrgDate
from orgmode.liborgmode.orgdate import OrgDateTime
class TestHeadingRecognizeDatesInHeading(unittest.TestCase):
def setUp(self):
self.allowed_todo_states = ["TODO"]
tmp = ["* This heading is earlier <2011-08-24 Wed>"]
self.h1 = Heading.parse_heading_from_data(tmp, self.allowed_todo_states)
tmp = ["* This heading is later <2011-08-25 Thu>"]
self.h2 = Heading.parse_heading_from_data(tmp, self.allowed_todo_states)
tmp = ["* This heading is later <2011-08-25 Thu 10:20>"]
self.h2_datetime = Heading.parse_heading_from_data(tmp, self.allowed_todo_states)
tmp = ["* This heading is later <2011-08-26 Fri 10:20>"]
self.h3 = Heading.parse_heading_from_data(tmp, self.allowed_todo_states)
tmp = ["* This heading has no date and should be later than the rest"]
self.h_no_date = Heading.parse_heading_from_data(tmp,
self.allowed_todo_states)
def test_heading_parsing_no_date(self):
"""""
'text' doesn't contain any valid date.
"""
text = ["* TODO This is a test :hallo:"]
h = Heading.parse_heading_from_data(text, self.allowed_todo_states)
self.assertEqual(None, h.active_date)
text = ["* TODO This is a test <2011-08-25>"]
h = Heading.parse_heading_from_data(text, self.allowed_todo_states)
self.assertEqual(None, h.active_date)
text = ["* TODO This is a test <2011-08-25 Wednesday>"]
h = Heading.parse_heading_from_data(text, self.allowed_todo_states)
self.assertEqual(None, h.active_date)
text = ["* TODO This is a test <20110825>"]
h = Heading.parse_heading_from_data(text, self.allowed_todo_states)
self.assertEqual(None, h.active_date)
def test_heading_parsing_with_date(self):
"""""
'text' does contain valid dates.
"""
# orgdate
text = ["* TODO This is a test <2011-08-24 Wed> :hallo:"]
odate = OrgDate(True, 2011, 8, 24)
h = Heading.parse_heading_from_data(text, self.allowed_todo_states)
self.assertEqual(odate, h.active_date)
# orgdatetime
text = ["* TODO This is a test <2011-08-25 Thu 10:10> :hallo:"]
odate = OrgDateTime(True, 2011, 8, 25, 10, 10)
h = Heading.parse_heading_from_data(text, self.allowed_todo_states)
self.assertEqual(odate, h.active_date)
def test_heading_parsing_with_date_and_body(self):
"""""
'text' contains valid dates (in the body).
"""
# orgdatetime
text = ["* TODO This is a test <2011-08-25 Thu 10:10> :hallo:",
"some body text",
"some body text"]
h = Heading.parse_heading_from_data(text, self.allowed_todo_states)
self.assertTrue(isinstance(h.active_date, OrgDateTime))
self.assertEqual("<2011-08-25 Thu 10:10>", str(h.active_date))
text = ["* TODO This is a test :hallo:",
"some body text",
"some body text<2011-08-25 Thu 10:10>"]
h = Heading.parse_heading_from_data(text, self.allowed_todo_states)
self.assertTrue(isinstance(h.active_date, OrgDateTime))
self.assertEqual("<2011-08-25 Thu 10:10>", str(h.active_date))
text = ["* TODO This is a test :hallo:",
"some body text <2011-08-24 Wed>",
"some body text<2011-08-25 Thu 10:10>"]
h = Heading.parse_heading_from_data(text, self.allowed_todo_states)
odate = OrgDate(True, 2011, 8, 24)
self.assertEqual(odate, h.active_date)
def test_less_than_for_dates_in_heading(self):
self.assertTrue(self.h1 < self.h2)
self.assertTrue(self.h1 < self.h3)
self.assertTrue(self.h1 < self.h_no_date)
self.assertTrue(self.h2 < self.h_no_date)
self.assertTrue(self.h2 < self.h3)
self.assertTrue(self.h3 < self.h_no_date)
self.assertFalse(self.h2 < self.h1)
self.assertFalse(self.h3 < self.h2)
def test_less_equal_for_dates_in_heading(self):
self.assertTrue(self.h1 <= self.h2)
self.assertTrue(self.h1 <= self.h_no_date)
self.assertTrue(self.h2 <= self.h_no_date)
self.assertTrue(self.h2 <= self.h2_datetime)
self.assertTrue(self.h2 <= self.h3)
def test_greate_than_for_dates_in_heading(self):
self.assertTrue(self.h2 > self.h1)
self.assertTrue(self.h_no_date > self.h1)
self.assertTrue(self.h_no_date > self.h2)
self.assertFalse(self.h2 > self.h2_datetime)
def test_greate_equal_for_dates_in_heading(self):
self.assertTrue(self.h2 >= self.h1)
self.assertTrue(self.h_no_date >= self.h1)
self.assertTrue(self.h_no_date >= self.h2)
self.assertTrue(self.h2 >= self.h2_datetime)
def test_sorting_of_headings(self):
"""Headings should be sortable."""
self.assertEqual([self.h1, self.h2], sorted([self.h2, self.h1]))
self.assertEqual([self.h1, self.h2_datetime],
sorted([self.h2_datetime, self.h1]))
self.assertEqual([self.h2_datetime, self.h2],
sorted([self.h2_datetime, self.h2]))
self.assertEqual([self.h1, self.h2], sorted([self.h1, self.h2]))
self.assertEqual([self.h1, self.h_no_date],
sorted([self.h1, self.h_no_date]))
self.assertEqual([self.h1, self.h_no_date],
sorted([self.h_no_date, self.h1]))
self.assertEqual([self.h1, self.h2, self.h_no_date],
sorted([self.h2, self.h_no_date, self.h1]))
self.assertEqual(
[self.h1, self.h2_datetime, self.h2, self.h3, self.h_no_date],
sorted([self.h2_datetime, self.h3, self.h2, self.h_no_date, self.h1]))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(
TestHeadingRecognizeDatesInHeading)
# vim: set noexpandtab:

View file

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
import sys
import unittest
from datetime import date
sys.path.append(u'../ftplugin')
from orgmode.liborgmode.orgdate import OrgDate
from orgmode.py3compat.unicode_compatibility import *
class OrgDateTestCase(unittest.TestCase):
u"""
Tests all the functionality of the OrgDate
"""
def setUp(self):
self.date = date(2011, 8, 29)
self.year = 2011
self.month = 8
self.day = 29
self.text = u'<2011-08-29 Mon>'
self.textinactive = u'[2011-08-29 Mon]'
def test_OrgDate_ctor_active(self):
u"""OrdDate should be created."""
today = date.today()
od = OrgDate(True, today.year, today.month, today.day)
self.assertTrue(isinstance(od, OrgDate))
self.assertTrue(od.active)
def test_OrgDate_ctor_inactive(self):
u"""OrdDate should be created."""
today = date.today()
od = OrgDate(False, today.year, today.month, today.day)
self.assertTrue(isinstance(od, OrgDate))
self.assertFalse(od.active)
def test_OrdDate_str_active(self):
u"""Representation of OrgDates"""
od = OrgDate(True, self.year, self.month, self.day)
self.assertEqual(self.text, unicode(od))
def test_OrdDate_str_inactive(self):
od = OrgDate(False, self.year, self.month, self.day)
self.assertEqual(self.textinactive, unicode(od))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(OrgDateTestCase)
# vi: noexpandtab

View file

@ -0,0 +1,248 @@
# -*- coding: utf-8 -*-
import sys
import unittest
sys.path.append(u'../ftplugin')
from orgmode.liborgmode.orgdate import get_orgdate
from orgmode.liborgmode.orgdate import OrgDate
from orgmode.liborgmode.orgdate import OrgDateTime
from orgmode.liborgmode.orgdate import OrgTimeRange
from orgmode.py3compat.unicode_compatibility import *
class OrgDateParsingTestCase(unittest.TestCase):
u"""
Tests the functionality of the parsing function of OrgDate.
Mostly function get_orgdate().
"""
def setUp(self):
self.text = u'<2011-08-29 Mon>'
self.textinactive = u'[2011-08-29 Mon]'
def test_get_orgdate_parsing_active(self):
u"""
get_orgdate should recognize all orgdates in a given text
"""
result = get_orgdate(self.text)
self.assertNotEqual(result, None)
self.assertTrue(isinstance(result, OrgDate))
self.assertTrue(isinstance(get_orgdate(u"<2011-08-30 Tue>"), OrgDate))
self.assertEqual(get_orgdate(u"<2011-08-30 Tue>").year, 2011)
self.assertEqual(get_orgdate(u"<2011-08-30 Tue>").month, 8)
self.assertEqual(get_orgdate(u"<2011-08-30 Tue>").day, 30)
self.assertTrue(get_orgdate(u"<2011-08-30 Tue>").active)
datestr = u"This date <2011-08-30 Tue> is embedded"
self.assertTrue(isinstance(get_orgdate(datestr), OrgDate))
def test_get_orgdatetime_parsing_active(self):
u"""
get_orgdate should recognize all orgdatetimes in a given text
"""
result = get_orgdate(u"<2011-09-12 Mon 10:20>")
self.assertNotEqual(result, None)
self.assertTrue(isinstance(result, OrgDateTime))
self.assertEqual(result.year, 2011)
self.assertEqual(result.month, 9)
self.assertEqual(result.day, 12)
self.assertEqual(result.hour, 10)
self.assertEqual(result.minute, 20)
self.assertTrue(result.active)
result = get_orgdate(u"some datetime <2011-09-12 Mon 10:20> stuff")
self.assertTrue(isinstance(result, OrgDateTime))
def test_get_orgtimerange_parsing_active(self):
u"""
get_orgdate should recognize all orgtimeranges in a given text
"""
daterangestr = u"<2011-09-12 Mon>--<2011-09-13 Tue>"
result = get_orgdate(daterangestr)
self.assertNotEqual(result, None)
self.assertTrue(isinstance(result, OrgTimeRange))
self.assertEqual(unicode(result), daterangestr)
self.assertTrue(result.active)
daterangestr = u"<2011-09-12 Mon 10:20>--<2011-09-13 Tue 13:20>"
result = get_orgdate(daterangestr)
self.assertNotEqual(result, None)
self.assertTrue(isinstance(result, OrgTimeRange))
self.assertEqual(unicode(result), daterangestr)
self.assertTrue(result.active)
daterangestr = u"<2011-09-12 Mon 10:20-13:20>"
result = get_orgdate(daterangestr)
self.assertNotEqual(result, None)
self.assertTrue(isinstance(result, OrgTimeRange))
self.assertEqual(unicode(result), daterangestr)
self.assertTrue(result.active)
def test_get_orgdate_parsing_inactive(self):
u"""
get_orgdate should recognize all inactive orgdates in a given text
"""
result = get_orgdate(self.textinactive)
self.assertNotEqual(result, None)
self.assertTrue(isinstance(result, OrgDate))
self.assertTrue(isinstance(get_orgdate(u"[2011-08-30 Tue]"), OrgDate))
self.assertEqual(get_orgdate(u"[2011-08-30 Tue]").year, 2011)
self.assertEqual(get_orgdate(u"[2011-08-30 Tue]").month, 8)
self.assertEqual(get_orgdate(u"[2011-08-30 Tue]").day, 30)
self.assertFalse(get_orgdate(u"[2011-08-30 Tue]").active)
datestr = u"This date [2011-08-30 Tue] is embedded"
self.assertTrue(isinstance(get_orgdate(datestr), OrgDate))
def test_get_orgdatetime_parsing_passive(self):
u"""
get_orgdate should recognize all orgdatetimes in a given text
"""
result = get_orgdate(u"[2011-09-12 Mon 10:20]")
self.assertNotEqual(result, None)
self.assertTrue(isinstance(result, OrgDateTime))
self.assertEqual(result.year, 2011)
self.assertEqual(result.month, 9)
self.assertEqual(result.day, 12)
self.assertEqual(result.hour, 10)
self.assertEqual(result.minute, 20)
self.assertFalse(result.active)
result = get_orgdate(u"some datetime [2011-09-12 Mon 10:20] stuff")
self.assertTrue(isinstance(result, OrgDateTime))
def test_get_orgdate_parsing_with_list_of_texts(self):
u"""
get_orgdate should return the first date in the list.
"""
datelist = [u"<2011-08-29 Mon>"]
result = get_orgdate(datelist)
self.assertNotEquals(result, None)
self.assertTrue(isinstance(result, OrgDate))
self.assertEqual(result.year, 2011)
self.assertEqual(result.month, 8)
self.assertEqual(result.day, 29)
datelist = [u"<2011-08-29 Mon>",
u"<2012-03-30 Fri>"]
result = get_orgdate(datelist)
self.assertNotEquals(result, None)
self.assertTrue(isinstance(result, OrgDate))
self.assertEqual(result.year, 2011)
self.assertEqual(result.month, 8)
self.assertEqual(result.day, 29)
datelist = [u"some <2011-08-29 Mon>text",
u"<2012-03-30 Fri> is here"]
result = get_orgdate(datelist)
self.assertNotEquals(result, None)
self.assertTrue(isinstance(result, OrgDate))
self.assertEqual(result.year, 2011)
self.assertEqual(result.month, 8)
self.assertEqual(result.day, 29)
datelist = [u"here is no date",
u"some <2011-08-29 Mon>text",
u"<2012-03-30 Fri> is here"]
result = get_orgdate(datelist)
self.assertNotEquals(result, None)
self.assertTrue(isinstance(result, OrgDate))
self.assertEqual(result.year, 2011)
self.assertEqual(result.month, 8)
self.assertEqual(result.day, 29)
datelist = [u"here is no date",
u"some <2011-08-29 Mon 20:10> text",
u"<2012-03-30 Fri> is here"]
result = get_orgdate(datelist)
self.assertNotEquals(result, None)
self.assertTrue(isinstance(result, OrgDateTime))
self.assertEqual(result.year, 2011)
self.assertEqual(result.month, 8)
self.assertEqual(result.day, 29)
self.assertEqual(result.hour, 20)
self.assertEqual(result.minute, 10)
def test_get_orgdate_parsing_with_invalid_input(self):
self.assertEquals(get_orgdate(u"NONSENSE"), None)
self.assertEquals(get_orgdate(u"No D<2011- Date 08-29 Mon>"), None)
self.assertEquals(get_orgdate(u"2011-08-r9 Mon]"), None)
self.assertEquals(get_orgdate(u"<2011-08-29 Mon"), None)
self.assertEquals(get_orgdate(u"<2011-08-29 Mon]"), None)
self.assertEquals(get_orgdate(u"2011-08-29 Mon"), None)
self.assertEquals(get_orgdate(u"2011-08-29"), None)
self.assertEquals(get_orgdate(u"2011-08-29 mon"), None)
self.assertEquals(get_orgdate(u"<2011-08-29 mon>"), None)
self.assertEquals(get_orgdate(u"wrong date embedded <2011-08-29 mon>"), None)
self.assertEquals(get_orgdate(u"wrong date <2011-08-29 mon>embedded "), None)
def test_get_orgdate_parsing_with_invalid_dates(self):
u"""
Something like <2011-14-29 Mon> (invalid dates, they don't exist)
should not be parsed
"""
datestr = u"<2011-14-30 Tue>"
self.assertEqual(get_orgdate(datestr), None)
datestr = u"<2012-03-40 Tue>"
self.assertEqual(get_orgdate(datestr), None)
datestr = u"<2012-03-40 Tue 24:70>"
self.assertEqual(get_orgdate(datestr), None)
def test_get_orgdate_parsing_with_utf8(self):
u"""
get_orgdate should recognize all orgdates within a given utf-8 text
"""
result = get_orgdate(u'<2016-05-07 Sáb>')
self.assertNotEqual(result, None)
self.assertTrue(isinstance(result, OrgDate))
self.assertEqual(result.year, 2016)
self.assertEqual(result.month, 5)
self.assertEqual(result.day, 7)
self.assertTrue(result.active)
datestr = u"This date <2016-05-07 Sáb> is embedded"
self.assertTrue(isinstance(get_orgdate(datestr), OrgDate))
result = get_orgdate(u'[2016-05-07 Sáb]')
self.assertFalse(result.active)
datestr = u"This date [2016-05-07 Sáb] is embedded"
self.assertTrue(isinstance(get_orgdate(datestr), OrgDate))
def test_get_orgdatetime_parsing_with_utf8(self):
u"""
get_orgdate should recognize all orgdatetimes in a given utf-8 text
"""
result = get_orgdate(u"<2016-05-07 Sáb 10:20>")
self.assertNotEqual(result, None)
self.assertTrue(isinstance(result, OrgDateTime))
self.assertEqual(result.year, 2016)
self.assertEqual(result.month, 5)
self.assertEqual(result.day, 7)
self.assertEqual(result.hour, 10)
self.assertEqual(result.minute, 20)
self.assertTrue(result.active)
result = get_orgdate(u"some datetime <2016-05-07 Sáb 10:20> stuff")
self.assertTrue(isinstance(result, OrgDateTime))
result = get_orgdate(u"[2016-05-07 Sáb 10:20]")
self.assertFalse(result.active)
result = get_orgdate(u"some datetime [2016-05-07 Sáb 10:20] stuff")
self.assertTrue(isinstance(result, OrgDateTime))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(OrgDateParsingTestCase)
# vim: noexpandtab

View file

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
import sys
import unittest
import locale
import threading
from datetime import date
from contextlib import contextmanager
from orgmode.py3compat.unicode_compatibility import *
sys.path.append(u'../ftplugin')
from orgmode.liborgmode.orgdate import OrgDate
class OrgDateUtf8TestCase(unittest.TestCase):
u"""
Tests OrgDate with utf-8 enabled locales
"""
LOCALE_LOCK = threading.Lock()
UTF8_LOCALE = "pt_BR.utf-8"
@contextmanager
def setlocale(self, name):
with self.LOCALE_LOCK:
saved = locale.setlocale(locale.LC_ALL)
try:
yield locale.setlocale(locale.LC_ALL, name)
finally:
locale.setlocale(locale.LC_ALL, saved)
def setUp(self):
self.year = 2016
self.month = 5
self.day = 7
self.text = u'<2016-05-07 Sáb>'
self.textinactive = u'[2016-05-07 Sáb]'
def test_OrdDate_str_unicode_active(self):
with self.setlocale(self.UTF8_LOCALE):
od = OrgDate(True, self.year, self.month, self.day)
self.assertEqual(self.text, unicode(od))
def test_OrdDate_str_unicode_inactive(self):
with self.setlocale(self.UTF8_LOCALE):
od = OrgDate(False, self.year, self.month, self.day)
self.assertEqual(self.textinactive, unicode(od))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(OrgDateUtf8TestCase)
# vi: noexpandtab

View file

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
import sys
import unittest
from datetime import datetime
sys.path.append(u'../ftplugin')
from orgmode.liborgmode.orgdate import OrgDateTime
from orgmode.py3compat.unicode_compatibility import *
class OrgDateTimeTestCase(unittest.TestCase):
u"""
Tests all the functionality of the OrgDateTime
"""
def test_OrgDateTime_ctor_active(self):
u"""OrdDateTime should be created."""
today = datetime.today()
odt = OrgDateTime(True, today.year, today.month, today.day, today.hour,
today.minute)
self.assertTrue(isinstance(odt, OrgDateTime))
self.assertTrue(odt.active)
def test_OrgDateTime_ctor_inactive(self):
u"""OrdDateTime should be created."""
today = datetime.today()
odt = OrgDateTime(False, today.year, today.month, today.day, today.hour,
today.minute)
self.assertTrue(isinstance(odt, OrgDateTime))
self.assertFalse(odt.active)
def test_OrdDateTime_str_active(self):
u"""Representation of OrgDateTime"""
t = 2011, 9, 8, 10, 20
odt = OrgDateTime(False, t[0], t[1], t[2], t[3], t[4])
self.assertEqual(u"[2011-09-08 Thu 10:20]", unicode(odt))
def test_OrdDateTime_str_inactive(self):
u"""Representation of OrgDateTime"""
t = 2011, 9, 8, 10, 20
odt = OrgDateTime(True, t[0], t[1], t[2], t[3], t[4])
self.assertEqual(u"<2011-09-08 Thu 10:20>", unicode(odt))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(OrgDateTimeTestCase)
# vim: noexpandtab

View file

@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
import sys
import unittest
from datetime import date
from datetime import datetime
sys.path.append(u'../ftplugin')
from orgmode.liborgmode.orgdate import OrgTimeRange
class OrgTimeRangeTestCase(unittest.TestCase):
def setUp(self):
self.date = date(2011, 8, 29)
self.year = 2011
self.month = 8
self.day = 29
self.text = '<2011-08-29 Mon>'
self.textinactive = '[2011-08-29 Mon]'
def test_OrgTimeRange_ctor_active(self):
u"""
timerange should be created.
"""
start = date(2011, 9 , 12)
end = date(2011, 9 , 13)
timerange = OrgTimeRange(True, start, end)
self.assertTrue(isinstance(timerange, OrgTimeRange))
self.assertTrue(timerange.active)
def test_OrgTimeRange_ctor_inactive(self):
u"""
timerange should be created.
"""
start = date(2011, 9 , 12)
end = date(2011, 9 , 13)
timerange = OrgTimeRange(False, start, end)
self.assertTrue(isinstance(timerange, OrgTimeRange))
self.assertFalse(timerange.active)
def test_OrdDate_str_active(self):
u"""Representation of OrgDates"""
start = date(2011, 9 , 12)
end = date(2011, 9 , 13)
timerange = OrgTimeRange(True, start, end)
expected = "<2011-09-12 Mon>--<2011-09-13 Tue>"
self.assertEqual(str(timerange), expected)
start = datetime(2011, 9 , 12, 20, 00)
end = datetime(2011, 9 , 13, 21, 59)
timerange = OrgTimeRange(True, start, end)
expected = "<2011-09-12 Mon 20:00>--<2011-09-13 Tue 21:59>"
self.assertEqual(str(timerange), expected)
start = datetime(2011, 9 , 12, 20, 00)
end = datetime(2011, 9 , 12, 21, 00)
timerange = OrgTimeRange(True, start, end)
expected = "<2011-09-12 Mon 20:00-21:00>"
self.assertEqual(str(timerange), expected)
def test_OrdDate_str_inactive(self):
u"""Representation of OrgDates"""
start = date(2011, 9 , 12)
end = date(2011, 9 , 13)
timerange = OrgTimeRange(False, start, end)
expected = "[2011-09-12 Mon]--[2011-09-13 Tue]"
self.assertEqual(str(timerange), expected)
start = datetime(2011, 9 , 12, 20, 00)
end = datetime(2011, 9 , 13, 21, 59)
timerange = OrgTimeRange(False, start, end)
expected = "[2011-09-12 Mon 20:00]--[2011-09-13 Tue 21:59]"
self.assertEqual(str(timerange), expected)
start = datetime(2011, 9 , 12, 20, 00)
end = datetime(2011, 9 , 12, 21, 00)
timerange = OrgTimeRange(False, start, end)
expected = "[2011-09-12 Mon 20:00-21:00]"
self.assertEqual(str(timerange), expected)
def suite():
return unittest.TestLoader().loadTestsFromTestCase(OrgTimeRangeTestCase)
# vim: noexpandtab

View file

@ -0,0 +1,174 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import unittest
import sys
sys.path.append(u'../ftplugin')
from datetime import date
from datetime import datetime
from orgmode.plugins.Date import Date
class DateTestCase(unittest.TestCase):
u"""Tests all the functionality of the Date plugin.
Also see:
http://orgmode.org/manual/The-date_002ftime-prompt.html#The-date_002ftime-prompt
"""
def setUp(self):
self.d = date(2011, 5, 22)
def test_modify_time_with_None(self):
# no modification should happen
res = Date._modify_time(self.d, None)
self.assertEquals(self.d, res)
def test_modify_time_with_dot(self):
# no modification should happen
res = Date._modify_time(self.d, u'.')
self.assertEquals(self.d, res)
def test_modify_time_with_given_relative_days(self):
# modifier and expected result
test_data = [(u'+0d', self.d),
(u'+1d', date(2011, 5, 23)),
(u'+2d', date(2011, 5, 24)),
(u'+7d', date(2011, 5, 29)),
(u'+9d', date(2011, 5, 31)),
(u'+10d', date(2011, 6, 1)),
(u'7d', self.d)] # wrong format: plus is missing
for modifier, expected in test_data:
self.assertEquals(expected, Date._modify_time(self.d, modifier))
def test_modify_time_with_given_relative_days_without_d(self):
# modifier and expected result
test_data = [(u'+0', self.d),
(u'+1', date(2011, 5, 23)),
(u'+2', date(2011, 5, 24)),
(u'+7', date(2011, 5, 29)),
(u'+9', date(2011, 5, 31)),
(u'+10', date(2011, 6, 1))]
for modifier, expected in test_data:
result = Date._modify_time(self.d, modifier)
self.assertEquals(expected, result)
def test_modify_time_with_given_relative_weeks(self):
# modifier and expected result
test_data = [(u'+1w', date(2011, 5, 29)),
(u'+2w', date(2011, 6, 5)),
(u'+3w', date(2011, 6, 12)),
(u'+3w', date(2011, 6, 12)),
(u'+0w', self.d),
(u'3w', self.d), # wrong format
(u'+w', self.d)] # wrong format
for modifier, expected in test_data:
self.assertEquals(expected, Date._modify_time(self.d, modifier))
def test_modify_time_with_given_relative_months(self):
test_data = [(u'+0m', self.d),
(u'+1m', date(2011, 6, 22)),
(u'+2m', date(2011, 7, 22))]
for modifier, expected in test_data:
self.assertEquals(expected, Date._modify_time(self.d, modifier))
def test_modify_time_with_given_relative_years(self):
test_data = [(u'+1y', date(2012, 5, 22)),
(u'+10y', date(2021, 5, 22)),
(u'+0y', self.d)]
for modifier, expected in test_data:
self.assertEquals(expected, Date._modify_time(self.d, modifier))
def test_modify_time_with_given_weekday(self):
# use custom day instead of self.d to ease testing
cust_day = date(2011, 5, 25) # it's a Wednesday
#print(cust_day.weekday()) # 2
test_data = [(u'Thu', date(2011, 5, 26)),
(u'thu', date(2011, 5, 26)),
(u'tHU', date(2011, 5, 26)),
(u'THU', date(2011, 5, 26)),
(u'Fri', date(2011, 5, 27)),
(u'sat', date(2011, 5, 28)),
(u'sun', date(2011, 5, 29)),
(u'mon', date(2011, 5, 30)),
(u'tue', date(2011, 5, 31)),
(u'wed', date(2011, 6, 1))]
for modifier, expected in test_data:
self.assertEquals(expected, Date._modify_time(cust_day, modifier))
def test_modify_time_with_month_and_day(self):
cust_date = date(2006, 6, 13)
test_data = [(u'sep 15', date(2006, 9, 15)),
(u'Sep 15', date(2006, 9, 15)),
(u'SEP 15', date(2006, 9, 15)),
(u'feb 15', date(2007, 2, 15)),
(u'jan 1', date(2007, 1, 1)),
(u'7/5', date(2006, 7, 5)),
(u'2/5', date(2007, 2, 5)),]
for modifier, expected in test_data:
self.assertEquals(expected, Date._modify_time(cust_date, modifier))
def test_modify_time_with_time(self):
cust_date = date(2006, 6, 13)
test_data = [(u'12:45', datetime(2006, 6, 13, 12, 45)),
(u'1:45', datetime(2006, 6, 13, 1, 45)),
(u'1:05', datetime(2006, 6, 13, 1, 5)),]
for modifier, expected in test_data:
res = Date._modify_time(cust_date, modifier)
self.assertTrue(isinstance(res, datetime))
self.assertEquals(expected, res)
def test_modify_time_with_full_dates(self):
result = Date._modify_time(self.d, u'2011-01-12')
expected = date(2011, 1, 12)
self.assertEquals(expected, result)
reults = Date._modify_time(self.d, u'2015-03-12')
expected = date(2015, 3, 12)
self.assertEquals(expected, reults)
cust_date = date(2006, 6, 13)
test_data = [(u'3-2-5', date(2003, 2, 5)),
(u'12-2-28', date(2012, 2, 28)),
(u'2/5/3', date(2003, 2, 5)),
(u'sep 12 9', date(2009, 9, 12)),
(u'jan 2 99', date(2099, 1, 2)),]
for modifier, expected in test_data:
self.assertEquals(expected, Date._modify_time(cust_date, modifier))
def test_modify_time_with_only_days(self):
cust_date = date(2006, 6, 13)
test_data = [(u'14', date(2006, 6, 14)),
(u'12', date(2006, 7, 12)),
(u'1', date(2006, 7, 1)),
(u'29', date(2006, 6, 29)),]
for modifier, expected in test_data:
self.assertEquals(expected, Date._modify_time(cust_date, modifier))
def test_modify_time_with_day_and_time(self):
cust_date = date(2006, 6, 13)
test_data = [(u'+1 10:20', datetime(2006, 6, 14, 10, 20)),
(u'+1w 10:20', datetime(2006, 6, 20, 10, 20)),
(u'+2 10:30', datetime(2006, 6, 15, 10, 30)),
(u'+2d 10:30', datetime(2006, 6, 15, 10, 30))]
for modifier, expected in test_data:
result = Date._modify_time(cust_date, modifier)
self.assertEquals(expected, result)
def suite():
return unittest.TestLoader().loadTestsFromTestCase(DateTestCase)
# vi: noexpandtab

View file

@ -0,0 +1,241 @@
# -*- coding: utf-8 -*-
import unittest
import sys
sys.path.append(u'../ftplugin')
import vim
from orgmode._vim import ORGMODE
from orgmode.py3compat.encode_compatibility import *
PLUGIN_NAME = u'EditCheckbox'
bufnr = 10
def set_vim_buffer(buf=None, cursor=(2, 0), bufnr=0):
if buf is None:
buf = []
vim.current.buffer[:] = buf
vim.current.window.cursor = cursor
vim.current.buffer.number = bufnr
counter = 0
class EditCheckboxTestCase(unittest.TestCase):
def setUp(self):
if PLUGIN_NAME not in ORGMODE.plugins:
ORGMODE.register_plugin(PLUGIN_NAME)
self.editcheckbox = ORGMODE.plugins[PLUGIN_NAME]
vim.EVALRESULTS = {
# no org_todo_keywords for b
u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'),
# global values for org_todo_keywords
u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'),
u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')],
u_encode(u'exists("g:org_improve_split_heading")'): u_encode(u'0'),
u_encode(u'exists("b:org_improve_split_heading")'): u_encode(u'0'),
u_encode(u'exists("g:org_debug")'): u_encode(u'0'),
u_encode(u'exists("b:org_debug")'): u_encode(u'0'),
u_encode(u'exists("*repeat#set()")'): u_encode(u'0'),
u_encode(u'b:changedtick'): u_encode(u'%d' % counter),
u_encode(u'&ts'): u_encode(u'8'),
u_encode(u'exists("g:org_tag_column")'): u_encode(u'0'),
u_encode(u'exists("b:org_tag_column")'): u_encode(u'0'),
u_encode(u"v:count"): u_encode(u'0'),
# jump to insert mode after adding heading/checkbox
u_encode(u'exists("g:org_prefer_insert_mode")'): u_encode(u'0'),
u_encode(u'exists("b:org_prefer_insert_mode")'): u_encode(u'0')}
self.c1 = u"""
* heading1 [%]
- [ ] checkbox1 [/]
- [ ] checkbox2
- [ ] checkbox3
- [ ] checkbox4
- [ ] checkbox5
- [ ] checkbox6
- [ ] checkbox7
- [ ] checkbox8
""".split(u'\n')
self.c2 = u"""
* a checkbox list [%]
- checkbox [0%]
- [ ] test1
- [ ] test2
- [ ] test3
""".split(u'\n')
self.c3 = u"""
* heading
1. [ ] another main task [%]
- [ ] sub task 1
- [ ] sub task 2
2. [ ] another main task
""".split(u'\n')
self.c4 = u"""
* heading
""".split(u'\n')
self.c5 = u"""
* heading1
1. item
9. item
}. item
a. item
z. item
A. item
Z. item
aa. item
""".split("\n")
def test_toggle(self):
global bufnr
bufnr += 1
# test on self.c1
set_vim_buffer(buf=self.c1, cursor=(6, 0), bufnr=bufnr)
# update_checkboxes_status
self.editcheckbox.update_checkboxes_status()
self.assertEqual(vim.current.buffer[1], u"* heading1 [0%]")
# toggle
self.editcheckbox.toggle()
self.assertEqual(vim.current.buffer[5], u" - [X] checkbox4")
bufnr += 1
set_vim_buffer(buf=self.c1, cursor=(9, 0), bufnr=bufnr)
# toggle and check checkbox status
self.editcheckbox.toggle()
self.assertEqual(vim.current.buffer[8], u" - [X] checkbox7")
self.assertEqual(vim.current.buffer[7], u" - [-] checkbox6")
self.assertEqual(vim.current.buffer[6], u" - [-] checkbox5")
# new_checkbox
bufnr += 1
set_vim_buffer(buf=self.c1, cursor=(9, 0), bufnr=bufnr)
vim.current.window.cursor = (9, 0)
self.assertEqual(vim.current.buffer[9], u' - [ ] checkbox8')
self.editcheckbox.new_checkbox(below=True)
# vim's buffer behave just opposite to Python's list when inserting a
# new item. The new entry is appended in vim put prepended in Python!
self.assertEqual(vim.current.buffer[10], u' - [ ] checkbox8')
self.assertEqual(vim.current.buffer[9], u' - [ ] ')
self.editcheckbox.update_checkboxes_status()
def test_no_status_checkbox(self):
global bufnr
bufnr += 1
# test on self.c2
set_vim_buffer(buf=self.c2, bufnr=bufnr)
self.assertEqual(vim.current.buffer[2], u" - checkbox [0%]")
# toggle
vim.current.window.cursor = (4, 0)
self.editcheckbox.toggle()
self.assertEqual(vim.current.buffer[3], u" - [X] test1")
# self.editcheckbox.update_checkboxes_status()
# see if the no status checkbox update its status
self.assertEqual(vim.current.buffer[2], u" - checkbox [33%]")
def test_number_list(self):
global bufnr
bufnr += 1
set_vim_buffer(buf=self.c3, bufnr=bufnr)
vim.current.window.cursor = (6, 0)
self.editcheckbox.toggle()
self.assertEqual(vim.current.buffer[5], u" 2. [X] another main task")
def test_new_checkbox(self):
global bufnr
bufnr += 1
set_vim_buffer(buf=self.c4, bufnr=bufnr)
vim.current.window.cursor = (2, 1)
self.editcheckbox.new_checkbox(below=True)
self.assertEqual(vim.current.buffer[2], u" - [ ] ")
def test_item_decrement(self):
global bufnr
bufnr += 1
set_vim_buffer(buf=self.c5, bufnr=bufnr)
vim.current.window.cursor = (3, 1)
self.editcheckbox.new_checkbox(below=False, plain=True)
self.assertEqual(vim.current.buffer[2], u" 0. ")
self.assertEqual(vim.current.buffer[3], u" 1. item")
vim.current.window.cursor = (3, 1)
self.editcheckbox.new_checkbox(below=False, plain=True)
self.assertEqual(vim.current.buffer[1], u"* heading1")
self.assertEqual(vim.current.buffer[2], u" 0. ")
self.assertEqual(vim.current.buffer[3], u" 1. item")
vim.current.window.cursor = (5, 1)
self.editcheckbox.new_checkbox(below=False, plain=True)
self.assertEqual(vim.current.buffer[4], u" 8. ")
self.assertEqual(vim.current.buffer[5], u" 9. item")
vim.current.window.cursor = (8, 1)
self.editcheckbox.new_checkbox(below=False, plain=True)
# no further decrement than a
self.assertEqual(vim.current.buffer[6], u" }. item")
self.assertEqual(vim.current.buffer[7], u" a. item")
self.assertEqual(vim.current.buffer[8], u" z. item")
def test_item_decrementA(self):
global bufnr
bufnr += 1
set_vim_buffer(buf=self.c5, bufnr=bufnr)
vim.current.window.cursor = (8, 1)
self.editcheckbox.new_checkbox(below=False, plain=True)
# decrement from A to z
self.assertEqual(vim.current.buffer[7], u" z. ")
self.assertEqual(vim.current.buffer[8], u" A. item")
def test_item_increment(self):
global bufnr
bufnr += 1
set_vim_buffer(buf=self.c5, bufnr=bufnr)
vim.current.window.cursor = (3, 1)
self.editcheckbox.new_checkbox(below=True, plain=True)
self.assertEqual(vim.current.buffer[2], u" 1. item")
self.assertEqual(vim.current.buffer[3], u" 2. ")
vim.current.window.cursor = (5, 1)
self.editcheckbox.new_checkbox(below=True, plain=True)
self.assertEqual(vim.current.buffer[4], u" 9. item")
self.assertEqual(vim.current.buffer[5], u" }. item")
self.assertEqual(vim.current.buffer[6], u" 10. ")
def test_item_incrementz(self):
global bufnr
bufnr += 1
set_vim_buffer(buf=self.c5, bufnr=bufnr)
vim.current.window.cursor = (6, 1)
self.editcheckbox.new_checkbox(below=True, plain=True)
self.assertEqual(vim.current.buffer[5], u" a. item")
self.assertEqual(vim.current.buffer[6], u" b. ")
vim.current.window.cursor = (8, 1)
self.editcheckbox.new_checkbox(below=True, plain=True)
self.assertEqual(vim.current.buffer[7], u" z. item")
self.assertEqual(vim.current.buffer[8], u" A. ")
vim.current.window.cursor = (11, 1)
self.editcheckbox.new_checkbox(below=True, plain=True)
self.assertEqual(vim.current.buffer[10], u" Z. item")
self.assertEqual(vim.current.buffer[11], u" aa. item")
self.assertEqual(vim.current.buffer[12], u"")
vim.current.window.cursor = (12, 1)
self.editcheckbox.new_checkbox(below=True, plain=True)
self.assertEqual(vim.current.buffer[11], u" aa. item")
self.assertEqual(vim.current.buffer[12], u"")
def suite():
return unittest.TestLoader().loadTestsFromTestCase(EditCheckboxTestCase)
# vim: set noexpandtab

View file

@ -0,0 +1,387 @@
# -*- coding: utf-8 -*-
import unittest
import sys
sys.path.append(u'../ftplugin')
import vim
from orgmode._vim import ORGMODE
from orgmode.py3compat.encode_compatibility import *
counter = 0
class EditStructureTestCase(unittest.TestCase):
def setUp(self):
global counter
counter += 1
vim.CMDHISTORY = []
vim.CMDRESULTS = {}
vim.EVALHISTORY = []
vim.EVALRESULTS = {
# no org_todo_keywords for b
u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'),
# global values for org_todo_keywords
u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'),
u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')],
u_encode(u'exists("g:org_improve_split_heading")'): u_encode(u'0'),
u_encode(u'exists("b:org_improve_split_heading")'): u_encode(u'0'),
u_encode(u'exists("g:org_debug")'): u_encode(u'0'),
u_encode(u'exists("b:org_debug")'): u_encode(u'0'),
u_encode(u'exists("*repeat#set()")'): u_encode(u'0'),
u_encode(u'b:changedtick'): u_encode(u'%d' % counter),
u_encode(u'&ts'): u_encode(u'8'),
u_encode(u'exists("g:org_tag_column")'): u_encode(u'0'),
u_encode(u'exists("b:org_tag_column")'): u_encode(u'0'),
u_encode(u"v:count"): u_encode(u'0'),
# jump to insert mode after adding heading/checkbox
u_encode(u'exists("g:org_prefer_insert_mode")'): u_encode(u'0'),
u_encode(u'exists("b:org_prefer_insert_mode")'): u_encode(u'0')}
if not u'EditStructure' in ORGMODE.plugins:
ORGMODE.register_plugin(u'EditStructure')
self.editstructure = ORGMODE.plugins[u'EditStructure']
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* Überschrift 1
Text 1
Bla bla
** Überschrift 1.1
Text 2
Bla Bla bla
** Überschrift 1.2
Text 3
**** Überschrift 1.2.1.falsch
Bla Bla bla bla
*** Überschrift 1.2.1
* Überschrift 2
* Überschrift 3
asdf sdf
""".split(u'\n')]
def test_new_heading_below_normal_behavior(self):
vim.current.window.cursor = (1, 0)
self.assertNotEqual(self.editstructure.new_heading(below=True), None)
self.assertEqual(vim.current.buffer[0], u_encode(u'* '))
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1'))
def test_new_heading_above_normal_behavior(self):
vim.current.window.cursor = (1, 1)
self.assertNotEqual(self.editstructure.new_heading(below=False), None)
self.assertEqual(vim.current.buffer[0], u_encode(u'* '))
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1'))
def test_new_heading_below(self):
vim.current.window.cursor = (2, 0)
vim.current.buffer[5] = u_encode(u'** Überschrift 1.1 :Tag:')
self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=False), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 6gg"|startinsert!'))
self.assertEqual(vim.current.buffer[4], u_encode(u'Bla bla'))
self.assertEqual(vim.current.buffer[5], u_encode(u'* '))
self.assertEqual(vim.current.buffer[6], u_encode(u'** Überschrift 1.1 :Tag:'))
self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2'))
self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2'))
def test_new_heading_below_insert_mode(self):
vim.current.window.cursor = (2, 1)
self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 3gg"|startinsert!'))
self.assertEqual(vim.current.buffer[2], u_encode(u'* Überschrift 1'))
self.assertEqual(vim.current.buffer[5], u_encode(u'Bla bla'))
self.assertEqual(vim.current.buffer[6], u_encode(u'** Überschrift 1.1'))
self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2'))
self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2'))
def test_new_heading_below_split_text_at_the_end(self):
vim.current.buffer[1] = u_encode(u'* Überschriftx1')
vim.current.window.cursor = (2, 14)
self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 3gg"|startinsert!'))
self.assertEqual(vim.current.buffer[2], u_encode(u'* '))
self.assertEqual(vim.current.buffer[5], u_encode(u'Bla bla'))
self.assertEqual(vim.current.buffer[6], u_encode(u'** Überschrift 1.1'))
self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2'))
self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2'))
def test_new_heading_below_split_text_at_the_end_insert_parts(self):
vim.current.window.cursor = (2, 14)
self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 3gg"|startinsert!'))
self.assertEqual(vim.current.buffer[2], u_encode(u'* 1'))
self.assertEqual(vim.current.buffer[5], u_encode(u'Bla bla'))
self.assertEqual(vim.current.buffer[6], u_encode(u'** Überschrift 1.1'))
self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2'))
self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2'))
def test_new_heading_below_in_the_middle(self):
vim.current.window.cursor = (10, 0)
self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 13gg"|startinsert!'))
self.assertEqual(vim.current.buffer[11], u_encode(u''))
self.assertEqual(vim.current.buffer[12], u_encode(u'** '))
self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch'))
def test_new_heading_below_in_the_middle2(self):
vim.current.window.cursor = (13, 0)
self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 16gg"|startinsert!'))
self.assertEqual(vim.current.buffer[14], u_encode(u'Bla Bla bla bla'))
self.assertEqual(vim.current.buffer[15], u_encode(u'**** '))
self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1'))
def test_new_heading_below_in_the_middle3(self):
vim.current.window.cursor = (16, 0)
self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 17gg"|startinsert!'))
self.assertEqual(vim.current.buffer[15], u_encode(u'*** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[16], u_encode(u'*** '))
self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2'))
def test_new_heading_below_at_the_end(self):
vim.current.window.cursor = (18, 0)
self.assertNotEqual(self.editstructure.new_heading(below=True, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 21gg"|startinsert!'))
self.assertEqual(vim.current.buffer[19], u_encode(u''))
self.assertEqual(vim.current.buffer[20], u_encode(u'* '))
self.assertEqual(len(vim.current.buffer), 21)
def test_new_heading_above(self):
vim.current.window.cursor = (2, 0)
self.assertNotEqual(self.editstructure.new_heading(below=False, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 2gg"|startinsert!'))
self.assertEqual(vim.current.buffer[0], u_encode(u''))
self.assertEqual(vim.current.buffer[1], u_encode(u'* '))
self.assertEqual(vim.current.buffer[2], u_encode(u'* Überschrift 1'))
def test_new_heading_above_in_the_middle(self):
vim.current.window.cursor = (10, 0)
self.assertNotEqual(self.editstructure.new_heading(below=False, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 10gg"|startinsert!'))
self.assertEqual(vim.current.buffer[8], u_encode(u'Bla Bla bla'))
self.assertEqual(vim.current.buffer[9], u_encode(u'** '))
self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2'))
def test_new_heading_above_in_the_middle2(self):
vim.current.window.cursor = (13, 0)
self.assertNotEqual(self.editstructure.new_heading(below=False, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 13gg"|startinsert!'))
self.assertEqual(vim.current.buffer[11], u_encode(u''))
self.assertEqual(vim.current.buffer[12], u_encode(u'**** '))
self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch'))
def test_new_heading_above_in_the_middle3(self):
vim.current.window.cursor = (16, 0)
self.assertNotEqual(self.editstructure.new_heading(below=False, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 16gg"|startinsert!'))
self.assertEqual(vim.current.buffer[14], u_encode(u'Bla Bla bla bla'))
self.assertEqual(vim.current.buffer[15], u_encode(u'*** '))
self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1'))
def test_new_heading_above_at_the_end(self):
vim.current.window.cursor = (18, 0)
self.assertNotEqual(self.editstructure.new_heading(below=False, insert_mode=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 18gg"|startinsert!'))
self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2'))
self.assertEqual(vim.current.buffer[17], u_encode(u'* '))
self.assertEqual(vim.current.buffer[18], u_encode(u'* Überschrift 3'))
def test_new_heading_below_split_heading_title(self):
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* Überschrift 1 :Tag:
Text 1
Bla bla
** Überschrift 1.1
Text 2
Bla Bla bla
** Überschrift 1.2
Text 3
**** Überschrift 1.2.1.falsch
Bla Bla bla bla
*** Überschrift 1.2.1
* Überschrift 2
* Überschrift 3
asdf sdf
""".split(u'\n')]
vim.current.window.cursor = (2, 6)
self.assertNotEqual(self.editstructure.new_heading(insert_mode=True), None)
self.assertEqual(vim.current.buffer[0], u_encode(u''))
self.assertEqual(vim.current.buffer[1], u_encode(u'* Über :Tag:'))
self.assertEqual(vim.current.buffer[2], u_encode(u'* schrift 1'))
self.assertEqual(vim.current.buffer[3], u_encode(u'Text 1'))
def test_new_heading_below_split_heading_title_with_todo(self):
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* TODO Überschrift 1 :Tag:
Text 1
Bla bla
** Überschrift 1.1
Text 2
Bla Bla bla
** Überschrift 1.2
Text 3
**** Überschrift 1.2.1.falsch
Bla Bla bla bla
*** Überschrift 1.2.1
* Überschrift 2
* Überschrift 3
asdf sdf
""".split(u'\n')]
vim.current.window.cursor = (2, 5)
self.assertNotEqual(self.editstructure.new_heading(insert_mode=True), None)
self.assertEqual(vim.current.buffer[0], u_encode(u''))
self.assertEqual(vim.current.buffer[1], u_encode(u'* TODO :Tag:'))
self.assertEqual(vim.current.buffer[2], u_encode(u'* Überschrift 1'))
self.assertEqual(vim.current.buffer[3], u_encode(u'Text 1'))
def test_demote_heading(self):
vim.current.window.cursor = (13, 0)
self.assertNotEqual(self.editstructure.demote_heading(), None)
self.assertEqual(vim.current.buffer[10], u_encode(u'Text 3'))
self.assertEqual(vim.current.buffer[11], u_encode(u''))
self.assertEqual(vim.current.buffer[12], u_encode(u'***** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[13], u_encode(u''))
# actually the indentation comes through vim, just the heading is updated
self.assertEqual(vim.current.buffer[14], u_encode(u'Bla Bla bla bla'))
self.assertEqual(vim.current.buffer[15], u_encode(u'*** Überschrift 1.2.1'))
self.assertEqual(vim.current.window.cursor, (13, 1))
def test_demote_newly_created_level_one_heading(self):
vim.current.window.cursor = (2, 0)
self.assertNotEqual(self.editstructure.new_heading(below=True), None)
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1'))
self.assertEqual(vim.current.buffer[5], u_encode(u'* '))
self.assertEqual(vim.current.buffer[6], u_encode(u'** Überschrift 1.1'))
self.assertEqual(vim.current.buffer[10], u_encode(u'** Überschrift 1.2'))
self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2'))
vim.current.window.cursor = (6, 2)
self.assertNotEqual(self.editstructure.demote_heading(), None)
self.assertEqual(vim.current.buffer[5], u_encode(u'** '))
self.assertEqual(vim.current.buffer[6], u_encode(u'*** Überschrift 1.1'))
self.assertEqual(vim.current.buffer[10], u_encode(u'*** Überschrift 1.2'))
self.assertEqual(vim.current.buffer[13], u_encode(u'***** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[16], u_encode(u'**** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2'))
def test_demote_newly_created_level_two_heading(self):
vim.current.window.cursor = (10, 0)
self.assertNotEqual(self.editstructure.new_heading(below=True), None)
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1'))
self.assertEqual(vim.current.buffer[5], u_encode(u'** Überschrift 1.1'))
self.assertEqual(vim.current.buffer[9], u_encode(u'** Überschrift 1.2'))
self.assertEqual(vim.current.buffer[12], u_encode(u'** '))
self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2'))
vim.current.window.cursor = (13, 3)
self.assertNotEqual(self.editstructure.demote_heading(including_children=False, on_heading=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'exe "normal 13gg"|startinsert!'))
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1'))
self.assertEqual(vim.current.buffer[5], u_encode(u'** Überschrift 1.1'))
self.assertEqual(vim.current.buffer[9], u_encode(u'** Überschrift 1.2'))
self.assertEqual(vim.current.buffer[12], u_encode(u'*** '))
self.assertEqual(vim.current.buffer[13], u_encode(u'**** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[16], u_encode(u'*** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[17], u_encode(u'* Überschrift 2'))
def test_demote_last_heading(self):
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* Überschrift 2
* Überschrift 3""".split('\n')]
vim.current.window.cursor = (3, 0)
h = ORGMODE.get_document().current_heading()
self.assertNotEqual(self.editstructure.demote_heading(), None)
self.assertEqual(h.end, 2)
self.assertFalse(vim.CMDHISTORY)
self.assertEqual(vim.current.buffer[2], u_encode(u'** Überschrift 3'))
self.assertEqual(vim.current.window.cursor, (3, 1))
def test_promote_heading(self):
vim.current.window.cursor = (13, 0)
self.assertNotEqual(self.editstructure.promote_heading(), None)
self.assertEqual(vim.current.buffer[10], u_encode(u'Text 3'))
self.assertEqual(vim.current.buffer[11], u_encode(u''))
self.assertEqual(vim.current.buffer[12], u_encode(u'*** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[13], u_encode(u''))
# actually the indentation comes through vim, just the heading is updated
self.assertEqual(vim.current.buffer[14], u_encode(u'Bla Bla bla bla'))
self.assertEqual(vim.current.buffer[15], u_encode(u'*** Überschrift 1.2.1'))
self.assertEqual(vim.current.window.cursor, (13, -1))
def test_promote_level_one_heading(self):
vim.current.window.cursor = (2, 0)
self.assertEqual(self.editstructure.promote_heading(), None)
self.assertEqual(len(vim.CMDHISTORY), 0)
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_demote_parent_heading(self):
vim.current.window.cursor = (2, 0)
self.assertNotEqual(self.editstructure.demote_heading(), None)
self.assertEqual(vim.current.buffer[1], u_encode(u'** Überschrift 1'))
self.assertEqual(vim.current.buffer[5], u_encode(u'*** Überschrift 1.1'))
self.assertEqual(vim.current.buffer[9], u_encode(u'*** Überschrift 1.2'))
self.assertEqual(vim.current.buffer[12], u_encode(u'***** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[15], u_encode(u'**** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2'))
self.assertEqual(vim.current.window.cursor, (2, 1))
def test_promote_parent_heading(self):
vim.current.window.cursor = (10, 0)
self.assertNotEqual(self.editstructure.promote_heading(), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal 10ggV16gg='))
self.assertEqual(vim.current.buffer[5], u_encode(u'** Überschrift 1.1'))
self.assertEqual(vim.current.buffer[9], u_encode(u'* Überschrift 1.2'))
self.assertEqual(vim.current.buffer[12], u_encode(u'*** Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[15], u_encode(u'** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2'))
self.assertEqual(vim.current.window.cursor, (10, -1))
# run tests with count
def test_demote_parent_heading_count(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u"v:count"] = u_encode(u'3')
self.assertNotEqual(self.editstructure.demote_heading(), None)
self.assertEqual(vim.current.buffer[1], u_encode(u'**** Überschrift 1'))
self.assertEqual(vim.current.buffer[5], u_encode(u'***** Überschrift 1.1'))
self.assertEqual(vim.current.buffer[9], u_encode(u'***** Überschrift 1.2'))
self.assertEqual(vim.current.buffer[12], u_encode(u'******* Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[15], u_encode(u'****** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2'))
self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2'))
self.assertEqual(vim.current.window.cursor, (2, 3))
def test_promote_parent_heading(self):
vim.current.window.cursor = (13, 0)
vim.EVALRESULTS[u"v:count"] = u_encode(u'3')
self.assertNotEqual(self.editstructure.promote_heading(), None)
self.assertEqual(vim.current.buffer[5], u_encode(u'** Überschrift 1.1'))
self.assertEqual(vim.current.buffer[9], u_encode(u'** Überschrift 1.2'))
self.assertEqual(vim.current.buffer[12], u_encode(u'* Überschrift 1.2.1.falsch'))
self.assertEqual(vim.current.buffer[15], u_encode(u'** Überschrift 1.2.1'))
self.assertEqual(vim.current.buffer[16], u_encode(u'* Überschrift 2'))
self.assertEqual(vim.current.window.cursor, (13, -3))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(EditStructureTestCase)

View file

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys
sys.path.append(u'../ftplugin')
import unittest
import orgmode.settings
from orgmode.exceptions import PluginError
from orgmode._vim import ORGMODE
from orgmode.keybinding import MODE_ALL, Plug
import vim
from orgmode.py3compat.encode_compatibility import *
ORG_PLUGINS = ['ShowHide', '|', 'Navigator', 'EditStructure', '|', 'Hyperlinks', '|', 'Todo', 'TagsProperties', 'Date', 'Agenda', 'Misc', '|', 'Export']
class MappingTestCase(unittest.TestCase):
u"""Tests all plugins for overlapping mappings."""
def test_non_overlapping_plug_mappings(self):
def find_overlapping_mappings(kb, all_keybindings):
found_overlapping_mapping = False
for tkb in all_keybindings:
if kb.mode == tkb.mode or MODE_ALL in (kb.mode, tkb.mode):
if isinstance(kb._action, Plug) and isinstance(tkb._action, Plug):
akb = kb.action
atkb = tkb.action
if (akb.startswith(atkb) or atkb.startswith(akb)) and akb != atkb:
print(u'\nERROR: Found overlapping mapping: %s (%s), %s (%s)' % (kb.key, akb, tkb.key, atkb))
found_overlapping_mapping = True
if all_keybindings:
res = find_overlapping_mappings(all_keybindings[0], all_keybindings[1:])
if not found_overlapping_mapping:
return res
return found_overlapping_mapping
if self.keybindings:
self.assertFalse(find_overlapping_mappings(self.keybindings[0], self.keybindings[1:]))
def setUp(self):
self.keybindings = []
vim.EVALRESULTS = {
u'exists("g:org_debug")': 0,
u'exists("b:org_debug")': 0,
u'exists("*repeat#set()")': 0,
u'b:changedtick': 0,
u_encode(u'exists("b:org_plugins")'): 0,
u_encode(u'exists("g:org_plugins")'): 1,
u_encode(u'g:org_plugins'): ORG_PLUGINS,
}
for plugin in filter(lambda p: p != '|', ORG_PLUGINS):
try:
ORGMODE.register_plugin(plugin)
except PluginError:
pass
if plugin in ORGMODE._plugins:
self.keybindings.extend(ORGMODE._plugins[plugin].keybindings)
def suite():
return unittest.TestLoader().loadTestsFromTestCase(MappingTestCase)
# vi: noexpandtab

View file

@ -0,0 +1,164 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
import sys
sys.path.append(u'../ftplugin')
import vim
from orgmode._vim import indent_orgmode, fold_orgmode, ORGMODE
from orgmode.py3compat.encode_compatibility import *
ORGMODE.debug = True
START = True
END = False
counter = 0
class MiscTestCase(unittest.TestCase):
def setUp(self):
global counter
counter += 1
vim.CMDHISTORY = []
vim.CMDRESULTS = {}
vim.EVALHISTORY = []
vim.EVALRESULTS = {
# no org_todo_keywords for b
u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'),
# global values for org_todo_keywords
u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'),
u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')],
u_encode(u'exists("g:org_debug")'): u_encode(u'0'),
u_encode(u'exists("g:org_debug")'): u_encode(u'0'),
u_encode(u'exists("*repeat#set()")'): u_encode(u'0'),
u_encode(u"v:count"): u_encode(u'0'),
u_encode(u'b:changedtick'): u_encode(u'%d' % counter),
u_encode(u"v:lnum"): u_encode(u'0')}
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* Überschrift 1
Text 1
Bla bla
** Überschrift 1.1
Text 2
Bla Bla bla
** Überschrift 1.2
Text 3
**** Überschrift 1.2.1.falsch
Bla Bla bla bla
*** Überschrift 1.2.1
* Überschrift 2
* Überschrift 3
asdf sdf
""".split(u'\n') ]
def test_indent_noheading(self):
# test first heading
vim.current.window.cursor = (1, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'1')
indent_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 0)
def test_indent_heading(self):
# test first heading
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'2')
indent_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 0)
def test_indent_heading_middle(self):
# test first heading
vim.current.window.cursor = (3, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'3')
indent_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:indent_level = 2'))
def test_indent_heading_middle2(self):
# test first heading
vim.current.window.cursor = (4, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'4')
indent_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:indent_level = 2'))
def test_indent_heading_end(self):
# test first heading
vim.current.window.cursor = (5, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'5')
indent_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:indent_level = 2'))
def test_fold_heading_start(self):
# test first heading
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'2')
fold_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = ">1"'))
def test_fold_heading_middle(self):
# test first heading
vim.current.window.cursor = (3, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'3')
fold_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = 1'))
def test_fold_heading_end(self):
# test first heading
vim.current.window.cursor = (5, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'5')
fold_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = 1'))
def test_fold_heading_end_of_last_child(self):
# test first heading
vim.current.window.cursor = (16, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'16')
fold_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 1)
# which is also end of the parent heading <1
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = ">3"'))
def test_fold_heading_end_of_last_child_next_heading(self):
# test first heading
vim.current.window.cursor = (17, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'17')
fold_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = ">1"'))
def test_fold_middle_subheading(self):
# test first heading
vim.current.window.cursor = (13, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'13')
fold_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = ">4"'))
def test_fold_middle_subheading2(self):
# test first heading
vim.current.window.cursor = (14, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'14')
fold_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = 4'))
def test_fold_middle_subheading3(self):
# test first heading
vim.current.window.cursor = (15, 0)
vim.EVALRESULTS[u_encode(u'v:lnum')] = u_encode(u'15')
fold_orgmode()
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'let b:fold_expr = 4'))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(MiscTestCase)

View file

@ -0,0 +1,633 @@
# -*- coding: utf-8 -*-
import unittest
import sys
sys.path.append(u'../ftplugin')
import vim
from orgmode._vim import ORGMODE
from orgmode.py3compat.encode_compatibility import *
START = True
END = False
def set_visual_selection(visualmode, line_start, line_end, col_start=1,
col_end=1, cursor_pos=START):
if visualmode not in (u'', u'V', u'v'):
raise ValueError(u'Illegal value for visualmode, must be in , V, v')
vim.EVALRESULTS['visualmode()'] = visualmode
# getpos results [bufnum, lnum, col, off]
vim.EVALRESULTS['getpos("\'<")'] = ('', '%d' % line_start, '%d' %
col_start, '')
vim.EVALRESULTS['getpos("\'>")'] = ('', '%d' % line_end, '%d' %
col_end, '')
if cursor_pos == START:
vim.current.window.cursor = (line_start, col_start)
else:
vim.current.window.cursor = (line_end, col_end)
counter = 0
class NavigatorTestCase(unittest.TestCase):
def setUp(self):
global counter
counter += 1
vim.CMDHISTORY = []
vim.CMDRESULTS = {}
vim.EVALHISTORY = []
vim.EVALRESULTS = {
# no org_todo_keywords for b
u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'),
# global values for org_todo_keywords
u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'),
u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')],
u_encode(u'exists("g:org_debug")'): u_encode(u'0'),
u_encode(u'exists("g:org_debug")'): u_encode(u'0'),
u_encode(u'exists("*repeat#set()")'): u_encode(u'0'),
u_encode(u'b:changedtick'): u_encode(u'%d' % counter),
u_encode(u"v:count"): u_encode(u'0'),
}
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* Überschrift 1
Text 1
Bla bla
** Überschrift 1.1
Text 2
Bla Bla bla
** Überschrift 1.2
Text 3
**** Überschrift 1.2.1.falsch
Bla Bla bla bla
*** Überschrift 1.2.1
* Überschrift 2
* Überschrift 3
asdf sdf
""".split(u'\n') ]
if not u'Navigator' in ORGMODE.plugins:
ORGMODE.register_plugin(u'Navigator')
self.navigator = ORGMODE.plugins[u'Navigator']
def test_movement(self):
# test movement outside any heading
vim.current.window.cursor = (1, 0)
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (1, 0))
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (2, 2))
def test_forward_movement(self):
# test forward movement
vim.current.window.cursor = (2, 0)
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (6, 3))
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (10, 3))
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (13, 5))
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (16, 4))
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (17, 2))
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (18, 2))
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (18, 2))
## don't move cursor if last heading is already focussed
vim.current.window.cursor = (19, 6)
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (19, 6))
## test movement with count
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'-1')
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (6, 3))
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'0')
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (6, 3))
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'1')
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (6, 3))
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'3')
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (16, 4))
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (18, 2))
self.navigator.next(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (18, 2))
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'0')
def test_backward_movement(self):
# test backward movement
vim.current.window.cursor = (19, 6)
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (18, 2))
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (17, 2))
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (16, 4))
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (13, 5))
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (10, 3))
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (6, 3))
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (2, 2))
## test movement with count
vim.current.window.cursor = (19, 6)
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'-1')
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (18, 2))
vim.current.window.cursor = (19, 6)
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'0')
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (18, 2))
vim.current.window.cursor = (19, 6)
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'3')
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (16, 4))
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'4')
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (2, 2))
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'4')
self.navigator.previous(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (2, 2))
def test_parent_movement(self):
# test movement to parent
vim.current.window.cursor = (2, 0)
self.assertEqual(self.navigator.parent(mode=u'normal'), None)
self.assertEqual(vim.current.window.cursor, (2, 0))
vim.current.window.cursor = (3, 4)
self.navigator.parent(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (3, 4))
vim.current.window.cursor = (16, 4)
self.navigator.parent(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (10, 3))
self.navigator.parent(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (2, 2))
vim.current.window.cursor = (15, 6)
self.navigator.parent(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (10, 3))
self.navigator.parent(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (2, 2))
## test movement with count
vim.current.window.cursor = (16, 4)
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'-1')
self.navigator.parent(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (10, 3))
vim.current.window.cursor = (16, 4)
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'0')
self.navigator.parent(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (10, 3))
vim.current.window.cursor = (16, 4)
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'1')
self.navigator.parent(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (10, 3))
vim.current.window.cursor = (16, 4)
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'2')
self.navigator.parent(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (2, 2))
vim.current.window.cursor = (16, 4)
vim.EVALRESULTS[u_encode(u"v:count")] = u_encode(u'3')
self.navigator.parent(mode=u'normal')
self.assertEqual(vim.current.window.cursor, (2, 2))
def test_next_parent_movement(self):
# test movement to parent
vim.current.window.cursor = (6, 0)
self.assertNotEqual(self.navigator.parent_next_sibling(mode=u'normal'), None)
self.assertEqual(vim.current.window.cursor, (17, 2))
def test_forward_movement_visual(self):
# selection start: <<
# selection end: >>
# cursor poistion: |
# << text
# text| >>
# text
# heading
set_visual_selection(u'V', 2, 4, cursor_pos=END)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV5gg'))
# << text
# text
# text| >>
# heading
set_visual_selection(u'V', 2, 5, cursor_pos=END)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV9gg'))
# << text
# x. heading
# text| >>
# heading
set_visual_selection(u'V', 12, 14, cursor_pos=END)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV15gg'))
set_visual_selection(u'V', 12, 15, cursor_pos=END)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV16gg'))
set_visual_selection(u'V', 12, 16, cursor_pos=END)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV17gg'))
# << text
# text
# text| >>
# heading
# EOF
set_visual_selection(u'V', 15, 17, cursor_pos=END)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 15ggV20gg'))
# << text >>
# heading
set_visual_selection(u'V', 1, 1, cursor_pos=START)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 1ggV5gg'))
# << heading >>
# text
# heading
set_visual_selection(u'V', 2, 2, cursor_pos=START)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV5gg'))
# << text >>
# heading
set_visual_selection(u'V', 1, 1, cursor_pos=END)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 1ggV5gg'))
# << |text
# heading
# text
# heading
# text >>
set_visual_selection(u'V', 1, 8, cursor_pos=START)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV8ggo'))
# << |heading
# text
# heading
# text >>
set_visual_selection(u'V', 2, 8, cursor_pos=START)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 6ggV8ggo'))
# << |heading
# text >>
# heading
set_visual_selection(u'V', 6, 8, cursor_pos=START)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 8ggV9gg'))
# << |x. heading
# text >>
# heading
set_visual_selection(u'V', 13, 15, cursor_pos=START)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 15ggV15gg'))
set_visual_selection(u'V', 13, 16, cursor_pos=START)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16ggV16ggo'))
set_visual_selection(u'V', 16, 16, cursor_pos=START)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16ggV17gg'))
# << |x. heading
# text >>
# heading
# EOF
set_visual_selection(u'V', 17, 17, cursor_pos=START)
self.assertNotEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 17ggV20gg'))
# << |heading
# text>>
# text
# EOF
set_visual_selection(u'V', 18, 19, cursor_pos=START)
self.assertEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 19ggV20gg'))
# << heading
# text|>>
# text
# EOF
set_visual_selection(u'V', 18, 19, cursor_pos=END)
self.assertEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 18ggV20gg'))
# << heading
# text|>>
# EOF
set_visual_selection(u'V', 18, 20, cursor_pos=END)
self.assertEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 18ggV20gg'))
# << |heading
# text>>
# EOF
set_visual_selection(u'V', 20, 20, cursor_pos=START)
self.assertEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 20ggV20gg'))
def test_forward_movement_visual_to_the_end_of_the_file(self):
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* Überschrift 1
Text 1
Bla bla
** Überschrift 1.1
Text 2
Bla Bla bla
** Überschrift 1.2
Text 3
**** Überschrift 1.2.1.falsch
Bla Bla bla bla
test
""".split(u'\n') ]
# << |heading
# text>>
# EOF
set_visual_selection(u'V', 15, 15, cursor_pos=START)
self.assertEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 15ggV17gg'))
set_visual_selection(u'V', 15, 17, cursor_pos=END)
self.assertEqual(self.navigator.next(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 15ggV17gg'))
def test_backward_movement_visual(self):
# selection start: <<
# selection end: >>
# cursor poistion: |
# << text | >>
# text
# heading
set_visual_selection(u'V', 1, 1, cursor_pos=START)
self.assertEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! gv'))
set_visual_selection(u'V', 1, 1, cursor_pos=END)
self.assertEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! gv'))
# << heading| >>
# text
# heading
set_visual_selection(u'V', 2, 2, cursor_pos=START)
self.assertEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV2ggo'))
set_visual_selection(u'V', 2, 2, cursor_pos=END)
self.assertEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV2ggo'))
# heading
# text
# << |text
# text >>
set_visual_selection(u'V', 3, 5, cursor_pos=START)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV5ggo'))
# heading
# text
# << text
# text| >>
set_visual_selection(u'V', 3, 5, cursor_pos=END)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV3ggo'))
# heading
# text
# << text
# text| >>
set_visual_selection(u'V', 8, 9, cursor_pos=END)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 6ggV8ggo'))
# heading
# << text
# x. heading
# text| >>
set_visual_selection(u'V', 12, 14, cursor_pos=END)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV12gg'))
set_visual_selection(u'V', 12, 15, cursor_pos=END)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV12gg'))
# heading
# << |text
# x. heading
# text >>
set_visual_selection(u'V', 12, 15, cursor_pos=START)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 10ggV15ggo'))
# heading
# << text
# x. heading| >>
set_visual_selection(u'V', 12, 13, cursor_pos=END)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV12gg'))
# heading
# << text
# heading
# text
# x. heading| >>
set_visual_selection(u'V', 12, 16, cursor_pos=END)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV15gg'))
# << text
# heading
# text
# heading| >>
set_visual_selection(u'V', 15, 17, cursor_pos=END)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 15ggV16gg'))
# heading
# << |text
# text
# heading
# text >>
set_visual_selection(u'V', 4, 8, cursor_pos=START)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV8ggo'))
# heading
# << text
# text
# heading
# text| >>
set_visual_selection(u'V', 4, 8, cursor_pos=END)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 4ggV5gg'))
# heading
# << text
# text
# heading
# text| >>
set_visual_selection(u'V', 4, 5, cursor_pos=END)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV4ggo'))
# BOF
# << |heading
# text
# heading
# text >>
set_visual_selection(u'V', 2, 8, cursor_pos=START)
self.assertEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV8ggo'))
# BOF
# heading
# << text
# text| >>
set_visual_selection(u'V', 3, 4, cursor_pos=END)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV3ggo'))
# BOF
# << heading
# text
# text| >>
set_visual_selection(u'V', 2, 4, cursor_pos=END)
self.assertNotEqual(self.navigator.previous(mode=u'visual'), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV2ggo'))
# << text
# heading
# text
# x. heading
# text| >>
set_visual_selection(u'V', 8, 14, cursor_pos=END)
self.navigator.previous(mode=u'visual')
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 8ggV12gg'))
def test_parent_movement_visual(self):
# selection start: <<
# selection end: >>
# cursor poistion: |
# heading
# << text|
# text
# text >>
set_visual_selection(u'V', 4, 8, cursor_pos=START)
self.navigator.parent(mode=u'visual')
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! gv'))
# heading
# << text|
# text
# text >>
set_visual_selection(u'V', 6, 8, cursor_pos=START)
self.navigator.parent(mode=u'visual')
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV8ggo'))
# heading
# << text
# text
# text| >>
set_visual_selection(u'V', 6, 8, cursor_pos=END)
self.navigator.parent(mode=u'visual')
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 6ggV5gg'))
# << |heading
# text
# text
# text >>
set_visual_selection(u'V', 2, 8, cursor_pos=START)
self.assertEqual(self.navigator.parent(mode=u'visual'), None)
# << heading
# text
# heading
# text| >>
set_visual_selection(u'V', 2, 8, cursor_pos=END)
self.navigator.parent(mode=u'visual')
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV5gg'))
set_visual_selection(u'V', 7, 8, cursor_pos=START)
self.navigator.parent(mode=u'visual')
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV8ggo'))
# heading
# heading
# << text
# text| >>
set_visual_selection(u'V', 12, 13, cursor_pos=END)
self.navigator.parent(mode=u'visual')
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 12ggV12gg'))
set_visual_selection(u'V', 10, 12, cursor_pos=START)
self.navigator.parent(mode=u'visual')
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggV12ggo'))
# heading
# << text
# text
# heading| >>
set_visual_selection(u'V', 11, 17, cursor_pos=END)
self.assertEqual(self.navigator.parent(mode=u'visual'), None)
# << text
# heading
# text
# x. heading
# text| >>
set_visual_selection(u'V', 8, 14, cursor_pos=END)
self.navigator.parent(mode=u'visual')
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 8ggV12gg'))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(NavigatorTestCase)

View file

@ -0,0 +1,385 @@
# -*- coding: utf-8 -*-
import unittest
import sys
sys.path.append(u'../ftplugin')
import vim
from orgmode._vim import ORGMODE
from orgmode.py3compat.encode_compatibility import *
counter = 0
class ShowHideTestCase(unittest.TestCase):
def setUp(self):
global counter
counter += 1
vim.CMDHISTORY = []
vim.CMDRESULTS = {}
vim.EVALHISTORY = []
vim.EVALRESULTS = {
# no org_todo_keywords for b
u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'),
# global values for org_todo_keywords
u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'),
u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')],
u_encode(u'exists("g:org_debug")'): u_encode(u'0'),
u_encode(u'exists("b:org_debug")'): u_encode(u'0'),
u_encode(u'exists("*repeat#set()")'): u_encode(u'0'),
u_encode(u'b:changedtick'): u_encode(u'%d' % counter),
u_encode(u"v:count"): u_encode(u'0')}
if not u'ShowHide' in ORGMODE.plugins:
ORGMODE.register_plugin(u'ShowHide')
self.showhide = ORGMODE.plugins[u'ShowHide']
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* Überschrift 1
Text 1
Bla bla
** Überschrift 1.1
Text 2
Bla Bla bla
** Überschrift 1.2
Text 3
**** Überschrift 1.2.1.falsch
Bla Bla bla bla
*** Überschrift 1.2.1
* Überschrift 2
* Überschrift 3
asdf sdf
""".split(u'\n') ]
def test_no_heading_toggle_folding(self):
vim.current.window.cursor = (1, 0)
self.assertEqual(self.showhide.toggle_folding(), None)
self.assertEqual(vim.EVALHISTORY[-1], u_encode(u'feedkeys("<Tab>", "n")'))
self.assertEqual(vim.current.window.cursor, (1, 0))
def test_toggle_folding_first_heading_with_no_children(self):
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* Überschrift 1
Text 1
Bla bla
* Überschrift 2
* Überschrift 3
asdf sdf
""".split(u'\n') ]
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'2'),
u_encode(u'foldclosed(6)'): u_encode(u'-1'),
u_encode(u'foldclosed(7)'): u_encode(u'-1'),
})
vim.current.window.cursor = (2, 0)
self.assertNotEqual(self.showhide.toggle_folding(), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 1zo'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_close_one(self):
vim.current.window.cursor = (13, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(13)'): u_encode(u'-1'),
})
self.assertNotEqual(self.showhide.toggle_folding(), None)
self.assertEqual(len(vim.CMDHISTORY), 2)
self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'13,15foldclose!'))
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2zo'))
self.assertEqual(vim.current.window.cursor, (13, 0))
def test_toggle_folding_open_one(self):
vim.current.window.cursor = (10, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(10)'): u_encode(u'10'),
})
self.assertNotEqual(self.showhide.toggle_folding(), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 1zo'))
self.assertEqual(vim.current.window.cursor, (10, 0))
def test_toggle_folding_close_multiple_all_open(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'-1'),
u_encode(u'foldclosed(10)'): u_encode(u'-1'),
u_encode(u'foldclosed(13)'): u_encode(u'-1'),
u_encode(u'foldclosed(16)'): u_encode(u'-1'),
})
self.assertNotEqual(self.showhide.toggle_folding(), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'2,16foldclose!'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_all_closed(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'2'),
})
self.assertNotEqual(self.showhide.toggle_folding(), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 1zo'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_first_level_open(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'6'),
u_encode(u'foldclosed(10)'): u_encode(u'10'),
u_encode(u'foldclosed(13)'): u_encode(u'13'),
u_encode(u'foldclosed(16)'): u_encode(u'16'),
})
self.assertNotEqual(self.showhide.toggle_folding(), None)
self.assertEqual(len(vim.CMDHISTORY), 2)
self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 6gg1zo'))
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 10gg1zo'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_second_level_half_open(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'-1'),
u_encode(u'foldclosed(10)'): u_encode(u'10'),
u_encode(u'foldclosed(13)'): u_encode(u'13'),
u_encode(u'foldclosed(16)'): u_encode(u'16'),
})
self.assertNotEqual(self.showhide.toggle_folding(), None)
self.assertEqual(len(vim.CMDHISTORY), 4)
self.assertEqual(vim.CMDHISTORY[-4], u_encode(u'normal! 6gg2zo'))
self.assertEqual(vim.CMDHISTORY[-3], u_encode(u'normal! 10gg2zo'))
self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13gg2zo'))
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16gg2zo'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_other_second_level_half_open(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'6'),
u_encode(u'foldclosed(10)'): u_encode(u'-1'),
u_encode(u'foldclosed(13)'): u_encode(u'13'),
u_encode(u'foldclosed(16)'): u_encode(u'16'),
})
self.assertNotEqual(self.showhide.toggle_folding(), None)
self.assertEqual(len(vim.CMDHISTORY), 4)
self.assertEqual(vim.CMDHISTORY[-4], u_encode(u'normal! 6gg2zo'))
self.assertEqual(vim.CMDHISTORY[-3], u_encode(u'normal! 10gg2zo'))
self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13gg2zo'))
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16gg2zo'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_third_level_half_open(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'-1'),
u_encode(u'foldclosed(10)'): u_encode(u'-1'),
u_encode(u'foldclosed(13)'): u_encode(u'-1'),
u_encode(u'foldclosed(16)'): u_encode(u'16'),
})
self.assertNotEqual(self.showhide.toggle_folding(), None)
self.assertEqual(len(vim.CMDHISTORY), 4)
self.assertEqual(vim.CMDHISTORY[-4], u_encode(u'normal! 6gg3zo'))
self.assertEqual(vim.CMDHISTORY[-3], u_encode(u'normal! 10gg3zo'))
self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13gg3zo'))
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16gg3zo'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_other_third_level_half_open(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'-1'),
u_encode(u'foldclosed(10)'): u_encode(u'-1'),
u_encode(u'foldclosed(13)'): u_encode(u'13'),
u_encode(u'foldclosed(16)'): u_encode(u'-1'),
})
self.assertNotEqual(self.showhide.toggle_folding(), None)
self.assertEqual(len(vim.CMDHISTORY), 4)
self.assertEqual(vim.CMDHISTORY[-4], u_encode(u'normal! 6gg3zo'))
self.assertEqual(vim.CMDHISTORY[-3], u_encode(u'normal! 10gg3zo'))
self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13gg3zo'))
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16gg3zo'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_other_third_level_half_open_second_level_half_closed(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'6'),
u_encode(u'foldclosed(10)'): u_encode(u'-1'),
u_encode(u'foldclosed(13)'): u_encode(u'13'),
u_encode(u'foldclosed(16)'): u_encode(u'-1'),
})
self.assertNotEqual(self.showhide.toggle_folding(), None)
self.assertEqual(len(vim.CMDHISTORY), 4)
self.assertEqual(vim.CMDHISTORY[-4], u_encode(u'normal! 6gg3zo'))
self.assertEqual(vim.CMDHISTORY[-3], u_encode(u'normal! 10gg3zo'))
self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13gg3zo'))
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16gg3zo'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_no_heading_toggle_folding_reverse(self):
vim.current.window.cursor = (1, 0)
self.assertEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(vim.EVALHISTORY[-1], u_encode(u'feedkeys("<Tab>", "n")'))
self.assertEqual(vim.current.window.cursor, (1, 0))
def test_toggle_folding_first_heading_with_no_children_reverse(self):
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* Überschrift 1
Text 1
Bla bla
* Überschrift 2
* Überschrift 3
asdf sdf
""".split(u'\n') ]
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'2'),
u_encode(u'foldclosed(6)'): u_encode(u'-1'),
u_encode(u'foldclosed(7)'): u_encode(u'-1'),
})
vim.current.window.cursor = (2, 0)
self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'2,5foldopen!'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_close_one_reverse(self):
vim.current.window.cursor = (13, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(13)'): u_encode(u'-1'),
})
self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 13ggzc'))
self.assertEqual(vim.current.window.cursor, (13, 0))
def test_toggle_folding_open_one_reverse(self):
vim.current.window.cursor = (10, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(10)'): u_encode(u'10'),
})
self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'10,16foldopen!'))
self.assertEqual(vim.current.window.cursor, (10, 0))
def test_toggle_folding_close_multiple_all_open_reverse(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'-1'),
u_encode(u'foldclosed(10)'): u_encode(u'-1'),
u_encode(u'foldclosed(13)'): u_encode(u'-1'),
u_encode(u'foldclosed(16)'): u_encode(u'-1'),
})
self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(len(vim.CMDHISTORY), 2)
self.assertEqual(vim.CMDHISTORY[-2], u_encode(u'normal! 13ggzc'))
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16ggzc'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_all_closed_reverse(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'2'),
})
self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'2,16foldopen!'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_first_level_open_reverse(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'6'),
u_encode(u'foldclosed(10)'): u_encode(u'10'),
u_encode(u'foldclosed(13)'): u_encode(u'13'),
u_encode(u'foldclosed(16)'): u_encode(u'16'),
})
self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 2ggzc'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_second_level_half_open_reverse(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'-1'),
u_encode(u'foldclosed(10)'): u_encode(u'10'),
u_encode(u'foldclosed(13)'): u_encode(u'13'),
u_encode(u'foldclosed(16)'): u_encode(u'16'),
})
self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 6ggzc'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_other_second_level_half_open_reverse(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'6'),
u_encode(u'foldclosed(10)'): u_encode(u'-1'),
u_encode(u'foldclosed(13)'): u_encode(u'13'),
u_encode(u'foldclosed(16)'): u_encode(u'16'),
})
self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 10ggzc'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_third_level_half_open_reverse(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'-1'),
u_encode(u'foldclosed(10)'): u_encode(u'-1'),
u_encode(u'foldclosed(13)'): u_encode(u'-1'),
u_encode(u'foldclosed(16)'): u_encode(u'16'),
})
self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 13ggzc'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_other_third_level_half_open_reverse(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'-1'),
u_encode(u'foldclosed(10)'): u_encode(u'-1'),
u_encode(u'foldclosed(13)'): u_encode(u'13'),
u_encode(u'foldclosed(16)'): u_encode(u'-1'),
})
self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16ggzc'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def test_toggle_folding_open_multiple_other_third_level_half_open_second_level_half_closed_reverse(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS.update({
u_encode(u'foldclosed(2)'): u_encode(u'-1'),
u_encode(u'foldclosed(6)'): u_encode(u'6'),
u_encode(u'foldclosed(10)'): u_encode(u'-1'),
u_encode(u'foldclosed(13)'): u_encode(u'13'),
u_encode(u'foldclosed(16)'): u_encode(u'-1'),
})
self.assertNotEqual(self.showhide.toggle_folding(reverse=True), None)
self.assertEqual(len(vim.CMDHISTORY), 1)
self.assertEqual(vim.CMDHISTORY[-1], u_encode(u'normal! 16ggzc'))
self.assertEqual(vim.current.window.cursor, (2, 0))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(ShowHideTestCase)

View file

@ -0,0 +1,177 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
import sys
sys.path.append(u'../ftplugin')
import vim
from orgmode._vim import indent_orgmode, fold_orgmode, ORGMODE
from orgmode.py3compat.encode_compatibility import *
ORGMODE.debug = True
START = True
END = False
counter = 0
class TagsPropertiesTestCase(unittest.TestCase):
def setUp(self):
global counter
counter += 1
vim.CMDHISTORY = []
vim.CMDRESULTS = {}
vim.EVALHISTORY = []
vim.EVALRESULTS = {
# no org_todo_keywords for b
u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'),
# global values for org_todo_keywords
u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'),
u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')],
u_encode(u'&ts'): u_encode(u'6'),
u_encode(u'exists("b:org_tag_column")'): u_encode(u'0'),
u_encode(u'exists("g:org_tag_column")'): u_encode(u'0'),
u_encode(u'exists("g:org_debug")'): u_encode(u'0'),
u_encode(u'exists("b:org_debug")'): u_encode(u'0'),
u_encode(u'exists("*repeat#set()")'): u_encode(u'0'),
u_encode(u'b:changedtick'): (u_encode(u'%d' % counter)),
u_encode(u"v:count"): u_encode(u'0')}
if not u'TagsProperties' in ORGMODE.plugins:
ORGMODE.register_plugin(u'TagsProperties')
self.tagsproperties = ORGMODE.plugins[u'TagsProperties']
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* Überschrift 1
Text 1
Bla bla
** Überschrift 1.1
Text 2
Bla Bla bla
** Überschrift 1.2
Text 3
**** Überschrift 1.2.1.falsch
Bla Bla bla bla
*** Überschrift 1.2.1
* Überschrift 2
* Überschrift 3
asdf sdf
""".split(u'\n') ]
def test_new_property(self):
u""" TODO: Docstring for test_new_property
:returns: TODO
"""
pass
def test_set_tags(self):
# set first tag
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u':hello:')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:'))
# set second tag
vim.EVALRESULTS[u_encode(u'input("Tags: ", ":hello:", "customlist,Org_complete_tags")')] = u_encode(u':hello:world:')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:'))
def test_parse_tags_no_colons_single_tag(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u'hello')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:'))
def test_parse_tags_no_colons_multiple_tags(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u'hello:world')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:'))
def test_parse_tags_single_colon_left_single_tag(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u':hello')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:'))
def test_parse_tags_single_colon_left_multiple_tags(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u':hello:world')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:'))
def test_parse_tags_single_colon_right_single_tag(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u'hello:')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:'))
def test_parse_tags_single_colon_right_multiple_tags(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u'hello:world:')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:'))
def test_filter_empty_tags(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u'::hello::')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:'))
def test_delete_tags(self):
# set up
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u':hello:world:')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:'))
# delete second of two tags
vim.EVALRESULTS[u_encode(u'input("Tags: ", ":hello:world:", "customlist,Org_complete_tags")')] = u_encode(u':hello:')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t\t :hello:'))
# delete last tag
vim.EVALRESULTS[u_encode(u'input("Tags: ", ":hello:", "customlist,Org_complete_tags")')] = u_encode(u'')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1'))
def test_realign_tags_noop(self):
vim.current.window.cursor = (2, 0)
self.tagsproperties.realign_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1'))
def test_realign_tags_remove_spaces(self):
# remove spaces in multiple locations
vim.current.buffer[1] = u_encode(u'* Überschrift 1 ')
vim.current.window.cursor = (2, 0)
self.tagsproperties.realign_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1'))
# remove tabs and spaces in multiple locations
vim.current.buffer[1] = u_encode(u'*\t \tÜberschrift 1 \t')
vim.current.window.cursor = (2, 0)
self.tagsproperties.realign_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1'))
def test_realign_tags(self):
vim.current.window.cursor = (2, 0)
vim.EVALRESULTS[u_encode(u'input("Tags: ", "", "customlist,Org_complete_tags")')] = u_encode(u':hello:world:')
self.tagsproperties.set_tags()
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:'))
d = ORGMODE.get_document()
heading = d.find_current_heading()
self.assertEqual(str(heading), u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:'))
self.tagsproperties.realign_tags()
heading = d.find_current_heading()
self.assertEqual(str(heading), u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:'))
self.assertEqual(vim.current.buffer[1], u_encode(u'* Überschrift 1\t\t\t\t\t\t\t\t :hello:world:'))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(TagsPropertiesTestCase)

View file

@ -0,0 +1,424 @@
# -*- coding: utf-8 -*-
import sys
sys.path.append(u'../ftplugin')
import unittest
from orgmode.liborgmode.base import Direction
from orgmode.vimbuffer import VimBuffer
from orgmode.plugins.Todo import Todo
import vim
from orgmode.py3compat.encode_compatibility import *
counter = 0
class TodoTestCase(unittest.TestCase):
u"""Tests all the functionality of the TODO module."""
def setUp(self):
# set content of the buffer
global counter
counter += 1
vim.EVALHISTORY = []
vim.EVALRESULTS = {
# no org_todo_keywords for b
u_encode(u'exists("b:org_todo_keywords")'): u_encode('0'),
# global values for org_todo_keywords
u_encode(u'exists("g:org_todo_keywords")'): u_encode('1'),
u_encode(u'g:org_todo_keywords'): [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')],
u_encode(u'exists("g:org_debug")'): u_encode(u'0'),
u_encode(u'exists("b:org_debug")'): u_encode(u'0'),
u_encode(u'exists("*repeat#set()")'): u_encode(u'0'),
u_encode(u'b:changedtick'): u_encode(u'%d' % counter),
u_encode(u"v:count"): u_encode(u'0')
}
vim.current.buffer[:] = [ u_encode(i) for i in u"""
* Heading 1
** Text 1
*** Text 2
* Text 1
** Text 1
some text that is
no heading
""".split(u'\n') ]
# toggle
def test_toggle_todo_with_no_heading(self):
# nothing should happen
vim.current.window.cursor = (1, 0)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[0], u'')
# and repeat it -> it should not change
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[0], u'')
def test_todo_toggle_NOTODO(self):
vim.current.window.cursor = (2, 0)
vim.current.buffer[1] = u_encode(u'** NOTODO Überschrift 1.1')
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u_encode(u'** TODO NOTODO Überschrift 1.1'))
def test_toggle_todo_in_heading_with_no_todo_state_different_levels(self):
# level 1
vim.current.window.cursor = (2, 0)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u'* TODO Heading 1')
self.assertEqual((2, 0), vim.current.window.cursor)
# level 2
vim.current.window.cursor = (3, 0)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[2], u'** TODO Text 1')
# level 2
vim.current.window.cursor = (4, 4)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[3], u'*** TODO Text 2')
self.assertEqual((4, 9), vim.current.window.cursor)
def test_circle_through_todo_states(self):
# * Heading 1 -->
# * TODO Heading 1 -->
# * DONE Heading 1 -->
# * Heading 1 -->
# * TODO Heading 1 -->
# * DONE Heading 1
vim.current.window.cursor = (2, 6)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u'* TODO Heading 1')
self.assertEqual((2, 11), vim.current.window.cursor)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u'* DONE Heading 1')
self.assertEqual((2, 11), vim.current.window.cursor)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u'* Heading 1')
self.assertEqual((2, 6), vim.current.window.cursor)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u'* TODO Heading 1')
self.assertEqual((2, 11), vim.current.window.cursor)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u'* DONE Heading 1')
self.assertEqual((2, 11), vim.current.window.cursor)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u'* Heading 1')
self.assertEqual((2, 6), vim.current.window.cursor)
def test_circle_through_todo_states_with_more_states(self):
# * Heading 1 -->
# * TODO Heading 1 -->
# * STARTED Heading 1 -->
# * DONE Heading 1 -->
# * Heading 1 -->
vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'STARTED'), u_encode(u'DONE'),
u_encode(u'|')]
vim.current.window.cursor = (2, 0)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u'* TODO Heading 1')
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u'* STARTED Heading 1')
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u'* DONE Heading 1')
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[1], u'* Heading 1')
def test_toggle_todo_with_cursor_in_text_not_heading(self):
# nothing should happen
vim.current.window.cursor = (7, 0)
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[5], u'** TODO Text 1')
self.assertEqual(vim.current.window.cursor, (7, 0))
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[5], u'** DONE Text 1')
self.assertEqual(vim.current.window.cursor, (7, 0))
Todo.toggle_todo_state()
self.assertEqual(vim.current.buffer[5], u'** Text 1')
self.assertEqual(vim.current.window.cursor, (7, 0))
# get_states
def test_get_states_without_seperator(self):
u"""The last element in the todostates shouold be used as DONE-state when no sperator is given"""
vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'DONE')]
states_todo, states_done = VimBuffer().get_todo_states()[0]
expected_todo, expected_done = [u'TODO'], [u'DONE']
self.assertEqual(states_todo, expected_todo)
self.assertEqual(states_done, expected_done)
vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'INPROGRESS'), u_encode(u'DONE')]
states_todo, states_done = VimBuffer().get_todo_states()[0]
expected_todo = [u'TODO', u'INPROGRESS']
expected_done = [u'DONE']
self.assertEqual(states_todo, expected_todo)
self.assertEqual(states_done, expected_done)
vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'INPROGRESS'),
u_encode(u'DUMMY'), u_encode(u'DONE')]
states_todo, states_done = VimBuffer().get_todo_states()[0]
expected_todo = [u'TODO', u'INPROGRESS', u'DUMMY']
expected_done = [u'DONE']
self.assertEqual(states_todo, expected_todo)
self.assertEqual(states_done, expected_done)
def test_get_states_with_seperator(self):
vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')]
states_todo, states_done = VimBuffer().get_todo_states()[0]
expected_todo = [u'TODO']
expected_done = [u'DONE']
self.assertEqual(states_todo, expected_todo)
self.assertEqual(states_done, expected_done)
vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'INPROGRESS'), u_encode(u'|'),
u_encode(u'DONE')]
states_todo, states_done = VimBuffer().get_todo_states()[0]
expected_todo = [u'TODO', u'INPROGRESS']
expected_done = [u'DONE']
self.assertEqual(states_todo, expected_todo)
self.assertEqual(states_done, expected_done)
vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'INPROGRESS'),
u_encode(u'DUMMY'), u_encode(u'|'), u_encode(u'DONE')]
states_todo, states_done = VimBuffer().get_todo_states()[0]
expected_todo = [u'TODO', u'INPROGRESS', u'DUMMY']
expected_done = [u'DONE']
self.assertEqual(states_todo, expected_todo)
self.assertEqual(states_done, expected_done)
vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'INPROGRESS'),
u_encode(u'DUMMY'), u_encode(u'|'), u_encode(u'DELEGATED'), u_encode(u'DONE')]
states_todo, states_done = VimBuffer().get_todo_states()[0]
expected_todo =[u'TODO', u'INPROGRESS', u'DUMMY']
expected_done = [u'DELEGATED', u'DONE']
self.assertEqual(states_todo, expected_todo)
self.assertEqual(states_done, expected_done)
vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONEX'),
u_encode(u'DUMMY'), u_encode(u'DELEGATED'), u_encode(u'DONE')]
states_todo, states_done = VimBuffer().get_todo_states()[0]
expected_todo = [u'TODO']
expected_done = [u'DONEX', u'DUMMY', u'DELEGATED', u'DONE']
self.assertEqual(states_todo, expected_todo)
self.assertEqual(states_done, expected_done)
vim.EVALRESULTS[u_encode(u'g:org_todo_keywords')] = [[u_encode(u'TODO(t)'), u_encode(u'|'), u_encode(u'DONEX')],
[u_encode(u'DUMMY'), u_encode(u'DELEGATED'), u_encode(u'DONE')]]
states_todo, states_done = VimBuffer().get_todo_states()[0]
expected_todo = [u'TODO']
expected_done = [u'DONEX']
self.assertEqual(states_todo, expected_todo)
self.assertEqual(states_done, expected_done)
# get_next_state
def test_get_next_state_with_no_current_state(self):
states = [((u'TODO', ), (u'DONE', ))]
current_state = u''
self.assertEquals(Todo._get_next_state(current_state, states), u'TODO')
states = [((u'TODO', u'NEXT'), (u'DELEGATED', u'DONE'))]
self.assertEquals(Todo._get_next_state(current_state, states), u'TODO')
states = [((u'NEXT', ), (u'DELEGATED', u'DONE'))]
self.assertEquals(Todo._get_next_state(current_state, states), u'NEXT')
def test_get_next_state_backward_with_no_current_state(self):
states = [((u'TODO', ), (u'DONE', ))]
current_state = u''
self.assertEquals(Todo._get_next_state(current_state, states,
Direction.BACKWARD), u'DONE')
states = [((u'TODO', u'NEXT'), (u'DELEGATED', u'DONE'))]
self.assertEquals(Todo._get_next_state(current_state, states,
Direction.BACKWARD), u'DONE')
states = [((u'NEXT', ), (u'DELEGATED', u'DONE'))]
self.assertEquals(Todo._get_next_state(current_state, states,
Direction.BACKWARD), u'DONE')
def test_get_next_state_with_invalid_current_state(self):
states = [((u'TODO', ), (u'DONE', ))]
current_state = u'STI'
self.assertEquals(Todo._get_next_state(current_state, states), u'TODO')
states = [((u'TODO', u'NEXT'), (u'DELEGATED', u'DONE'))]
self.assertEquals(Todo._get_next_state(current_state, states), u'TODO')
states = [((u'NEXT', ), (u'DELEGATED', u'DONE'))]
self.assertEquals(Todo._get_next_state(current_state, states), u'NEXT')
def test_get_next_state_backward_with_invalid_current_state(self):
states = [((u'TODO', ), (u'DONE', ))]
current_state = u'STI'
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, u'DONE')
states = [((u'TODO', u'NEXT'), (u'DELEGATED', u'DONE'))]
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, u'DONE')
states = [((u'NEXT', ), (u'DELEGATED', u'DONE'))]
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, u'DONE')
def test_get_next_state_with_current_state_equals_todo_state(self):
states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE'))]
current_state = u'TODO'
self.assertEquals(Todo._get_next_state(current_state, states), u'NEXT')
current_state = u'NEXT'
self.assertEquals(Todo._get_next_state(current_state, states), u'NOW')
def test_get_next_state_backward_with_current_state_equals_todo_state(self):
states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE'))]
current_state = u'TODO'
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, None)
def test_get_next_state_backward_misc(self):
states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE'))]
current_state = u'DONE'
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, u'DELEGATED')
current_state = u'DELEGATED'
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, u'NOW')
current_state = u'NOW'
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, u'NEXT')
current_state = u'NEXT'
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, u'TODO')
current_state = u'TODO'
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, None)
current_state = None
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, u'DONE')
def test_get_next_state_with_jump_from_todo_to_done(self):
states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE'))]
current_state = u'NOW'
self.assertEquals(Todo._get_next_state(current_state, states), u'DELEGATED')
def test_get_next_state_with_jump_from_done_to_todo(self):
states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE'))]
current_state = u'DONE'
self.assertEquals(Todo._get_next_state(current_state, states), None)
def test_get_next_state_in_current_sequence(self):
states = [((u'TODO', u'NEXT', u'NOW'), (u'DELEGATED', u'DONE')), ((u'QA', ), (u'RELEASED', ))]
current_state = u'QA'
result = Todo._get_next_state(current_state, states,
Direction.FORWARD)
self.assertEquals(result, u'RELEASED')
def test_get_next_state_in_current_sequence_with_access_keys(self):
states = [((u'TODO(t)', u'NEXT(n)', u'NOW(w)'), (u'DELEGATED(g)', u'DONE(d)')), ((u'QA(q)', ), (u'RELEASED(r)', ))]
current_state = u'QA'
result = Todo._get_next_state(current_state, states,
Direction.FORWARD)
self.assertEquals(result, u'RELEASED')
current_state = u'NEXT'
result = Todo._get_next_state(current_state, states,
Direction.FORWARD)
self.assertEquals(result, u'NOW')
current_state = u'TODO'
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, None)
current_state = None
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD)
self.assertEquals(result, u'RELEASED')
def test_get_next_keyword_sequence(self):
states = [((u'TODO(t)', u'NEXT(n)', u'NOW(w)'), (u'DELEGATED(g)', u'DONE(d)')), ((u'QA(q)', ), (u'RELEASED(r)', ))]
current_state = None
result = Todo._get_next_state(current_state, states,
Direction.FORWARD, next_set=True)
self.assertEquals(result, u'TODO')
current_state = None
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD, next_set=True)
self.assertEquals(result, u'QA')
current_state = u'TODO'
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD, next_set=True)
self.assertEquals(result, None)
current_state = u'TODO'
result = Todo._get_next_state(current_state, states,
Direction.FORWARD, next_set=True)
self.assertEquals(result, u'QA')
current_state = u'NOW'
result = Todo._get_next_state(current_state, states,
Direction.FORWARD, next_set=True)
self.assertEquals(result, u'QA')
current_state = u'DELEGATED'
result = Todo._get_next_state(current_state, states,
Direction.FORWARD, next_set=True)
self.assertEquals(result, u'QA')
current_state = u'QA'
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD, next_set=True)
self.assertEquals(result, u'TODO')
current_state = u'QA'
result = Todo._get_next_state(current_state, states,
Direction.FORWARD, next_set=True)
self.assertEquals(result, None)
current_state = u'RELEASED'
result = Todo._get_next_state(current_state, states,
Direction.FORWARD, next_set=True)
self.assertEquals(result, None)
current_state = u'RELEASED'
result = Todo._get_next_state(current_state, states,
Direction.BACKWARD, next_set=True)
self.assertEquals(result, u'TODO')
def suite():
return unittest.TestLoader().loadTestsFromTestCase(TodoTestCase)
# vi: noexpandtab

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-
class VimWindow(object):
u""" Docstring for VimWindow """
def __init__(self, test):
object.__init__(self)
self._test = test
self.cursor = (1, 0)
def buffer():
def fget(self):
return self._test.buffer
def fset(self, value):
self._test.buffer = value
return locals()
buffer = property(**buffer())
class VimBuffer(list):
def __init__(self, iterable=None):
self.number = 0
if iterable is not None:
list.__init__(self, iterable)
else:
list.__init__(self)
def append(self, o):
u"""
mimic the specific behavior of vim.current.buffer
"""
if isinstance(o, list) or isinstance(o, tuple):
for i in o:
list.append(self, i)
else:
list.append(self, o)
class VimTest(object):
u""" Replacement for vim API """
def __init__(self):
object.__init__(self)
self._buffer = VimBuffer()
self.window = VimWindow(self)
def buffer():
def fget(self):
return self._buffer
def fset(self, value):
self._buffer = VimBuffer(value)
return locals()
buffer = property(**buffer())
EVALHISTORY = []
EVALRESULTS = {
u'exists("g:org_debug")': 0,
u'exists("b:org_debug")': 0,
u'exists("*repeat#set()")': 0,
u'exists("b:org_plugins")': 0,
u'exists("g:org_plugins")': 0,
u'b:changedtick': 0,
}
def eval(cmd):
u""" evaluate command
:returns: results stored in EVALRESULTS
"""
EVALHISTORY.append(cmd)
return EVALRESULTS.get(cmd, None)
CMDHISTORY = []
CMDRESULTS = {}
def command(cmd):
CMDHISTORY.append(cmd)
return CMDRESULTS.get(cmd, None)
current = VimTest()

Some files were not shown because too many files have changed in this diff Show more