I broke up with neovim....vim is my best friend now
This commit is contained in:
5
dot_vim/plugged/vim-orgmode/ftplugin/org.cnf
Normal file
5
dot_vim/plugged/vim-orgmode/ftplugin/org.cnf
Normal 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/
|
||||
170
dot_vim/plugged/vim-orgmode/ftplugin/org.vim
Normal file
170
dot_vim/plugged/vim-orgmode/ftplugin/org.vim
Normal file
@@ -0,0 +1,170 @@
|
||||
" 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 glob, vim, os, sys
|
||||
|
||||
for p in vim.eval("&runtimepath").split(','):
|
||||
dname = os.path.join(p, "ftplugin")
|
||||
matches = glob.glob(dname)
|
||||
for match in matches:
|
||||
if os.path.exists(os.path.join(match, "orgmode")):
|
||||
if match not in sys.path:
|
||||
sys.path.append(match)
|
||||
break
|
||||
|
||||
from orgmode._vim import ORGMODE, insert_at_cursor, get_user_input, date_to_str
|
||||
ORGMODE.start()
|
||||
|
||||
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)
|
||||
exe s:py_version . "selected_date = " . printf("datetime.date(%d, %d, %d)", a:year, a:month, a:day)
|
||||
exe s:py_version . "org_timestamp = '" . g:org_timestamp_template . "' % date_to_str(selected_date)"
|
||||
|
||||
" get_user_input
|
||||
exe s:py_version . "modifier = get_user_input(org_timestamp)"
|
||||
" change date according to user input
|
||||
exe s:py_version . "newdate = Date._modify_time(selected_date, modifier)"
|
||||
exe s:py_version . "newdate = date_to_str(newdate)"
|
||||
" close Calendar
|
||||
exe "q"
|
||||
" goto previous window
|
||||
exe "wincmd p"
|
||||
exe s:py_version . "timestamp = '" . g:org_timestamp_template . "' % newdate"
|
||||
exe s:py_version . "if modifier != None: insert_at_cursor(timestamp)"
|
||||
" restore calendar_action
|
||||
let g:calendar_action = g:org_calendar_action_backup
|
||||
endf
|
||||
1
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/__init__.py
Normal file
1
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
408
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/_vim.py
Normal file
408
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/_vim.py
Normal file
@@ -0,0 +1,408 @@
|
||||
# -*- 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.
|
||||
"""
|
||||
try:
|
||||
vim.command(u_encode(u'call inputsave()'))
|
||||
vim.command(u_encode(u"let user_input = input('" + message + u": ')"))
|
||||
vim.command(u_encode(u'call inputrestore()'))
|
||||
return u_decode(vim.eval(u_encode(u'user_input')))
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_bufnumber(bufname):
|
||||
"""
|
||||
Return the number of the buffer for the given bufname if it exist;
|
||||
else None.
|
||||
"""
|
||||
for b in vim.buffers:
|
||||
if b.name == bufname:
|
||||
return int(b.number)
|
||||
|
||||
|
||||
def get_bufname(bufnr):
|
||||
"""
|
||||
Return the name of the buffer for the given bufnr if it exist; else None.
|
||||
"""
|
||||
for b in vim.buffers:
|
||||
if b.number == bufnr:
|
||||
return b.name
|
||||
|
||||
|
||||
def indent_orgmode():
|
||||
u""" Set the indent value for the current line in the variable
|
||||
b:indent_level
|
||||
|
||||
Vim prerequisites:
|
||||
:setlocal indentexpr=Method-which-calls-indent_orgmode
|
||||
|
||||
:returns: None
|
||||
"""
|
||||
line = int(vim.eval(u_encode(u'v:lnum')))
|
||||
d = ORGMODE.get_document()
|
||||
heading = d.current_heading(line - 1)
|
||||
if heading and line != heading.start_vim:
|
||||
heading.init_checkboxes()
|
||||
checkbox = heading.current_checkbox()
|
||||
level = heading.level + 1
|
||||
if checkbox:
|
||||
if line != checkbox.start_vim:
|
||||
# indent body up to the beginning of the checkbox' text
|
||||
# if checkbox isn't indented to the proper location, the body
|
||||
# won't be indented either
|
||||
level = checkbox.level + len(checkbox.type) + 1 + \
|
||||
(4 if checkbox.status else 0)
|
||||
vim.command(u_encode((u'let b:indent_level = %d' % level)))
|
||||
|
||||
|
||||
def fold_text(allow_dirty=False):
|
||||
u""" Set the fold text
|
||||
:setlocal foldtext=Method-which-calls-foldtext
|
||||
|
||||
:allow_dirty: Perform a query without (re)building the DOM if True
|
||||
:returns: None
|
||||
"""
|
||||
line = int(vim.eval(u_encode(u'v:foldstart')))
|
||||
d = ORGMODE.get_document(allow_dirty=allow_dirty)
|
||||
heading = None
|
||||
if allow_dirty:
|
||||
heading = d.find_current_heading(line - 1)
|
||||
else:
|
||||
heading = d.current_heading(line - 1)
|
||||
if heading:
|
||||
str_heading = unicode(heading)
|
||||
|
||||
# expand tabs
|
||||
ts = int(vim.eval(u_encode(u'&ts')))
|
||||
idx = str_heading.find(u'\t')
|
||||
if idx != -1:
|
||||
tabs, spaces = divmod(idx, ts)
|
||||
str_heading = str_heading.replace(u'\t', u' ' * (ts - spaces), 1)
|
||||
str_heading = str_heading.replace(u'\t', u' ' * ts)
|
||||
|
||||
# Workaround for vim.command seems to break the completion menu
|
||||
vim.eval(u_encode(u'SetOrgFoldtext("%s...")' % (re.sub(r'\[\[([^[\]]*\]\[)?([^[\]]+)\]\]', r'\2',
|
||||
str_heading).replace( u'\\', u'\\\\').replace(u'"', u'\\"'), )))
|
||||
|
||||
|
||||
def fold_orgmode(allow_dirty=False):
|
||||
u""" Set the fold expression/value for the current line in the variable
|
||||
b:fold_expr
|
||||
|
||||
Vim prerequisites:
|
||||
:setlocal foldmethod=expr
|
||||
:setlocal foldexpr=Method-which-calls-fold_orgmode
|
||||
|
||||
:allow_dirty: Perform a query without (re)building the DOM if True
|
||||
:returns: None
|
||||
"""
|
||||
line = int(vim.eval(u_encode(u'v:lnum')))
|
||||
d = ORGMODE.get_document(allow_dirty=allow_dirty)
|
||||
heading = None
|
||||
if allow_dirty:
|
||||
heading = d.find_current_heading(line - 1)
|
||||
else:
|
||||
heading = d.current_heading(line - 1)
|
||||
|
||||
# if cache_heading != heading:
|
||||
# heading.init_checkboxes()
|
||||
# checkbox = heading.current_checkbox()
|
||||
|
||||
# cache_heading = heading
|
||||
if heading:
|
||||
# if checkbox:
|
||||
# vim.command((u'let b:fold_expr = ">%d"' % heading.level + checkbox.level).encode(u'utf-8'))
|
||||
if 0:
|
||||
pass
|
||||
elif line == heading.start_vim:
|
||||
vim.command(u_encode(u'let b:fold_expr = ">%d"' % heading.level))
|
||||
#elif line == heading.end_vim:
|
||||
# vim.command((u'let b:fold_expr = "<%d"' % heading.level).encode(u'utf-8'))
|
||||
# end_of_last_child_vim is a performance junky and is actually not needed
|
||||
#elif line == heading.end_of_last_child_vim:
|
||||
# vim.command((u'let b:fold_expr = "<%d"' % heading.level).encode(u'utf-8'))
|
||||
else:
|
||||
vim.command(u_encode(u'let b:fold_expr = %d' % heading.level))
|
||||
|
||||
|
||||
def date_to_str(date):
|
||||
if isinstance(date, datetime):
|
||||
date = date.strftime(u_decode(u_encode(u'%Y-%m-%d %a %H:%M')))
|
||||
else:
|
||||
date = date.strftime(u_decode(u_encode(u'%Y-%m-%d %a')))
|
||||
return date
|
||||
|
||||
class OrgMode(object):
|
||||
u""" Vim Buffer """
|
||||
|
||||
def __init__(self):
|
||||
object.__init__(self)
|
||||
self.debug = bool(int(orgmode.settings.get(u'org_debug', False)))
|
||||
|
||||
self.orgmenu = orgmode.menu.Submenu(u'&Org')
|
||||
self._plugins = {}
|
||||
# list of vim buffer objects
|
||||
self._documents = {}
|
||||
|
||||
# agenda manager
|
||||
self.agenda_manager = AgendaManager()
|
||||
|
||||
def get_document(self, bufnr=0, allow_dirty=False):
|
||||
""" Retrieve instance of vim buffer document. This Document should be
|
||||
used for manipulating the vim buffer.
|
||||
|
||||
:bufnr: Retrieve document with bufnr
|
||||
:allow_dirty: Allow the retrieved document to be dirty
|
||||
|
||||
:returns: vim buffer instance
|
||||
"""
|
||||
if bufnr == 0:
|
||||
bufnr = vim.current.buffer.number
|
||||
|
||||
if bufnr in self._documents:
|
||||
if allow_dirty or self._documents[bufnr].is_insync:
|
||||
return self._documents[bufnr]
|
||||
self._documents[bufnr] = VimBuffer(bufnr).init_dom()
|
||||
return self._documents[bufnr]
|
||||
|
||||
@property
|
||||
def plugins(self):
|
||||
return self._plugins.copy()
|
||||
|
||||
@orgmode.keybinding.register_keybindings
|
||||
@orgmode.keybinding.register_commands
|
||||
@orgmode.menu.register_menu
|
||||
def register_plugin(self, plugin):
|
||||
if not isinstance(plugin, basestring):
|
||||
raise ValueError(u'Parameter plugin is not of type string')
|
||||
|
||||
if plugin == u'|':
|
||||
self.orgmenu + orgmode.menu.Separator()
|
||||
self.orgmenu.children[-1].create()
|
||||
return
|
||||
|
||||
if plugin in self._plugins:
|
||||
raise PluginError(u'Plugin %s has already been loaded')
|
||||
|
||||
# a python module
|
||||
module = None
|
||||
|
||||
# actual plugin class
|
||||
_class = None
|
||||
|
||||
# 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()
|
||||
230
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/docs/Makefile
Normal file
230
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/docs/Makefile
Normal 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."
|
||||
387
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/docs/conf.py
Normal file
387
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/docs/conf.py
Normal 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
|
||||
22
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/docs/index.rst
Normal file
22
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/docs/index.rst
Normal 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`
|
||||
281
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/docs/make.bat
Normal file
281
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/docs/make.bat
Normal 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
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
21
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/exceptions.py
Normal file
21
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/exceptions.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- 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)
|
||||
214
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/keybinding.py
Normal file
214
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/keybinding.py
Normal file
@@ -0,0 +1,214 @@
|
||||
# -*- 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))
|
||||
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
@@ -0,0 +1,61 @@
|
||||
# -*- 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)
|
||||
@@ -0,0 +1,91 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
u"""
|
||||
agendafilter
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
AgendaFilter contains all the filters that can be applied to create the
|
||||
agenda.
|
||||
|
||||
|
||||
All functions except filter_items() in the module are filters. Given a
|
||||
heading they return if the heading meets the criteria of the filter.
|
||||
|
||||
The function filter_items() can combine different filters and only returns
|
||||
the filtered headings.
|
||||
"""
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
|
||||
try:
|
||||
from itertools import ifilter as filter
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def filter_items(headings, filters):
|
||||
u""" Filter the given headings.
|
||||
|
||||
Args:
|
||||
headings (list): Contains headings
|
||||
filters (list): Filters that will be applied. All functions in
|
||||
this module (except this function) are filters.
|
||||
|
||||
Returns:
|
||||
filter iterator: Headings which were not filtered.
|
||||
|
||||
Examples:
|
||||
>>> filtered = filter_items(headings, [contains_active_date,
|
||||
contains_active_todo])
|
||||
"""
|
||||
filtered = headings
|
||||
for f in filters:
|
||||
filtered = filter(f, filtered)
|
||||
return filtered
|
||||
|
||||
|
||||
def is_within_week(heading):
|
||||
u""" Test if headings date is within a week
|
||||
|
||||
Returns:
|
||||
bool: True if the date in the deading is within a week in the future (or
|
||||
older False otherwise.
|
||||
"""
|
||||
if contains_active_date(heading):
|
||||
next_week = datetime.today() + timedelta(days=7)
|
||||
if heading.active_date < next_week:
|
||||
return True
|
||||
|
||||
|
||||
def is_within_week_and_active_todo(heading):
|
||||
u"""
|
||||
Returns:
|
||||
bool: True if heading contains an active TODO and the date is within a
|
||||
week.
|
||||
"""
|
||||
return is_within_week(heading) and contains_active_todo(heading)
|
||||
|
||||
|
||||
def contains_active_todo(heading):
|
||||
u"""
|
||||
|
||||
Returns:
|
||||
bool: True if heading contains an active TODO.
|
||||
"""
|
||||
# TODO make this more efficient by checking some val and not calling the
|
||||
# function
|
||||
# TODO why is this import failing at top level? circular dependency...
|
||||
from orgmode._vim import ORGMODE
|
||||
active = []
|
||||
for act in ORGMODE.get_document().get_todo_states():
|
||||
active.extend(act[0])
|
||||
return heading.todo in active
|
||||
|
||||
|
||||
def contains_active_date(heading):
|
||||
u"""
|
||||
|
||||
Returns:
|
||||
bool: True if heading contains an active date.
|
||||
"""
|
||||
return not(heading.active_date is None)
|
||||
193
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/liborgmode/base.py
Normal file
193
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/liborgmode/base.py
Normal file
@@ -0,0 +1,193 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
base
|
||||
~~~~~~~~~~
|
||||
|
||||
Here are some really basic data structures that are used throughout
|
||||
the liborgmode.
|
||||
"""
|
||||
|
||||
try:
|
||||
from collections import UserList
|
||||
except:
|
||||
from UserList import UserList
|
||||
|
||||
try:
|
||||
from collections.abc import Iterable
|
||||
except ImportError:
|
||||
# preserve compatibility with python < 3.10
|
||||
from collections import Iterable
|
||||
|
||||
import sys
|
||||
from orgmode.py3compat.unicode_compatibility import *
|
||||
|
||||
|
||||
def flatten_list(lst):
|
||||
""" Flattens a list
|
||||
|
||||
Args:
|
||||
lst (iterable): An iterable that will is non-flat
|
||||
|
||||
Returns:
|
||||
list: Flat list
|
||||
"""
|
||||
# TODO write tests
|
||||
def gen_lst(item):
|
||||
if isinstance(item, basestring) or isinstance(item, bytes):
|
||||
yield item
|
||||
elif isinstance(item, Iterable):
|
||||
# yield from would be so nice... but c'est la vie
|
||||
for val in item:
|
||||
for final in gen_lst(val):
|
||||
yield final
|
||||
else:
|
||||
yield item
|
||||
return [i for i in gen_lst(lst)]
|
||||
|
||||
|
||||
class Direction():
|
||||
u"""
|
||||
Direction is used to indicate the direction of certain actions.
|
||||
|
||||
Example: it defines the direction headings get parted in.
|
||||
"""
|
||||
FORWARD = 1
|
||||
BACKWARD = 2
|
||||
|
||||
|
||||
class MultiPurposeList(UserList):
|
||||
u"""
|
||||
A Multi Purpose List is a list that calls a user defined hook on
|
||||
change. The implementation is very basic - the hook is called without any
|
||||
parameters. Otherwise the Multi Purpose List can be used like any other
|
||||
list.
|
||||
|
||||
The member element "data" can be used to fill the list without causing the
|
||||
list to be marked dirty. This should only be used during initialization!
|
||||
"""
|
||||
|
||||
def __init__(self, initlist=None, on_change=None):
|
||||
UserList.__init__(self, initlist)
|
||||
self._on_change = on_change
|
||||
|
||||
def _changed(self):
|
||||
u""" Call hook """
|
||||
if callable(self._on_change):
|
||||
self._on_change()
|
||||
|
||||
def __setitem__(self, i, item):
|
||||
if sys.version_info < (3, ) and isinstance(i, slice):
|
||||
start, stop, _ = i.indices(len(self))
|
||||
UserList.__setslice__(self, start, stop, item)
|
||||
else:
|
||||
UserList.__setitem__(self, i, item)
|
||||
self._changed()
|
||||
|
||||
def __delitem__(self, i):
|
||||
if sys.version_info < (3, ) and isinstance(i, slice):
|
||||
start, stop, _ = i.indices(len(self))
|
||||
UserList.__delslice__(self, start, stop)
|
||||
else:
|
||||
UserList.__delitem__(self, i)
|
||||
self._changed()
|
||||
|
||||
def __getitem__(self, i):
|
||||
if sys.version_info < (3, ):
|
||||
if isinstance(i, slice):
|
||||
# TODO Return just a list. Why?
|
||||
return [self[i] for i in range(*i.indices(len(self)))]
|
||||
# return UserList([self[i] for i in range(*i.indices(len(self)))])
|
||||
return UserList.__getitem__(self, i)
|
||||
|
||||
# NOTE: These wrappers are necessary because of python 2
|
||||
def __setslice__(self, i, j, other):
|
||||
self.__setitem__(slice(i, j), other)
|
||||
|
||||
def __delslice__(self, i, j):
|
||||
self.__delitem__(slice(i, j))
|
||||
|
||||
def __getslice__(self, i, j):
|
||||
return self.__getitem__(slice(i, j))
|
||||
|
||||
def __iadd__(self, other):
|
||||
res = UserList.__iadd__(self, other)
|
||||
self._changed()
|
||||
return res
|
||||
|
||||
def __imul__(self, n):
|
||||
res = UserList.__imul__(self, n)
|
||||
self._changed()
|
||||
return res
|
||||
|
||||
def append(self, item):
|
||||
UserList.append(self, item)
|
||||
self._changed()
|
||||
|
||||
def insert(self, i, item):
|
||||
UserList.insert(self, i, item)
|
||||
self._changed()
|
||||
|
||||
def pop(self, i=-1):
|
||||
item = self[i]
|
||||
del self[i]
|
||||
return item
|
||||
|
||||
def remove(self, item):
|
||||
self.__delitem__(self.index(item))
|
||||
|
||||
def reverse(self):
|
||||
UserList.reverse(self)
|
||||
self._changed()
|
||||
|
||||
def sort(self, *args, **kwds):
|
||||
UserList.sort(self, *args, **kwds)
|
||||
self._changed()
|
||||
|
||||
def extend(self, other):
|
||||
UserList.extend(self, other)
|
||||
self._changed()
|
||||
|
||||
|
||||
def get_domobj_range(content=[], position=0, direction=Direction.FORWARD, identify_fun=None):
|
||||
u"""
|
||||
Get the start and end line number of the dom obj lines from content.
|
||||
|
||||
:content: String to be recognized dom obj
|
||||
:position: Line number in content
|
||||
:direction: Search direction
|
||||
:identify_fun: A identify function to recognize dom obj(Heading, Checkbox) title string.
|
||||
|
||||
:return: Start and end line number for the recognized dom obj.
|
||||
"""
|
||||
len_cb = len(content)
|
||||
|
||||
if position < 0 or position > len_cb:
|
||||
return (None, None)
|
||||
|
||||
tmp_line = position
|
||||
start = None
|
||||
end = None
|
||||
|
||||
if direction == Direction.FORWARD:
|
||||
while tmp_line < len_cb:
|
||||
if identify_fun(content[tmp_line]) is not None:
|
||||
if start is None:
|
||||
start = tmp_line
|
||||
elif end is None:
|
||||
end = tmp_line - 1
|
||||
if start is not None and end is not None:
|
||||
break
|
||||
tmp_line += 1
|
||||
else:
|
||||
while tmp_line >= 0 and tmp_line < len_cb:
|
||||
if identify_fun(content[tmp_line]) is not None:
|
||||
if start is None:
|
||||
start = tmp_line
|
||||
elif end is None:
|
||||
end = tmp_line - 1
|
||||
if start is not None and end is not None:
|
||||
break
|
||||
tmp_line -= 1 if start is None else -1
|
||||
|
||||
return (start, end)
|
||||
@@ -0,0 +1,403 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
checkboxes
|
||||
~~~~~~~~~~
|
||||
|
||||
TODO: explain this :)
|
||||
"""
|
||||
|
||||
import re
|
||||
try:
|
||||
from collections import UserList
|
||||
except:
|
||||
from UserList import UserList
|
||||
|
||||
import vim
|
||||
from orgmode.liborgmode.base import MultiPurposeList, flatten_list
|
||||
from orgmode.liborgmode.orgdate import OrgTimeRange
|
||||
from orgmode.liborgmode.orgdate import get_orgdate
|
||||
from orgmode.liborgmode.dom_obj import DomObj, DomObjList, REGEX_SUBTASK, REGEX_SUBTASK_PERCENT, REGEX_HEADING, REGEX_CHECKBOX
|
||||
|
||||
from orgmode.py3compat.encode_compatibility import *
|
||||
from orgmode.py3compat.unicode_compatibility import *
|
||||
|
||||
|
||||
class Checkbox(DomObj):
|
||||
u""" Structural checkbox object """
|
||||
STATUS_ON = u'[X]'
|
||||
STATUS_OFF = u'[ ]'
|
||||
# intermediate status
|
||||
STATUS_INT = u'[-]'
|
||||
|
||||
def __init__(self, level=1, type=u'-', title=u'', status=u'[ ]', body=None):
|
||||
u"""
|
||||
:level: Indent level of the checkbox
|
||||
:type: Type of the checkbox list (-, +, *)
|
||||
:title: Title of the checkbox
|
||||
:status: Status of the checkbox ([ ], [X], [-])
|
||||
:body: Body of the checkbox
|
||||
"""
|
||||
DomObj.__init__(self, level=level, title=title, body=body)
|
||||
|
||||
# heading
|
||||
self._heading = None
|
||||
|
||||
self._children = CheckboxList(obj=self)
|
||||
self._dirty_checkbox = False
|
||||
# list type
|
||||
self._type = u'-'
|
||||
if type:
|
||||
self.type = type
|
||||
# status
|
||||
self._status = Checkbox.STATUS_OFF
|
||||
if status:
|
||||
self.status = status
|
||||
|
||||
def __unicode__(self):
|
||||
return u' ' * self.level + self.type + u' ' + \
|
||||
(self.status + u' ' if self.status else u'') + self.title
|
||||
|
||||
def __str__(self):
|
||||
return u_encode(self.__unicode__())
|
||||
|
||||
def __len__(self):
|
||||
# 1 is for the heading's title
|
||||
return 1 + len(self.body)
|
||||
|
||||
def copy(self, including_children=True, parent=None):
|
||||
u"""
|
||||
Create a copy of the current checkbox. The checkbox will be completely
|
||||
detached and not even belong to a document anymore.
|
||||
|
||||
:including_children: If True a copy of all children is create as
|
||||
well. If False the returned checkbox doesn't
|
||||
have any children.
|
||||
:parent: Don't use this parameter. It's set
|
||||
automatically.
|
||||
"""
|
||||
checkbox = self.__class__(
|
||||
level=self.level, title=self.title,
|
||||
body=self.body[:])
|
||||
if parent:
|
||||
parent.children.append(checkbox)
|
||||
if including_children and self.children:
|
||||
for item in self.children:
|
||||
item.copy(
|
||||
including_children=including_children,
|
||||
parent=checkbox)
|
||||
checkbox._orig_start = self._orig_start
|
||||
checkbox._orig_len = self._orig_len
|
||||
|
||||
checkbox._dirty_heading = self.is_dirty_checkbox
|
||||
|
||||
return checkbox
|
||||
|
||||
@classmethod
|
||||
def parse_checkbox_from_data(cls, data, heading=None, orig_start=None):
|
||||
u""" Construct a new checkbox from the provided data
|
||||
|
||||
:data: List of lines
|
||||
:heading: The heading object this checkbox belongs to
|
||||
:orig_start: The original start of the heading in case it was read
|
||||
from a document. If orig_start is provided, the
|
||||
resulting heading will not be marked dirty.
|
||||
|
||||
:returns: The newly created checkbox
|
||||
"""
|
||||
def parse_title(heading_line):
|
||||
# checkbox is not heading
|
||||
if REGEX_HEADING.match(heading_line) is not None:
|
||||
return None
|
||||
m = REGEX_CHECKBOX.match(heading_line)
|
||||
if m:
|
||||
r = m.groupdict()
|
||||
return (len(r[u'level']), r[u'type'], r[u'status'], r[u'title'])
|
||||
|
||||
return None
|
||||
|
||||
if not data:
|
||||
raise ValueError(u'Unable to create checkbox, no data provided.')
|
||||
|
||||
# create new checkbox
|
||||
nc = cls()
|
||||
nc.level, nc.type, nc.status, nc.title = parse_title(data[0])
|
||||
nc.body = data[1:]
|
||||
if orig_start is not None:
|
||||
nc._dirty_heading = False
|
||||
nc._dirty_body = False
|
||||
nc._orig_start = orig_start
|
||||
nc._orig_len = len(nc)
|
||||
if heading:
|
||||
nc._heading = heading
|
||||
|
||||
return nc
|
||||
|
||||
def update_subtasks(self, total=0, on=0):
|
||||
if total != 0:
|
||||
percent = (on * 100) / total
|
||||
else:
|
||||
percent = 0
|
||||
|
||||
count = "%d/%d" % (on, total)
|
||||
self.title = REGEX_SUBTASK.sub("[%s]" % (count), self.title)
|
||||
self.title = REGEX_SUBTASK_PERCENT.sub("[%d%%]" % (percent), self.title)
|
||||
d = self._heading.document.write_checkbox(self, including_children=False)
|
||||
|
||||
@classmethod
|
||||
def identify_checkbox(cls, line):
|
||||
u""" Test if a certain line is a checkbox or not.
|
||||
|
||||
:line: the line to check
|
||||
|
||||
:returns: indent_level
|
||||
"""
|
||||
# checkbox is not heading
|
||||
if REGEX_HEADING.match(line) is not None:
|
||||
return None
|
||||
m = REGEX_CHECKBOX.match(line)
|
||||
if m:
|
||||
r = m.groupdict()
|
||||
return len(r[u'level'])
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_dirty(self):
|
||||
u""" Return True if the heading's body is marked dirty """
|
||||
return self._dirty_checkbox or self._dirty_body
|
||||
|
||||
@property
|
||||
def is_dirty_checkbox(self):
|
||||
u""" Return True if the heading is marked dirty """
|
||||
return self._dirty_checkbox
|
||||
|
||||
def get_index_in_parent_list(self):
|
||||
""" Retrieve the index value of current checkbox in the parents list of
|
||||
checkboxes. This works also for top level checkboxes.
|
||||
|
||||
:returns: Index value or None if heading doesn't have a
|
||||
parent/document or is not in the list of checkboxes
|
||||
"""
|
||||
if self.parent:
|
||||
return super(Checkbox, self).get_index_in_parent_list()
|
||||
elif self.document:
|
||||
l = self.get_parent_list()
|
||||
if l:
|
||||
return l.index(self)
|
||||
|
||||
def get_parent_list(self):
|
||||
""" Retrieve the parents' list of headings. This works also for top
|
||||
level headings.
|
||||
|
||||
:returns: List of headings or None if heading doesn't have a
|
||||
parent/document or is not in the list of headings
|
||||
"""
|
||||
if self.parent:
|
||||
return super(Checkbox, self).get_parent_list()
|
||||
elif self.document:
|
||||
if self in self.document.checkboxes:
|
||||
return self.document.checkboxes
|
||||
|
||||
def set_dirty(self):
|
||||
u""" Mark the heading and body dirty so that it will be rewritten when
|
||||
saving the document """
|
||||
self._dirty_checkbox = True
|
||||
self._dirty_body = True
|
||||
if self._document:
|
||||
self._document.set_dirty_document()
|
||||
|
||||
def set_dirty_checkbox(self):
|
||||
u""" Mark the checkbox dirty so that it will be rewritten when saving the
|
||||
document """
|
||||
self._dirty_checkbox = True
|
||||
if self._document:
|
||||
self._document.set_dirty_document()
|
||||
|
||||
@property
|
||||
def previous_checkbox(self):
|
||||
u""" Serialized access to the previous checkbox """
|
||||
return super(Checkbox, self).previous_item
|
||||
|
||||
@property
|
||||
def next_checkbox(self):
|
||||
u""" Serialized access to the next checkbox """
|
||||
return super(Checkbox, self).next_item
|
||||
|
||||
@property
|
||||
def first_checkbox(self):
|
||||
u""" Access to the first child heading or None if no children exist """
|
||||
if self.children:
|
||||
return self.children[0]
|
||||
|
||||
@property
|
||||
def start(self):
|
||||
u""" Access to the starting line of the checkbox """
|
||||
return super(Checkbox, self).start
|
||||
|
||||
def toggle(self):
|
||||
u""" Toggle status of this checkbox """
|
||||
if self.status == Checkbox.STATUS_OFF or self.status is None:
|
||||
self.status = Checkbox.STATUS_ON
|
||||
else:
|
||||
self.status = Checkbox.STATUS_OFF
|
||||
self.set_dirty()
|
||||
|
||||
def all_siblings(self):
|
||||
if not self.parent:
|
||||
p = self._heading
|
||||
else:
|
||||
p = self.parent
|
||||
if not p.children:
|
||||
return
|
||||
|
||||
c = p.first_checkbox
|
||||
while c:
|
||||
yield c
|
||||
c = c.next_sibling
|
||||
return
|
||||
|
||||
def all_children(self):
|
||||
if not self.children:
|
||||
return
|
||||
|
||||
c = self.first_checkbox
|
||||
while c:
|
||||
yield c
|
||||
for d in c.all_children():
|
||||
yield d
|
||||
c = c.next_sibling
|
||||
|
||||
return
|
||||
|
||||
def all_children_status(self):
|
||||
u""" Return checkboxes status for current checkbox's all children
|
||||
|
||||
:return: (total, on)
|
||||
total: total # of checkboxes
|
||||
on: # of checkboxes which are on
|
||||
"""
|
||||
total, on = 0, 0
|
||||
for c in self.all_children():
|
||||
if c.status is not None:
|
||||
total += 1
|
||||
|
||||
if c.status == Checkbox.STATUS_ON:
|
||||
on += 1
|
||||
|
||||
return (total, on)
|
||||
|
||||
def all_siblings_status(self):
|
||||
u""" Return checkboxes status for current checkbox's all siblings
|
||||
|
||||
:return: (total, on)
|
||||
total: total # of checkboxes
|
||||
on: # of checkboxes which are on
|
||||
"""
|
||||
total, on = 0, 0
|
||||
for c in self.all_siblings():
|
||||
if c.status is not None:
|
||||
total += 1
|
||||
|
||||
if c.status == Checkbox.STATUS_ON:
|
||||
on += 1
|
||||
|
||||
return (total, on)
|
||||
|
||||
def are_children_all(self, status):
|
||||
u""" Check all children checkboxes status """
|
||||
clen = len(self.children)
|
||||
for i in range(clen):
|
||||
if self.children[i].status != status:
|
||||
return False
|
||||
# recursively check children's status
|
||||
if not self.children[i].are_children_all(status):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def is_child_one(self, status):
|
||||
u""" Return true, if there is one child with given status """
|
||||
clen = len(self.children)
|
||||
for i in range(clen):
|
||||
if self.children[i].status == status:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def are_siblings_all(self, status):
|
||||
u""" Check all sibling checkboxes status """
|
||||
for c in self.all_siblings():
|
||||
if c.status != status:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@DomObj.level.setter
|
||||
def level(self, value):
|
||||
u""" Set the checkbox level and mark the checkbox and the document
|
||||
dirty """
|
||||
self._level = int(value)
|
||||
self.set_dirty_checkbox()
|
||||
|
||||
@DomObj.title.setter
|
||||
def title(self, value):
|
||||
u""" Set the title and mark the document and the checkbox dirty """
|
||||
if type(value) not in (unicode, str):
|
||||
raise ValueError(u'Title must be a string.')
|
||||
v = value
|
||||
if type(v) == str:
|
||||
v = u_decode(v)
|
||||
self._title = v.strip()
|
||||
self.set_dirty_checkbox()
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
u""" status of current checkbox """
|
||||
return self._status
|
||||
|
||||
@status.setter
|
||||
def status(self, value):
|
||||
self._status = value
|
||||
self.set_dirty()
|
||||
|
||||
@status.deleter
|
||||
def status(self):
|
||||
self._status = u''
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
u""" type of current checkbox list type """
|
||||
return self._type
|
||||
|
||||
@type.setter
|
||||
def type(self, value):
|
||||
self._type = value
|
||||
|
||||
@type.deleter
|
||||
def type(self):
|
||||
self._type = u''
|
||||
|
||||
|
||||
class CheckboxList(DomObjList):
|
||||
u"""
|
||||
Checkbox List
|
||||
"""
|
||||
def __init__(self, initlist=None, obj=None):
|
||||
"""
|
||||
:initlist: Initial data
|
||||
:obj: Link to a concrete Checkbox or Document object
|
||||
"""
|
||||
# it's not necessary to register a on_change hook because the heading
|
||||
# list will itself take care of marking headings dirty or adding
|
||||
# headings to the deleted headings list
|
||||
DomObjList.__init__(self, initlist, obj)
|
||||
|
||||
@classmethod
|
||||
def is_checkbox(cls, obj):
|
||||
return CheckboxList.is_domobj(obj)
|
||||
|
||||
def _get_heading(self):
|
||||
if self.__class__.is_checkbox(self._obj):
|
||||
return self._obj._document
|
||||
return self._obj
|
||||
@@ -0,0 +1,312 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
documents
|
||||
~~~~~~~~~
|
||||
|
||||
TODO: explain this :)
|
||||
"""
|
||||
|
||||
try:
|
||||
from collections import UserList
|
||||
except:
|
||||
from UserList import UserList
|
||||
|
||||
from orgmode.liborgmode.base import MultiPurposeList, flatten_list, Direction, get_domobj_range
|
||||
from orgmode.liborgmode.headings import Heading, HeadingList
|
||||
|
||||
from orgmode.py3compat.encode_compatibility import *
|
||||
from orgmode.py3compat.unicode_compatibility import *
|
||||
|
||||
class Document(object):
|
||||
u"""
|
||||
Representation of a whole org-mode document.
|
||||
|
||||
A Document consists basically of headings (see Headings) and some metadata.
|
||||
|
||||
TODO: explain the 'dirty' mechanism
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
u"""
|
||||
Don't call this constructor directly but use one of the concrete
|
||||
implementations.
|
||||
|
||||
TODO: what are the concrete implementatiions?
|
||||
"""
|
||||
object.__init__(self)
|
||||
|
||||
# is a list - only the Document methods should work on this list!
|
||||
self._content = None
|
||||
self._dirty_meta_information = False
|
||||
self._dirty_document = False
|
||||
self._meta_information = MultiPurposeList(
|
||||
on_change=self.set_dirty_meta_information)
|
||||
self._orig_meta_information_len = None
|
||||
self._headings = HeadingList(obj=self)
|
||||
self._deleted_headings = []
|
||||
|
||||
# settings needed to align tags properly
|
||||
self._tabstop = 8
|
||||
self._tag_column = 77
|
||||
|
||||
# TODO this doesn't differentiate between ACTIVE and FINISHED todo's
|
||||
self.todo_states = [u'TODO', u'DONE']
|
||||
|
||||
def __unicode__(self):
|
||||
if self.meta_information is None:
|
||||
return u'\n'.join(self.all_headings())
|
||||
return u'\n'.join(self.meta_information) + u'\n' + u'\n'.join([u'\n'.join([unicode(i)] + i.body) for i in self.all_headings()])
|
||||
|
||||
def __str__(self):
|
||||
return u_encode(self.__unicode__())
|
||||
|
||||
def get_all_todo_states(self):
|
||||
u""" Convenience function that returns all todo and done states and
|
||||
sequences in one big list.
|
||||
|
||||
Returns:
|
||||
list: [all todo/done states]
|
||||
"""
|
||||
# TODO This is not necessary remove
|
||||
return flatten_list(self.get_todo_states())
|
||||
|
||||
def get_todo_states(self):
|
||||
u""" Returns a list containing a tuple of two lists of allowed todo
|
||||
states split by todo and done states. Multiple todo-done state
|
||||
sequences can be defined.
|
||||
|
||||
Returns:
|
||||
list: [([todo states], [done states]), ..]
|
||||
"""
|
||||
# TODO this should be made into property so todo states can be set like
|
||||
# this too.. or there was also some todo property around... oh well..
|
||||
# TODO there is the same method in vimbuffer
|
||||
return self.todo_states
|
||||
|
||||
@property
|
||||
def tabstop(self):
|
||||
u""" Tabstop for this document """
|
||||
return self._tabstop
|
||||
|
||||
@tabstop.setter
|
||||
def tabstop(self, value):
|
||||
self._tabstop = value
|
||||
|
||||
@property
|
||||
def tag_column(self):
|
||||
u""" The column all tags are right-aligned to """
|
||||
return self._tag_column
|
||||
|
||||
@tag_column.setter
|
||||
def tag_column(self, value):
|
||||
self._tag_column = value
|
||||
|
||||
def init_dom(self, heading=Heading):
|
||||
u""" Initialize all headings in document - build DOM. This method
|
||||
should be call prior to accessing the document.
|
||||
|
||||
Returns:
|
||||
self
|
||||
"""
|
||||
def init_heading(_h):
|
||||
u"""
|
||||
:returns the initialized heading
|
||||
"""
|
||||
start = _h.end + 1
|
||||
prev_heading = None
|
||||
while True:
|
||||
new_heading = self.find_heading(start, heading=heading)
|
||||
|
||||
# * Heading 1 <- heading
|
||||
# * Heading 1 <- sibling
|
||||
# or
|
||||
# * Heading 2 <- heading
|
||||
# * Heading 1 <- parent's sibling
|
||||
if not new_heading or \
|
||||
new_heading.level <= _h.level:
|
||||
break
|
||||
|
||||
# * Heading 1 <- heading
|
||||
# * Heading 2 <- first child
|
||||
# * Heading 2 <- another child
|
||||
new_heading._parent = _h
|
||||
if prev_heading:
|
||||
prev_heading._next_sibling = new_heading
|
||||
new_heading._previous_sibling = prev_heading
|
||||
_h.children.data.append(new_heading)
|
||||
# the start and end computation is only
|
||||
# possible when the new heading was properly
|
||||
# added to the document structure
|
||||
init_heading(new_heading)
|
||||
if new_heading.children:
|
||||
# skip children
|
||||
start = new_heading.end_of_last_child + 1
|
||||
else:
|
||||
start = new_heading.end + 1
|
||||
prev_heading = new_heading
|
||||
|
||||
return _h
|
||||
|
||||
h = self.find_heading(heading=heading)
|
||||
# initialize meta information
|
||||
if h:
|
||||
self._meta_information.data.extend(self._content[:h._orig_start])
|
||||
else:
|
||||
self._meta_information.data.extend(self._content[:])
|
||||
self._orig_meta_information_len = len(self.meta_information)
|
||||
|
||||
# initialize dom tree
|
||||
prev_h = None
|
||||
while h:
|
||||
if prev_h:
|
||||
prev_h._next_sibling = h
|
||||
h._previous_sibling = prev_h
|
||||
self.headings.data.append(h)
|
||||
init_heading(h)
|
||||
prev_h = h
|
||||
h = self.find_heading(h.end_of_last_child + 1, heading=heading)
|
||||
|
||||
return self
|
||||
|
||||
@property
|
||||
def meta_information(self):
|
||||
u""" Meta information is text that precedes all headings in an org-mode
|
||||
document. It might contain additional information about the document,
|
||||
e.g. author
|
||||
"""
|
||||
return self._meta_information
|
||||
|
||||
@meta_information.setter
|
||||
def meta_information(self, value):
|
||||
if self._orig_meta_information_len is None:
|
||||
self._orig_meta_information_len = len(self.meta_information)
|
||||
if type(value) in (list, tuple) or isinstance(value, UserList):
|
||||
self._meta_information[:] = flatten_list(value)
|
||||
elif type(value) in (str, ):
|
||||
self._meta_information[:] = u_decode(value).split(u'\n')
|
||||
elif type(value) in (unicode, ):
|
||||
self._meta_information[:] = value.split(u'\n')
|
||||
self.set_dirty_meta_information()
|
||||
|
||||
@meta_information.deleter
|
||||
def meta_information(self):
|
||||
self.meta_information = u''
|
||||
|
||||
@property
|
||||
def headings(self):
|
||||
u""" List of top level headings """
|
||||
return self._headings
|
||||
|
||||
@headings.setter
|
||||
def headings(self, value):
|
||||
self._headings[:] = value
|
||||
|
||||
@headings.deleter
|
||||
def headings(self):
|
||||
del self.headings[:]
|
||||
|
||||
def write(self):
|
||||
u""" Write the document
|
||||
|
||||
Returns:
|
||||
bool: True if something was written, otherwise False
|
||||
"""
|
||||
raise NotImplementedError(u'Abstract method, please use concrete implementation!')
|
||||
|
||||
def set_dirty_meta_information(self):
|
||||
u""" Mark the meta information dirty.
|
||||
|
||||
Note:
|
||||
Causes meta information to be rewritten when saving the document
|
||||
"""
|
||||
self._dirty_meta_information = True
|
||||
|
||||
def set_dirty_document(self):
|
||||
u""" Mark the whole document dirty.
|
||||
|
||||
Note:
|
||||
When changing a heading this method must be executed in order to
|
||||
changed computation of start and end positions from a static to a
|
||||
dynamic computation
|
||||
"""
|
||||
self._dirty_document = True
|
||||
|
||||
@property
|
||||
def is_dirty(self):
|
||||
u""" Return information about unsaved changes for the document and all
|
||||
related headings.
|
||||
|
||||
Returns:
|
||||
bool: True if document contains unsaved changes.
|
||||
"""
|
||||
if self.is_dirty_meta_information:
|
||||
return True
|
||||
|
||||
if self.is_dirty_document:
|
||||
return True
|
||||
|
||||
if self._deleted_headings:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_dirty_meta_information(self):
|
||||
u""" Return True if the meta information is marked dirty """
|
||||
return self._dirty_meta_information
|
||||
|
||||
@property
|
||||
def is_dirty_document(self):
|
||||
u""" Return True if the document is marked dirty """
|
||||
return self._dirty_document
|
||||
|
||||
def all_headings(self):
|
||||
u""" Iterate over all headings of the current document in serialized
|
||||
order
|
||||
|
||||
:returns: Returns an iterator object which returns all headings of
|
||||
the current file in serialized order
|
||||
"""
|
||||
if not self.headings:
|
||||
return
|
||||
|
||||
h = self.headings[0]
|
||||
while h:
|
||||
yield h
|
||||
h = h.next_heading
|
||||
return
|
||||
|
||||
def find_heading(
|
||||
self, position=0, direction=Direction.FORWARD, heading=Heading,
|
||||
connect_with_document=True):
|
||||
u""" Find heading in the given direction
|
||||
|
||||
Args:
|
||||
position (int): starting line, counting from 0 (in vim you start
|
||||
counting from 1, don't forget)
|
||||
direction: downwards == Direction.FORWARD,
|
||||
upwards == Direction.BACKWARD
|
||||
heading: Heading class from which new heading objects will be
|
||||
instantiated
|
||||
connect_with_document: if True, the newly created heading will be
|
||||
connected with the document, otherwise not
|
||||
|
||||
Returns:
|
||||
heading or None: New heading
|
||||
"""
|
||||
start, end = get_domobj_range(
|
||||
content=self._content, position=position, direction=direction,
|
||||
identify_fun=heading.identify_heading)
|
||||
|
||||
if start is None:
|
||||
return None
|
||||
|
||||
if end is None:
|
||||
end = len(self._content) - 1
|
||||
|
||||
document = self if connect_with_document else None
|
||||
|
||||
return heading.parse_heading_from_data(
|
||||
self._content[start:end + 1], self.get_all_todo_states(),
|
||||
document=document, orig_start=start)
|
||||
@@ -0,0 +1,502 @@
|
||||
# -*- 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)
|
||||
@@ -0,0 +1,886 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
headings
|
||||
~~~~~~~~~
|
||||
|
||||
TODO: explain this :)
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
import vim
|
||||
from orgmode.liborgmode.base import MultiPurposeList, flatten_list, Direction, get_domobj_range
|
||||
from orgmode.liborgmode.orgdate import OrgTimeRange
|
||||
from orgmode.liborgmode.orgdate import get_orgdate
|
||||
from orgmode.liborgmode.checkboxes import Checkbox, CheckboxList
|
||||
from orgmode.liborgmode.dom_obj import DomObj, DomObjList, REGEX_SUBTASK, REGEX_SUBTASK_PERCENT, REGEX_HEADING, REGEX_TAG, REGEX_TODO
|
||||
|
||||
from orgmode.py3compat.xrange_compatibility import *
|
||||
from orgmode.py3compat.encode_compatibility import *
|
||||
from orgmode.py3compat.unicode_compatibility import *
|
||||
|
||||
try:
|
||||
from collections import UserList
|
||||
except:
|
||||
from UserList import UserList
|
||||
from itertools import ifilter as filter
|
||||
|
||||
class Heading(DomObj):
|
||||
u""" Structural heading object """
|
||||
|
||||
def __init__(self, level=1, title=u'', tags=None, todo=None, body=None, active_date=None):
|
||||
u"""
|
||||
:level: Level of the heading
|
||||
:title: Title of the heading
|
||||
:tags: Tags of the heading
|
||||
:todo: Todo state of the heading
|
||||
:body: Body of the heading
|
||||
:active_date: active date that is used in the agenda
|
||||
"""
|
||||
DomObj.__init__(self, level=level, title=title, body=body)
|
||||
|
||||
self._children = HeadingList(obj=self)
|
||||
self._dirty_heading = False
|
||||
|
||||
# todo
|
||||
self._todo = None
|
||||
if todo:
|
||||
self.todo = todo
|
||||
|
||||
# tags
|
||||
self._tags = MultiPurposeList(on_change=self.set_dirty_heading)
|
||||
if tags:
|
||||
self.tags = tags
|
||||
|
||||
# active date
|
||||
self._active_date = active_date
|
||||
if active_date:
|
||||
self.active_date = active_date
|
||||
|
||||
# checkboxes
|
||||
self._checkboxes = CheckboxList(obj=self)
|
||||
self._cached_checkbox = None
|
||||
|
||||
def __unicode__(self):
|
||||
res = u'*' * self.level
|
||||
if self.todo:
|
||||
res = u' '.join((res, self.todo))
|
||||
if self.title:
|
||||
res = u' '.join((res, self.title))
|
||||
|
||||
# compute position of tags
|
||||
if self.tags:
|
||||
tabs = 0
|
||||
spaces = 2
|
||||
tags = u':%s:' % (u':'.join(self.tags), )
|
||||
|
||||
# FIXME this is broken because of missing associations for headings
|
||||
ts = 6
|
||||
tag_column = 77
|
||||
if self.document:
|
||||
ts = self.document.tabstop
|
||||
tag_column = self.document.tag_column
|
||||
|
||||
len_heading = len(res)
|
||||
len_tags = len(tags)
|
||||
if len_heading + spaces + len_tags < tag_column:
|
||||
spaces_to_next_tabstop = ts - divmod(len_heading, ts)[1]
|
||||
|
||||
if len_heading + spaces_to_next_tabstop + len_tags < tag_column:
|
||||
tabs, spaces = divmod(
|
||||
tag_column - (len_heading + spaces_to_next_tabstop + len_tags),
|
||||
ts)
|
||||
|
||||
if spaces_to_next_tabstop:
|
||||
tabs += 1
|
||||
else:
|
||||
spaces = tag_column - (len_heading + len_tags)
|
||||
|
||||
res += u'\t' * tabs + u' ' * spaces + tags
|
||||
|
||||
# append a trailing space when there are just * and no text
|
||||
if len(res) == self.level:
|
||||
res += u' '
|
||||
return res
|
||||
|
||||
def __str__(self):
|
||||
return u_encode(self.__unicode__())
|
||||
|
||||
def __len__(self):
|
||||
# 1 is for the heading's title
|
||||
return 1 + len(self.body)
|
||||
|
||||
def __lt__(self, other):
|
||||
"""
|
||||
Headings can be sorted by date.
|
||||
"""
|
||||
try:
|
||||
if self.active_date < other.active_date:
|
||||
return True
|
||||
elif self.active_date == other.active_date:
|
||||
return False
|
||||
elif self.active_date > other.active_date:
|
||||
return False
|
||||
except:
|
||||
if self.active_date and not other.active_date:
|
||||
return True
|
||||
elif not self.active_date and other.active_date:
|
||||
return False
|
||||
elif not self.active_date and not other.active_date:
|
||||
return False
|
||||
|
||||
def __le__(self, other):
|
||||
"""
|
||||
Headings can be sorted by date.
|
||||
"""
|
||||
try:
|
||||
if self.active_date < other.active_date:
|
||||
return True
|
||||
elif self.active_date == other.active_date:
|
||||
return True
|
||||
elif self.active_date > other.active_date:
|
||||
return False
|
||||
except:
|
||||
if self.active_date and not other.active_date:
|
||||
return True
|
||||
elif not self.active_date and other.active_date:
|
||||
return False
|
||||
elif not self.active_date and not other.active:
|
||||
return True
|
||||
|
||||
def __ge__(self, other):
|
||||
"""
|
||||
Headings can be sorted by date.
|
||||
"""
|
||||
try:
|
||||
if self.active_date > other.active_date:
|
||||
return True
|
||||
elif self.active_date == other.active_date:
|
||||
return True
|
||||
elif self.active_date < other.active_date:
|
||||
return False
|
||||
except:
|
||||
if not self.active_date and other.active_date:
|
||||
return True
|
||||
elif self.active_date and not other.active_date:
|
||||
return False
|
||||
elif not self.active_date and not other.active:
|
||||
return True
|
||||
|
||||
def __gt__(self, other):
|
||||
"""
|
||||
Headings can be sorted by date.
|
||||
"""
|
||||
try:
|
||||
if self.active_date > other.active_date:
|
||||
return True
|
||||
elif self.active_date == other.active_date:
|
||||
return False
|
||||
elif self.active_date < other.active_date:
|
||||
return False
|
||||
except:
|
||||
if not self.active_date and other.active_date:
|
||||
return True
|
||||
elif self.active_date and not other.active_date:
|
||||
return False
|
||||
elif not self.active_date and not other.active:
|
||||
return False
|
||||
|
||||
def copy(self, including_children=True, parent=None):
|
||||
u"""
|
||||
Create a copy of the current heading. The heading will be completely
|
||||
detached and not even belong to a document anymore.
|
||||
|
||||
:including_children: If True a copy of all children is create as
|
||||
well. If False the returned heading doesn't
|
||||
have any children.
|
||||
:parent: Don't use this parameter. It's set
|
||||
automatically.
|
||||
"""
|
||||
heading = self.__class__(
|
||||
level=self.level, title=self.title,
|
||||
tags=self.tags, todo=self.todo, body=self.body[:])
|
||||
if parent:
|
||||
parent.children.append(heading)
|
||||
if including_children and self.children:
|
||||
for item in self.children:
|
||||
item.copy(
|
||||
including_children=including_children,
|
||||
parent=heading)
|
||||
heading._orig_start = self._orig_start
|
||||
heading._orig_len = self._orig_len
|
||||
|
||||
heading._dirty_heading = self.is_dirty_heading
|
||||
|
||||
return heading
|
||||
|
||||
def all_checkboxes(self):
|
||||
u""" Iterate over all checkboxes of the current heading in serialized
|
||||
order
|
||||
|
||||
:returns: Returns an iterator object which returns all checkboxes of
|
||||
the current heading in serialized order
|
||||
"""
|
||||
if not self.checkboxes:
|
||||
return
|
||||
|
||||
c = self.first_checkbox
|
||||
while c:
|
||||
yield c
|
||||
c = c.next_checkbox
|
||||
return
|
||||
|
||||
def all_toplevel_checkboxes(self):
|
||||
u""" return all top level checkboxes for current heading """
|
||||
if not self.checkboxes:
|
||||
return
|
||||
|
||||
c = self.first_checkbox
|
||||
while c:
|
||||
yield c
|
||||
c = c.next_sibling
|
||||
return
|
||||
|
||||
def find_checkbox(self, position=0, direction=Direction.FORWARD,
|
||||
checkbox=Checkbox, connect_with_heading=True):
|
||||
u""" Find checkbox in the given direction
|
||||
|
||||
:position: starting line, counting from 0 (in vim you start
|
||||
counting from 1, don't forget)
|
||||
:direction: downwards == Direction.FORWARD,
|
||||
upwards == Direction.BACKWARD
|
||||
:checkbox: Checkbox class from which new checkbox objects will be
|
||||
instantiated
|
||||
:connect_with_heading: if True, the newly created checkbox will be
|
||||
connected with the heading, otherwise not
|
||||
|
||||
:returns: New checkbox object or None
|
||||
"""
|
||||
doc = self.document
|
||||
(start, end) = get_domobj_range(content=doc._content, position=position, direction=direction, identify_fun=checkbox.identify_checkbox)
|
||||
# if out of current headinig range, return None
|
||||
heading_end = self.start + len(self) - 1
|
||||
if start is not None and start > heading_end:
|
||||
return None
|
||||
|
||||
if end is not None and end > heading_end:
|
||||
end = heading_end
|
||||
|
||||
if start is not None and end is None:
|
||||
end = heading_end
|
||||
if start is not None and end is not None:
|
||||
return checkbox.parse_checkbox_from_data(
|
||||
doc._content[start:end + 1],
|
||||
heading=self if connect_with_heading else None, orig_start=start)
|
||||
|
||||
def init_checkboxes(self, checkbox=Checkbox):
|
||||
u""" Initialize all checkboxes in current heading - build DOM.
|
||||
|
||||
:returns: self
|
||||
"""
|
||||
def init_checkbox(_c):
|
||||
u"""
|
||||
:returns the initialized checkbox
|
||||
"""
|
||||
start = _c.end + 1
|
||||
prev_checkbox = None
|
||||
while True:
|
||||
new_checkbox = self.find_checkbox(start, checkbox=checkbox)
|
||||
|
||||
# * Checkbox 1 <- checkbox
|
||||
# * Checkbox 1 <- sibling
|
||||
# or
|
||||
# * Checkbox 2 <- checkbox
|
||||
# * Checkbox 1 <- parent's sibling
|
||||
if not new_checkbox or \
|
||||
new_checkbox.level <= _c.level:
|
||||
break
|
||||
|
||||
# * Checkbox 1 <- heading
|
||||
# * Checkbox 2 <- first child
|
||||
# * Checkbox 2 <- another child
|
||||
new_checkbox._parent = _c
|
||||
if prev_checkbox:
|
||||
prev_checkbox._next_sibling = new_checkbox
|
||||
new_checkbox._previous_sibling = prev_checkbox
|
||||
_c.children.data.append(new_checkbox)
|
||||
# the start and end computation is only
|
||||
# possible when the new checkbox was properly
|
||||
# added to the document structure
|
||||
init_checkbox(new_checkbox)
|
||||
if new_checkbox.children:
|
||||
# skip children
|
||||
start = new_checkbox.end_of_last_child + 1
|
||||
else:
|
||||
start = new_checkbox.end + 1
|
||||
prev_checkbox = new_checkbox
|
||||
|
||||
return _c
|
||||
|
||||
c = self.find_checkbox(checkbox=checkbox, position=self.start)
|
||||
|
||||
# initialize dom tree
|
||||
prev_c = None
|
||||
while c:
|
||||
if prev_c and prev_c.level == c.level:
|
||||
prev_c._next_sibling = c
|
||||
c._previous_sibling = prev_c
|
||||
self.checkboxes.data.append(c)
|
||||
init_checkbox(c)
|
||||
prev_c = c
|
||||
c = self.find_checkbox(c.end_of_last_child + 1, checkbox=checkbox)
|
||||
|
||||
return self
|
||||
|
||||
def current_checkbox(self, position=None):
|
||||
u""" Find the current checkbox (search backward) and return the related object
|
||||
:returns: Checkbox object or None
|
||||
"""
|
||||
if position is None:
|
||||
position = vim.current.window.cursor[0] - 1
|
||||
|
||||
if not self.checkboxes:
|
||||
return
|
||||
|
||||
def binaryFindInHeading():
|
||||
hi = len(self.checkboxes)
|
||||
lo = 0
|
||||
while lo < hi:
|
||||
mid = (lo + hi) // 2
|
||||
c = self.checkboxes[mid]
|
||||
if c.end_of_last_child < position:
|
||||
lo = mid + 1
|
||||
elif c.start > position:
|
||||
hi = mid
|
||||
else:
|
||||
return binaryFindCheckbox(c)
|
||||
|
||||
def binaryFindCheckbox(checkbox):
|
||||
if not checkbox.children or checkbox.end >= position:
|
||||
return checkbox
|
||||
|
||||
hi = len(checkbox.children)
|
||||
lo = 0
|
||||
while lo < hi:
|
||||
mid = (lo + hi) // 2
|
||||
c = checkbox.children[mid]
|
||||
if c.end_of_last_child < position:
|
||||
lo = mid + 1
|
||||
elif c.start > position:
|
||||
hi = mid
|
||||
else:
|
||||
return binaryFindCheckbox(c)
|
||||
|
||||
# look at the cache to find the heading
|
||||
c_tmp = self._cached_checkbox
|
||||
if c_tmp is not None:
|
||||
if c_tmp.end_of_last_child > position and \
|
||||
c_tmp.start < position:
|
||||
if c_tmp.end < position:
|
||||
self._cached_checkbox = binaryFindCheckbox(c_tmp)
|
||||
return self._cached_checkbox
|
||||
|
||||
self._cached_checkbox = binaryFindInHeading()
|
||||
return self._cached_checkbox
|
||||
|
||||
@property
|
||||
def first_checkbox(self):
|
||||
u""" Access to the first child checkbox or None if no children exist """
|
||||
if self.checkboxes:
|
||||
return self.checkboxes[0]
|
||||
|
||||
@classmethod
|
||||
def parse_heading_from_data(
|
||||
cls, data, allowed_todo_states, document=None,
|
||||
orig_start=None):
|
||||
u""" Construct a new heading from the provided data
|
||||
|
||||
:data: List of lines
|
||||
:allowed_todo_states: TODO???
|
||||
:document: The document object this heading belongs to
|
||||
:orig_start: The original start of the heading in case it was read
|
||||
from a document. If orig_start is provided, the
|
||||
resulting heading will not be marked dirty.
|
||||
|
||||
:returns: The newly created heading
|
||||
"""
|
||||
test_not_empty = lambda x: x != u''
|
||||
|
||||
def parse_title(heading_line):
|
||||
# WARNING this regular expression fails if there is just one or no
|
||||
# word in the heading but a tag!
|
||||
m = REGEX_HEADING.match(heading_line)
|
||||
if m:
|
||||
r = m.groupdict()
|
||||
level = len(r[u'level'])
|
||||
todo = None
|
||||
title = u''
|
||||
tags = filter(test_not_empty, r[u'tags'].split(u':')) if r[u'tags'] else []
|
||||
tags = list(tags)
|
||||
|
||||
# if there is just one or no word in the heading, redo the parsing
|
||||
mt = REGEX_TAG.match(r[u'title'])
|
||||
if not tags and mt:
|
||||
r = mt.groupdict()
|
||||
tags = filter(test_not_empty, r[u'tags'].split(u':')) if r[u'tags'] else []
|
||||
tags = list(tags)
|
||||
if r[u'title'] is not None:
|
||||
_todo_title = [i.strip() for i in r[u'title'].split(None, 1)]
|
||||
if _todo_title and _todo_title[0] in allowed_todo_states:
|
||||
todo = _todo_title[0]
|
||||
if len(_todo_title) > 1:
|
||||
title = _todo_title[1]
|
||||
else:
|
||||
title = r[u'title'].strip()
|
||||
|
||||
return (level, todo, title, tags)
|
||||
raise ValueError(u'Data doesn\'t start with a heading definition.')
|
||||
|
||||
if not data:
|
||||
raise ValueError(u'Unable to create heading, no data provided.')
|
||||
|
||||
# create new heaing
|
||||
new_heading = cls()
|
||||
new_heading.level, new_heading.todo, new_heading.title, new_heading.tags = parse_title(data[0])
|
||||
new_heading.body = data[1:]
|
||||
if orig_start is not None:
|
||||
new_heading._dirty_heading = False
|
||||
new_heading._dirty_body = False
|
||||
new_heading._orig_start = orig_start
|
||||
new_heading._orig_len = len(new_heading)
|
||||
if document:
|
||||
new_heading._document = document
|
||||
|
||||
# try to find active dates
|
||||
tmp_orgdate = get_orgdate(data)
|
||||
if tmp_orgdate and tmp_orgdate.active \
|
||||
and not isinstance(tmp_orgdate, OrgTimeRange):
|
||||
new_heading.active_date = tmp_orgdate
|
||||
else:
|
||||
new_heading.active_date = None
|
||||
|
||||
return new_heading
|
||||
|
||||
def update_subtasks(self, total=0, on=0):
|
||||
u""" Update subtask information for current heading
|
||||
:total: total # of top level checkboxes
|
||||
:on: # of top level checkboxes which are on
|
||||
"""
|
||||
if total != 0:
|
||||
percent = (on * 100) / total
|
||||
else:
|
||||
percent = 0
|
||||
|
||||
count = "%d/%d" % (on, total)
|
||||
self.title = REGEX_SUBTASK.sub("[%s]" % (count), self.title)
|
||||
self.title = REGEX_SUBTASK_PERCENT.sub("[%d%%]" % (percent), self.title)
|
||||
self.document.write_heading(self, including_children=False)
|
||||
|
||||
@staticmethod
|
||||
def identify_heading(line):
|
||||
u""" Test if a certain line is a heading or not.
|
||||
|
||||
Args:
|
||||
line (str): the line to check
|
||||
|
||||
Returns:
|
||||
int or None: level of heading or None if line is not heading
|
||||
"""
|
||||
# TODO would it make sense to return 0 for heading level?
|
||||
# TODO add tests e.g. '*** abc', '**', '', '* ', '*\t', '*'
|
||||
for i, item in enumerate(line):
|
||||
if item == '*':
|
||||
continue
|
||||
elif i and item in ('\t', ' '):
|
||||
return i
|
||||
break
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_dirty(self):
|
||||
u""" Return True if the heading's body is marked dirty """
|
||||
return self._dirty_heading or self._dirty_body
|
||||
|
||||
@property
|
||||
def is_dirty_heading(self):
|
||||
u""" Return True if the heading is marked dirty """
|
||||
return self._dirty_heading
|
||||
|
||||
def get_index_in_parent_list(self):
|
||||
""" Retrieve the index value of current heading in the parents list of
|
||||
headings. This works also for top level headings.
|
||||
|
||||
:returns: Index value or None if heading doesn't have a
|
||||
parent/document or is not in the list of headings
|
||||
"""
|
||||
if self.parent:
|
||||
return super(Heading, self).get_index_in_parent_list()
|
||||
elif self.document:
|
||||
l = self.get_parent_list()
|
||||
if l:
|
||||
return l.index(self)
|
||||
|
||||
def get_parent_list(self):
|
||||
""" Retrieve the parents' list of headings. This works also for top
|
||||
level headings.
|
||||
|
||||
:returns: List of headings or None if heading doesn't have a
|
||||
parent/document or is not in the list of headings
|
||||
"""
|
||||
if self.parent:
|
||||
return super(Heading, self).get_parent_list()
|
||||
elif self.document:
|
||||
if self in self.document.headings:
|
||||
return self.document.headings
|
||||
|
||||
def set_dirty(self):
|
||||
u""" Mark the heading and body dirty so that it will be rewritten when
|
||||
saving the document """
|
||||
self._dirty_heading = True
|
||||
self._dirty_body = True
|
||||
if self._document:
|
||||
self._document.set_dirty_document()
|
||||
|
||||
def set_dirty_heading(self):
|
||||
u""" Mark the heading dirty so that it will be rewritten when saving the
|
||||
document """
|
||||
self._dirty_heading = True
|
||||
if self._document:
|
||||
self._document.set_dirty_document()
|
||||
|
||||
@property
|
||||
def previous_heading(self):
|
||||
u""" Serialized access to the previous heading """
|
||||
return super(Heading, self).previous_item
|
||||
|
||||
@property
|
||||
def next_heading(self):
|
||||
u""" Serialized access to the next heading """
|
||||
return super(Heading, self).next_item
|
||||
|
||||
@property
|
||||
def start(self):
|
||||
u""" Access to the starting line of the heading """
|
||||
if self.document is None or not self.document.is_dirty:
|
||||
return self._orig_start
|
||||
|
||||
meta_len = len(self.document.meta_information) if \
|
||||
self.document.meta_information else 0
|
||||
return super(Heading, self).start + meta_len
|
||||
|
||||
@DomObj.level.setter
|
||||
def level(self, value):
|
||||
u""" Set the heading level and mark the heading and the document dirty """
|
||||
self._level = int(value)
|
||||
self.set_dirty_heading()
|
||||
|
||||
@property
|
||||
def todo(self):
|
||||
u""" Todo state of current heading. When todo state is set"""
|
||||
# extract todo state from heading
|
||||
return self._todo
|
||||
|
||||
@todo.setter
|
||||
def todo(self, value):
|
||||
# update todo state
|
||||
if type(value) not in (unicode, str, type(None)):
|
||||
raise ValueError(u'Todo state must be a string or None.')
|
||||
if value and not REGEX_TODO.match(value):
|
||||
raise ValueError(u'Found non allowed character in todo state! %s' % value)
|
||||
if not value:
|
||||
self._todo = None
|
||||
else:
|
||||
v = value
|
||||
if type(v) == str:
|
||||
v = u_decode(v)
|
||||
self._todo = v
|
||||
self.set_dirty_heading()
|
||||
|
||||
@todo.deleter
|
||||
def todo(self):
|
||||
self.todo = None
|
||||
|
||||
@property
|
||||
def active_date(self):
|
||||
u"""
|
||||
active date of the hearing.
|
||||
|
||||
active dates are used in the agenda view. they can be part of the
|
||||
heading and/or the body.
|
||||
"""
|
||||
return self._active_date
|
||||
|
||||
@active_date.setter
|
||||
def active_date(self, value):
|
||||
self._active_date = value
|
||||
|
||||
@active_date.deleter
|
||||
def active_date(self):
|
||||
self._active_date = None
|
||||
|
||||
@DomObj.title.setter
|
||||
def title(self, value):
|
||||
u""" Set the title and mark the document and the heading dirty """
|
||||
# TODO these setter should be rewritten to also reuse code from DOM OBJ
|
||||
if type(value) not in (unicode, str):
|
||||
raise ValueError(u'Title must be a string.')
|
||||
v = value
|
||||
if type(v) == str:
|
||||
v = u_decode(v)
|
||||
self._title = v.strip()
|
||||
self.set_dirty_heading()
|
||||
|
||||
@property
|
||||
def tags(self):
|
||||
u""" Tags of the current heading """
|
||||
return self._tags
|
||||
|
||||
@tags.setter
|
||||
def tags(self, value):
|
||||
v = value
|
||||
if type(v) in (unicode, str):
|
||||
v = list(unicode(v))
|
||||
if type(v) not in (list, tuple) and not isinstance(v, UserList):
|
||||
v = list(unicode(v))
|
||||
v = flatten_list(v)
|
||||
v_decoded = []
|
||||
for i in v:
|
||||
if type(i) not in (unicode, str):
|
||||
raise ValueError(u'Found non string value in tags! %s' % unicode(i))
|
||||
if u':' in i:
|
||||
raise ValueError(u'Found non allowed character in tag! %s' % i)
|
||||
i_tmp = i.strip().replace(' ', '_').replace('\t', '_')
|
||||
if type(i) == str:
|
||||
i_tmp = u_decode(i)
|
||||
v_decoded.append(i_tmp)
|
||||
|
||||
self._tags[:] = v_decoded
|
||||
|
||||
@tags.deleter
|
||||
def tags(self):
|
||||
self.tags = []
|
||||
|
||||
@property
|
||||
def checkboxes(self):
|
||||
u""" All checkboxes in current heading """
|
||||
return self._checkboxes
|
||||
|
||||
@checkboxes.setter
|
||||
def checkboxes(self, value):
|
||||
self._checkboxes[:] = value
|
||||
|
||||
@checkboxes.deleter
|
||||
def checkboxes(self):
|
||||
del self.checkboxes[:]
|
||||
|
||||
|
||||
class HeadingList(DomObjList):
|
||||
u"""
|
||||
A Heading List just contains headings. It's used for documents to store top
|
||||
level headings and for headings to store subheadings.
|
||||
|
||||
A Heading List must be linked to a Document or Heading!
|
||||
|
||||
See documentation of MultiPurposeList for more information.
|
||||
"""
|
||||
def __init__(self, initlist=None, obj=None):
|
||||
"""
|
||||
:initlist: Initial data
|
||||
:obj: Link to a concrete Heading or Document object
|
||||
"""
|
||||
# it's not necessary to register a on_change hook because the heading
|
||||
# list will itself take care of marking headings dirty or adding
|
||||
# headings to the deleted headings list
|
||||
DomObjList.__init__(self, initlist, obj)
|
||||
|
||||
@classmethod
|
||||
def is_heading(cls, obj):
|
||||
# TODO no need to make this or is_domobj a class methods
|
||||
return HeadingList.is_domobj(obj)
|
||||
|
||||
def _get_document(self):
|
||||
if self.__class__.is_heading(self._obj):
|
||||
return self._obj._document
|
||||
return self._obj
|
||||
|
||||
def _add_to_deleted_headings(self, item):
|
||||
u"""
|
||||
Serialize headings so that all subheadings are also marked for deletion
|
||||
"""
|
||||
if not self._get_document():
|
||||
# HeadingList has not yet been associated
|
||||
return
|
||||
|
||||
if type(item) in (list, tuple) or isinstance(item, UserList):
|
||||
for i in flatten_list(item):
|
||||
self._add_to_deleted_headings(i)
|
||||
else:
|
||||
self._get_document()._deleted_headings.append(
|
||||
item.copy(including_children=False))
|
||||
self._add_to_deleted_headings(item.children)
|
||||
self._get_document().set_dirty_document()
|
||||
|
||||
def _associate_heading(
|
||||
self, heading, previous_sibling, next_sibling,
|
||||
children=False, taint=True):
|
||||
"""
|
||||
:heading: The heading or list to associate with the current heading
|
||||
:previous_sibling: The previous sibling of the current heading. If
|
||||
heading is a list the first heading will be
|
||||
connected with the previous sibling and the last
|
||||
heading with the next sibling. The items in between
|
||||
will be linked with one another.
|
||||
:next_sibling: The next sibling of the current heading. If
|
||||
heading is a list the first heading will be
|
||||
connected with the previous sibling and the last
|
||||
heading with the next sibling. The items in between
|
||||
will be linked with one another.
|
||||
:children: Marks whether children are processed in the current
|
||||
iteration or not (should not be use, it's set
|
||||
automatically)
|
||||
:taint: If not True, the heading is not marked dirty at the end
|
||||
of the association process and its orig_start and
|
||||
orig_len values are not updated.
|
||||
"""
|
||||
# TODO this method should be externalized and moved to the Heading class
|
||||
# TODO should this method work with slice?
|
||||
if type(heading) in (list, tuple) or isinstance(heading, UserList):
|
||||
prev = previous_sibling
|
||||
current = None
|
||||
for _next in flatten_list(heading):
|
||||
if current:
|
||||
self._associate_heading(
|
||||
current, prev, _next,
|
||||
children=children, taint=taint)
|
||||
prev = current
|
||||
current = _next
|
||||
if current:
|
||||
self._associate_heading(
|
||||
current, prev, next_sibling,
|
||||
children=children, taint=taint)
|
||||
else:
|
||||
if taint:
|
||||
heading._orig_start = None
|
||||
heading._orig_len = None
|
||||
d = self._get_document()
|
||||
if heading._document != d:
|
||||
heading._document = d
|
||||
if not children:
|
||||
# connect heading with previous and next headings
|
||||
heading._previous_sibling = previous_sibling
|
||||
if previous_sibling:
|
||||
previous_sibling._next_sibling = heading
|
||||
heading._next_sibling = next_sibling
|
||||
if next_sibling:
|
||||
next_sibling._previous_sibling = heading
|
||||
|
||||
if d == self._obj:
|
||||
# self._obj is a Document
|
||||
heading._parent = None
|
||||
elif heading._parent != self._obj:
|
||||
# self._obj is a Heading
|
||||
heading._parent = self._obj
|
||||
if taint:
|
||||
heading.set_dirty()
|
||||
|
||||
self._associate_heading(
|
||||
heading.children, None, None,
|
||||
children=True, taint=taint)
|
||||
|
||||
def __setitem__(self, i, item):
|
||||
if isinstance(i, slice):
|
||||
start, stop, step = i.indices(len(self))
|
||||
items = item
|
||||
if self.__class__.is_heading(items):
|
||||
items = (items, )
|
||||
items = flatten_list(items)
|
||||
for head in items:
|
||||
if not self.__class__.is_heading(head):
|
||||
raise ValueError(u'List contains items that are not a heading!')
|
||||
|
||||
self._add_to_deleted_headings(self[i])
|
||||
self._associate_heading(
|
||||
items,
|
||||
self[start - 1] if start - 1 >= 0 else None,
|
||||
self[stop] if stop < len(self) else None)
|
||||
MultiPurposeList.__setitem__(self, i, items)
|
||||
else:
|
||||
if not self.__class__.is_heading(item):
|
||||
raise ValueError(u'Item is not a heading!')
|
||||
if item in self:
|
||||
raise ValueError(u'Heading is already part of this list!')
|
||||
self._add_to_deleted_headings(self[i])
|
||||
self._associate_heading(
|
||||
item,
|
||||
self[i - 1] if i - 1 >= 0 else None,
|
||||
self[i + 1] if i + 1 < len(self) else None)
|
||||
MultiPurposeList.__setitem__(self, i, item)
|
||||
|
||||
def __delitem__(self, i, taint=True):
|
||||
# TODO refactor this item, it works the same in dom_obj except taint?
|
||||
if isinstance(i, slice):
|
||||
items = self[i]
|
||||
if items:
|
||||
first = items[0]
|
||||
last = items[-1]
|
||||
if first.previous_sibling:
|
||||
first.previous_sibling._next_sibling = last.next_sibling
|
||||
if last.next_sibling:
|
||||
last.next_sibling._previous_sibling = first.previous_sibling
|
||||
if taint:
|
||||
self._add_to_deleted_headings(items)
|
||||
MultiPurposeList.__delitem__(self, i)
|
||||
else:
|
||||
item = self[i]
|
||||
if item.previous_sibling:
|
||||
item.previous_sibling._next_sibling = item.next_sibling
|
||||
if item.next_sibling:
|
||||
item.next_sibling._previous_sibling = item.previous_sibling
|
||||
|
||||
if taint:
|
||||
self._add_to_deleted_headings(item)
|
||||
MultiPurposeList.__delitem__(self, i)
|
||||
|
||||
def __iadd__(self, other):
|
||||
o = other
|
||||
if self.__class__.is_heading(o):
|
||||
o = (o, )
|
||||
for item in flatten_list(o):
|
||||
if not self.__class__.is_heading(item):
|
||||
raise ValueError(u'List contains items that are not a heading!')
|
||||
self._associate_heading(o, self[-1] if len(self) > 0 else None, None)
|
||||
return MultiPurposeList.__iadd__(self, o)
|
||||
|
||||
def append(self, item, taint=True):
|
||||
if not self.__class__.is_heading(item):
|
||||
raise ValueError(u'Item is not a heading!')
|
||||
if item in self:
|
||||
raise ValueError(u'Heading is already part of this list!')
|
||||
self._associate_heading(
|
||||
item, self[-1] if len(self) > 0 else None,
|
||||
None, taint=taint)
|
||||
MultiPurposeList.append(self, item)
|
||||
|
||||
def insert(self, i, item, taint=True):
|
||||
self._associate_heading(
|
||||
item,
|
||||
self[i - 1] if i - 1 >= 0 and i - 1 < len(self) else None,
|
||||
self[i] if i >= 0 and i < len(self) else None, taint=taint)
|
||||
MultiPurposeList.insert(self, i, item)
|
||||
|
||||
def pop(self, i=-1):
|
||||
item = self[i]
|
||||
self._add_to_deleted_headings(item)
|
||||
del self[i]
|
||||
return item
|
||||
|
||||
def extend(self, other):
|
||||
o = other
|
||||
if self.__class__.is_heading(o):
|
||||
o = (o, )
|
||||
for item in o:
|
||||
if not self.__class__.is_heading(item):
|
||||
raise ValueError(u'List contains items that are not a heading!')
|
||||
self._associate_heading(o, self[-1] if len(self) > 0 else None, None)
|
||||
MultiPurposeList.extend(self, o)
|
||||
@@ -0,0 +1,294 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
u"""
|
||||
OrgDate
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module contains all date/time/timerange representations that exist in
|
||||
orgmode.
|
||||
|
||||
There exist three different kinds:
|
||||
|
||||
* OrgDate: is similar to a date object in python and it looks like
|
||||
'2011-09-07 Wed'.
|
||||
|
||||
* OrgDateTime: is similar to a datetime object in python and looks like
|
||||
'2011-09-07 Wed 10:30'
|
||||
|
||||
* OrgTimeRange: indicates a range of time. It has a start and and end date:
|
||||
* <2011-09-07 Wed>--<2011-09-08 Fri>
|
||||
* <2011-09-07 Wed 10:00-13:00>
|
||||
|
||||
All OrgTime oblects can be active or inactive.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import re
|
||||
|
||||
from orgmode.py3compat.encode_compatibility import *
|
||||
|
||||
# <2011-09-12 Mon>
|
||||
_DATE_REGEX = re.compile(r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>", re.UNICODE)
|
||||
# [2011-09-12 Mon]
|
||||
_DATE_PASSIVE_REGEX = re.compile(r"\[(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w\]", re.UNICODE)
|
||||
|
||||
# <2011-09-12 Mon 10:20>
|
||||
_DATETIME_REGEX = re.compile(
|
||||
r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d{1,2}):(\d\d)>", re.UNICODE)
|
||||
# [2011-09-12 Mon 10:20]
|
||||
_DATETIME_PASSIVE_REGEX = re.compile(
|
||||
r"\[(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d{1,2}):(\d\d)\]", re.UNICODE)
|
||||
|
||||
# <2011-09-12 Mon>--<2011-09-13 Tue>
|
||||
_DATERANGE_REGEX = re.compile(
|
||||
# <2011-09-12 Mon>--
|
||||
r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>--"
|
||||
# <2011-09-13 Tue>
|
||||
r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w>", re.UNICODE)
|
||||
# <2011-09-12 Mon 10:00>--<2011-09-12 Mon 11:00>
|
||||
_DATETIMERANGE_REGEX = re.compile(
|
||||
# <2011-09-12 Mon 10:00>--
|
||||
r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)>--"
|
||||
# <2011-09-12 Mon 11:00>
|
||||
r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)>", re.UNICODE)
|
||||
# <2011-09-12 Mon 10:00--12:00>
|
||||
_DATETIMERANGE_SAME_DAY_REGEX = re.compile(
|
||||
r"<(\d\d\d\d)-(\d\d)-(\d\d) [A-Z]\w\w (\d\d):(\d\d)-(\d\d):(\d\d)>", re.UNICODE)
|
||||
|
||||
|
||||
def get_orgdate(data):
|
||||
u"""
|
||||
Parse the given data (can be a string or list). Return an OrgDate if data
|
||||
contains a string representation of an OrgDate; otherwise return None.
|
||||
|
||||
data can be a string or a list containing strings.
|
||||
"""
|
||||
# TODO maybe it should be checked just for iterable? Does it affect here if
|
||||
# in base __getitem__(slice(i,j)) doesn't return a list but userlist...
|
||||
if isinstance(data, list):
|
||||
return _findfirst(_text2orgdate, data)
|
||||
else:
|
||||
return _text2orgdate(data)
|
||||
# if no dates found
|
||||
return None
|
||||
|
||||
|
||||
def _findfirst(f, seq):
|
||||
u"""
|
||||
Return first item in sequence seq where f(item) == True.
|
||||
|
||||
TODO: this is a general help function and it should be moved somewhere
|
||||
else; preferably into the standard lib :)
|
||||
"""
|
||||
for found in (f(item) for item in seq if f(item)):
|
||||
return found
|
||||
|
||||
|
||||
def _text2orgdate(string):
|
||||
u"""
|
||||
Transform the given string into an OrgDate.
|
||||
Return an OrgDate if data contains a string representation of an OrgDate;
|
||||
otherwise return None.
|
||||
"""
|
||||
# handle active datetime with same day
|
||||
result = _DATETIMERANGE_SAME_DAY_REGEX.search(string)
|
||||
if result:
|
||||
try:
|
||||
(syear, smonth, sday, shour, smin, ehour, emin) = \
|
||||
[int(m) for m in result.groups()]
|
||||
start = datetime.datetime(syear, smonth, sday, shour, smin)
|
||||
end = datetime.datetime(syear, smonth, sday, ehour, emin)
|
||||
return OrgTimeRange(True, start, end)
|
||||
except BaseException:
|
||||
return None
|
||||
|
||||
# handle active datetime
|
||||
result = _DATETIMERANGE_REGEX.search(string)
|
||||
if result:
|
||||
try:
|
||||
tmp = [int(m) for m in result.groups()]
|
||||
(syear, smonth, sday, shour, smin, eyear, emonth, eday, ehour, emin) = tmp
|
||||
start = datetime.datetime(syear, smonth, sday, shour, smin)
|
||||
end = datetime.datetime(eyear, emonth, eday, ehour, emin)
|
||||
return OrgTimeRange(True, start, end)
|
||||
except BaseException:
|
||||
return None
|
||||
|
||||
# handle active datetime
|
||||
result = _DATERANGE_REGEX.search(string)
|
||||
if result:
|
||||
try:
|
||||
tmp = [int(m) for m in result.groups()]
|
||||
syear, smonth, sday, eyear, emonth, ehour = tmp
|
||||
start = datetime.date(syear, smonth, sday)
|
||||
end = datetime.date(eyear, emonth, ehour)
|
||||
return OrgTimeRange(True, start, end)
|
||||
except BaseException:
|
||||
return None
|
||||
|
||||
# handle active datetime
|
||||
result = _DATETIME_REGEX.search(string)
|
||||
if result:
|
||||
try:
|
||||
year, month, day, hour, minutes = [int(m) for m in result.groups()]
|
||||
return OrgDateTime(True, year, month, day, hour, minutes)
|
||||
except BaseException:
|
||||
return None
|
||||
|
||||
# handle passive datetime
|
||||
result = _DATETIME_PASSIVE_REGEX.search(string)
|
||||
if result:
|
||||
try:
|
||||
year, month, day, hour, minutes = [int(m) for m in result.groups()]
|
||||
return OrgDateTime(False, year, month, day, hour, minutes)
|
||||
except BaseException:
|
||||
return None
|
||||
|
||||
# handle passive dates
|
||||
result = _DATE_PASSIVE_REGEX.search(string)
|
||||
if result:
|
||||
try:
|
||||
year, month, day = [int(m) for m in result.groups()]
|
||||
return OrgDate(False, year, month, day)
|
||||
except BaseException:
|
||||
return None
|
||||
|
||||
# handle active dates
|
||||
result = _DATE_REGEX.search(string)
|
||||
if result:
|
||||
try:
|
||||
year, month, day = [int(m) for m in result.groups()]
|
||||
return OrgDate(True, year, month, day)
|
||||
except BaseException:
|
||||
return None
|
||||
|
||||
|
||||
class OrgDate(datetime.date):
|
||||
u"""
|
||||
OrgDate represents a normal date like '2011-08-29 Mon'.
|
||||
|
||||
OrgDates can be active or inactive.
|
||||
|
||||
NOTE: date is immutable. That's why there needs to be __new__().
|
||||
See: http://docs.python.org/reference/datamodel.html#object.__new__
|
||||
"""
|
||||
def __init__(self, active, year, month, day):
|
||||
self.active = active
|
||||
pass
|
||||
|
||||
def __new__(cls, active, year, month, day):
|
||||
return datetime.date.__new__(cls, year, month, day)
|
||||
|
||||
def __unicode__(self):
|
||||
u"""
|
||||
Return a string representation.
|
||||
"""
|
||||
if self.active:
|
||||
return self.strftime(u'<%Y-%m-%d %a>')
|
||||
else:
|
||||
return self.strftime(u'[%Y-%m-%d %a]')
|
||||
|
||||
def __str__(self):
|
||||
return u_encode(self.__unicode__())
|
||||
|
||||
def strftime(self, fmt):
|
||||
return u_decode(datetime.date.strftime(self, u_encode(fmt)))
|
||||
|
||||
|
||||
class OrgDateTime(datetime.datetime):
|
||||
u"""
|
||||
OrgDateTime represents a normal date like '2011-08-29 Mon'.
|
||||
|
||||
OrgDateTime can be active or inactive.
|
||||
|
||||
NOTE: date is immutable. That's why there needs to be __new__().
|
||||
See: http://docs.python.org/reference/datamodel.html#object.__new__
|
||||
"""
|
||||
|
||||
def __init__(self, active, year, month, day, hour, mins):
|
||||
self.active = active
|
||||
|
||||
def __new__(cls, active, year, month, day, hour, minute):
|
||||
return datetime.datetime.__new__(cls, year, month, day, hour, minute)
|
||||
|
||||
def __unicode__(self):
|
||||
u"""
|
||||
Return a string representation.
|
||||
"""
|
||||
if self.active:
|
||||
return self.strftime(u'<%Y-%m-%d %a %H:%M>')
|
||||
else:
|
||||
return self.strftime(u'[%Y-%m-%d %a %H:%M]')
|
||||
|
||||
def __str__(self):
|
||||
return u_encode(self.__unicode__())
|
||||
|
||||
def 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__())
|
||||
170
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/menu.py
Normal file
170
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/menu.py
Normal file
@@ -0,0 +1,170 @@
|
||||
# -*- 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()
|
||||
312
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/Agenda.py
Normal file
312
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/Agenda.py
Normal file
@@ -0,0 +1,312 @@
|
||||
# -*- 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
|
||||
formatted = u" %(bufname)s (%(bufnr)d) %(todo)s %(title)s" % {
|
||||
'bufname': bufname,
|
||||
'bufnr': h.document.bufnr,
|
||||
'todo': h.todo,
|
||||
'title': h.title
|
||||
}
|
||||
final_agenda.append(formatted)
|
||||
cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
|
||||
|
||||
# show agenda
|
||||
vim.current.buffer[:] = [u_encode(i) for i in final_agenda]
|
||||
vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'))
|
||||
# try to jump to the position of today
|
||||
try:
|
||||
vim.command(u_encode(u'normal! %sgg<CR>' % today_row))
|
||||
except:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def list_all_todos(cls, current_buffer=False):
|
||||
u""" List all todos in one buffer.
|
||||
|
||||
Args:
|
||||
current_buffer (bool):
|
||||
False: all agenda files
|
||||
True: current org_file
|
||||
"""
|
||||
if current_buffer:
|
||||
agenda_documents = vim.current.buffer.name
|
||||
loaded_agendafiles = cls._load_agendafiles([agenda_documents])
|
||||
else:
|
||||
loaded_agendafiles = cls._get_agendadocuments()
|
||||
if not loaded_agendafiles:
|
||||
return
|
||||
raw_agenda = ORGMODE.agenda_manager.get_todo(loaded_agendafiles)
|
||||
|
||||
cls.line2doc = {}
|
||||
# create buffer at bottom
|
||||
cmd = [u'setlocal filetype=orgagenda']
|
||||
cls._switch_to(u'AGENDA', cmd)
|
||||
|
||||
# format text of agenda
|
||||
final_agenda = []
|
||||
for i, h in enumerate(raw_agenda):
|
||||
tmp = u"%s %s" % (h.todo, h.title)
|
||||
final_agenda.append(tmp)
|
||||
cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
|
||||
|
||||
# show agenda
|
||||
vim.current.buffer[:] = [u_encode(i) for i in final_agenda]
|
||||
vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'))
|
||||
|
||||
@classmethod
|
||||
def list_timeline(cls):
|
||||
"""
|
||||
List a timeline of the current buffer to get an overview of the
|
||||
current file.
|
||||
"""
|
||||
raw_agenda = ORGMODE.agenda_manager.get_timestamped_items(
|
||||
[ORGMODE.get_document()])
|
||||
|
||||
# create buffer at bottom
|
||||
cmd = [u'setlocal filetype=orgagenda']
|
||||
cls._switch_to(u'AGENDA', cmd)
|
||||
|
||||
cls.line2doc = {}
|
||||
# format text of agenda
|
||||
final_agenda = []
|
||||
for i, h in enumerate(raw_agenda):
|
||||
tmp = fmt.format('{} {}', h.todo, h.title).lstrip().rstrip()
|
||||
final_agenda.append(tmp)
|
||||
cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
|
||||
|
||||
# show agenda
|
||||
vim.current.buffer[:] = [u_encode(i) for i in final_agenda]
|
||||
vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'))
|
||||
|
||||
def register(self):
|
||||
u"""
|
||||
Registration of the plugin.
|
||||
|
||||
Key bindings and other initialization should be done here.
|
||||
"""
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u"OrgAgendaTodo",
|
||||
function=u'%s ORGMODE.plugins[u"Agenda"].list_all_todos()' % VIM_PY_CALL,
|
||||
key_mapping=u'<localleader>cat',
|
||||
menu_desrc=u'Agenda for all TODOs'
|
||||
)
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u"OrgBufferAgendaTodo",
|
||||
function=u'%s ORGMODE.plugins[u"Agenda"].list_all_todos(current_buffer=True)' % VIM_PY_CALL,
|
||||
key_mapping=u'<localleader>caT',
|
||||
menu_desrc=u'Agenda for all TODOs based on current buffer'
|
||||
)
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u"OrgAgendaWeek",
|
||||
function=u'%s ORGMODE.plugins[u"Agenda"].list_next_week()' % VIM_PY_CALL,
|
||||
key_mapping=u'<localleader>caa',
|
||||
menu_desrc=u'Agenda for the week'
|
||||
)
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u"OrgBufferAgendaWeek",
|
||||
function=u'%s ORGMODE.plugins[u"Agenda"].list_next_week_for_buffer()' % VIM_PY_CALL,
|
||||
key_mapping=u'<localleader>caA',
|
||||
menu_desrc=u'Agenda for the week based on current buffer'
|
||||
)
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u'OrgAgendaTimeline',
|
||||
function=u'%s ORGMODE.plugins[u"Agenda"].list_timeline()' % VIM_PY_CALL,
|
||||
key_mapping=u'<localleader>caL',
|
||||
menu_desrc=u'Timeline for this buffer'
|
||||
)
|
||||
316
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/Date.py
Normal file
316
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/Date.py
Normal file
@@ -0,0 +1,316 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
from datetime import timedelta, date, datetime
|
||||
|
||||
import operator
|
||||
|
||||
import vim
|
||||
|
||||
from orgmode._vim import ORGMODE, echom, insert_at_cursor, get_user_input
|
||||
from orgmode import settings
|
||||
from orgmode.keybinding import Keybinding, Plug
|
||||
from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu
|
||||
|
||||
from orgmode.py3compat.encode_compatibility import *
|
||||
from orgmode.py3compat.unicode_compatibility import *
|
||||
from orgmode.py3compat.py_py3_string import *
|
||||
|
||||
class Date(object):
|
||||
u"""
|
||||
Handles all date and timestamp related tasks.
|
||||
|
||||
TODO: extend functionality (calendar, repetitions, ranges). See
|
||||
http://orgmode.org/guide/Dates-and-Times.html#Dates-and-Times
|
||||
"""
|
||||
|
||||
date_regex = r"\d\d\d\d-\d\d-\d\d"
|
||||
datetime_regex = r"[A-Z]\w\w \d\d\d\d-\d\d-\d\d \d\d:\d\d>"
|
||||
|
||||
month_mapping = {
|
||||
u'jan': 1, u'feb': 2, u'mar': 3, u'apr': 4, u'may': 5,
|
||||
u'jun': 6, u'jul': 7, u'aug': 8, u'sep': 9, u'oct': 10, u'nov': 11,
|
||||
u'dec': 12}
|
||||
|
||||
def __init__(self):
|
||||
u""" Initialize plugin """
|
||||
object.__init__(self)
|
||||
# menu entries this plugin should create
|
||||
self.menu = ORGMODE.orgmenu + Submenu(u'Dates and Scheduling')
|
||||
|
||||
# key bindings for this plugin
|
||||
# key bindings are also registered through the menu so only additional
|
||||
# bindings should be put in this variable
|
||||
self.keybindings = []
|
||||
|
||||
# commands for this plugin
|
||||
self.commands = []
|
||||
|
||||
# set speeddating format that is compatible with orgmode
|
||||
try:
|
||||
if int(vim.eval(u_encode(u'exists(":SpeedDatingFormat")'))) == 2:
|
||||
vim.command(u_encode(u':1SpeedDatingFormat %Y-%m-%d %a'))
|
||||
vim.command(u_encode(u':1SpeedDatingFormat %Y-%m-%d %a %H:%M'))
|
||||
else:
|
||||
echom(u'Speeddating plugin not installed. Please install it.')
|
||||
except:
|
||||
echom(u'Speeddating plugin not installed. Please install it.')
|
||||
|
||||
@classmethod
|
||||
def _modify_time(cls, startdate, modifier):
|
||||
u"""Modify the given startdate according to modifier. Return the new
|
||||
date or datetime.
|
||||
|
||||
See http://orgmode.org/manual/The-date_002ftime-prompt.html
|
||||
"""
|
||||
if modifier is None or modifier == '' or modifier == '.':
|
||||
return startdate
|
||||
|
||||
# rm crap from modifier
|
||||
modifier = modifier.strip()
|
||||
|
||||
ops = {'-': operator.sub, '+': operator.add}
|
||||
|
||||
# check real date
|
||||
date_regex = r"(\d\d\d\d)-(\d\d)-(\d\d)"
|
||||
match = re.search(date_regex, modifier)
|
||||
if match:
|
||||
year, month, day = match.groups()
|
||||
newdate = date(int(year), int(month), int(day))
|
||||
|
||||
# check abbreviated date, separated with '-'
|
||||
date_regex = u"(\\d{1,2})-(\\d+)-(\\d+)"
|
||||
match = re.search(date_regex, modifier)
|
||||
if match:
|
||||
year, month, day = match.groups()
|
||||
newdate = date(2000 + int(year), int(month), int(day))
|
||||
|
||||
# check abbreviated date, separated with '/'
|
||||
# month/day
|
||||
date_regex = u"(\\d{1,2})/(\\d{1,2})"
|
||||
match = re.search(date_regex, modifier)
|
||||
if match:
|
||||
month, day = match.groups()
|
||||
newdate = date(startdate.year, int(month), int(day))
|
||||
# date should be always in the future
|
||||
if newdate < startdate:
|
||||
newdate = date(startdate.year + 1, int(month), int(day))
|
||||
|
||||
# check full date, separated with 'space'
|
||||
# month day year
|
||||
# 'sep 12 9' --> 2009 9 12
|
||||
date_regex = u"(\\w\\w\\w) (\\d{1,2}) (\\d{1,2})"
|
||||
match = re.search(date_regex, modifier)
|
||||
if match:
|
||||
gr = match.groups()
|
||||
day = int(gr[1])
|
||||
month = int(cls.month_mapping[gr[0]])
|
||||
year = 2000 + int(gr[2])
|
||||
newdate = date(year, int(month), int(day))
|
||||
|
||||
# check days as integers
|
||||
date_regex = u"^(\\d{1,2})$"
|
||||
match = re.search(date_regex, modifier)
|
||||
if match:
|
||||
newday, = match.groups()
|
||||
newday = int(newday)
|
||||
if newday > startdate.day:
|
||||
newdate = date(startdate.year, startdate.month, newday)
|
||||
else:
|
||||
# TODO: DIRTY, fix this
|
||||
# this does NOT cover all edge cases
|
||||
newdate = startdate + timedelta(days=28)
|
||||
newdate = date(newdate.year, newdate.month, newday)
|
||||
|
||||
# check for full days: Mon, Tue, Wed, Thu, Fri, Sat, Sun
|
||||
modifier_lc = modifier.lower()
|
||||
match = re.search(u'mon|tue|wed|thu|fri|sat|sun', modifier_lc)
|
||||
if match:
|
||||
weekday_mapping = {
|
||||
u'mon': 0, u'tue': 1, u'wed': 2, u'thu': 3,
|
||||
u'fri': 4, u'sat': 5, u'sun': 6}
|
||||
diff = (weekday_mapping[modifier_lc] - startdate.weekday()) % 7
|
||||
# use next weeks weekday if current weekday is the same as modifier
|
||||
if diff == 0:
|
||||
diff = 7
|
||||
newdate = startdate + timedelta(days=diff)
|
||||
|
||||
# check for days modifier with appended d
|
||||
match = re.search(u'^(\\+|-)(\\d*)d', modifier)
|
||||
if match:
|
||||
op, days = match.groups()
|
||||
newdate = ops[op](startdate, timedelta(days=int(days)))
|
||||
|
||||
# check for days modifier without appended d
|
||||
match = re.search(u'^(\\+|-)(\\d*) |^(\\+|-)(\\d*)$', modifier)
|
||||
if match:
|
||||
groups = match.groups()
|
||||
try:
|
||||
op = groups[0]
|
||||
days = int(groups[1])
|
||||
except:
|
||||
op = groups[2]
|
||||
days = int(groups[3])
|
||||
newdate = ops[op](startdate, timedelta(days=days))
|
||||
|
||||
# check for week modifier
|
||||
match = re.search(u'^(\\+|-)(\\d+)w', modifier)
|
||||
if match:
|
||||
op, weeks = match.groups()
|
||||
newdate = ops[op](startdate, timedelta(weeks=int(weeks)))
|
||||
|
||||
# check for month modifier
|
||||
match = re.search(u'^(\\+|-)(\\d+)m', modifier)
|
||||
if match:
|
||||
op, months = match.groups()
|
||||
newdate = date(startdate.year, ops[op](startdate.month, int(months)),
|
||||
startdate.day)
|
||||
|
||||
# check for year modifier
|
||||
match = re.search(u'^(\\+|-)(\\d*)y', modifier)
|
||||
if match:
|
||||
op, years = match.groups()
|
||||
newdate = date(ops[op](startdate.year, int(years)), startdate.month,
|
||||
startdate.day)
|
||||
|
||||
# check for month day
|
||||
match = re.search(
|
||||
u'(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) (\\d{1,2})',
|
||||
modifier.lower())
|
||||
if match:
|
||||
month = cls.month_mapping[match.groups()[0]]
|
||||
day = int(match.groups()[1])
|
||||
newdate = date(startdate.year, int(month), int(day))
|
||||
# date should be always in the future
|
||||
if newdate < startdate:
|
||||
newdate = date(startdate.year + 1, int(month), int(day))
|
||||
|
||||
# check abbreviated date, separated with '/'
|
||||
# month/day/year
|
||||
date_regex = u"(\\d{1,2})/(\\d+)/(\\d+)"
|
||||
match = re.search(date_regex, modifier)
|
||||
if match:
|
||||
month, day, year = match.groups()
|
||||
newdate = date(2000 + int(year), int(month), int(day))
|
||||
|
||||
# check for month day year
|
||||
# sep 12 2011
|
||||
match = re.search(
|
||||
u'(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) (\\d{1,2}) (\\d{1,4})',
|
||||
modifier.lower())
|
||||
if match:
|
||||
month = int(cls.month_mapping[match.groups()[0]])
|
||||
day = int(match.groups()[1])
|
||||
if len(match.groups()[2]) < 4:
|
||||
year = 2000 + int(match.groups()[2])
|
||||
else:
|
||||
year = int(match.groups()[2])
|
||||
newdate = date(year, month, day)
|
||||
|
||||
# check for time: HH:MM
|
||||
# '12:45' --> datetime(2006, 06, 13, 12, 45))
|
||||
match = re.search(u'(\\d{1,2}):(\\d\\d)$', modifier)
|
||||
if match:
|
||||
try:
|
||||
startdate = newdate
|
||||
except:
|
||||
pass
|
||||
return datetime(
|
||||
startdate.year, startdate.month, startdate.day,
|
||||
int(match.groups()[0]), int(match.groups()[1]))
|
||||
|
||||
try:
|
||||
return newdate
|
||||
except:
|
||||
return startdate
|
||||
|
||||
@classmethod
|
||||
def insert_timestamp(cls, active=True):
|
||||
u"""
|
||||
Insert a timestamp at the cursor position.
|
||||
|
||||
TODO: show fancy calendar to pick the date from.
|
||||
TODO: add all modifier of orgmode.
|
||||
"""
|
||||
today = date.today()
|
||||
msg = u''.join([
|
||||
u'Inserting ',
|
||||
unicode(u_decode(today.strftime(u'%Y-%m-%d %a'))),
|
||||
u' | Modify date'])
|
||||
modifier = get_user_input(msg)
|
||||
|
||||
# abort if the user canceled the input prompt
|
||||
if modifier is None:
|
||||
return
|
||||
|
||||
newdate = cls._modify_time(today, modifier)
|
||||
|
||||
# format
|
||||
if isinstance(newdate, datetime):
|
||||
newdate = newdate.strftime(
|
||||
u_decode(u_encode(u'%Y-%m-%d %a %H:%M')))
|
||||
else:
|
||||
newdate = newdate.strftime(
|
||||
u_decode(u_encode(u'%Y-%m-%d %a')))
|
||||
timestamp = u'<%s>' % newdate if active else u'[%s]' % newdate
|
||||
|
||||
insert_at_cursor(timestamp)
|
||||
|
||||
@classmethod
|
||||
def insert_timestamp_with_calendar(cls, active=True):
|
||||
u"""
|
||||
Insert a timestamp at the cursor position.
|
||||
Show fancy calendar to pick the date from.
|
||||
|
||||
TODO: add all modifier of orgmode.
|
||||
"""
|
||||
if int(vim.eval(u_encode(u'exists(":CalendarH")'))) != 2:
|
||||
vim.command("echo 'Please install plugin Calendar to enable this function'")
|
||||
return
|
||||
vim.command("CalendarH")
|
||||
# backup calendar_action
|
||||
calendar_action = vim.eval("g:calendar_action")
|
||||
vim.command("let g:org_calendar_action_backup = '" + calendar_action + "'")
|
||||
vim.command("let g:calendar_action = 'CalendarAction'")
|
||||
|
||||
timestamp_template = u'<%s>' if active else u'[%s]'
|
||||
# timestamp template
|
||||
vim.command("let g:org_timestamp_template = '" + timestamp_template + "'")
|
||||
|
||||
def register(self):
|
||||
u"""
|
||||
Registration of the plugin.
|
||||
|
||||
Key bindings and other initialization should be done here.
|
||||
"""
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u'OrgDateInsertTimestampActiveCmdLine',
|
||||
key_mapping=u'<localleader>sa',
|
||||
function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp()' % VIM_PY_CALL,
|
||||
menu_desrc=u'Timest&'
|
||||
)
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u'OrgDateInsertTimestampInactiveCmdLine',
|
||||
key_mapping='<localleader>si',
|
||||
function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp(False)' % VIM_PY_CALL,
|
||||
menu_desrc=u'Timestamp (&inactive)'
|
||||
)
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u'OrgDateInsertTimestampActiveWithCalendar',
|
||||
key_mapping=u'<localleader>pa',
|
||||
function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp_with_calendar()' % VIM_PY_CALL,
|
||||
menu_desrc=u'Timestamp with Calendar'
|
||||
)
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u'OrgDateInsertTimestampInactiveWithCalendar',
|
||||
key_mapping=u'<localleader>pi',
|
||||
function=u'%s ORGMODE.plugins[u"Date"].insert_timestamp_with_calendar(False)' % VIM_PY_CALL,
|
||||
menu_desrc=u'Timestamp with Calendar(inactive)'
|
||||
)
|
||||
|
||||
submenu = self.menu + Submenu(u'Change &Date')
|
||||
submenu + ActionEntry(u'Day &Earlier', u'<C-x>', u'<C-x>')
|
||||
submenu + ActionEntry(u'Day &Later', u'<C-a>', u'<C-a>')
|
||||
@@ -0,0 +1,328 @@
|
||||
# -*- 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'
|
||||
)
|
||||
@@ -0,0 +1,428 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import vim
|
||||
|
||||
from orgmode._vim import ORGMODE, apply_count, repeat, realign_tags
|
||||
from orgmode import settings
|
||||
from orgmode.exceptions import HeadingDomError
|
||||
from orgmode.keybinding import Keybinding, Plug, MODE_INSERT, MODE_NORMAL
|
||||
from orgmode.menu import Submenu, Separator, ActionEntry
|
||||
from orgmode.liborgmode.base import Direction
|
||||
from orgmode.liborgmode.headings import Heading
|
||||
|
||||
from orgmode.py3compat.encode_compatibility import *
|
||||
from orgmode.py3compat.py_py3_string import *
|
||||
|
||||
|
||||
class EditStructure(object):
|
||||
u""" EditStructure plugin """
|
||||
|
||||
def __init__(self):
|
||||
u""" Initialize plugin """
|
||||
object.__init__(self)
|
||||
# menu entries this plugin should create
|
||||
self.menu = ORGMODE.orgmenu + Submenu(u'&Edit Structure')
|
||||
|
||||
# key bindings for this plugin
|
||||
# key bindings are also registered through the menu so only additional
|
||||
# bindings should be put in this variable
|
||||
self.keybindings = []
|
||||
|
||||
@classmethod
|
||||
def new_heading(cls, below=None, insert_mode=False, end_of_last_child=False):
|
||||
u"""
|
||||
:below: True, insert heading below current heading, False,
|
||||
insert heading above current heading, None, special
|
||||
behavior for insert mode, use the current text as
|
||||
heading
|
||||
:insert_mode: True, if action is performed in insert mode
|
||||
:end_of_last_child: True, insert heading at the end of last child,
|
||||
otherwise the newly created heading will "take
|
||||
over" the current heading's children
|
||||
"""
|
||||
d = ORGMODE.get_document()
|
||||
current_heading = d.current_heading()
|
||||
cursor = vim.current.window.cursor[:]
|
||||
if not current_heading:
|
||||
# the user is in meta data region
|
||||
pos = cursor[0] - 1
|
||||
heading = Heading(title=d.meta_information[pos], body=d.meta_information[pos + 1:])
|
||||
d.headings.insert(0, heading)
|
||||
del d.meta_information[pos:]
|
||||
d.write()
|
||||
vim.command(u_encode(u'exe "normal %dgg"|startinsert!' % (heading.start_vim, )))
|
||||
return heading
|
||||
|
||||
# check for plain list(checkbox)
|
||||
current_heading.init_checkboxes()
|
||||
c = current_heading.current_checkbox()
|
||||
if c is not None:
|
||||
ORGMODE.plugins[u"EditCheckbox"].new_checkbox(below, not c.status)
|
||||
return
|
||||
|
||||
heading = Heading(level=current_heading.level)
|
||||
|
||||
# it's weird but this is the behavior of original orgmode
|
||||
if below is None:
|
||||
below = cursor[1] != 0 or end_of_last_child
|
||||
|
||||
# insert newly created heading
|
||||
l = current_heading.get_parent_list()
|
||||
idx = current_heading.get_index_in_parent_list()
|
||||
if l is not None and idx is not None:
|
||||
l.insert(idx + (1 if below else 0), heading)
|
||||
else:
|
||||
raise HeadingDomError(u'Current heading is not properly linked in DOM')
|
||||
|
||||
if below and not end_of_last_child:
|
||||
# append heading at the end of current heading and also take
|
||||
# over the children of current heading
|
||||
for child in current_heading.children:
|
||||
heading.children.append(child, taint=False)
|
||||
current_heading.children.remove_slice(
|
||||
0, len(current_heading.children),
|
||||
taint=False)
|
||||
|
||||
# if cursor is currently on a heading, insert parts of it into the
|
||||
# newly created heading
|
||||
if insert_mode and cursor[1] != 0 and cursor[0] == current_heading.start_vim:
|
||||
offset = cursor[1] - current_heading.level - 1 - (
|
||||
len(current_heading.todo) + 1 if current_heading.todo else 0)
|
||||
if offset < 0:
|
||||
offset = 0
|
||||
if int(settings.get(u'org_improve_split_heading', u'1')) and \
|
||||
offset > 0 and len(current_heading.title) == offset + 1 \
|
||||
and current_heading.title[offset - 1] not in (u' ', u'\t'):
|
||||
offset += 1
|
||||
heading.title = current_heading.title[offset:]
|
||||
current_heading.title = current_heading.title[:offset]
|
||||
heading.body = current_heading.body[:]
|
||||
current_heading.body = []
|
||||
|
||||
d.write()
|
||||
# do not start insert upon adding new headings, unless already in insert mode. Issue #211
|
||||
if int(settings.get(u'org_prefer_insert_mode', u'1')) or insert_mode:
|
||||
vim.command(u_encode(u'exe "normal %dgg"|startinsert!' % (heading.start_vim, )))
|
||||
else:
|
||||
vim.command(u_encode(u'exe "normal %dgg$"' % (heading.start_vim, )))
|
||||
|
||||
# return newly created heading
|
||||
return heading
|
||||
|
||||
@classmethod
|
||||
def _append_heading(cls, heading, parent):
|
||||
if heading.level <= parent.level:
|
||||
raise ValueError('Heading level not is lower than parent level: %d ! > %d' % (heading.level, parent.level))
|
||||
|
||||
if parent.children and parent.children[-1].level < heading.level:
|
||||
cls._append_heading(heading, parent.children[-1])
|
||||
else:
|
||||
parent.children.append(heading, taint=False)
|
||||
|
||||
@classmethod
|
||||
def _change_heading_level(cls, level, including_children=True, on_heading=False, insert_mode=False):
|
||||
u"""
|
||||
Change level of heading realtively with or without including children.
|
||||
|
||||
:level: the number of levels to promote/demote heading
|
||||
:including_children: True if should should be included in promoting/demoting
|
||||
:on_heading: True if promoting/demoting should only happen when the cursor is on the heading
|
||||
:insert_mode: True if vim is in insert mode
|
||||
"""
|
||||
# TODO : current promote and demote works for only headings. Since
|
||||
# checkboxes also have tree structure. We should think of
|
||||
# expanding the functionality of promoting and demoting to
|
||||
# checkboxes as well
|
||||
d = ORGMODE.get_document()
|
||||
current_heading = d.current_heading()
|
||||
if not current_heading or on_heading and current_heading.start_vim != vim.current.window.cursor[0]:
|
||||
# TODO figure out the actually pressed keybinding and feed these
|
||||
# keys instead of making keys up like this
|
||||
if level > 0:
|
||||
if insert_mode:
|
||||
vim.eval(u_encode(u'feedkeys("\\<C-t>", "n")'))
|
||||
elif including_children:
|
||||
vim.eval(u_encode(u'feedkeys(">]]", "n")'))
|
||||
elif on_heading:
|
||||
vim.eval(u_encode(u'feedkeys(">>", "n")'))
|
||||
else:
|
||||
vim.eval(u_encode(u'feedkeys(">}", "n")'))
|
||||
else:
|
||||
if insert_mode:
|
||||
vim.eval(u_encode(u'feedkeys("\\<C-d>", "n")'))
|
||||
elif including_children:
|
||||
vim.eval(u_encode(u'feedkeys("<]]", "n")'))
|
||||
elif on_heading:
|
||||
vim.eval(u_encode(u'feedkeys("<<", "n")'))
|
||||
else:
|
||||
vim.eval(u_encode(u'feedkeys("<}", "n")'))
|
||||
# return True because otherwise apply_count will not work
|
||||
return True
|
||||
|
||||
# don't allow demotion below level 1
|
||||
if current_heading.level == 1 and level < 1:
|
||||
return False
|
||||
|
||||
# reduce level of demotion to a minimum heading level of 1
|
||||
if (current_heading.level + level) < 1:
|
||||
level = 1
|
||||
|
||||
def indent(heading, ic):
|
||||
if not heading:
|
||||
return
|
||||
heading.level += level
|
||||
|
||||
if ic:
|
||||
for child in heading.children:
|
||||
indent(child, ic)
|
||||
|
||||
# save cursor position
|
||||
c = vim.current.window.cursor[:]
|
||||
|
||||
# indent the promoted/demoted heading
|
||||
indent_end_vim = current_heading.end_of_last_child_vim if including_children else current_heading.end_vim
|
||||
indent(current_heading, including_children)
|
||||
|
||||
# when changing the level of a heading, its position in the DOM
|
||||
# needs to be updated. It's likely that the heading gets a new
|
||||
# parent and new children when demoted or promoted
|
||||
|
||||
# find new parent
|
||||
p = current_heading.parent
|
||||
pl = current_heading.get_parent_list()
|
||||
ps = current_heading.previous_sibling
|
||||
nhl = current_heading.level
|
||||
|
||||
if level > 0:
|
||||
# demotion
|
||||
# subheading or top level heading
|
||||
if ps and nhl > ps.level:
|
||||
pl.remove(current_heading, taint=False)
|
||||
# find heading that is the new parent heading
|
||||
oh = ps
|
||||
h = ps
|
||||
while nhl > h.level:
|
||||
oh = h
|
||||
if h.children:
|
||||
h = h.children[-1]
|
||||
else:
|
||||
break
|
||||
np = h if nhl > h.level else oh
|
||||
|
||||
# append current heading to new heading
|
||||
np.children.append(current_heading, taint=False)
|
||||
|
||||
# if children are not included, distribute them among the
|
||||
# parent heading and it's siblings
|
||||
if not including_children:
|
||||
for h in current_heading.children[:]:
|
||||
if h and h.level <= nhl:
|
||||
cls._append_heading(h, np)
|
||||
current_heading.children.remove(h, taint=False)
|
||||
else:
|
||||
# promotion
|
||||
if p and nhl <= p.level:
|
||||
idx = current_heading.get_index_in_parent_list() + 1
|
||||
# find the new parent heading
|
||||
oh = p
|
||||
h = p
|
||||
while nhl <= h.level:
|
||||
# append new children to current heading
|
||||
for child in h.children[idx:]:
|
||||
cls._append_heading(child, current_heading)
|
||||
h.children.remove_slice(idx, len(h.children), taint=False)
|
||||
idx = h.get_index_in_parent_list() + 1
|
||||
if h.parent:
|
||||
h = h.parent
|
||||
else:
|
||||
break
|
||||
ns = oh.next_sibling
|
||||
while ns and ns.level > current_heading.level:
|
||||
nns = ns.next_sibling
|
||||
cls._append_heading(ns, current_heading)
|
||||
ns = nns
|
||||
|
||||
# append current heading to new parent heading / document
|
||||
pl.remove(current_heading, taint=False)
|
||||
if nhl > h.level:
|
||||
h.children.insert(idx, current_heading, taint=False)
|
||||
else:
|
||||
d.headings.insert(idx, current_heading, taint=False)
|
||||
|
||||
d.write()
|
||||
|
||||
# restore cursor position
|
||||
vim.current.window.cursor = (c[0], c[1] + level)
|
||||
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
@realign_tags
|
||||
@repeat
|
||||
@apply_count
|
||||
def demote_heading(cls, including_children=True, on_heading=False, insert_mode=False):
|
||||
if cls._change_heading_level(1, including_children=including_children, on_heading=on_heading, insert_mode=insert_mode):
|
||||
if including_children:
|
||||
return u'OrgDemoteSubtree'
|
||||
return u'OrgDemoteHeading'
|
||||
|
||||
@classmethod
|
||||
@realign_tags
|
||||
@repeat
|
||||
@apply_count
|
||||
def promote_heading(cls, including_children=True, on_heading=False, insert_mode=False):
|
||||
if cls._change_heading_level(-1, including_children=including_children, on_heading=on_heading, insert_mode=insert_mode):
|
||||
if including_children:
|
||||
return u'OrgPromoteSubtreeNormal'
|
||||
return u'OrgPromoteHeadingNormal'
|
||||
|
||||
@classmethod
|
||||
def _move_heading(cls, direction=Direction.FORWARD, including_children=True):
|
||||
u""" Move heading up or down
|
||||
|
||||
:returns: heading or None
|
||||
"""
|
||||
d = ORGMODE.get_document()
|
||||
current_heading = d.current_heading()
|
||||
if not current_heading or \
|
||||
(direction == Direction.FORWARD and not current_heading.next_sibling) or \
|
||||
(direction == Direction.BACKWARD and not current_heading.previous_sibling):
|
||||
return None
|
||||
|
||||
cursor_offset = vim.current.window.cursor[0] - (current_heading._orig_start + 1)
|
||||
l = current_heading.get_parent_list()
|
||||
if l is None:
|
||||
raise HeadingDomError(u'Current heading is not properly linked in DOM')
|
||||
|
||||
if not including_children:
|
||||
if current_heading.previous_sibling:
|
||||
npl = current_heading.previous_sibling.children
|
||||
for child in current_heading.children:
|
||||
npl.append(child, taint=False)
|
||||
elif current_heading.parent:
|
||||
# if the current heading doesn't have a previous sibling it
|
||||
# must be the first heading
|
||||
np = current_heading.parent
|
||||
for child in current_heading.children:
|
||||
cls._append_heading(child, np)
|
||||
else:
|
||||
# if the current heading doesn't have a parent, its children
|
||||
# must be added as top level headings to the document
|
||||
npl = l
|
||||
for child in current_heading.children[::-1]:
|
||||
npl.insert(0, child, taint=False)
|
||||
current_heading.children.remove_slice(0, len(current_heading.children), taint=False)
|
||||
|
||||
idx = current_heading.get_index_in_parent_list()
|
||||
if idx is None:
|
||||
raise HeadingDomError(u'Current heading is not properly linked in DOM')
|
||||
|
||||
offset = 1 if direction == Direction.FORWARD else -1
|
||||
del l[idx]
|
||||
l.insert(idx + offset, current_heading)
|
||||
|
||||
d.write()
|
||||
|
||||
vim.current.window.cursor = (
|
||||
current_heading.start_vim + cursor_offset,
|
||||
vim.current.window.cursor[1])
|
||||
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
@repeat
|
||||
@apply_count
|
||||
def move_heading_upward(cls, including_children=True):
|
||||
if cls._move_heading(direction=Direction.BACKWARD, including_children=including_children):
|
||||
if including_children:
|
||||
return u'OrgMoveSubtreeUpward'
|
||||
return u'OrgMoveHeadingUpward'
|
||||
|
||||
@classmethod
|
||||
@repeat
|
||||
@apply_count
|
||||
def move_heading_downward(cls, including_children=True):
|
||||
if cls._move_heading(direction=Direction.FORWARD, including_children=including_children):
|
||||
if including_children:
|
||||
return u'OrgMoveSubtreeDownward'
|
||||
return u'OrgMoveHeadingDownward'
|
||||
|
||||
def register(self):
|
||||
u"""
|
||||
Registration of plugin. Key bindings and other initialization should be done.
|
||||
"""
|
||||
# EditStructure related default settings
|
||||
settings.set(u'org_improve_split_heading', u'1')
|
||||
# EditStructure related keybindings
|
||||
self.keybindings.append(Keybinding(u'<C-S-CR>',
|
||||
Plug(u'OrgNewHeadingAboveNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=False)<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'New Heading &above', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u'<localleader>hN', u'<Plug>OrgNewHeadingAboveNormal', mode=MODE_NORMAL))
|
||||
self.keybindings.append(Keybinding(u'<localleader><CR>', u'<Plug>OrgNewHeadingAboveNormal', mode=MODE_NORMAL))
|
||||
|
||||
self.keybindings.append(Keybinding(u'<S-CR>',
|
||||
Plug(u'OrgNewHeadingBelowNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=True)<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'New Heading &below', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u'<localleader>hh', u'<Plug>OrgNewHeadingBelowNormal', mode=MODE_NORMAL))
|
||||
self.keybindings.append(Keybinding(u'<localleader><CR>', u'<Plug>OrgNewHeadingBelowNormal', mode=MODE_NORMAL))
|
||||
|
||||
self.keybindings.append(Keybinding(u'<C-CR>', Plug(u'OrgNewHeadingBelowAfterChildrenNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=True, end_of_last_child=True)<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'New Heading below, after &children', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u'<localleader>hn', u'<Plug>OrgNewHeadingBelowAfterChildrenNormal', mode=MODE_NORMAL))
|
||||
self.keybindings.append(Keybinding(u'<CR>', u'<Plug>OrgNewHeadingBelowAfterChildrenNormal', mode=MODE_NORMAL))
|
||||
|
||||
self.keybindings.append(Keybinding(u'<C-S-CR>', Plug(u'OrgNewHeadingAboveInsert', u'<C-o>:<C-u>silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=False, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT)))
|
||||
self.keybindings.append(Keybinding(u'<S-CR>', Plug(u'OrgNewHeadingBelowInsert', u'<C-o>:<C-u>silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(below=True, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT)))
|
||||
self.keybindings.append(Keybinding(u'<C-CR>', Plug(u'OrgNewHeadingBelowAfterChildrenInsert', u'<C-o>:<C-u>silent! %s ORGMODE.plugins[u"EditStructure"].new_heading(insert_mode=True, end_of_last_child=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT)))
|
||||
|
||||
self.menu + Separator()
|
||||
|
||||
self.keybindings.append(Keybinding(u'm{', Plug(u'OrgMoveHeadingUpward',
|
||||
u'%s ORGMODE.plugins[u"EditStructure"].move_heading_upward(including_children=False)<CR>' % VIM_PY_CALL)))
|
||||
self.keybindings.append(Keybinding(u'm[[',
|
||||
Plug(u'OrgMoveSubtreeUpward', u'%s ORGMODE.plugins[u"EditStructure"].move_heading_upward()<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'Move Subtree &Up', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u'm}',
|
||||
Plug(u'OrgMoveHeadingDownward', u'%s ORGMODE.plugins[u"EditStructure"].move_heading_downward(including_children=False)<CR>' % VIM_PY_CALL)))
|
||||
self.keybindings.append(Keybinding(u'm]]',
|
||||
Plug(u'OrgMoveSubtreeDownward', u'%s ORGMODE.plugins[u"EditStructure"].move_heading_downward()<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'Move Subtree &Down', self.keybindings[-1])
|
||||
|
||||
self.menu + Separator()
|
||||
|
||||
self.menu + ActionEntry(u'&Copy Heading', u'yah', u'yah')
|
||||
self.menu + ActionEntry(u'C&ut Heading', u'dah', u'dah')
|
||||
|
||||
self.menu + Separator()
|
||||
|
||||
self.menu + ActionEntry(u'&Copy Subtree', u'yar', u'yar')
|
||||
self.menu + ActionEntry(u'C&ut Subtree', u'dar', u'dar')
|
||||
self.menu + ActionEntry(u'&Paste Subtree', u'p', u'p')
|
||||
|
||||
self.menu + Separator()
|
||||
|
||||
self.keybindings.append(Keybinding(u'<ah', Plug(u'OrgPromoteHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False)<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'&Promote Heading', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u'<<', Plug(u'OrgPromoteOnHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False, on_heading=True)<CR>' % VIM_PY_CALL)))
|
||||
self.keybindings.append(Keybinding(u'<{', u'<Plug>OrgPromoteHeadingNormal', mode=MODE_NORMAL))
|
||||
self.keybindings.append(Keybinding(u'<ih', u'<Plug>OrgPromoteHeadingNormal', mode=MODE_NORMAL))
|
||||
|
||||
self.keybindings.append(Keybinding(u'<ar', Plug(u'OrgPromoteSubtreeNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading()<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'&Promote Subtree', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u'<[[', u'<Plug>OrgPromoteSubtreeNormal', mode=MODE_NORMAL))
|
||||
self.keybindings.append(Keybinding(u'<ir', u'<Plug>OrgPromoteSubtreeNormal', mode=MODE_NORMAL))
|
||||
|
||||
self.keybindings.append(Keybinding(u'>ah', Plug(u'OrgDemoteHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False)<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'&Demote Heading', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u'>>', Plug(u'OrgDemoteOnHeadingNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False, on_heading=True)<CR>' % VIM_PY_CALL)))
|
||||
self.keybindings.append(Keybinding(u'>}', u'<Plug>OrgDemoteHeadingNormal', mode=MODE_NORMAL))
|
||||
self.keybindings.append(Keybinding(u'>ih', u'<Plug>OrgDemoteHeadingNormal', mode=MODE_NORMAL))
|
||||
|
||||
self.keybindings.append(Keybinding(u'>ar', Plug(u'OrgDemoteSubtreeNormal', u':silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading()<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'&Demote Subtree', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u'>]]', u'<Plug>OrgDemoteSubtreeNormal', mode=MODE_NORMAL))
|
||||
self.keybindings.append(Keybinding(u'>ir', u'<Plug>OrgDemoteSubtreeNormal', mode=MODE_NORMAL))
|
||||
|
||||
# other keybindings
|
||||
self.keybindings.append(Keybinding(u'<C-d>', Plug(u'OrgPromoteOnHeadingInsert', u'<C-o>:silent! %s ORGMODE.plugins[u"EditStructure"].promote_heading(including_children=False, on_heading=True, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT)))
|
||||
self.keybindings.append(Keybinding(u'<C-t>', Plug(u'OrgDemoteOnHeadingInsert', u'<C-o>:silent! %s ORGMODE.plugins[u"EditStructure"].demote_heading(including_children=False, on_heading=True, insert_mode=True)<CR>' % VIM_PY_CALL, mode=MODE_INSERT)))
|
||||
181
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/Export.py
Normal file
181
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/Export.py
Normal file
@@ -0,0 +1,181 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import vim
|
||||
|
||||
from orgmode._vim import ORGMODE, echoe, echom
|
||||
from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu
|
||||
from orgmode.keybinding import Keybinding, Plug, Command
|
||||
from orgmode import settings
|
||||
|
||||
from orgmode.py3compat.py_py3_string import *
|
||||
|
||||
class Export(object):
|
||||
u"""
|
||||
Export a orgmode file using emacs orgmode.
|
||||
|
||||
This is a *very simple* wrapper of the emacs/orgmode export. emacs and
|
||||
orgmode need to be installed. We simply call emacs with some options to
|
||||
export the .org.
|
||||
|
||||
TODO: Offer export options in vim. Don't use the menu.
|
||||
TODO: Maybe use a native implementation.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
u""" Initialize plugin """
|
||||
object.__init__(self)
|
||||
# menu entries this plugin should create
|
||||
self.menu = ORGMODE.orgmenu + Submenu(u'Export')
|
||||
|
||||
# key bindings for this plugin
|
||||
# key bindings are also registered through the menu so only additional
|
||||
# bindings should be put in this variable
|
||||
self.keybindings = []
|
||||
|
||||
# commands for this plugin
|
||||
self.commands = []
|
||||
|
||||
@classmethod
|
||||
def _get_init_script(cls):
|
||||
init_script = settings.get(u'org_export_init_script', u'')
|
||||
if init_script:
|
||||
init_script = os.path.expandvars(os.path.expanduser(init_script))
|
||||
if os.path.exists(init_script):
|
||||
return init_script
|
||||
else:
|
||||
echoe(u'Unable to find init script %s' % init_script)
|
||||
|
||||
@classmethod
|
||||
def _export(cls, format_):
|
||||
"""Export current file to format.
|
||||
|
||||
Args:
|
||||
format_: pdf or html
|
||||
|
||||
Returns:
|
||||
return code
|
||||
"""
|
||||
emacsbin = os.path.expandvars(os.path.expanduser(
|
||||
settings.get(u'org_export_emacs', u'/usr/bin/emacs')))
|
||||
if not os.path.exists(emacsbin):
|
||||
echoe(u'Unable to find emacs binary %s' % emacsbin)
|
||||
|
||||
# build the export command
|
||||
cmd = [
|
||||
emacsbin,
|
||||
u'-nw',
|
||||
u'--batch',
|
||||
u'--visit=%s' % vim.eval(u'expand("%:p")'),
|
||||
u'--funcall=%s' % format_
|
||||
]
|
||||
# source init script as well
|
||||
init_script = cls._get_init_script()
|
||||
if init_script:
|
||||
cmd.extend(['--script', init_script])
|
||||
|
||||
# export
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
p.wait()
|
||||
|
||||
if p.returncode != 0 or settings.get(u'org_export_verbose') == 1:
|
||||
echom('\n'.join(map(lambda x: x.decode(), p.communicate())))
|
||||
return p.returncode
|
||||
|
||||
@classmethod
|
||||
def topdf(cls):
|
||||
u"""Export the current buffer as pdf using emacs orgmode."""
|
||||
ret = cls._export(u'org-latex-export-to-pdf')
|
||||
if ret != 0:
|
||||
echoe(u'PDF export failed.')
|
||||
else:
|
||||
echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'pdf'))
|
||||
|
||||
@classmethod
|
||||
def tobeamer(cls):
|
||||
u"""Export the current buffer as beamer pdf using emacs orgmode."""
|
||||
ret = cls._export(u'org-beamer-export-to-pdf')
|
||||
if ret != 0:
|
||||
echoe(u'PDF export failed.')
|
||||
else:
|
||||
echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'pdf'))
|
||||
|
||||
@classmethod
|
||||
def tohtml(cls):
|
||||
u"""Export the current buffer as html using emacs orgmode."""
|
||||
ret = cls._export(u'org-html-export-to-html')
|
||||
if ret != 0:
|
||||
echoe(u'HTML export failed.')
|
||||
else:
|
||||
echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'html'))
|
||||
|
||||
@classmethod
|
||||
def tolatex(cls):
|
||||
u"""Export the current buffer as latex using emacs orgmode."""
|
||||
ret = cls._export(u'org-latex-export-to-latex')
|
||||
if ret != 0:
|
||||
echoe(u'latex export failed.')
|
||||
else:
|
||||
echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'tex'))
|
||||
|
||||
@classmethod
|
||||
def tomarkdown(cls):
|
||||
u"""Export the current buffer as markdown using emacs orgmode."""
|
||||
ret = cls._export(u'org-md-export-to-markdown')
|
||||
if ret != 0:
|
||||
echoe('Markdown export failed. Make sure org-md-export-to-markdown is loaded in emacs, see the manual for details.')
|
||||
else:
|
||||
echom(u'Export successful: %s.%s' % (vim.eval(u'expand("%:r")'), 'md'))
|
||||
|
||||
def register(self):
|
||||
u"""Registration and keybindings."""
|
||||
|
||||
# path to emacs executable
|
||||
settings.set(u'org_export_emacs', u'/usr/bin/emacs')
|
||||
# verbose output for export
|
||||
settings.set(u'org_export_verbose', 0)
|
||||
# allow the user to define an initialization script
|
||||
settings.set(u'org_export_init_script', u'')
|
||||
|
||||
# to PDF
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u'OrgExportToPDF',
|
||||
function=u':%s ORGMODE.plugins[u"Export"].topdf()<CR>' % VIM_PY_CALL,
|
||||
key_mapping=u'<localleader>ep',
|
||||
menu_desrc=u'To PDF (via Emacs)'
|
||||
)
|
||||
# to Beamer PDF
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u'OrgExportToBeamerPDF',
|
||||
function=u':%s ORGMODE.plugins[u"Export"].tobeamer()<CR>' % VIM_PY_CALL,
|
||||
key_mapping=u'<localleader>eb',
|
||||
menu_desrc=u'To Beamer PDF (via Emacs)'
|
||||
)
|
||||
# to latex
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u'OrgExportToLaTeX',
|
||||
function=u':%s ORGMODE.plugins[u"Export"].tolatex()<CR>' % VIM_PY_CALL,
|
||||
key_mapping=u'<localleader>el',
|
||||
menu_desrc=u'To LaTeX (via Emacs)'
|
||||
)
|
||||
# to HTML
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u'OrgExportToHTML',
|
||||
function=u':%s ORGMODE.plugins[u"Export"].tohtml()<CR>' % VIM_PY_CALL,
|
||||
key_mapping=u'<localleader>eh',
|
||||
menu_desrc=u'To HTML (via Emacs)'
|
||||
)
|
||||
# to Markdown
|
||||
add_cmd_mapping_menu(
|
||||
self,
|
||||
name=u'OrgExportToMarkdown',
|
||||
function=u':%s ORGMODE.plugins[u"Export"].tomarkdown()<CR>' % VIM_PY_CALL,
|
||||
key_mapping=u'<localleader>em',
|
||||
menu_desrc=u'To Markdown (via Emacs)'
|
||||
)
|
||||
@@ -0,0 +1,219 @@
|
||||
# -*- 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])
|
||||
@@ -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])
|
||||
171
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/Misc.py
Normal file
171
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/Misc.py
Normal file
@@ -0,0 +1,171 @@
|
||||
# -*- 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))
|
||||
@@ -0,0 +1,324 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import vim
|
||||
|
||||
from orgmode._vim import echo, ORGMODE, apply_count
|
||||
from orgmode.menu import Submenu, ActionEntry
|
||||
from orgmode.keybinding import Keybinding, MODE_VISUAL, MODE_OPERATOR, Plug
|
||||
from orgmode.liborgmode.documents import Direction
|
||||
|
||||
from orgmode.py3compat.encode_compatibility import *
|
||||
from orgmode.py3compat.py_py3_string import *
|
||||
|
||||
class Navigator(object):
|
||||
u""" Implement navigation in org-mode documents """
|
||||
|
||||
def __init__(self):
|
||||
object.__init__(self)
|
||||
self.menu = ORGMODE.orgmenu + Submenu(u'&Navigate Headings')
|
||||
self.keybindings = []
|
||||
|
||||
@classmethod
|
||||
@apply_count
|
||||
def parent(cls, mode):
|
||||
u"""
|
||||
Focus parent heading
|
||||
|
||||
:returns: parent heading or None
|
||||
"""
|
||||
heading = ORGMODE.get_document().current_heading()
|
||||
if not heading:
|
||||
if mode == u'visual':
|
||||
vim.command(u_encode(u'normal! gv'))
|
||||
else:
|
||||
echo(u'No heading found')
|
||||
return
|
||||
|
||||
if not heading.parent:
|
||||
if mode == u'visual':
|
||||
vim.command(u_encode(u'normal! gv'))
|
||||
else:
|
||||
echo(u'No parent heading found')
|
||||
return
|
||||
|
||||
p = heading.parent
|
||||
|
||||
if mode == u'visual':
|
||||
cls._change_visual_selection(heading, p, direction=Direction.BACKWARD, parent=True)
|
||||
else:
|
||||
vim.current.window.cursor = (p.start_vim, p.level + 1)
|
||||
return p
|
||||
|
||||
@classmethod
|
||||
@apply_count
|
||||
def parent_next_sibling(cls, mode):
|
||||
u"""
|
||||
Focus the parent's next sibling
|
||||
|
||||
:returns: parent's next sibling heading or None
|
||||
"""
|
||||
heading = ORGMODE.get_document().current_heading()
|
||||
if not heading:
|
||||
if mode == u'visual':
|
||||
vim.command(u_encode(u'normal! gv'))
|
||||
else:
|
||||
echo(u'No heading found')
|
||||
return
|
||||
|
||||
if not heading.parent or not heading.parent.next_sibling:
|
||||
if mode == u'visual':
|
||||
vim.command(u_encode(u'normal! gv'))
|
||||
else:
|
||||
echo(u'No parent heading found')
|
||||
return
|
||||
|
||||
ns = heading.parent.next_sibling
|
||||
|
||||
if mode == u'visual':
|
||||
cls._change_visual_selection(heading, ns, direction=Direction.FORWARD, parent=False)
|
||||
elif mode == u'operator':
|
||||
vim.current.window.cursor = (ns.start_vim, 0)
|
||||
else:
|
||||
vim.current.window.cursor = (ns.start_vim, ns.level + 1)
|
||||
return ns
|
||||
|
||||
@classmethod
|
||||
def _change_visual_selection(cls, current_heading, heading, direction=Direction.FORWARD, noheadingfound=False, parent=False):
|
||||
current = vim.current.window.cursor[0]
|
||||
line_start, col_start = [int(i) for i in vim.eval(u_encode(u'getpos("\'<")'))[1:3]]
|
||||
line_end, col_end = [int(i) for i in vim.eval(u_encode(u'getpos("\'>")'))[1:3]]
|
||||
|
||||
f_start = heading.start_vim
|
||||
f_end = heading.end_vim
|
||||
swap_cursor = True
|
||||
|
||||
# << |visual start
|
||||
# selection end >>
|
||||
if current == line_start:
|
||||
if (direction == Direction.FORWARD and line_end < f_start) or noheadingfound and not direction == Direction.BACKWARD:
|
||||
swap_cursor = False
|
||||
|
||||
# focus heading HERE
|
||||
# << |visual start
|
||||
# selection end >>
|
||||
|
||||
# << |visual start
|
||||
# focus heading HERE
|
||||
# selection end >>
|
||||
if f_start < line_start and direction == Direction.BACKWARD:
|
||||
if current_heading.start_vim < line_start and not parent:
|
||||
line_start = current_heading.start_vim
|
||||
else:
|
||||
line_start = f_start
|
||||
|
||||
elif (f_start < line_start or f_start < line_end) and not noheadingfound:
|
||||
line_start = f_start
|
||||
|
||||
# << |visual start
|
||||
# selection end >>
|
||||
# focus heading HERE
|
||||
else:
|
||||
if direction == Direction.FORWARD:
|
||||
if line_end < f_start and not line_start == f_start - 1 and current_heading:
|
||||
# focus end of previous heading instead of beginning of next heading
|
||||
line_start = line_end
|
||||
line_end = f_start - 1
|
||||
else:
|
||||
# focus end of next heading
|
||||
line_start = line_end
|
||||
line_end = f_end
|
||||
elif direction == Direction.BACKWARD:
|
||||
if line_end < f_end:
|
||||
pass
|
||||
else:
|
||||
line_start = line_end
|
||||
line_end = f_end
|
||||
|
||||
# << visual start
|
||||
# selection end| >>
|
||||
else:
|
||||
# focus heading HERE
|
||||
# << visual start
|
||||
# selection end| >>
|
||||
if line_start > f_start and line_end > f_end and not parent:
|
||||
line_end = f_end
|
||||
swap_cursor = False
|
||||
|
||||
elif (line_start > f_start or line_start == f_start) and \
|
||||
line_end <= f_end and direction == Direction.BACKWARD:
|
||||
line_end = line_start
|
||||
line_start = f_start
|
||||
|
||||
# << visual start
|
||||
# selection end and focus heading end HERE| >>
|
||||
|
||||
# << visual start
|
||||
# focus heading HERE
|
||||
# selection end| >>
|
||||
|
||||
# << visual start
|
||||
# selection end| >>
|
||||
# focus heading HERE
|
||||
else:
|
||||
if direction == Direction.FORWARD:
|
||||
if line_end < f_start - 1:
|
||||
# focus end of previous heading instead of beginning of next heading
|
||||
line_end = f_start - 1
|
||||
else:
|
||||
# focus end of next heading
|
||||
line_end = f_end
|
||||
else:
|
||||
line_end = f_end
|
||||
swap_cursor = False
|
||||
|
||||
move_col_start = u'%dl' % (col_start - 1) if (col_start - 1) > 0 and (col_start - 1) < 2000000000 else u''
|
||||
move_col_end = u'%dl' % (col_end - 1) if (col_end - 1) > 0 and (col_end - 1) < 2000000000 else u''
|
||||
swap = u'o' if swap_cursor else u''
|
||||
|
||||
vim.command(u_encode(u'normal! %dgg%s%s%dgg%s%s' % (line_start, move_col_start, vim.eval(u_encode(u'visualmode()')), line_end, move_col_end, swap)))
|
||||
|
||||
@classmethod
|
||||
def _focus_heading(cls, mode, direction=Direction.FORWARD, skip_children=False):
|
||||
u"""
|
||||
Focus next or previous heading in the given direction
|
||||
|
||||
:direction: True for next heading, False for previous heading
|
||||
:returns: next heading or None
|
||||
"""
|
||||
d = ORGMODE.get_document()
|
||||
current_heading = d.current_heading()
|
||||
heading = current_heading
|
||||
focus_heading = None
|
||||
# FIXME this is just a piece of really ugly and unmaintainable code. It
|
||||
# should be rewritten
|
||||
if not heading:
|
||||
if direction == Direction.FORWARD and d.headings \
|
||||
and vim.current.window.cursor[0] < d.headings[0].start_vim:
|
||||
# the cursor is in the meta information are, therefore focus
|
||||
# first heading
|
||||
focus_heading = d.headings[0]
|
||||
if not (heading or focus_heading):
|
||||
if mode == u'visual':
|
||||
# restore visual selection when no heading was found
|
||||
vim.command(u_encode(u'normal! gv'))
|
||||
else:
|
||||
echo(u'No heading found')
|
||||
return
|
||||
elif direction == Direction.BACKWARD:
|
||||
if vim.current.window.cursor[0] != heading.start_vim:
|
||||
# the cursor is in the body of the current heading, therefore
|
||||
# the current heading will be focused
|
||||
if mode == u'visual':
|
||||
line_start, col_start = [int(i) for i in
|
||||
vim.eval(u_encode(u'getpos("\'<")'))[1:3]]
|
||||
line_end, col_end = [int(i) for i in vim.eval(u_encode(u'getpos("\'>")'))[1:3]]
|
||||
if line_start >= heading.start_vim and line_end > heading.start_vim:
|
||||
focus_heading = heading
|
||||
else:
|
||||
focus_heading = heading
|
||||
|
||||
# so far no heading has been found that the next focus should be on
|
||||
if not focus_heading:
|
||||
if not skip_children and direction == Direction.FORWARD and heading.children:
|
||||
focus_heading = heading.children[0]
|
||||
elif direction == Direction.FORWARD and heading.next_sibling:
|
||||
focus_heading = heading.next_sibling
|
||||
elif direction == Direction.BACKWARD and heading.previous_sibling:
|
||||
focus_heading = heading.previous_sibling
|
||||
if not skip_children:
|
||||
while focus_heading.children:
|
||||
focus_heading = focus_heading.children[-1]
|
||||
else:
|
||||
if direction == Direction.FORWARD:
|
||||
focus_heading = current_heading.next_heading
|
||||
else:
|
||||
focus_heading = current_heading.previous_heading
|
||||
|
||||
noheadingfound = False
|
||||
if not focus_heading:
|
||||
if mode in (u'visual', u'operator'):
|
||||
# the cursor seems to be on the last or first heading of this
|
||||
# document and performs another next/previous operation
|
||||
focus_heading = heading
|
||||
noheadingfound = True
|
||||
else:
|
||||
if direction == Direction.FORWARD:
|
||||
echo(u'Already focussing last heading')
|
||||
else:
|
||||
echo(u'Already focussing first heading')
|
||||
return
|
||||
|
||||
if mode == u'visual':
|
||||
cls._change_visual_selection(current_heading, focus_heading, direction=direction, noheadingfound=noheadingfound)
|
||||
elif mode == u'operator':
|
||||
if direction == Direction.FORWARD and vim.current.window.cursor[0] >= focus_heading.start_vim:
|
||||
vim.current.window.cursor = (focus_heading.end_vim, len(u_decode(vim.current.buffer[focus_heading.end])))
|
||||
else:
|
||||
vim.current.window.cursor = (focus_heading.start_vim, 0)
|
||||
else:
|
||||
vim.current.window.cursor = (focus_heading.start_vim, focus_heading.level + 1)
|
||||
if noheadingfound:
|
||||
return
|
||||
return focus_heading
|
||||
|
||||
@classmethod
|
||||
@apply_count
|
||||
def previous(cls, mode, skip_children=False):
|
||||
u"""
|
||||
Focus previous heading
|
||||
"""
|
||||
return cls._focus_heading(mode, direction=Direction.BACKWARD, skip_children=skip_children)
|
||||
|
||||
@classmethod
|
||||
@apply_count
|
||||
def next(cls, mode, skip_children=False):
|
||||
u"""
|
||||
Focus next heading
|
||||
"""
|
||||
return cls._focus_heading(mode, direction=Direction.FORWARD, skip_children=skip_children)
|
||||
|
||||
def register(self):
|
||||
# normal mode
|
||||
self.keybindings.append(Keybinding(u'g{', Plug('OrgJumpToParentNormal',
|
||||
u'%s ORGMODE.plugins[u"Navigator"].parent(mode=u"normal")<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'&Up', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u'g}',
|
||||
Plug('OrgJumpToParentsSiblingNormal', u'%s ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"normal")<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'&Down', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u'{',
|
||||
Plug(u'OrgJumpToPreviousNormal', u'%s ORGMODE.plugins[u"Navigator"].previous(mode=u"normal")<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'&Previous', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextNormal',
|
||||
u'%s ORGMODE.plugins[u"Navigator"].next(mode=u"normal")<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'&Next', self.keybindings[-1])
|
||||
|
||||
# visual mode
|
||||
self.keybindings.append(Keybinding(u'g{', Plug(u'OrgJumpToParentVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].parent(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
|
||||
self.keybindings.append(Keybinding(u'g}', Plug('OrgJumpToParentsSiblingVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
|
||||
self.keybindings.append(Keybinding(u'{', Plug(u'OrgJumpToPreviousVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
|
||||
self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"visual")<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
|
||||
|
||||
# operator-pending mode
|
||||
self.keybindings.append(Keybinding(u'g{', Plug(u'OrgJumpToParentOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].parent(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
|
||||
self.keybindings.append(Keybinding(u'g}', Plug('OrgJumpToParentsSiblingOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].parent_next_sibling(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
|
||||
self.keybindings.append(Keybinding(u'{', Plug(u'OrgJumpToPreviousOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
|
||||
self.keybindings.append(Keybinding(u'}', Plug(u'OrgJumpToNextOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"operator")<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
|
||||
|
||||
# section wise movement (skip children)
|
||||
# normal mode
|
||||
self.keybindings.append(Keybinding(u'[[',
|
||||
Plug(u'OrgJumpToPreviousSkipChildrenNormal',
|
||||
u'%s ORGMODE.plugins[u"Navigator"].previous(mode=u"normal", skip_children=True)<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'Ne&xt Same Level', self.keybindings[-1])
|
||||
self.keybindings.append(Keybinding(u']]',
|
||||
Plug(u'OrgJumpToNextSkipChildrenNormal',
|
||||
u'%s ORGMODE.plugins[u"Navigator"].next(mode=u"normal", skip_children=True)<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'Pre&vious Same Level', self.keybindings[-1])
|
||||
|
||||
# visual mode
|
||||
self.keybindings.append(Keybinding(u'[[', Plug(u'OrgJumpToPreviousSkipChildrenVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"visual", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
|
||||
self.keybindings.append(Keybinding(u']]', Plug(u'OrgJumpToNextSkipChildrenVisual', u'<Esc>:<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"visual", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_VISUAL)))
|
||||
|
||||
# operator-pending mode
|
||||
self.keybindings.append(Keybinding(u'[[', Plug(u'OrgJumpToPreviousSkipChildrenOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].previous(mode=u"operator", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
|
||||
self.keybindings.append(Keybinding(u']]', Plug(u'OrgJumpToNextSkipChildrenOperator', u':<C-u>%s ORGMODE.plugins[u"Navigator"].next(mode=u"operator", skip_children=True)<CR>' % VIM_PY_CALL, mode=MODE_OPERATOR)))
|
||||
181
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/ShowHide.py
Normal file
181
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/ShowHide.py
Normal 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 because the former closing of the fold closed all levels, including parents!
|
||||
vim.command(u_encode(u'normal! %dzo' % (p, )))
|
||||
else:
|
||||
# close the last level of folds
|
||||
close_fold(heading)
|
||||
|
||||
# restore cursor position
|
||||
vim.current.window.cursor = cursor
|
||||
return heading
|
||||
|
||||
@classmethod
|
||||
@apply_count
|
||||
def global_toggle_folding(cls, reverse=False):
|
||||
""" Toggle folding globally
|
||||
|
||||
:reverse: If False open folding by one level otherwise close it by one.
|
||||
"""
|
||||
d = ORGMODE.get_document()
|
||||
if reverse:
|
||||
foldlevel = int(vim.eval(u_encode(u'&foldlevel')))
|
||||
if foldlevel == 0:
|
||||
# open all folds because the user tries to close folds beyond 0
|
||||
vim.eval(u_encode(u'feedkeys("zR", "n")'))
|
||||
else:
|
||||
# vim can reduce the foldlevel on its own
|
||||
vim.eval(u_encode(u'feedkeys("zm", "n")'))
|
||||
else:
|
||||
found = False
|
||||
for h in d.headings:
|
||||
res = cls._fold_depth(h)
|
||||
if res:
|
||||
found = res[1]
|
||||
if found:
|
||||
break
|
||||
if not found:
|
||||
# no fold found and the user tries to advance the fold level
|
||||
# beyond maximum so close everything
|
||||
vim.eval(u_encode(u'feedkeys("zM", "n")'))
|
||||
else:
|
||||
# fold found, vim can increase the foldlevel on its own
|
||||
vim.eval(u_encode(u'feedkeys("zr", "n")'))
|
||||
|
||||
return d
|
||||
|
||||
def register(self):
|
||||
u"""
|
||||
Registration of plugin. Key bindings and other initialization should be done.
|
||||
"""
|
||||
# register plug
|
||||
|
||||
self.keybindings.append(Keybinding(u'<Tab>',
|
||||
Plug(u'OrgToggleFoldingNormal', u'%s ORGMODE.plugins[u"ShowHide"].toggle_folding()<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'&Cycle Visibility', self.keybindings[-1])
|
||||
|
||||
self.keybindings.append(Keybinding(u'<S-Tab>',
|
||||
Plug(u'OrgToggleFoldingReverse', u'%s ORGMODE.plugins[u"ShowHide"].toggle_folding(reverse=True)<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'Cycle Visibility &Reverse', self.keybindings[-1])
|
||||
|
||||
self.keybindings.append(Keybinding(u'<localleader>.',
|
||||
Plug(u'OrgGlobalToggleFoldingNormal', u'%s ORGMODE.plugins[u"ShowHide"].global_toggle_folding()<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'Cycle Visibility &Globally', self.keybindings[-1])
|
||||
|
||||
self.keybindings.append(Keybinding(u'<localleader>,',
|
||||
Plug(u'OrgGlobalToggleFoldingReverse',
|
||||
u'%s ORGMODE.plugins[u"ShowHide"].global_toggle_folding(reverse=True)<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'Cycle Visibility Reverse G&lobally', self.keybindings[-1])
|
||||
|
||||
for i in range(0, 10):
|
||||
self.keybindings.append(Keybinding(u'<localleader>%d' % (i, ), u'zM:set fdl=%d<CR>' % i, mode=MODE_NORMAL))
|
||||
@@ -0,0 +1,213 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import vim
|
||||
|
||||
from orgmode._vim import ORGMODE, repeat
|
||||
from orgmode.menu import Submenu, ActionEntry
|
||||
from orgmode.keybinding import Keybinding, Plug, Command
|
||||
from orgmode import settings
|
||||
|
||||
from orgmode.py3compat.encode_compatibility import *
|
||||
from orgmode.py3compat.py_py3_string import *
|
||||
|
||||
class TagsProperties(object):
|
||||
u""" TagsProperties plugin """
|
||||
|
||||
def __init__(self):
|
||||
u""" Initialize plugin """
|
||||
object.__init__(self)
|
||||
# menu entries this plugin should create
|
||||
self.menu = ORGMODE.orgmenu + Submenu(u'&TAGS and Properties')
|
||||
|
||||
# key bindings for this plugin
|
||||
# key bindings are also registered through the menu so only additional
|
||||
# bindings should be put in this variable
|
||||
self.keybindings = []
|
||||
|
||||
# commands for this plugin
|
||||
self.commands = []
|
||||
|
||||
@classmethod
|
||||
def complete_tags(cls):
|
||||
u""" build a list of tags and store it in variable b:org_tag_completion
|
||||
"""
|
||||
d = ORGMODE.get_document()
|
||||
heading = d.current_heading()
|
||||
if not heading:
|
||||
return
|
||||
|
||||
leading_portion = u_decode(vim.eval(u'a:ArgLead'))
|
||||
cursor = int(vim.eval(u'a:CursorPos'))
|
||||
|
||||
# extract currently completed tag
|
||||
idx_orig = leading_portion.rfind(u':', 0, cursor)
|
||||
if idx_orig == -1:
|
||||
idx = 0
|
||||
else:
|
||||
idx = idx_orig
|
||||
|
||||
current_tag = leading_portion[idx: cursor].lstrip(u':')
|
||||
head = leading_portion[:idx + 1]
|
||||
if idx_orig == -1:
|
||||
head = u''
|
||||
tail = leading_portion[cursor:]
|
||||
|
||||
# extract all tags of the current file
|
||||
all_tags = set()
|
||||
for h in d.all_headings():
|
||||
for t in h.tags:
|
||||
all_tags.add(t)
|
||||
|
||||
ignorecase = bool(int(settings.get(u'org_tag_completion_ignorecase', int(vim.eval(u'&ignorecase')))))
|
||||
possible_tags = []
|
||||
# TODO current tags never used...
|
||||
current_tags = heading.tags
|
||||
for t in all_tags:
|
||||
if ignorecase:
|
||||
if t.lower().startswith(current_tag.lower()):
|
||||
possible_tags.append(t)
|
||||
elif t.startswith(current_tag):
|
||||
possible_tags.append(t)
|
||||
|
||||
vim.command(u_encode(u'let b:org_complete_tags = [%s]' % u', '.join([u'"%s%s:%s"' % (head, i, tail) for i in possible_tags])))
|
||||
|
||||
@classmethod
|
||||
@repeat
|
||||
def set_tags(cls):
|
||||
u""" Set tags for current heading
|
||||
"""
|
||||
d = ORGMODE.get_document()
|
||||
heading = d.current_heading()
|
||||
if not heading:
|
||||
return
|
||||
|
||||
# retrieve tags
|
||||
res = None
|
||||
if heading.tags:
|
||||
res = vim.eval(u'input("Tags: ", ":%s:", "customlist,Org_complete_tags")' % u':'.join(heading.tags))
|
||||
else:
|
||||
res = vim.eval(u'input("Tags: ", "", "customlist,Org_complete_tags")')
|
||||
|
||||
if res is None:
|
||||
# user pressed <Esc> abort any further processing
|
||||
return
|
||||
|
||||
# remove empty tags
|
||||
heading.tags = [x for x in u_decode(res).strip().strip(u':').split(u':') if x.strip() != u'']
|
||||
|
||||
d.write()
|
||||
|
||||
return u'OrgSetTags'
|
||||
|
||||
@classmethod
|
||||
def find_tags(cls):
|
||||
""" Find tags in current file
|
||||
"""
|
||||
tags = vim.eval(u'input("Find Tags: ", "", "customlist,Org_complete_tags")')
|
||||
if tags is None:
|
||||
# user pressed <Esc> abort any further processing
|
||||
return
|
||||
|
||||
tags = [x for x in u_decode(tags).strip().strip(u':').split(u':') if x.strip() != u'']
|
||||
if tags:
|
||||
searchstring = u'\\('
|
||||
first = True
|
||||
for t1 in tags:
|
||||
if first:
|
||||
first = False
|
||||
searchstring += u'%s' % t1
|
||||
else:
|
||||
searchstring += u'\\|%s' % t1
|
||||
|
||||
for t2 in tags:
|
||||
if t1 == t2:
|
||||
continue
|
||||
searchstring += u'\\(:[a-zA-Z:]*\\)\\?:%s' % t2
|
||||
searchstring += u'\\)'
|
||||
|
||||
vim.command(u'/\\zs:%s:\\ze' % searchstring)
|
||||
return u'OrgFindTags'
|
||||
|
||||
@classmethod
|
||||
def realign_tags(cls):
|
||||
u"""
|
||||
Updates tags when user finished editing a heading
|
||||
"""
|
||||
d = ORGMODE.get_document(allow_dirty=True)
|
||||
heading = d.find_current_heading()
|
||||
if not heading:
|
||||
return
|
||||
|
||||
if vim.current.window.cursor[0] == heading.start_vim:
|
||||
heading.set_dirty_heading()
|
||||
d.write_heading(heading, including_children=False)
|
||||
|
||||
@classmethod
|
||||
def realign_all_tags(cls):
|
||||
u"""
|
||||
Updates tags when user finishes editing a heading
|
||||
"""
|
||||
d = ORGMODE.get_document()
|
||||
for heading in d.all_headings():
|
||||
heading.set_dirty_heading()
|
||||
|
||||
d.write()
|
||||
|
||||
def register(self):
|
||||
u"""
|
||||
Registration of plugin. Key bindings and other initialization should be done.
|
||||
"""
|
||||
# an Action menu entry which binds "keybinding" to action ":action"
|
||||
settings.set(u'org_tag_column', vim.eval(u'&textwidth'))
|
||||
settings.set(u'org_tag_completion_ignorecase', int(vim.eval(u'&ignorecase')))
|
||||
|
||||
cmd = Command(
|
||||
u'OrgSetTags',
|
||||
u'%s ORGMODE.plugins[u"TagsProperties"].set_tags()' % VIM_PY_CALL)
|
||||
self.commands.append(cmd)
|
||||
keybinding = Keybinding(
|
||||
u'<localleader>st',
|
||||
Plug(u'OrgSetTags', cmd))
|
||||
self.keybindings.append(keybinding)
|
||||
self.menu + ActionEntry(u'Set &Tags', keybinding)
|
||||
|
||||
cmd = Command(
|
||||
u'OrgFindTags',
|
||||
u'%s ORGMODE.plugins[u"TagsProperties"].find_tags()' % VIM_PY_CALL)
|
||||
self.commands.append(cmd)
|
||||
keybinding = Keybinding(
|
||||
u'<localleader>ft',
|
||||
Plug(u'OrgFindTags', cmd))
|
||||
self.keybindings.append(keybinding)
|
||||
self.menu + ActionEntry(u'&Find Tags', keybinding)
|
||||
|
||||
cmd = Command(
|
||||
u'OrgTagsRealign',
|
||||
u"%s ORGMODE.plugins[u'TagsProperties'].realign_all_tags()" % VIM_PY_CALL)
|
||||
self.commands.append(cmd)
|
||||
|
||||
# workaround to align tags when user is leaving insert mode
|
||||
vim.command(u_encode(u"function Org_complete_tags(ArgLead, CmdLine, CursorPos)\n"
|
||||
+ sys.executable.split('/')[-1] + u""" << EOF
|
||||
ORGMODE.plugins[u'TagsProperties'].complete_tags()
|
||||
EOF
|
||||
if exists('b:org_complete_tags')
|
||||
let tmp = b:org_complete_tags
|
||||
unlet b:org_complete_tags
|
||||
return tmp
|
||||
else
|
||||
return []
|
||||
endif
|
||||
endfunction"""))
|
||||
|
||||
vim.command(u_encode(u"""function Org_realign_tags_on_insert_leave()
|
||||
if !exists('b:org_complete_tag_on_insertleave_au')
|
||||
:au orgmode InsertLeave <buffer> %s ORGMODE.plugins[u'TagsProperties'].realign_tags()
|
||||
let b:org_complete_tag_on_insertleave_au = 1
|
||||
endif
|
||||
endfunction""" % VIM_PY_CALL))
|
||||
|
||||
# this is for all org files opened after this file
|
||||
vim.command(u_encode(u"au orgmode FileType org call Org_realign_tags_on_insert_leave()"))
|
||||
# this is for the current file
|
||||
vim.command(u_encode(u"call Org_realign_tags_on_insert_leave()"))
|
||||
344
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/Todo.py
Normal file
344
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/plugins/Todo.py
Normal file
@@ -0,0 +1,344 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import vim
|
||||
import re
|
||||
import itertools as it
|
||||
|
||||
from orgmode._vim import echom, ORGMODE, apply_count, repeat, realign_tags
|
||||
from orgmode import settings
|
||||
from orgmode.liborgmode.base import Direction
|
||||
from orgmode.menu import Submenu, ActionEntry
|
||||
from orgmode.keybinding import Keybinding, Plug
|
||||
from orgmode.exceptions import PluginError
|
||||
|
||||
# temporary todo states for different orgmode buffers
|
||||
ORGTODOSTATES = {}
|
||||
|
||||
from orgmode.py3compat.xrange_compatibility import *
|
||||
from orgmode.py3compat.encode_compatibility import *
|
||||
from orgmode.py3compat.unicode_compatibility import *
|
||||
from orgmode.py3compat.py_py3_string import *
|
||||
|
||||
|
||||
def split_access_key(t, sub=None):
|
||||
u""" Split access key
|
||||
|
||||
Args:
|
||||
t (str): Todo state
|
||||
sub: A value that will be returned instead of access key if there was
|
||||
not access key
|
||||
|
||||
Returns:
|
||||
tuple: Todo state and access key separated (TODO, ACCESS_KEY)
|
||||
|
||||
Example:
|
||||
>>> split_access_key('TODO(t)')
|
||||
>>> ('TODO', '(t)')
|
||||
>>> split_access_key('WANT', sub='(hi)')
|
||||
>>> ('WANT', '(hi)')
|
||||
"""
|
||||
if type(t) != unicode:
|
||||
echom("String must be unicode")
|
||||
return (None, None)
|
||||
|
||||
idx = t.find(u'(')
|
||||
|
||||
v, k = (t, sub)
|
||||
if idx != -1 and t[idx + 1:-1]:
|
||||
v, k = (t[:idx], t[idx + 1:-1])
|
||||
return (v, k)
|
||||
|
||||
|
||||
class Todo(object):
|
||||
u"""
|
||||
Todo plugin.
|
||||
|
||||
Description taken from orgmode.org:
|
||||
|
||||
You can use TODO keywords to indicate different sequential states in the
|
||||
process of working on an item, for example:
|
||||
|
||||
["TODO", "FEEDBACK", "VERIFY", "|", "DONE", "DELEGATED"]
|
||||
|
||||
The vertical bar separates the TODO keywords (states that need action) from
|
||||
the DONE states (which need no further action). If you don't provide the
|
||||
separator bar, the last state is used as the DONE state. With this setup,
|
||||
the command ``,d`` will cycle an entry from TODO to FEEDBACK, then to
|
||||
VERIFY, and finally to DONE and DELEGATED.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
u""" Initialize plugin """
|
||||
object.__init__(self)
|
||||
# menu entries this plugin should create
|
||||
self.menu = ORGMODE.orgmenu + Submenu(u'&TODO Lists')
|
||||
|
||||
# key bindings for this plugin
|
||||
# key bindings are also registered through the menu so only additional
|
||||
# bindings should be put in this variable
|
||||
self.keybindings = []
|
||||
|
||||
@classmethod
|
||||
def _process_all_states(cls, all_states):
|
||||
u""" verify if states defined by user is valid.
|
||||
Return cleaned_todo and flattened if is. Raise Exception if not.
|
||||
Valid checking:
|
||||
* no two state share a same name
|
||||
"""
|
||||
# TODO Write tests. -- Ron89
|
||||
cleaned_todos = [[
|
||||
split_access_key(todo)[0] for todo in it.chain.from_iterable(x)]
|
||||
for x in all_states] + [[None]]
|
||||
|
||||
flattened_todos = list(it.chain.from_iterable(cleaned_todos))
|
||||
if len(flattened_todos) != len(set(flattened_todos)):
|
||||
raise PluginError(u"Duplicate names detected in TODO keyword list. Please examine `g/b:org_todo_keywords`")
|
||||
# TODO This is the case when there are 2 todo states with the same
|
||||
# name. It should be handled by making a simple class to hold TODO
|
||||
# states, which would avoid mixing 2 todo states with the same name
|
||||
# since they would have a different reference (but same content),
|
||||
# albeit this can fail because python optimizes short strings (i.e.
|
||||
# they hold the same ref) so care should be taken in implementation
|
||||
return (cleaned_todos, flattened_todos)
|
||||
|
||||
@classmethod
|
||||
def _get_next_state(
|
||||
cls, current_state, all_states, direction=Direction.FORWARD,
|
||||
next_set=False):
|
||||
u""" Get the next todo state
|
||||
|
||||
Args:
|
||||
current_state (str): The current todo state
|
||||
all_states (list): A list containing all todo states within
|
||||
sublists. The todo states may contain access keys
|
||||
direction: Direction of state or keyword set change (forward or
|
||||
backward)
|
||||
next_set: Advance to the next keyword set in defined direction.
|
||||
|
||||
Returns:
|
||||
str or None: next todo state, or None if there is no next state.
|
||||
|
||||
Note: all_states should have the form of:
|
||||
[(['TODO(t)'], ['DONE(d)']),
|
||||
(['REPORT(r)', 'BUG(b)', 'KNOWNCAUSE(k)'], ['FIXED(f)']),
|
||||
([], ['CANCELED(c)'])]
|
||||
"""
|
||||
cleaned_todos, flattened_todos = cls._process_all_states(all_states)
|
||||
|
||||
# backward direction should really be -1 not 2
|
||||
next_dir = -1 if direction == Direction.BACKWARD else 1
|
||||
# work only with top level index
|
||||
if next_set:
|
||||
top_set = next((
|
||||
todo_set[0] for todo_set in enumerate(cleaned_todos)
|
||||
if current_state in todo_set[1]), -1)
|
||||
ind = (top_set + next_dir) % len(cleaned_todos)
|
||||
if ind != len(cleaned_todos) - 1:
|
||||
echom("Using set: %s" % str(all_states[ind]))
|
||||
else:
|
||||
echom("Keyword removed.")
|
||||
return cleaned_todos[ind][0]
|
||||
# No next set, cycle around everything
|
||||
else:
|
||||
ind = next((
|
||||
todo_iter[0] for todo_iter in enumerate(flattened_todos)
|
||||
if todo_iter[1] == current_state), -1)
|
||||
return flattened_todos[(ind + next_dir) % len(flattened_todos)]
|
||||
|
||||
@classmethod
|
||||
@realign_tags
|
||||
@repeat
|
||||
@apply_count
|
||||
def toggle_todo_state(
|
||||
cls, direction=Direction.FORWARD, interactive=False, next_set=False):
|
||||
u""" Toggle state of TODO item
|
||||
|
||||
:returns: The changed heading
|
||||
"""
|
||||
d = ORGMODE.get_document(allow_dirty=True)
|
||||
|
||||
# get heading
|
||||
heading = d.find_current_heading()
|
||||
if not heading:
|
||||
vim.eval(u'feedkeys("^", "n")')
|
||||
return
|
||||
|
||||
todo_states = d.get_todo_states(strip_access_key=False)
|
||||
# get todo states
|
||||
if not todo_states:
|
||||
echom(u'No todo keywords configured.')
|
||||
return
|
||||
|
||||
current_state = heading.todo
|
||||
|
||||
# get new state interactively
|
||||
if interactive:
|
||||
# determine position of the interactive prompt
|
||||
prompt_pos = settings.get(u'org_todo_prompt_position', u'botright')
|
||||
if prompt_pos not in [u'botright', u'topleft']:
|
||||
prompt_pos = u'botright'
|
||||
|
||||
# pass todo states to new window
|
||||
ORGTODOSTATES[d.bufnr] = todo_states
|
||||
settings.set(
|
||||
u'org_current_state_%d' % d.bufnr,
|
||||
current_state if current_state is not None else u'', overwrite=True)
|
||||
todo_buffer_exists = bool(int(vim.eval(u_encode(
|
||||
u'bufexists("org:todo/%d")' % (d.bufnr, )))))
|
||||
if todo_buffer_exists:
|
||||
# if the buffer already exists, reuse it
|
||||
vim.command(u_encode(
|
||||
u'%s sbuffer org:todo/%d' % (prompt_pos, d.bufnr, )))
|
||||
else:
|
||||
# create a new window
|
||||
vim.command(u_encode(
|
||||
u'keepalt %s %dsplit org:todo/%d' % (prompt_pos, len(todo_states), d.bufnr)))
|
||||
else:
|
||||
new_state = Todo._get_next_state(
|
||||
current_state, todo_states, direction=direction,
|
||||
next_set=next_set)
|
||||
|
||||
cls.set_todo_state(new_state)
|
||||
|
||||
# plug
|
||||
plug = u'OrgTodoForward'
|
||||
if direction == Direction.BACKWARD:
|
||||
plug = u'OrgTodoBackward'
|
||||
|
||||
return plug
|
||||
|
||||
@classmethod
|
||||
def set_todo_state(cls, state):
|
||||
u""" Set todo state for buffer.
|
||||
|
||||
:bufnr: Number of buffer the todo state should be updated for
|
||||
:state: The new todo state
|
||||
"""
|
||||
lineno, colno = vim.current.window.cursor
|
||||
d = ORGMODE.get_document(allow_dirty=True)
|
||||
heading = d.find_current_heading()
|
||||
|
||||
if not heading:
|
||||
return
|
||||
|
||||
current_state = heading.todo
|
||||
|
||||
# set new headline
|
||||
heading.todo = state
|
||||
d.write_heading(heading)
|
||||
|
||||
# move cursor along with the inserted state only when current position
|
||||
# is in the heading; otherwite do nothing
|
||||
if heading.start_vim == lineno and colno > heading.level:
|
||||
if current_state is not None and \
|
||||
colno <= heading.level + len(current_state):
|
||||
# the cursor is actually on the todo keyword
|
||||
# move it back to the beginning of the keyword in that case
|
||||
vim.current.window.cursor = (lineno, heading.level + 1)
|
||||
else:
|
||||
# the cursor is somewhere in the text, move it along
|
||||
if current_state is None and state is None:
|
||||
offset = 0
|
||||
elif current_state is None and state is not None:
|
||||
offset = len(state) + 1
|
||||
elif current_state is not None and state is None:
|
||||
offset = -len(current_state) - 1
|
||||
else:
|
||||
offset = len(state) - len(current_state)
|
||||
vim.current.window.cursor = (lineno, colno + offset)
|
||||
|
||||
@classmethod
|
||||
def init_org_todo(cls):
|
||||
u""" Initialize org todo selection window.
|
||||
"""
|
||||
bufnr = int(re.findall('\d+$',vim.current.buffer.name)[0])
|
||||
all_states = ORGTODOSTATES.get(bufnr, None)
|
||||
|
||||
vim_commands = [
|
||||
u'let g:org_sav_timeoutlen=&timeoutlen',
|
||||
u'au orgmode BufEnter <buffer> :if ! exists("g:org_sav_timeoutlen")|let g:org_sav_timeoutlen=&timeoutlen|set timeoutlen=1|endif',
|
||||
u'au orgmode BufLeave <buffer> :if exists("g:org_sav_timeoutlen")|let &timeoutlen=g:org_sav_timeoutlen|unlet g:org_sav_timeoutlen|endif',
|
||||
u'setlocal nolist tabstop=16 buftype=nofile timeout timeoutlen=1 winfixheight',
|
||||
u'setlocal statusline=Org\\ todo\\ (%s)' % vim.eval(u_encode(u'fnameescape(fnamemodify(bufname(%d), ":t"))' % bufnr)),
|
||||
u'nnoremap <silent> <buffer> <Esc> :%sbw<CR>' % vim.eval(u_encode(u'bufnr("%")')),
|
||||
u'nnoremap <silent> <buffer> <CR> :let g:org_state = fnameescape(expand("<cword>"))<Bar>bw<Bar>exec "%s ORGMODE.plugins[u\'Todo\'].set_todo_state(\'".g:org_state."\')"<Bar>unlet! g:org_state<CR>' % VIM_PY_CALL,
|
||||
]
|
||||
# because timeoutlen can only be set globally it needs to be stored and
|
||||
# restored later
|
||||
# make window a scratch window and set the statusline differently
|
||||
for cmd in vim_commands:
|
||||
vim.command(u_encode(cmd))
|
||||
|
||||
if all_states is None:
|
||||
vim.command(u_encode(u'bw'))
|
||||
echom(u'No todo states available for buffer %s' % vim.current.buffer.name)
|
||||
|
||||
for idx, state in enumerate(all_states):
|
||||
pairs = [split_access_key(x, sub=u' ') for x in it.chain(*state)]
|
||||
line = u'\t'.join(u''.join((u'[%s] ' % x[1], x[0])) for x in pairs)
|
||||
vim.current.buffer.append(u_encode(line))
|
||||
for todo, key in pairs:
|
||||
# FIXME if double key is used for access modified this doesn't work
|
||||
vim.command(u_encode(u'nnoremap <silent> <buffer> %s :bw<CR><c-w><c-p>%s ORGMODE.plugins[u"Todo"].set_todo_state("%s")<CR>' % (key, VIM_PY_CALL, u_decode(todo))))
|
||||
|
||||
# position the cursor of the current todo item
|
||||
vim.command(u_encode(u'normal! G'))
|
||||
current_state = settings.unset(u'org_current_state_%d' % bufnr)
|
||||
if current_state is not None and current_state != '':
|
||||
for i, buf in enumerate(vim.current.buffer):
|
||||
idx = buf.find(current_state)
|
||||
if idx != -1:
|
||||
vim.current.window.cursor = (i + 1, idx)
|
||||
break
|
||||
else:
|
||||
vim.current.window.cursor = (2, 4)
|
||||
|
||||
# finally make buffer non modifiable
|
||||
vim.command(u_encode(u'setfiletype orgtodo'))
|
||||
vim.command(u_encode(u'setlocal nomodifiable'))
|
||||
|
||||
# remove temporary todo states for the current buffer
|
||||
del ORGTODOSTATES[bufnr]
|
||||
|
||||
def register(self):
|
||||
u"""
|
||||
Registration of plugin. Key bindings and other initialization should be done.
|
||||
"""
|
||||
self.keybindings.append(Keybinding(u'<localleader>ct', Plug(
|
||||
u'OrgTodoToggleNonInteractive',
|
||||
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(interactive=False)<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'&TODO/DONE/-', self.keybindings[-1])
|
||||
|
||||
self.keybindings.append(Keybinding(u'<localleader>d', Plug(
|
||||
u'OrgTodoToggleInteractive',
|
||||
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(interactive=True)<CR>' % VIM_PY_CALL)))
|
||||
self.menu + ActionEntry(u'&TODO/DONE/- (interactive)', self.keybindings[-1])
|
||||
|
||||
# add submenu
|
||||
submenu = self.menu + Submenu(u'Select &keyword')
|
||||
|
||||
self.keybindings.append(Keybinding(u'<S-Right>', Plug(
|
||||
u'OrgTodoForward',
|
||||
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state()<CR>' % VIM_PY_CALL)))
|
||||
submenu + ActionEntry(u'&Next keyword', self.keybindings[-1])
|
||||
|
||||
self.keybindings.append(Keybinding(u'<S-Left>', Plug(
|
||||
u'OrgTodoBackward',
|
||||
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(direction=2)<CR>' % VIM_PY_CALL)))
|
||||
submenu + ActionEntry(u'&Previous keyword', self.keybindings[-1])
|
||||
|
||||
self.keybindings.append(Keybinding(u'<C-S-Right>', Plug(
|
||||
u'OrgTodoSetForward',
|
||||
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(next_set=True)<CR>' % VIM_PY_CALL)))
|
||||
submenu + ActionEntry(u'Next keyword &set', self.keybindings[-1])
|
||||
|
||||
self.keybindings.append(Keybinding(u'<C-S-Left>', Plug(
|
||||
u'OrgTodoSetBackward',
|
||||
u'%s ORGMODE.plugins[u"Todo"].toggle_todo_state(direction=2, next_set=True)<CR>' % VIM_PY_CALL)))
|
||||
submenu + ActionEntry(u'Previous &keyword set', self.keybindings[-1])
|
||||
|
||||
settings.set(u'org_todo_keywords', [u_encode(u'TODO'), u_encode(u'|'), u_encode(u'DONE')])
|
||||
|
||||
settings.set(u'org_todo_prompt_position', u'botright')
|
||||
|
||||
vim.command(u_encode(u'au orgmode BufReadCmd org:todo/* %s ORGMODE.plugins[u"Todo"].init_org_todo()' % VIM_PY_CALL))
|
||||
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
@@ -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
|
||||
@@ -0,0 +1,17 @@
|
||||
import sys
|
||||
from string import Formatter
|
||||
|
||||
|
||||
if sys.version_info < (3,):
|
||||
VIM_PY_CALL = u':py'
|
||||
else:
|
||||
VIM_PY_CALL = u':py3'
|
||||
|
||||
|
||||
class NoneAsEmptyFormatter(Formatter):
|
||||
def get_value(self, key, args, kwargs):
|
||||
v = super().get_value(key, args, kwargs)
|
||||
return '' if v is None else v
|
||||
|
||||
|
||||
fmt = NoneAsEmptyFormatter()
|
||||
@@ -0,0 +1,4 @@
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
basestring = unicode = str
|
||||
@@ -0,0 +1,4 @@
|
||||
try:
|
||||
from __builtin__ import xrange as range
|
||||
except:
|
||||
pass
|
||||
95
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/settings.py
Normal file
95
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/settings.py
Normal file
@@ -0,0 +1,95 @@
|
||||
# -*- 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
|
||||
500
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/vimbuffer.py
Normal file
500
dot_vim/plugged/vim-orgmode/ftplugin/orgmode/vimbuffer.py
Normal file
@@ -0,0 +1,500 @@
|
||||
# -*- 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)
|
||||
Reference in New Issue
Block a user