" Vimball Archiver by Charles E. Campbell, Jr., Ph.D. UseVimball finish doc/ttodo.txt [[[1 496 *ttodo.txt* Edit, view, sort, and filter todo.txt files Author: Tom Link, micathom at gmail com This plugin provides the |:Ttodo| command that allow easy filtering and viewing of todo.txt files (see http://todotxt.com) via |tlib#input#List()|. The plugin supports: - syntax highlighting for todo.txt files - search across multiple todo.txt files - filter tasks with specific tags, lists etc. - subtasks (outlines of complex tasks) - hide tasks until a threshold date - recurring tasks. Examples: > " Show all tasks; default map (|g:ttodo_nmap|): 1 :Ttodo " Show important tasks; default map (|g:ttodo_nmap_important|): ! :Ttodo! " Show tasks that are due in the next two weeks :Ttodo --due=2w " Show tasks with priorities A to C :Ttodo --pri=A-C " Show tasks matching @Test :Ttodo @Test " By default, |:Ttodo| scans the todo.txt files in |g:ttodo#dirs|. Users " can also scan loaded buffers. " Show tasks in the current buffer :Ttodo --bufname=% " Command-line options can be collected in preference sets (see " |g:ttodo#prefs|) :Ttodo --pref=work Global maps: 1 ............ Show tasks (:Ttodo) ! ............ Show important tasks (:Ttodo --pref=important) If filetype is ttodo, the following buffer-local keymaps are enabled: ................. Add a new task; if |g:ttodo#ftplugin#add_at_eof| is true, add the task at the end of file ............... Add a new task; copy any list and tags from the current task; if |g:ttodo#ftplugin#add_at_eof| is true, add the task at the end of file ............... Add a new subtask tx ...... Mark the current task as "done" td ...... Mark the current task as due in N days tw ...... Mark the current task as due in N weeks tm ...... Mark the current task as due in N months ty ...... Set the current task's priority ta ...... Archive completed tasks tb ...... View, filter tasks in the current buffer t* ...... View tasks in the current buffer that match the word under the cursor tn ...... Add a note (the filename uses the first @list tag) ti ...... Add a hopefully unique ID (an Adler32 hash) tD ...... Add a dependency If filetype is ttodo, the following buffer-local commands are enabled: |:Ttodoarchive| ...... Archive completed tasks in the current buffer |:Ttodobuffer| ....... Show tasks in the current buffer only |:Ttodonote| ......... Add a new note to the task at the cursor |:Ttodosort| ......... Sort the tasks in the current buffer As a ftplugin, ttodo supports the following syntax/extensions to todo.txt (see also https://github.com/ginatrapani/todo.txt-cli/wiki/The-Todo.txt-Format): (A) DATE TASK ........ A priority as upper case letter (at the beginning of the line) DATE TASK ............ An open task x DATE DATE TASK ..... A completed task (with completion date) indented tasks ..... A indented task is a subtask of the parent task; unless `has_subtasks` is true, |:Ttodo| will only show tasks with no open subtasks; this is similar to how the outline addon handles subtasks (see also https://github.com/samuelsnyder/outline-todo.txt) A TASK may contain the following tags: @list ............ a "list" tag +keyword ......... a "keyword" tag h:1 .............. hidden task due:YYYY-MM-DD ... due dates rec:+Nx .......... When marking a task a "done", add a new task with a due date in N d(ays), w(eeks), m(onths), y(ears); with the leading '+' use the original due date; else use the completion date; this is similar to how the Simpletask Android app handles `rec` tags t:YYYY-MM-DD ..... Hide the tasks until the given date t:-Nd ............ Hide the tasks until N days before the due date id:ID ............ Define a tasks ID string (an ID should consist of alphanumeric characters only) dep:IDs .......... Depends on task with ID (if the other task is not completed yet, mark the current task as pending); IDs is a comma-separated list of IDs parent:ID ........ In outlines: the current task is the child of task with ID If you set |g:ttodo_enable_ftdetect| to 0, you can use the |:Ttodo| command in conjunction with other syntax files/ftplugins such as: - https://github.com/freitass/todo.txt-vim - todo-txt.vim : Vim plugin for Todo.txt https://github.com/dbeniamine/todo.txt-vim or http://www.vim.org/scripts/script.php?script_id=5134 - https://github.com/mivok/vimtodo or http://www.vim.org/scripts/script.php?script_id=3264 - https://github.com/davidoc/todo.txt-vim - My fork: https://github.com/tomtom/todo.txt-vim-1 - https://github.com/dsiroky/vim-todotxt - ... Related work: - https://github.com/elentok/todo.vim ----------------------------------------------------------------------- Install~ To install the vimball, edit the vba file and type: > :so % See :help vimball for details. To install from github, please use 1. git + a plugin loader like enable_vim, pathogen, vim-unbundle etc. 2. a plugin manager like VAM, Vundle, NeoBundle, vim-plug etc. The tlib_vim plugin is required: https://github.com/tomtom/tlib_vim Optional enhancement: - https://github.com/tomtom/autolinker_vim for hyperlinking Also available via git: http://github.com/tomtom/ttodo_vim Setup~ Please set |g:ttodo#dirs| in |vimrc| before using |:Ttodo|. ======================================================================== Contents~ g:ttodo_nmap .......................... |g:ttodo_nmap| g:ttodo_nmap_important ................ |g:ttodo_nmap_important| g:ttodo_enable_ftdetect ............... |g:ttodo_enable_ftdetect| :Ttodo ................................ |:Ttodo| :Ttodonew ............................. |:Ttodonew| :Ttodoinbox ........................... |:Ttodoinbox| :Ttodogrep ............................ |:Ttodogrep| g:ttodo#fileargs ...................... |g:ttodo#fileargs| g:ttodo#file_pattern .................. |g:ttodo#file_pattern| g:ttodo#file_include_rx ............... |g:ttodo#file_include_rx| g:ttodo#file_exclude_rx ............... |g:ttodo#file_exclude_rx| g:ttodo#task_include_rx ............... |g:ttodo#task_include_rx| g:ttodo#task_exclude_rx ............... |g:ttodo#task_exclude_rx| g:ttodo#task_hide_rx .................. |g:ttodo#task_hide_rx| g:ttodo#viewer ........................ |g:ttodo#viewer| g:ttodo#sort .......................... |g:ttodo#sort| g:ttodo#default_t ..................... |g:ttodo#default_t| g:ttodo#new_task ...................... |g:ttodo#new_task| g:ttodo#inbox ......................... |g:ttodo#inbox| ttodo#GetDirs ......................... |ttodo#GetDirs()| ttodo#ParseTask ....................... |ttodo#ParseTask()| ttodo#GetOpts ......................... |ttodo#GetOpts()| ttodo#GetOverdueRx .................... |ttodo#GetOverdueRx()| ttodo#InitListBuffer .................. |ttodo#InitListBuffer()| ttodo#CollectTags ..................... |ttodo#CollectTags()| ttodo#FiletypeDetect .................. |ttodo#FiletypeDetect()| ttodo#SortBuffer ...................... |ttodo#SortBuffer()| ttodo#NewTask ......................... |ttodo#NewTask()| ttodo#FormatTags ...................... |ttodo#FormatTags()| ttodo#MaybeAppend ..................... |ttodo#MaybeAppend()| ttodo#FileSources ..................... |ttodo#FileSources()| ttodo#InputNumber ..................... |ttodo#InputNumber()| g:ttodo#ftplugin#notef ................ |g:ttodo#ftplugin#notef| g:ttodo#ftplugin#note_prefix .......... |g:ttodo#ftplugin#note_prefix| g:ttodo#ftplugin#edit_note ............ |g:ttodo#ftplugin#edit_note| g:ttodo#ftplugin#add_at_eof ........... |g:ttodo#ftplugin#add_at_eof| g:ttodo#ftplugin#rec_copy ............. |g:ttodo#ftplugin#rec_copy| ttodo#ftplugin#Archive ................ |ttodo#ftplugin#Archive()| ttodo#ftplugin#ArchiveCurrentBuffer ... |ttodo#ftplugin#ArchiveCurrentBuffer()| ttodo#ftplugin#Note ................... |ttodo#ftplugin#Note()| ttodo#ftplugin#New .................... |ttodo#ftplugin#New()| ttodo#ftplugin#MarkDone ............... |ttodo#ftplugin#MarkDone()| ttodo#ftplugin#MarkDue ................ |ttodo#ftplugin#MarkDue()| ttodo#ftplugin#SetPriority ............ |ttodo#ftplugin#SetPriority()| ttodo#ftplugin#Agent .................. |ttodo#ftplugin#Agent()| ttodo#ftplugin#AddId .................. |ttodo#ftplugin#AddId()| ttodo#ftplugin#SyntaxDue .............. |ttodo#ftplugin#SyntaxDue()| ttodo#ftplugin#AddDep ................. |ttodo#ftplugin#AddDep()| n_ .............................. |n_| n_ .............................. |n_| n_ .............................. |n_| i_ .............................. |i_| i_ .............................. |i_| i_ .............................. |i_| :Ttodosort ............................ |:Ttodosort| :Ttodoarchive ......................... |:Ttodoarchive| :Ttodobuffer .......................... |:Ttodobuffer| :Ttodonote ............................ |:Ttodonote| ======================================================================== plugin/ttodo.vim~ *g:ttodo_nmap* g:ttodo_nmap (default: '1') *g:ttodo_nmap_important* g:ttodo_nmap_important (default: '!') *g:ttodo_enable_ftdetect* g:ttodo_enable_ftdetect (default: 1) Set this variable to 1 in |vimrc| in order to enable the ttodo filetype for todo.txt files. *:Ttodo* :Ttodo[!] [ARGS] [INITIAL FILTER] ARGS is an argument list. The following arguments are supported: --pref=PREF .... PREF is the name of a preferences set in |g:ttodo#prefs| (default: "default") --due=DATE ..... show only tasks with due dates >= DATE. DATE can be - a DATE in the form YYYY-MM-DD or - a number of days or - a number of weeks as in "4w" (default: |g:ttodo#default_due|) --undated ...... Show tasks with no due dates when using the due argument --done ......... Show completed tasks --pending ...... Show tasks with open dependencies --hidden ....... Show hidden tasks, i.e. tasks with a "h:1" tag or tasks matching |g:ttodo#task_hide_rx| --bufnr=BUFNR .. A comma-separated list of buffer numbers (must be numbers) --bufname=EXPR . A buffer name expression (see |bufname()|) --files=FILE1,FILE2... .. A comma-separated list of todo.txt files --path=PATH .... Search files in this path (default: use |g:ttodo#dirs|) --pattern=PAT .. Search files matching this pattern (default: |g:ttodo#file_pattern|) --encoding=ENC . Encoding of the task files (default: &enc) --sort=FIELDS .. default: |g:ttodo#sort| --has_subtasks . Show tasks with open subtasks (i.e. indented tasks below the parent task) --pri=PRI ..................... Show tasks with a priority matching [PRI] (see |/[]|) --ignore_pri=PRI .............. Ignore tasks with a priority matching [PRI] (see |/[]|) --has_lists=LIST1,.. .......... Show tasks with matching lists --ignore_lists=LIST1,.. ....... Ignore tasks with matching lista --has_tags=TAG1,.. ............ Show tasks with matching taga --ignore_tags=TAG1,.. ......... Ignore tasks with matching tags -A=RX, --file_include_rx=RX ... Default: |g:ttodo#file_include_rx| -R=RX, --file_exclude_rx=RX ... Default: |g:ttodo#file_exclude_rx| -i=RX, --task_include_rx=RX ... Default: |g:ttodo#task_include_rx| -x=RX, --task_exclude_rx=RX ... Default: |g:ttodo#task_exclude_rx| When the [!] is included show only important tasks. INITIAL FILTER is a |regexp| for filtering the task list. The interpretation of INITIAL FILTER depends on the value of |g:tlib#input#filter_mode|. The format of INITIAL FILTER depends on the value of |g:ttodo#viewer|. *:Ttodonew* :Ttodonew *:Ttodoinbox* :Ttodoinbox *:Ttodogrep* :Ttodogrep ======================================================================== autoload/ttodo.vim~ *g:ttodo#fileargs* g:ttodo#fileargs (default: {}) A dictionary of {filename |regexp|: {additional args}} -- see |g:ttodo#dirs| for details on supported arguments. *g:ttodo#file_pattern* g:ttodo#file_pattern (default: '*todo.txt') A glob pattern matching todo.txt files. *g:ttodo#file_include_rx* g:ttodo#file_include_rx (default: '') Consider only files matching this |regexp|. *g:ttodo#file_exclude_rx* g:ttodo#file_exclude_rx (default: '[\/]done\.txt$') Ignore files matching this |regexp|. *g:ttodo#task_include_rx* g:ttodo#task_include_rx (default: '') Include only tasks matching this |regexp| in the list. *g:ttodo#task_exclude_rx* g:ttodo#task_exclude_rx (default: '') Exclude tasks matching this |regexp| from the list. *g:ttodo#task_hide_rx* g:ttodo#task_hide_rx (default: '') *g:ttodo#viewer* g:ttodo#viewer (default: 'tlib') Supported values: tlib ...... Use the tlib_vim plugin; the syntax of |:Ttodo|'s initial filter depends on the value of |g:tlib#input#filter_mode| :COMMAND .. E.g. `:cwindow` or `:CtrlPQuickfix`. In this case initial filter is a standard |regexp|. *g:ttodo#sort* g:ttodo#sort (default: '-overdue,pri,due,done,lists,tags,idx') A comma-separated list of fields that determine the sort order for |:Ttodo| and |:Ttodosort|. A "-" as prefix reverses the sort order. *g:ttodo#default_t* g:ttodo#default_t (default: '-31d') If a task has no threshold date defined, assign this default threshold date. *g:ttodo#new_task* g:ttodo#new_task (default: {'lists': ['inbox'], 'pri': 'C'}) Defintion for tasks added via |:Ttodonew|. *g:ttodo#inbox* g:ttodo#inbox (default: 'todo.txt') A file basename (no directory). Tasks added by |:Ttodonew| will be added to this file in the default (i.e. the first) directory in |g:ttodo#dirs|. *ttodo#GetDirs()* ttodo#GetDirs() *ttodo#ParseTask()* ttodo#ParseTask(task, file, ...) *ttodo#GetOpts()* ttodo#GetOpts(bang, cmdargs) *ttodo#GetOverdueRx()* ttodo#GetOverdueRx(args) *ttodo#InitListBuffer()* ttodo#InitListBuffer() *ttodo#CollectTags()* ttodo#CollectTags(type) *ttodo#FiletypeDetect()* ttodo#FiletypeDetect(...) *ttodo#SortBuffer()* ttodo#SortBuffer(cmdargs) If called with --sortseps=tags,lists, an empty line is inserted after each main (i.e. first) list or tag. Sorting doesn't work for outlines, i.e. tasks with subtasks. *ttodo#NewTask()* ttodo#NewTask(cmdargs) *ttodo#FormatTags()* ttodo#FormatTags(prefix, lists) *ttodo#MaybeAppend()* ttodo#MaybeAppend(text, suffix) *ttodo#FileSources()* ttodo#FileSources(args) *ttodo#InputNumber()* ttodo#InputNumber(prompt) ======================================================================== autoload/ttodo/ftplugin.vim~ *g:ttodo#ftplugin#notef* g:ttodo#ftplugin#notef (default: 'notes/%s/%s-%s.md') *g:ttodo#ftplugin#note_prefix* g:ttodo#ftplugin#note_prefix (default: 'file://') *g:ttodo#ftplugin#edit_note* g:ttodo#ftplugin#edit_note (default: 'split') *g:ttodo#ftplugin#add_at_eof* g:ttodo#ftplugin#add_at_eof (default: 1) If true, the or map will make ttodo add a new task at the end of the file. Otherwise the task will be added below the current line. Subtasks will always be added below the current line. *g:ttodo#ftplugin#rec_copy* g:ttodo#ftplugin#rec_copy (default: 1) If true, marking a recurring task as "done" will mark the old task as completed and will then create a new updated task. *ttodo#ftplugin#Archive()* ttodo#ftplugin#Archive(filename) *ttodo#ftplugin#ArchiveCurrentBuffer()* ttodo#ftplugin#ArchiveCurrentBuffer() *ttodo#ftplugin#Note()* ttodo#ftplugin#Note() *ttodo#ftplugin#New()* ttodo#ftplugin#New(move, copytags, mode, ...) *ttodo#ftplugin#MarkDone()* ttodo#ftplugin#MarkDone(count, ...) *ttodo#ftplugin#MarkDue()* ttodo#ftplugin#MarkDue(unit, count) *ttodo#ftplugin#SetPriority()* ttodo#ftplugin#SetPriority(count) *ttodo#ftplugin#Agent()* ttodo#ftplugin#Agent(world, selected, fn, ...) *ttodo#ftplugin#AddId()* ttodo#ftplugin#AddId(count) *ttodo#ftplugin#SyntaxDue()* ttodo#ftplugin#SyntaxDue() *ttodo#ftplugin#AddDep()* ttodo#ftplugin#AddDep() ======================================================================== ftplugin/ttodo.vim~ *n_* n_ ... ttodo#ftplugin#New(g:ttodo#ftplugin#add_at_eof ? "G" : "", 0, "n") *n_* n_ ... ttodo#ftplugin#New(g:ttodo#ftplugin#add_at_eof ? "G" : "", 1, "n") *n_* n_ ... ttodo#ftplugin#New(">", 1, "n") *i_* i_ ... ttodo#ftplugin#New(g:ttodo#ftplugin#add_at_eof ? "G" : "", 0, "i") *i_* i_ ... ttodo#ftplugin#New(g:ttodo#ftplugin#add_at_eof ? "G" : "", 1, "i") *i_* i_ ... ttodo#ftplugin#New(">", 1, "i") *:Ttodosort* :Ttodosort Sort the tasks in the current buffer. Sorting task outlines (i.e. subtasks) is not supported. *:Ttodoarchive* :Ttodoarchive Archive completed tasks in the current buffer. *:Ttodobuffer* :Ttodobuffer View the tasks in the current buffer. *:Ttodonote* :Ttodonote Add a new task to the task at the cursor. vim:tw=78:fo=w2croql:isk=!-~,^*,^|,^":ts=8:ft=help:norl: plugin/ttodo.vim [[[1 104 " @Author: Tom Link (micathom AT gmail com?subject=[vim]) " @Website: https://github.com/tomtom " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Last Change: 2016-01-26 " @Revision: 99 " GetLatestVimScripts: 5262 0 :AutoInstall: ttodo.vim if &cp || exists("loaded_ttodo") finish endif let loaded_ttodo = 100 let s:save_cpo = &cpo set cpo&vim if !exists('g:ttodo_nmap') let g:ttodo_nmap = '1' "{{{2 endif if !exists('g:ttodo_nmap_important') let g:ttodo_nmap_important = '!' "{{{2 endif if !exists('g:ttodo_enable_ftdetect') " Set this variable to 1 in |vimrc| in order to enable the ttodo " filetype for todo.txt files. let g:ttodo_enable_ftdetect = 1 "{{{2 endif " :display: :Ttodo[!] [ARGS] [INITIAL FILTER] " " ARGS is an argument list. The following arguments are supported: " --pref=PREF .... PREF is the name of a preferences set in " |g:ttodo#prefs| (default: "default") " --due=DATE ..... show only tasks with due dates >= DATE. DATE can be " - a DATE in the form YYYY-MM-DD or " - a number of days or " - a number of weeks as in "4w" " (default: |g:ttodo#default_due|) " --undated ...... Show tasks with no due dates when using the due " argument " --done ......... Show completed tasks " --pending ...... Show tasks with open dependencies " --hidden ....... Show hidden tasks, i.e. tasks with a "h:1" tag or " tasks matching |g:ttodo#task_hide_rx| " --bufnr=BUFNR .. A comma-separated list of buffer numbers (must be " numbers) " --bufname=EXPR . A buffer name expression (see |bufname()|) " --files=FILE1,FILE2... .. A comma-separated list of todo.txt files " --path=PATH .... Search files in this path (default: use " |g:ttodo#dirs|) " --pattern=PAT .. Search files matching this pattern (default: " |g:ttodo#file_pattern|) " --encoding=ENC . Encoding of the task files (default: &enc) " --sort=FIELDS .. default: |g:ttodo#sort| " --has_subtasks . Show tasks with open subtasks (i.e. indented tasks " below the parent task) " --pri=PRI ..................... Show tasks with a priority matching " [PRI] (see |/[]|) " --ignore_pri=PRI .............. Ignore tasks with a priority " matching [PRI] (see |/[]|) " --has_lists=LIST1,.. .......... Show tasks with matching lists " --ignore_lists=LIST1,.. ....... Ignore tasks with matching lista " --has_tags=TAG1,.. ............ Show tasks with matching taga " --ignore_tags=TAG1,.. ......... Ignore tasks with matching tags " -A=RX, --file_include_rx=RX ... Default: |g:ttodo#file_include_rx| " -R=RX, --file_exclude_rx=RX ... Default: |g:ttodo#file_exclude_rx| " -i=RX, --task_include_rx=RX ... Default: |g:ttodo#task_include_rx| " -x=RX, --task_exclude_rx=RX ... Default: |g:ttodo#task_exclude_rx| " " When the [!] is included show only important tasks. " " INITIAL FILTER is a |regexp| for filtering the task list. The " interpretation of INITIAL FILTER depends on the value of " |g:tlib#input#filter_mode|. The format of INITIAL FILTER depends on " the value of |g:ttodo#viewer|. command! -bang -nargs=* -complete=customlist,ttodo#CComplete Ttodo call ttodo#Show(!empty(""), []) command! -nargs=+ -complete=customlist,ttodo#CComplete Ttodonew call ttodo#NewTask([]) command! -bar Ttodoinbox Ttodo --has_lists=inbox command! -bang -nargs=+ Ttodogrep if exists(':Trag') == 2 | Trag --file_sources=*ttodo#FileSources | else | echom ':Ttodogrep requires the trag_vim plugin to be installed!' | endif if !empty(g:ttodo_nmap) exec 'noremap' g:ttodo_nmap ':Ttodo' endif if !empty(g:ttodo_nmap_important) exec 'noremap' g:ttodo_nmap_important ':Ttodo!' endif let &cpo = s:save_cpo unlet s:save_cpo autoload/tcomment/types/ttodo.vim [[[1 9 " @Author: Tom Link (mailto:micathom AT gmail com?subject=[vim]) " @Website: https://github.com/tomtom " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Last Change: 2015-11-12 " @Revision: 2 " call tcomment#DefineType('ttodo', 'x '. strftime(g:tlib#date#date_format) .' %s') call tcomment#DefineType('ttodo', 'h:1 %s') autoload/ttodo.vim [[[1 1033 " @Author: Tom Link (mailto:micathom AT gmail com?subject=[vim]) " @Website: https://github.com/tomtom " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Last Change: 2016-01-12 " @Revision: 1296 if !exists('g:loaded_tlib') || g:loaded_tlib < 119 runtime plugin/tlib.vim if !exists('g:loaded_tlib') || g:loaded_tlib < 119 echoerr 'tlib >= 1.19 is required' finish endif endif if !exists('g:ttodo#dirs') " A list of directories containing todo.txt files. An item in this " list can be either: " - a directory as string " - a list of [directory, options] " - a dictionary of {'dir': directory, 'opts': options} " " Options is a dictionary that may contain the following keys: " " lists: LIST OF STRINGS .... Additional lists " tags: LIST OF STRINGS ..... Additional tags " inbox: BASENAME ........... Basename for new tasks (see " |g:ttodo#inbox|) " inboxfile: FILENAME ....... Full filename for new tasks " file_pattern: GLOB ........ See |g:ttodo#file_pattern| " file_include_rx: REGEXP ... See |g:ttodo#file_include_rx| " file_exclude_rx: REGEXP ... See |g:ttodo#file_exclude_rx| " encoding: ENC ............. If ENC != 'enc', then the file " contents will be transcoded with " |iconv()| " " |g:ttodo#fileargs| provides an alternative way to define " file-specific options. " " If the todotxt plugin is used, |g:todotxt#dir| is added to the " list. " " Changes to this variable will have an effect only after restart. let g:ttodo#dirs = [] endif if !exists('g:ttodo#fileargs') " A dictionary of {filename |regexp|: {additional args}} -- see " |g:ttodo#dirs| for details on supported arguments. let g:ttodo#fileargs = {} "{{{2 endif if !exists('g:ttodo#file_pattern') " A glob pattern matching todo.txt files. let g:ttodo#file_pattern = '*todo.txt' "{{{2 endif if !exists('g:ttodo#file_include_rx') " Consider only files matching this |regexp|. let g:ttodo#file_include_rx = '' "{{{2 endif if !exists('g:ttodo#file_exclude_rx') " Ignore files matching this |regexp|. let g:ttodo#file_exclude_rx = '[\/]done\.txt$' "{{{2 endif if !exists('g:ttodo#task_include_rx') " Include only tasks matching this |regexp| in the list. let g:ttodo#task_include_rx = '' "{{{2 endif if !exists('g:ttodo#task_exclude_rx') " Exclude tasks matching this |regexp| from the list. let g:ttodo#task_exclude_rx = '' "{{{2 endif if !exists('g:ttodo#task_hide_rx') let g:ttodo#task_hide_rx = '' "{{{2 endif if !exists('g:ttodo#viewer') " Supported values: " tlib ...... Use the tlib_vim plugin; the syntax of |:Ttodo|'s " initial filter depends on the value of " |g:tlib#input#filter_mode| " :COMMAND .. E.g. `:cwindow` or `:CtrlPQuickfix`. In this case " initial filter is a standard |regexp|. let g:ttodo#viewer = 'tlib' "{{{2 endif if !exists('g:ttodo#sort') " A comma-separated list of fields that determine the sort order for " |:Ttodo| and |:Ttodosort|. " " A "-" as prefix reverses the sort order. let g:ttodo#sort = '-overdue,pri,due,done,lists,tags,idx' "{{{2 endif if !exists('g:ttodo#sort_defaults') " :nodoc: let g:ttodo#sort_defaults = { \ 'pri': 'I', \ 'overdue': 0, \ 'due': strftime(g:tlib#date#date_format, localtime() + g:tlib#date#dayshift * 28), \ } endif if !exists('g:ttodo#prefs') " A dictionary of configurations that can be invoked with the " `--pref=NAME` command line option from |:Ttodo|. " " If no preference is given, "default" is used. let g:ttodo#prefs = {'default': {'ignore_pri': 'X-Z'}, 'important': {'undated': 1, 'due': '1w', 'pri': 'A-C', 'ignore_lists': ['someday', 'maybe']}} "{{{2 if exists('g:ttodo#prefs_user') let g:ttodo#prefs = tlib#eval#Extend(g:ttodo#prefs, g:ttodo#prefs_user) endif endif if !exists('g:ttodo#default_t') " If a task has no threshold date defined, assign this default threshold date. let g:ttodo#default_t = '-31d' "{{{2 endif if !exists('g:ttodo#parse_rx') ":nodoc: let g:ttodo#parse_rx = { \ 'id': '\', \ 't': '\', \ 'pri': '^\s*(\zs\u\ze)', \ 'hidden?': '\%(\\|'. g:ttodo#task_hide_rx .'\)', \ 'done?': '^\C\s*x\ze\s', \ 'donedate': '^\Cx\s\+\zs'. g:tlib#date#date_rx, \ 'rec': '\', \ } endif if exists('g:ttodo#parse_rx_user') call extend(g:ttodo#parse_rx, g:ttodo#parse_rx_user) endif if !exists('g:ttodo#rewrite_gsub') ":nodoc: let g:ttodo#rewrite_gsub = [ \ ['^\C\%((\u)\s\+\)\?\zs'. g:tlib#date#date_rx .'\s*', ''], \ ['\%(^\|\s\+\)\%(id\|parent\|t\):\S\+', ''], \ ] endif if exists('g:ttodo#rewrite_gsub_user') call extend(g:ttodo#rewrite_gsub, g:ttodo#rewrite_gsub_user) endif if !exists('g:ttodo#mapleader') let g:ttodo#mapleader = 't' "{{{2 endif if !exists('g:ttodo#new_task') " Defintion for tasks added via |:Ttodonew|. let g:ttodo#new_task = {'lists': ['inbox'], 'pri': 'C'} "{{{2 endif if !exists('g:ttodo#inbox') " A file basename (no directory). " " Tasks added by |:Ttodonew| will be added to this file in the " default (i.e. the first) directory in |g:ttodo#dirs|. let g:ttodo#inbox = 'todo.txt' "{{{2 endif call tlib#type#Define('ttodo_type_itemdef_opts', {'__name__': 's', '__rx__': 's'}) let s:list_env = { \ 'qfl_short_filename': 1, \ 'qfl_list_syntax': 'ttodo', \ 'qfl_list_syntax_nextgroup': '@TtodoTask', \ 'set_syntax': 'ttodo#InitListBuffer', \ 'scratch': '__Ttodo__', \ } let s:list_env['key_map'] = { \ 'default': { \ 24 : {'key': 24, 'agent': 'ttodo#ftplugin#Agent', 'args': ['ttodo#ftplugin#MarkDone', 0], 'key_name': '', 'help': 'Mark done'}, \ 4 : {'key': 4, 'agent': 'ttodo#ftplugin#Agent', 'args': ['ttodo#ftplugin#MarkDue', 'd', '\=ttodo#InputNumber("Number of days: ")'], 'key_name': '', 'help': 'Mark as due in N days'}, \ 23 : {'key': 23, 'agent': 'ttodo#ftplugin#Agent', 'args': ['ttodo#ftplugin#MarkDue', 'w', '\=ttodo#InputNumber("Number of weeks: ")'], 'key_name': '', 'help': 'Mark as due in N weeks'}, \ 25 : {'key': 25, 'agent': 'ttodo#ftplugin#Agent', 'args': ['ttodo#ftplugin#SetPriority', 0], 'key_name': '', 'help': 'Change task category'}, \ } \ } if exists('g:ttodo#world_user') let s:list_env = tlib#eval#Extend(s:list_env, g:ttodo#world_user) endif let s:ttodo_dirs = {} function! ttodo#GetDirs() abort "{{{3 return s:ttodo_dirs endf function! s:GetOpt(opts, name) abort "{{{3 return get(a:opts, a:name, g:ttodo#{a:name}) endf " :display: s:ItemDef(var, item, ?opts={}) " Register a new directory with todo.txt files. The first directory is " the default directory for |g:ttodo#inbox|. function! s:ItemDef(item, first) abort "{{{3 if type(a:item) == 1 let item = a:item let opts = {} elseif type(a:item) == 3 let [item, opts] = a:item elseif type(a:item) == 4 let item = get(a:item, 'file', get(a:item, 'dir', '')) if empty(item) throw 'Ttodo: No item: '. string(a:item) endif let opts = get(a:item, 'opts', {}) else throw 'Ttodo: Unsupported value for g:ttodo#dirs: '. string(g:ttodo#dirs) endif Tlibtrace 'ttodo', item, keys(opts) if a:first let opts.default = 1 endif let opts.__name__ = item let opts.__rx__ = '\V\^'. join(map(split(item, '[\/]', 1), 'tlib#rx#Escape(v:val, "V")'), '[\\/]') Tlibassert tlib#type#Has(opts, 'ttodo_type_itemdef_opts') return {item : opts} endf function! s:ItemsDefs(items, first) abort "{{{3 let itemsdefs = {} let i = 0 for item in a:items call extend(itemsdefs, s:ItemDef(item, a:first && i == 0)) let i += 1 endfor return itemsdefs endf if !empty(g:ttodo#dirs) call extend(s:ttodo_dirs, s:ItemsDefs(g:ttodo#dirs, empty(s:ttodo_dirs))) endif if exists('g:todotxt#dir') call extend(s:ttodo_dirs, s:ItemDef(g:todotxt#dir, empty(s:ttodo_dirs))) endif function! s:GetDefaultDirDef(args, default) abort "{{{3 let dirsdefs = s:GetDirsDefs(a:args) " TLogVAR dirsdefs let default_dirs = filter(copy(dirsdefs), 'get(v:val, "default", 0)') " TLogVAR keys(default_dirs) if empty(default_dirs) return {'__name__': a:default} else return default_dirs[keys(default_dirs)[0]] endif endf let s:ttodo_args = { \ 'help': ':Ttodo', \ 'handle_exit_code': 1, \ 'values': { \ 'bufname': {'type': 1}, \ 'bufnr': {'type': 1}, \ 'done': {'type': -1}, \ 'due': {'type': 1, 'validate': 'tlib#date#IsDate'}, \ 'encoding': {'type': 1}, \ 'file_exclude_rx': {'type': 1}, \ 'file_include_rx': {'type': 1}, \ 'hidden': {'type': -1}, \ 'inbox': {'type': 1}, \ 'pending': {'type': -1}, \ 'has_subtasks': {'type': -1}, \ 'has_lists': {'type': 3, 'complete_customlist': 'ttodo#CollectTags("lists")'}, \ 'ignore_lists': {'type': 3, 'complete_customlist': 'ttodo#CollectTags("lists")'}, \ 'has_tags': {'type': 3, 'complete_customlist': 'ttodo#CollectTags("tags")'}, \ 'ignore_tags': {'type': 3, 'complete_customlist': 'ttodo#CollectTags("tags")'}, \ 'lists': {'type': 3, 'complete_customlist': 'ttodo#CollectTags("lists")'}, \ 'tags': {'type': 3, 'complete_customlist': 'ttodo#CollectTags("tags")'}, \ 'sortseps': {'type': 3, 'complete_customlist': '["lists", "tags"]'}, \ 'files': {'type': 3, 'complete': 'files'}, \ 'dirs': {'type': 3, 'complete': 'dirs'}, \ 'path': {'type': 1, 'complete': 'dirs'}, \ 'pref': {'type': 1, 'complete_customlist': 'keys(g:ttodo#prefs)'}, \ 'pri': {'type': 1, 'complete_customlist': '["", "A-", "A-C", "A-Z", "X-Z"]'}, \ 'ignore_pri': {'type': 1, 'complete_customlist': '["", "X-Z"]'}, \ 'sort': {'type': 1, 'complete_customlist': 'map(keys(g:ttodo#parse_rx), "substitute(v:val, ''\\W$'', '''', ''g'')")'}, \ 'task_exclude_rx': {'type': 1}, \ 'task_include_rx': {'type': 1}, \ 'undated': {'type': -1}, \ 'threshold': {'type': -1}, \ }, \ 'flags': { \ 'A': '--file_include_rx', 'R': '--file_exclude_rx', \ 'i': '--task_include_rx', 'x': '--task_exclude_rx', \ }, \ } function! s:GetFiles(args) abort "{{{3 let bufname = get(a:args, 'bufname', '') let filedefs = [] if !empty(bufname) call add(filedefs, {'fileargs': {}, 'file': bufname(bufname)}) else let bufnr = get(a:args, 'bufnr', '') if !empty(bufnr) let bufnrs = tlib#string#SplitCommaList(bufnr) let files = map(bufnrs, 'bufname(str2nr(v:val))') for file in files call add(filedefs, {'fileargs': {}, 'file': file}) endfor else if has_key(a:args, 'files') call extend(filedefs, map(s:ItemsDefs(a:args.files, 1), '{"fileargs": v:val, "file": v:key}')) else let dirsdefs = s:GetDirsDefs(a:args) for [dir, dirargs] in items(dirsdefs) Tlibtrace 'ttodo', dir, keys(dirargs) let pattern = get(a:args, 'pattern', s:GetOpt(dirargs, 'file_pattern')) Tlibtrace 'ttodo', pattern let files = tlib#file#Glob(tlib#file#Join([dir, pattern])) let file_include_rx = get(a:args, 'file_include_rx', s:GetOpt(a:args, 'file_include_rx')) if !empty(file_include_rx) let files = filter(files, 'v:val =~# file_include_rx') endif let file_exclude_rx = get(a:args, 'file_exclude_rx', s:GetOpt(a:args, 'file_exclude_rx')) if !empty(file_exclude_rx) let files = filter(files, 'v:val !~# file_exclude_rx') for file in files call add(filedefs, {'fileargs': dirargs, 'file': file}) endfor endif endfor endif endif endif let filedefs = map(filedefs, 's:EnrichWithFileargs(v:val)') Tlibtrace 'ttodo', len(filedefs) Tlibassert tlib#type#Are(copy(filedefs), 'dict') Tlibassert tlib#type#Have(copy(filedefs), ['fileargs', 'file']) return filedefs endf function! s:GetDirsDefs(args) abort "{{{3 let dirs = s:GetDirs(a:args) if empty(dirs) throw 'TTodo: Please set dirs via g:ttodo#dirs' endif let dirsdefs = s:ItemsDefs(dirs, 1) return dirsdefs endf function! s:GetDirs(args) abort "{{{3 if has_key(a:args, 'path') let dirs = tlib#string#SplitCommaList(a:args.path) elseif has_key(a:args, 'dirs') let dirs = a:args.dirs else let dirs = keys(s:ttodo_dirs) endif return dirs endf function! s:EnrichWithFileargs(filedef) abort "{{{3 " TLogVAR a:filedef let filename = a:filedef.file for [rx, fileargs] in items(g:ttodo#fileargs) " TLogVAR rx if filename =~# rx " TLogVAR fileargs let a:filedef.fileargs = tlib#eval#Extend(copy(a:filedef.fileargs), fileargs) " TLogVAR a:filedef.fileargs endif endfor return a:filedef endf let s:filetasks = {} function! s:filetasks.GetQfeByLnum(lnum) abort dict "{{{3 return get(self.qfe_by_lnum, ''. a:lnum, {}) " for qfe in self.qfl " if qfe.lnum == a:lnum " return qfe " endif " endfor " return {} endf ":nodoc: function! ttodo#GetFileTasks(args, file, fileargs) abort "{{{3 " TLogVAR a:file, keys(a:fileargs) let qfl = [] let task_by_id = {} let qfe_by_lnum = {} let children = {} let lnum = 0 let pred_idx = -1 let filelines = s:GetLines(a:file, a:fileargs) let source = filelines.source for line in filelines.lines let lnum += 1 let task = ttodo#ParseTask(line, a:file, a:args) let id = get(task, 'id', '') let task.__source__ = source " TLogVAR task if get(task, 'subtask', 0) && pred_idx >= 0 " TLogVAR task let indentstr = matchstr(line, '^\%(\s\{1,'. &shiftwidth .'}\)\+') " TLogVAR indentstr let indentstr = substitute(indentstr, '\%(\s\{1,'. &shiftwidth .'}\)', '+', 'g') let indent = len(indentstr) " TLogVAR indentstr, indent let line = substitute(line, '^\s\+', '', '') let parent_idx = pred_idx let parent = qfl[parent_idx] " TLogVAR -1, parent while get(parent.task, '__indent__', 0) >= indent let parent_idx = parent.task.__parent_idx__ let parent = qfl[parent_idx] endwh let parent_indent = get(parent.task, '__indent__', 0) " TLogVAR indent, parent_indent, parent_idx, parent if !task.done " let parent.task.has_subtasks = get(parent.task, 'has_subtasks', 0) + 1 let parent.task.has_subtasks = 1 " TLogVAR parent, parent_idx, pred_idx let qfl[parent_idx] = parent endif if has_key(task, 'due') && task.due > get(parent.task, 'due', task.due) echohl WarningMsg echom 'WARN ttodo#GetFileTasks: subtask has due date after the parent''s task due date: '. line echohl NONE endif let task0 = task let task = tlib#eval#Extend(copy(parent.task), task) let task.has_subtasks = 0 let task.__level__ = get(parent.task, '__level__', 0) + 1 let task.__indent__ = indent let task.__parent_idx__ = parent_idx if !has_key(children, parent_idx) let children[parent_idx] = {} endif let children[parent_idx][pred_idx + 1] = 1 " TLogVAR task, qfl[parent_idx] let line .= ' | '. substitute(s:FormatTask(a:args, copy(parent), 0).text, '^\C\s*\%(x\s\+\)\?\%((\u)\s\+\)\?', '', '') if has_key(parent.task, 'pri') && !has_key(task0, 'pri') " TLogVAR task0 let line = '('. parent.task.pri .') '. line endif " TLogVAR line else " TLogVAR a:file, a:fileargs, line for [prefix, key] in [['@', 'lists'], ['+', 'tags']] if has_key(a:fileargs, key) let vals = a:fileargs[key] let task[key] += vals let line = ttodo#MaybeAppend(line, ttodo#FormatTags(prefix, vals)) endif endfor " TLogVAR line endif let pred_idx += 1 let task.idx = pred_idx let qfe = {"filename": a:file, "lnum": lnum, "text": line, "task": task} call add(qfl, qfe) if !empty(id) let task_by_id[id] = task endif let qfe_by_lnum[lnum] = qfe endfor let qfl = map(qfl, 's:SetPending(v:key, v:val, task_by_id)') let filetasks = extend({'qfl': qfl, 'task_by_id': task_by_id, 'qfe_by_lnum': qfe_by_lnum}, s:filetasks) return filetasks endf function! s:SetPending(idx, qfe, task_by_id) abort "{{{3 let deps = get(a:qfe.task, 'dep', '') if !empty(deps) for dep in tlib#string#SplitCommaList(deps) if has_key(a:task_by_id, dep) let done = get(a:task_by_id[dep], 'done', 1) if !done let a:qfe.task.pending = 1 " TLogVAR a:idx, done break endif endif endfor endif return a:qfe endf function! s:GetLines(filename, fileargs) abort "{{{3 let bufnr = bufnr(a:filename) if bufnr == -1 || !bufloaded(bufnr) let lines = readfile(a:filename) if has('iconv') " TLogVAR keys(a:fileargs) let tenc = get(a:fileargs, 'encoding', &enc) " TLogVAR tenc, &enc if tenc != &enc let lines = map(lines, 'iconv(v:val, tenc, &enc)') endif endif return {'source': 'file', 'lines': lines} else return {'source': 'buffer', 'lines': getbufline(bufnr, 1, '$')} endif endf function! s:GetTasks(args) abort "{{{3 let qfl = [] for filedef in s:GetFiles(a:args) let filetasks = ttodo#GetFileTasks(a:args, filedef.file, filedef.fileargs) if !empty(filetasks) let qfl = extend(qfl, filetasks.qfl) endif endfor return qfl endf let s:parsed_tasks = {} function! ttodo#ParseTask(task, file, ...) abort "{{{3 TVarArg ['args', {}] let cid = join([a:task, a:file, get(args, 'lists', []), get(args, 'tags', [])], "\n") if has_key(s:parsed_tasks, cid) " TLogVAR cid let task = deepcopy(s:parsed_tasks[cid]) else let task = {'text': a:task} for [key, rx] in items(g:ttodo#parse_rx) let val = matchstr(a:task, rx) if key =~ '?$' let key = substitute(key, '?$', '', '') let task[key] = !empty(val) elseif !empty(val) let task[key] = val endif endfor if has_key(task, 'due') if task.due < strftime(g:tlib#date#date_format) let task.overdue = 1 endif endif let task.lists = filter(map(split(a:task, '\ze@'), 'matchstr(v:val, ''^@\zs\S\+'')'), '!empty(v:val)') + get(args, 'lists', []) let task.tags = filter(map(split(a:task, '\ze+'), 'matchstr(v:val, ''^+\zs\S\+'')'), '!empty(v:val)') + get(args, 'tags', []) let task.file = a:file let s:parsed_tasks[cid] = deepcopy(task) endif return task endf function! s:FilterTasks(args) abort "{{{3 let qfl = s:GetTasks(a:args) let task_include_rx = get(a:args, 'task_include_rx', g:ttodo#task_include_rx) let task_exclude_rx = get(a:args, 'task_exclude_rx', g:ttodo#task_exclude_rx) let qfl = filter(qfl, '(empty(task_include_rx) || v:val.text =~ task_include_rx) && (empty(task_exclude_rx) || v:val.text !~ task_exclude_rx)') if has_key(a:args, 'due') let due = a:args.due let today = strftime(g:tlib#date#date_format) if due =~ '^t%\[oday]$' call filter(qfl, 'empty(get(v:val.task, "due", "")) || get(v:val.task, "due", "") <= '. string(today)) elseif due =~ '^\d\+-\d\+-\d\+$' call filter(qfl, 'empty(get(v:val.task, "due", "")) || get(v:val.task, "due", "") <= '. string(due)) else if due =~ '^\d\+w$' let due = matchstr(due, '^\d\+') * 7 endif call filter(qfl, 'empty(get(v:val.task, "due", "")) || tlib#date#DiffInDays(v:val.task.due) <= '. due) endif if get(a:args, 'undated', 0) call filter(qfl, '!empty(get(v:val.task, "due", ""))') endif endif for lst in ['lists', 'tags'] let key = 'has_'. lst if has_key(a:args, key) let vals = a:args[key] call filter(qfl, 's:HasAnyList(v:val.task[lst], vals)') endif let ikey = 'ignore_'. lst if has_key(a:args, ikey) let vals = a:args[ikey] call filter(qfl, '!s:HasAnyList(v:val.task[lst], vals)') endif endfor if !get(a:args, 'has_subtasks', 0) call filter(qfl, 'get(v:val.task, "has_subtasks", 0) == 0') endif if !get(a:args, 'done', 0) call filter(qfl, '!get(v:val.task, "done", 0)') endif if !get(a:args, 'hidden', 0) call filter(qfl, 'empty(get(v:val.task, "hidden", ""))') endif if !get(a:args, 'pending', 0) call filter(qfl, '!get(v:val.task, "pending", 0)') endif if has_key(a:args, 'pri') call filter(qfl, 'get(v:val.task, "pri", "") =~# ''^['. a:args.pri .']$''') endif if has_key(a:args, 'ignore_pri') call filter(qfl, 'empty(get(v:val.task, "pri", "")) || get(v:val.task, "pri", "") !~# ''^['. a:args.ignore_pri .']$''') endif if get(a:args, 'threshold', 1) let today = strftime(g:tlib#date#date_format) call filter(qfl, 's:CheckThreshold(get(v:val.task, "t", ""), get(v:val.task, "due", ""), today)') endif return qfl endf function! s:HasAnyList(tags, vals) abort "{{{3 let rv = !empty(filter(copy(a:tags), 'index(a:vals, v:val) != -1')) " TLogVAR a:tags, a:vals, rv return rv endf function! s:CheckThreshold(t, due, today) abort "{{{3 " TLogVAR a:t, a:due, a:today let t = 0 let tn = '' if !empty(a:t) if a:t =~ '^'. g:tlib#date#date_rx .'$' let t = a:t elseif !empty(a:due) let tn = a:t endif else let tn = g:ttodo#default_t endif if t == 0 && !empty(tn) && !empty(a:due) let n = str2nr(matchstr(tn, '^-\?\d\+\ze[d]$')) if n != 0 if tn =~ 'd$' let t = strftime(g:tlib#date#date_format, tlib#date#SecondsSince1970(a:due) + n * g:tlib#date#dayshift) endif endif endif " TLogVAR tn, t if !empty(t) return a:today >= t else return 1 endif endf let s:sort_params = {} function! s:sort_params.GetSortItem(qfe, task, item, default) abort dict "{{{3 let val = get(a:task, a:item, get(a:qfe, a:item, a:default)) if type(val) > 1 let tmp = val unlet val let val = string(tmp) unlet tmp endif return val endf " function! s:sort_params.GetSortTasks(a, b) abort dict "{{{3 " let api = get(a:a, '__parent_idx__', -1) " let bpi = get(a:b, '__parent_idx__', -1) " while api != bpi " " let al = get(a:a, '__level__', 0) " " let bl = get(a:b, '__level__', 0) " endwh " return " endf function! s:SortTasks(args, qfl) abort "{{{3 " TLogVAR a:qfl let params = {'qfl': a:qfl, 'fields': tlib#string#SplitCommaList(get(a:args, 'sort', g:ttodo#sort))} let params = extend(params, s:sort_params, 'keep') let qfl = sort(a:qfl, 's:SortTask', params) return qfl endf function! s:SortTask(a, b) dict abort "{{{3 let a = a:a.task let b = a:b.task for item in self.fields if item =~# '^-\S\+$' let mul = -1 let item = substitute(item, '^-', '', '') else let mul = 1 endif let default = get(g:ttodo#sort_defaults, item, '') let aa = self.GetSortItem(a:a, a, item, default) let bb = self.GetSortItem(a:b, b, item, default) if item == 'overdue' " TLogVAR aa, bb, default, a, b endif if aa != bb return mul * (aa > bb ? 1 : -1) endif unlet aa bb default endfor return 0 endf function! s:FormatTask(args, qfe, maybe_iconv) abort "{{{3 " TLogVAR a:qfe let text = a:qfe.text let text0 = text " TLogVAR text0 for [rx, subst] in g:ttodo#rewrite_gsub let text = substitute(text, rx, subst, 'g') " TLogVAR rx, subst, text endfor if text !=# text0 let qfe = copy(a:qfe) let qfe.text = text return qfe else return a:qfe endif endf function! ttodo#GetOpts(bang, cmdargs) abort "{{{3 let args = tlib#arg#GetOpts(a:cmdargs, s:ttodo_args) let pref = get(args, 'pref', a:bang ? 'important' : 'default') Tlibtrace 'ttodo', pref let args = tlib#eval#Extend(copy(g:ttodo#prefs[pref]), args) Tlibtrace 'ttodo', keys(args) return args endf ":nodoc: function! ttodo#Show(bang, cmdargs) abort "{{{3 let args = ttodo#GetOpts(a:bang, a:cmdargs) Tlibtrace 'ttodo', args " TLogVAR args if args.__exit__ return else " TLogVAR args let qfl = copy(s:FilterTasks(args)) let qfl = s:SortTasks(args, qfl) let qfl = map(qfl, 's:FormatTask(args, v:val, 1)') let flt = get(args, '__rest__', []) if !empty(qfl) if g:ttodo#viewer ==# 'tlib' let w = copy(s:list_env) if !empty(flt) Tlibtrace 'ttodo', flt let w.initial_filter = [[""], flt] endif let overdue_rx = s:QflOverdueRx(qfl) if !empty(overdue_rx) let w.overdue_rx = overdue_rx endif call tlib#qfl#QflList(qfl, w) elseif g:ttodo#viewer =~# '^:' if exists(g:ttodo#viewer) == 2 if !empty(flt) let qfl = filter(qfl, 's:FilterQFL(v:val, flt)') endif call setqflist(qfl) exec g:ttodo#viewer endif else throw 'TTodo: Unsupported value for g:ttodo#viewer: '. string(g:ttodo#viewer) endif endif endif endf function! ttodo#GetOverdueRx(args) abort "{{{3 let qfl = s:GetTasks(a:args) return s:QflOverdueRx(qfl) endf function! s:QflOverdueRx(qfl) abort "{{{3 let overdue = filter(copy(a:qfl), 'get(v:val.task, "overdue", 0)') if !empty(overdue) return '\V\' else return '' endif endf function! ttodo#InitListBuffer() dict abort "{{{3 call call('tlib#qfl#SetSyntax', [], self) if has_key(self, 'overdue_rx') Tlibtrace self.overdue_rx " TLogVAR self.overdue_rx exec 'syntax match TtodoOverdue /'. escape(self.overdue_rx, '/') .'/ contained containedin=TtodoTag' hi def link TtodoOverdue ErrorMsg syntax cluster TtodoTask add=TtodoOverdue endif endf function! s:FilterQFL(item, flts) abort "{{{3 let text = a:item.text for flt in a:flts if text !~ flt return 0 endif endfor return 1 endf ":nodoc: function! ttodo#CComplete(ArgLead, CmdLine, CursorPos) abort "{{{3 let words = tlib#arg#CComplete(s:ttodo_args, a:ArgLead) if !empty(a:ArgLead) if a:ArgLead =~# '^--pref=' let words = keys(g:ttodo#prefs) let apref = substitute(a:ArgLead, '^--pref=', '', '') if !empty(apref) let nchar = len(apref) call filter(words, 'strpart(v:val, 0, nchar) ==# apref') endif let words = map(words, '"--pref=". v:val') elseif a:ArgLead =~# '^@' let words = map(ttodo#CollectTags("lists"), '"@". v:val') let nchar = len(a:ArgLead) call filter(words, 'strpart(v:val, 0, nchar) ==# a:ArgLead') elseif a:ArgLead =~# '^+' let words = map(ttodo#CollectTags("tags"), '"+". v:val') let nchar = len(a:ArgLead) call filter(words, 'strpart(v:val, 0, nchar) ==# a:ArgLead') else let nchar = len(a:ArgLead) let texts = {} for task in s:GetTasks({}) for word in split(task.text, '\s\+') if strpart(word, 0, nchar) ==# a:ArgLead let texts[word] = 1 endif endfor endfor let words += sort(keys(texts)) endif endif return words endf function! ttodo#CollectTags(type) abort "{{{3 let args = {} if &ft == 'ttodo' let args.bufname = '%' endif let tasks = s:GetTasks(args) let tags = map(copy(tasks), 'v:val.task[a:type]') let tags = tlib#list#Flatten(tags) let tags = filter(tags, '!empty(v:val)') let tags = tlib#list#Uniq(tags) let tags = sort(tags) return tags endf function! ttodo#FiletypeDetect(...) abort "{{{3 let filename = a:0 >= 1 ? a:1 : expand('%:p') " TLogVAR filename let bdir = substitute(filename, '\\', '/', 'g') let bdir = substitute(bdir, '/[^/]\+$', '', '') let dirs = map(keys(s:ttodo_dirs), 'substitute(resolve(fnamemodify(v:val, ":p:h")), ''\\'', ''/'', ''g'')') " TLogVAR bdir, dirs if index(dirs, bdir, 0, !has('fname_case')) != -1 setf ttodo " TLogVAR &ft endif endf " If called with --sortseps=tags,lists, an empty line is inserted after " each main (i.e. first) list or tag. " " Sorting doesn't work for outlines, i.e. tasks with subtasks. function! ttodo#SortBuffer(cmdargs) abort "{{{3 let args = ttodo#GetOpts(0, a:cmdargs) let filename = expand('%:p') let filetasks = ttodo#GetFileTasks(args, filename, {}) let qfl = filetasks.qfl if !empty(filter(copy(qfl), 'get(v:val.task, "subtask", 0)')) throw 'ttodo#SortBuffer: Cannot sort task outlines!' endif let qfl = s:SortTasks(args, qfl) let seps = get(args, 'sortseps', []) " TLogVAR seps if empty(seps) let tasks = map(qfl, 'v:val.task.text') else let tasks = [] let last = {} for qfe in qfl for sep in seps if !empty(last) && get(get(last.task, sep, []), 0, '') != get(get(qfe.task, sep, []), 0, '') " TLogVAR last, qfe call add(tasks, '') break endif endfor call add(tasks, qfe.task.text) let last = qfe endfor endif let pos = getpos('.') try 1,$delete call append(1, tasks) 1delete finally call setpos('.', pos) endtry endf function! ttodo#NewTask(cmdargs) abort "{{{3 let args = extend(copy(g:ttodo#new_task), ttodo#GetOpts(0, a:cmdargs)) let filename = get(args, 'inboxfile', '') if empty(filename) let dirdef = s:GetDefaultDirDef(args, '.') " TLogVAR keys(dirdef), dirdef.__name__ let filename = tlib#file#Join([dirdef.__name__, get(args, 'inbox', get(dirdef, 'inbox', g:ttodo#inbox))]) endif let text = join(args.__rest__) let text = ttodo#MaybeAppend(text, get(args, 'suffix', '')) let text = ttodo#MaybeAppend(text, ttodo#FormatTags('@', get(args, 'lists', []))) let text = ttodo#MaybeAppend(text, ttodo#FormatTags('+', get(args, 'tags', []))) let args = extend(args, ttodo#ParseTask(text, filename)) exec 'tab drop' fnameescape(filename) let text = substitute(text, '\C^\s*(\u)\s*', '', '') exec 'norm!' ttodo#ftplugin#New('G', 0, 'n', args) . text endf function! ttodo#FormatTags(prefix, lists) abort "{{{3 " TLogVAR a:prefix, a:lists return join(map(copy(a:lists), 'a:prefix . v:val')) endf function! ttodo#MaybeAppend(text, suffix) abort "{{{3 if empty(a:suffix) return a:text else return a:text .' '. a:suffix endif endf function! ttodo#FileSources(args) abort "{{{3 Tlibtrace 'ttodo', a:args let args = a:args let pref = get(args, 'pref', 'default') Tlibtrace 'ttodo', pref let args = tlib#eval#Extend(copy(g:ttodo#prefs[pref]), args) let pattern = get(args, 'glob', get(args, 'deep', 1) ? '**' : '*') let globs = {} let dirsdefs = s:GetDirsDefs(args) for [dir, dirargs] in items(dirsdefs) let glob = tlib#file#Join([dir, pattern]) Tlibtrace 'ttodo', glob let globs[glob] = 1 endfor return keys(globs) endf function! ttodo#InputNumber(prompt) abort "{{{3 let s = tlib#string#Input(a:prompt) return empty(s) ? -1 : str2nr(s) endf autoload/ttodo/ftplugin.vim [[[1 357 " @Author: Tom Link (mailto:micathom AT gmail com?subject=[vim]) " @Website: https://github.com/tomtom " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Last Change: 2016-01-21 " @Revision: 282 if !exists('g:ttodo#ftplugin#notef') let g:ttodo#ftplugin#notef = 'notes/%s/%s-%s.md' "{{{2 endif if !exists('g:ttodo#ftplugin#note_prefix') let g:ttodo#ftplugin#note_prefix = 'file://' "{{{2 endif if !exists('g:ttodo#ftplugin#edit_note') let g:ttodo#ftplugin#edit_note = 'split' "{{{2 endif if !exists('g:ttodo#ftplugin#add_at_eof') " If true, the or map will make ttodo add a new task at " the end of the file. Otherwise the task will be added below the " current line. " Subtasks will always be added below the current line. let g:ttodo#ftplugin#add_at_eof = 1 "{{{2 endif if !exists('g:ttodo#ftplugin#rec_copy') " If true, marking a recurring task as "done" will mark the old task " as completed and will then create a new updated task. let g:ttodo#ftplugin#rec_copy = 1 "{{{2 endif function! ttodo#ftplugin#Archive(filename) abort "{{{3 let basename = fnamemodify(a:filename, ':t') let arc = fnamemodify(a:filename, ':p:h') .'/done.txt' if filereadable(arc) && !filewritable(arc) throw 'TTodo: Cannot write: '. arc endif if !filewritable(a:filename) throw 'TTodo: Cannot write: '. a:filename endif let done = [] let undone = [] let today = strftime(g:tlib#date#date_format) let filetasks = ttodo#GetFileTasks({}, a:filename, {}) let lines = readfile(a:filename) for lnum0 in range(len(lines)) let task = filetasks.GetQfeByLnum(lnum0 + 1).task " let line = lines[lnum0] let line = task.text if line != lines[lnum0] throw 'Ttodo: Internal error: Lines differ: '. string(line) .' != '. string(lines[lnum0]) endif if get(task, 'done', 0) && !get(task, 'has_subtasks', 0) && !get(task, 'pending', 0) let line .= ' archive:'. today let line .= ' source:'. basename call add(done, line) else call add(undone, line) endif endfor if !empty(done) if filereadable(arc) let done = readfile(arc) + done endif call writefile(done, arc) call writefile(undone, a:filename) endif endf function! ttodo#ftplugin#ArchiveCurrentBuffer() abort "{{{3 update call ttodo#ftplugin#Archive(expand('%:p')) edit endf function! ttodo#ftplugin#Note() abort "{{{3 let line = getline('.') let task = ttodo#ParseTask(line, expand('%:p')) Tlibtrace 'ttodo', task let nname = get(task.lists, 0, 'misc') let nname = substitute(nname, '\W', '_', 'g') let date = strftime('%Y%m%d') let dir = expand('%:p:h') Tlibtrace 'ttodo', nname, dir let n = 0 while 1 let shortname = printf(g:ttodo#ftplugin#notef, nname, date, n) let filename = tlib#file#Join([dir, shortname]) if filereadable(filename) let n += 1 else let notename = g:ttodo#ftplugin#note_prefix . shortname break endif endwh Tlibtrace 'ttodo', filename call setline('.', join([line, notename])) call tlib#dir#Ensure(fnamemodify(filename, ':p:h')) exec g:ttodo#ftplugin#edit_note fnameescape(filename) endf function! ttodo#ftplugin#New(move, copytags, mode, ...) abort "{{{3 " TLogVAR a:move, a:copytags if a:mode == 'i' let o = "\" else let o = "o" endif let new = strftime(g:tlib#date#date_format) if indent('.') > 0 && empty(a:move) return o . new .' ' elseif a:move == '>' return o ."\" . new .' ' else let o .= "\" let task = a:0 >= 1 ? a:1 : ttodo#ParseTask(getline('.'), expand('%:p')) if a:copytags let new = ttodo#MaybeAppend(new, ttodo#FormatTags('@', task.lists)) let new = ttodo#MaybeAppend(new, ttodo#FormatTags('+', task.tags)) endif if has_key(task, 'pri') let new = '('. task.pri .') '. new endif let move = a:move if a:mode == 'i' && !empty(move) let move = "\\" endif return move . o . new .' ' endif endf function! s:IsDone(line) abort "{{{3 return a:line =~# '^\s*x\s' endf function! ttodo#ftplugin#MarkDone(count, ...) abort "{{{3 TVarArg ['ignore_rec', 0] let donedate = strftime(g:tlib#date#date_format) let lnum0 = line('.') for lnum in range(lnum0, lnum0 + a:count) let line = getline(lnum) let task = ttodo#ParseTask(line, expand('%:p')) if !get(task, 'done', 0) if !ignore_rec let rec = get(task, 'rec', '') if !empty(rec) if get(task, 'subtask', 0) echohl Error echom "TTodo: Cannot complete subtasks with a 'rec:' tag:" line continue elseif get(task, 'has_subtask', 0) " TODO: Must copy the whole tree echohl Error echom "TTodo: Cannot complete top tasks with a 'rec:' tag:" line continue else if g:ttodo#ftplugin#rec_copy call ttodo#ftplugin#MarkDone(0, 1) call append('$', line) let lnum = line('$') endif let due = get(task, 'due', '') let shift = matchstr(rec, '\d\+\a$') let refdate = rec =~ '^+' && !empty(due) ? due : donedate let ndue = empty(due) ? donedate : due " TLogVAR rec, due, shift, refdate while 1 let ndue = tlib#date#Shift(ndue, shift) " TLogVAR ndue if ndue > refdate break endif endwh exec lnum call s:MarkDueDate(ndue) if has_key(task, 't') && task.t =~# g:tlib#date#date_rx let t0 = task.t let t0s = tlib#date#SecondsSince1970(t0) let dues = tlib#date#SecondsSince1970(due) let t0diff = dues - t0s let ndues = tlib#date#SecondsSince1970(ndue) let t1s = ndues - t0diff let t1 = tlib#date#Format(t1s) call s:SetTag('t', g:tlib#date#date_rx, t1) endif continue endif endif endif endif if get(task, 'subtask', 0) && !has_key(task, 'parent') let parent = s:GetParentId(lnum) if !empty(parent) call setline(lnum, line .' parent:'. parent) endif endif exec lnum .'s/^\s*\zs\C\%(x\s\+\%('. g:tlib#date#date_rx .'\s\+\)\?\)\?/x '. donedate .' /' endfor exec lnum0 + a:count endf function! s:GetParentId(lnum0) abort "{{{3 let indent0 = indent(a:lnum0) let filename = expand('%:p') for lnum in range(a:lnum0 - 1, 1, -1) let indent = indent(lnum) if indent < indent0 let line = getline(lnum) let task = ttodo#ParseTask(line, filename) if !has_key(task, 'id') exec lnum call ttodo#ftplugin#AddId(0) let task = ttodo#ParseTask(getline(lnum), filename) endif return task.id endif endfor return '' endf function! ttodo#ftplugin#MarkDue(unit, count) abort "{{{3 if a:count == -1 elseif s:IsDone(getline('.')) echohl WarningMsg throw 'Ttodo: Cannot change finshed task' echohl NONE else if type(a:count) == 0 let n = a:count elseif type(a:count) == 1 call inputsave() let counts = input(a:count) call inputrestore() if empty(counts) return else let n = str2nr(counts) endif else throw 'ttodo#ftplugin#MarkDue: count must be a number or a string' endif let today = strftime(g:tlib#date#date_format) let due = tlib#date#Shift(today, n . a:unit) call s:MarkDueDate(due) endif endf function! s:MarkDueDate(date) abort "{{{3 call s:SetTag('due', g:tlib#date#date_rx, a:date) " exec 's/\C\%(\s\+due:'. g:tlib#date#date_rx .'\|$\)/ due:'. a:date .'/' endf function! s:SetTag(name, rx, value) abort "{{{3 exec 's/\C\%(\s\+'. a:name .':'. escape(a:rx, '/') .'\|$\)/ '. a:name .':'. escape(a:value, '/') .'/' endf function! ttodo#ftplugin#SetPriority(count) abort "{{{3 " TLogVAR a:count if s:IsDone(getline('.')) echohl WarningMsg throw 'Ttodo: Cannot change finshed task' echohl NONE else call inputsave() let category = input('New task category [A-Z]: ') call inputrestore() let category = toupper(category) if category =~ '\C^[A-Z]$' exec 's/^\s*\zs\C\%((\u)\s\+\)\?/('. category .') /' else echohl WarningMsg echom 'Invalid category (must be A-Z):' category echohl NONE endif endif endf function! ttodo#ftplugin#Agent(world, selected, fn, ...) abort "{{{3 " TLogVAR a:fn, a:000 let cmd = printf('call call(%s, %s)', string(a:fn), string(map(copy(a:000), 's:AgentEvalArg(v:val)'))) Tlibtrace 'ttodo', cmd let world = tlib#qfl#RunCmdOnSelected(a:world, a:selected, cmd) return world endf function! s:AgentEvalArg(arg) abort "{{{3 if type(a:arg) == 1 && a:arg =~ '^\\=' return eval(substitute(a:arg, '^\\=', '', '')) elseif type(a:arg) == 1 && a:arg =~ '^".\{-}"$' return matchstr(a:arg, '^"\zs.\{-}\ze"$') else return a:arg endif endf function! ttodo#ftplugin#AddId(count) abort "{{{3 let filename = expand('%:p') let filetasks = ttodo#GetFileTasks({}, filename, {}) let fqfl = filetasks.qfl for lnum in range(line('.'), line('.') + a:count) let line = getline(lnum) let task = ttodo#ParseTask(line, filename) if !has_key(task, 'id') let qfe = filetasks.GetQfeByLnum(lnum) let ttask = string(empty(qfe) ? task : qfe) let id = tlib#hash#Adler32(ttask) let line .= ' id:'. id call setline(lnum, line) endif endfor endf function! ttodo#ftplugin#SyntaxDue() abort "{{{3 let overdue_rx = ttodo#GetOverdueRx({'bufname': '%'}) if !empty(overdue_rx) exec 'syntax match TtodoOverdue /'. escape(overdue_rx, '/') .'/' endif exec 'syntax match TtodoDue /\/' endf function! ttodo#ftplugin#AddDep() abort "{{{3 let filename = expand('%:p') let filetasks = ttodo#GetFileTasks({}, filename, {}) " TLogVAR len(filetasks.qfl), len(filetasks.task_by_id) let with_id = values(map(copy(filetasks.task_by_id), 'v:key ."\t". v:val.text')) if empty(with_id) echom 'ttodo#ftplugin#AddDep: No IDs' else let dep = tlib#input#List('s', 'Select dependency:', with_id) if !empty(dep) let id = matchstr(dep, '^[^\t]\+\ze\t') call setline('.', getline('.') .' dep:'. id) endif endif endf ftplugin/ttodo.vim [[[1 56 " @Author: Tom Link (mailto:micathom AT gmail com?subject=[vim]) " @Website: https://github.com/tomtom " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Last Change: 2015-11-30. " @Revision: 97 if exists("b:did_ftplugin") finish endif let b:did_ftplugin = 1 let s:save_cpo = &cpo set cpo&vim setlocal textwidth=0 setlocal linebreak setlocal commentstring=x\ %s setlocal formatoptions=cql exec 'nnoremap ' g:ttodo#mapleader .'x :call ttodo#ftplugin#MarkDone(v:count)' exec 'nnoremap ' g:ttodo#mapleader .'d :call ttodo#ftplugin#MarkDue("d", v:count)' exec 'nnoremap ' g:ttodo#mapleader .'w :call ttodo#ftplugin#MarkDue("w", v:count1)' exec 'nnoremap ' g:ttodo#mapleader .'m :call ttodo#ftplugin#MarkDue("m", v:count1)' exec 'nnoremap ' g:ttodo#mapleader .'y :call ttodo#ftplugin#SetPriority(v:count)' exec 'nnoremap ' g:ttodo#mapleader .'a :call ttodo#ftplugin#ArchiveCurrentBuffer()' exec 'nnoremap ' g:ttodo#mapleader .'n :call ttodo#ftplugin#Note()' exec 'nnoremap ' g:ttodo#mapleader .'i :call ttodo#ftplugin#AddId(v:count)' exec 'nnoremap ' g:ttodo#mapleader .'D :call ttodo#ftplugin#AddDep()' exec 'nnoremap ' g:ttodo#mapleader .'b :Ttodo --bufname=%' exec 'nnoremap ' g:ttodo#mapleader .'* :Ttodo --bufname=% ' nnoremap ttodo#ftplugin#New(g:ttodo#ftplugin#add_at_eof ? "G" : "", 0, "n") nnoremap ttodo#ftplugin#New(g:ttodo#ftplugin#add_at_eof ? "G" : "", 1, "n") nnoremap ttodo#ftplugin#New(">", 1, "n") inoremap ttodo#ftplugin#New(g:ttodo#ftplugin#add_at_eof ? "G" : "", 0, "i") inoremap ttodo#ftplugin#New(g:ttodo#ftplugin#add_at_eof ? "G" : "", 1, "i") inoremap ttodo#ftplugin#New(">", 1, "i") " Sort the tasks in the current buffer. " " Sorting task outlines (i.e. subtasks) is not supported. command! -buffer -bar -nargs=* -complete=customlist,ttodo#CComplete Ttodosort call ttodo#SortBuffer([]) " Archive completed tasks in the current buffer. command! -buffer -bar Ttodoarchive call ttodo#ftplugin#ArchiveCurrentBuffer() " View the tasks in the current buffer. command! -buffer -bar -nargs=* Ttodobuffer Ttodo --bufname=% " Add a new task to the task at the cursor. command! -buffer -bar Ttodonote call ttodo#ftplugin#Note() let &cpo = s:save_cpo unlet s:save_cpo ftdetect/ttodo.vim [[[1 2 autocmd BufNewFile,BufRead *todo.txt if exists('g:ttodo_enable_ftdetect') && g:ttodo_enable_ftdetect | call ttodo#FiletypeDetect(expand(":p")) | endif autocmd BufNewFile,BufRead done.txt if exists('g:ttodo_enable_ftdetect') && g:ttodo_enable_ftdetect | call ttodo#FiletypeDetect(expand(":p")) | endif syntax/ttodo.vim [[[1 66 " @Author: Tom Link (mailto:micathom AT gmail com?subject=[vim]) " @Website: https://github.com/tomtom " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Last Change: 2016-01-14. if version < 600 syntax clear elseif exists("b:current_syntax") finish endif if has('conceal') && &enc == 'utf-8' let s:sym_cluster = [] for [s:name, s:chars, s:cchar] in [ \ ['Dash', '--', '—'], \ ['Unequal', '!=', '≠'], \ ['Identity', '==', '≡'], \ ['Approx', '~~', '≈'], \ ['ArrowLR', '<-\+>', '↔'], \ ['ArrowL', '<-\+', '←'], \ ['ArrowR', '-\+>', '→'], \ ['ARROWLR', '<=\+>', '⇔'], \ ['ARROWL', '<=\+', '⇐'], \ ['ARROWR', '=\+>', '⇒'], \ ['ArrowTildeLR', '<~\+>', '↭'], \ ['ArrowTildeL', '<~\+', '↜'], \ ['ArrowTildeR', '~\+>', '↝'], \ ['Ellipsis', '...', '…'], \ ] exec 'syn match ttodoSymbol'. s:name .' /\V'. s:chars .'/ conceal cchar='. s:cchar endfor unlet! s:sym_cluster s:name s:chars s:cchar endif syntax match TtodoPri /([D-Z])/ syntax match TtodoPriA /(A)/ syntax match TtodoPriB /(B)/ syntax match TtodoPriC /(C)/ syntax match TtodoList /@\S\+/ syntax match TtodoKeyword /+\S\+/ syntax match TtodoTag /\<\a\+:\S\+/ syntax match TtodoDate /\<\d\{4}-\d\d-\d\d\>/ syntax match TtodoTime /\<\d\d:\d\d\>/ " syntax match TtodoDone /\.*$/ call ttodo#ftplugin#SyntaxDue() syntax cluster TtodoTask contains=TtodoPri,TtodoPriA,TtodoPriB,TtodoPriC,TtodoList,TtodoKeyword,TtodoTag,TtodoDate,TtodoTime,TtodoDone,TtodoHidden,TtodoOverdue hi def link TtodoPri Special hi def TtodoPriA term=bold,underline cterm=bold gui=bold guifg=Black ctermfg=Black ctermbg=Red guibg=Red hi def TtodoPriB term=bold,underline cterm=bold gui=bold guifg=Black ctermfg=Black ctermbg=Brown guibg=Orange hi def TtodoPriC term=bold,underline cterm=bold gui=bold guifg=Black ctermfg=Black ctermbg=Yellow guibg=Yellow hi def link TtodoList Identifier hi def link TtodoKeyword Constant hi def link TtodoTag Type hi def link TtodoDate Statement hi def link TtodoTime Statement hi def link TtodoDone Comment hi def link TtodoHidden Comment hi def link TtodoOverdue ErrorMsg hi def link TtodoDue WarningMsg let b:current_syntax = 'ttodo'