Add support for recurring tasks ("rec:" keyword)

This commit is contained in:
fretep
2017-10-15 20:22:22 +11:00
parent f982b9c581
commit d6256bcfe3
7 changed files with 389 additions and 38 deletions

View File

@@ -209,6 +209,26 @@ Defaults values are:
For more information on the available flags see `help :sort`
## Recurrence
By adding a `rec:` tag to your task, when you complete (`<LocalLeader>x`) or
postpone (`<LocalLeader>p`) the task, a new recurrence will be created due after
the specified amount of time.
The format is:
`rec:[+][count][d|w|m|y]`
Where:
d = days, w = weeks, m = months, y = years
The optional `+` specifies strict recurrence (see below)
Examples:
* `rec:2w` - Recurs two weeks after the task is completed.
* `rec:3d` - Recurs three days after the task is completed.
* `rec:+1w` - Recurs one week from the due date (strict)
This is a non-standard but widely adopted keyword.
## Mappings
By default todo-txt.vim sets all the mappings described in this section. To

View File

@@ -95,6 +95,7 @@ function! todo#UnMarkAsDone(status)
endfunction
function! todo#MarkAsDone(status)
call todo#CreateNewRecurrence(1)
exec ':s/\C^(\([A-Z]\))\(.*\)/\2 pri:\1/e'
if a:status!=''
exec 'normal! I'.a:status.' '
@@ -359,14 +360,99 @@ function! Todo_txt_InsertSpaceIfNeeded(str)
retur a:str
endfunction
" function todo#CreateNewRecurrence {{{2
function! todo#CreateNewRecurrence(triggerOnNonStrict)
" Given a line with a rec:timespan, create a new task based off the
" recurrence and move the recurring tasks due:date to the next occurrence.
"
" This is implemented by a few other systems, so we will try to be as
" compatible as possible with the existing specifications.
"
" Other example implementations:
" <http://swiftodoapp.com/>
" <https://github.com/bram85/todo.txt-tools/wiki/Recurrence>
"
let l:currentline = getline('.')
" Don't operate on complete tasks
if l:currentline =~# '^x '
return
endif
let l:rec_date_rex = '\v\c(^|\s)rec:(\+)?(\d+)([dwmy])(\s|$)'
let l:rec_parts = matchlist(l:currentline, l:rec_date_rex)
" Don't operate on tasks without a valid "rec:" keyword.
if empty(l:rec_parts)
" If a "rec:" keyword exists, but it didn't match our expectations, warn
" the user, and abort whatever is happening otherwise a recurring task
" might be marked complete without a new recurrence being created.
if l:currentline =~? '\v\c(^|\s)rec:'
throw "Recurrence pattern is invalid. Aborting operation."
endif
return
endif
" Operations like postponing a task should not trigger the task to be
" duplicated, non-strict mode allows the changing of the due date.
let l:is_strict = l:rec_parts[2] ==# "+"
if ! a:triggerOnNonStrict && ! l:is_strict
return
endif
let l:units = str2nr(l:rec_parts[3])
if l:units < 1
let l:units = 1
endif
let l:unit_type = l:rec_parts[4]
" If we had a space on both sides of the "rec:" that we are removing, then
" we need to insert a space, otherwise, not.
if l:rec_parts[1] ==# ' ' && l:rec_parts[5] ==# ' '
let l:replace_string = ' '
else
let l:replace_string = ''
endif
" New task should have the rec: keyword stripped
let l:newline = substitute(l:currentline, l:rec_date_rex, l:replace_string, '')
" Insert above current line
let l:new_task_line_num = line('.')
if append(l:new_task_line_num - 1, l:newline) != 0
throw "Failed at append line"
endif
" At this point, we need to change the due date of the recurring task.
" Modes:
" Strict mode: From the existing due date
" Non-Strict mode: From the current date
" So, we don't need to do anything for strict mode. Non-strict mode requires
" setting the current date.
if l:is_strict
call todo#ChangeDueDate(l:units, l:unit_type, '')
else
call todo#ChangeDueDate(l:units, l:unit_type, strftime('%Y-%m-%d'))
endif
" Move onto the copied task
call cursor(l:new_task_line_num, col('.'))
if l:new_task_line_num != line('.')
throw "Failed to move cursor"
endif
endfunction
" function todo#ChangeDueDate {{{2
function! todo#ChangeDueDate(units, unit_type)
function! todo#ChangeDueDate(units, unit_type, from_reference)
" Change the due:date on the current line by a number of days, months or
" years
"
" units is the number of unit_type to add or subtract, integer values only
" unit_type may be one of 'd' (days), 'm' (months) or 'y' (years), as
" handled by todo#DateAdd
" units The number of unit_type to add or subtract, integer
" values only
" unit_type May be one of 'd' (days), 'm' (months) or 'y' (years),
" as handled by todo#DateAdd
" from_reference Allows passing a different date to base the calculation
" on, ignoring the existing due date in the line. Leave as
" an empty string to use the due:date in the line,
" otherwise a date as a string in the form "YYYY-MM-DD".
let l:currentline = getline('.')
@@ -378,7 +464,7 @@ function! todo#ChangeDueDate(units, unit_type)
let l:dueDateRex = '\v\c(^|\s)due:\zs\d{4}\-\d{2}\-\d{2}\ze(\s|$)'
let l:duedate = matchstr(l:currentline, l:dueDateRex)
if l:duedate == ''
if l:duedate ==# ''
" No due date on current line, then add the due date as an offset from
" current date. I.e. a v:count of 1 is due tomorrow, etc
if l:currentline =~? '\v\c(^|\s)due:'
@@ -389,6 +475,10 @@ function! todo#ChangeDueDate(units, unit_type)
let l:duedate = strftime('%Y-%m-%d')
let l:currentline .= ' due:' . l:duedate
endif
" If a valid reference has been passed, let's use it.
if a:from_reference =~# '\v^\d{4}\-\d{2}\-\d{2}$'
let l:duedate = a:from_reference
endif
let l:duedate = todo#DateStringAdd(l:duedate, v:count1 * a:units, a:unit_type)
@@ -430,6 +520,7 @@ function! todo#DateAdd(year, month, day, units, unit_type)
" units is the number of unit_type to add or subtract, integer values only
" unit_type may be one of:
" d days
" w weeks, 7 days
" m months, keeps the day of the month static except in the case
" that the day is the last day in the month or the day is higher
" than the number of days in the resultant month, where the result
@@ -438,7 +529,7 @@ function! todo#DateAdd(year, month, day, units, unit_type)
" 2017-01-31 +1m 2017-02-28 +1m 2017-03-31 +1m 2017-04-30
" 2017-01-30 +1m 2017-02-28 +1m 2017-03-31
" 2017-01-30 +2m 2017-03-30
" y years
" y years, 12 months
" It is my understanding that VIM does not have date math functionality
@@ -446,7 +537,7 @@ function! todo#DateAdd(year, month, day, units, unit_type)
" all that scary to roll our own - we just need to watch out for leap years.
" Check and clean up input
if index(["d", "m", "y"], a:unit_type) < 0
if index(["d", "D", "w", "W", "m", "M", "y", "Y"], a:unit_type) < 0
throw 'Invalid unit "'. a:unit_type . '" passed to todo#DateAdd()'
endif
@@ -455,10 +546,13 @@ function! todo#DateAdd(year, month, day, units, unit_type)
let l:y = str2nr(a:year)
let l:i = str2nr(a:units)
" Years can be handled simply as 12 x months
if a:unit_type == "y"
" Years can be handled simply as 12 x months, weeks as 7 x days
if a:unit_type ==? "y"
let l:utype = "m"
let l:i = l:i * 12
elseif a:unit_type ==? "w"
let l:utype = "d"
let l:i = l:i * 7
else
let l:utype = a:unit_type
endif
@@ -472,7 +566,7 @@ function! todo#DateAdd(year, month, day, units, unit_type)
endif
endif
if l:m > 12
if l:i < 0 && l:utype == "m"
if l:i < 0 && l:utype ==? "m"
" Subtracting an invalid (high) month
" See comments for passing a high day below. Same reason for this.
let l:m = 13
@@ -508,7 +602,7 @@ function! todo#DateAdd(year, month, day, units, unit_type)
" let l:d = l:daysInMonth
" endif
if l:utype == "d"
if l:utype ==? "d"
" Adding DAYS
while l:i > 0
let l:d += 1
@@ -543,7 +637,7 @@ function! todo#DateAdd(year, month, day, units, unit_type)
endif
let l:i += 1
endwhile
elseif l:utype == "m"
elseif l:utype ==? "m"
if l:d >= l:daysInMonth
let l:wasLastDayOfMonth = 1
else

View File

@@ -22,12 +22,13 @@ Table of Contents *TodoTxt-Contents* ~
3. TodoTxt Files.................................|TodoTxt-Files|
4. Completion....................................|TodoTxt-Completion|
5. Hierarchical Sort.............................|TodoTxt-HierarchicalSort|
6. Mappings......................................|TodoTxt-Mappings|
6.1 Sort.....................................|TodoTxt-Sort|
6.2 Priorities...............................|TodoTxt-Priorities|
6.3 Dates....................................|TodoTxt-Dates|
6.4 Done.txt.................................|TodoTxt-Done|
6.5 Format...................................|TodoTxt-Format|
6. Recurrence....................................|TodoTxt-Recurrence|
7. Mappings......................................|TodoTxt-Mappings|
7.1 Sort.....................................|TodoTxt-Sort|
7.2 Priorities...............................|TodoTxt-Priorities|
7.3 Dates....................................|TodoTxt-Dates|
7.4 Done.txt.................................|TodoTxt-Done|
7.5 Format...................................|TodoTxt-Format|
===============================================================================
1. Release notes *TodoTxt-ReleaseNotes* ~
@@ -219,9 +220,28 @@ Defaults values are:
<
For more information on the available flags see |:sort|
===============================================================================
6. Mappings *TodoTxt-Mappings* ~
6. Recurrence *TodoTxt-Recurrence* ~
By adding a "rec:" tag to your task, when you complete (`<LocalLeader>x`) or
postpone (`<LocalLeader>p`) the task, a new recurrence will be created due after
the specified amount of time.
The format is:
`rec:[+][count][d|w|m|y]`
Where:
d = days, w = weeks, m = months, y = years
The optional `+` specifies strict recurrence (see below)
Examples:
* `rec:2w` - Recurs two weeks after the task is completed.
* `rec:3d` - Recurs three days after the task is completed.
* `rec:+1w` - Recurs one week from the due date (strict)
This is a non-standard but widely adopted keyword.
===============================================================================
7. Mappings *TodoTxt-Mappings* ~
By default todo-txt.vim set all the mappings described in this section. To
prevent this behavior, add the following line to your vimrc
@@ -229,7 +249,7 @@ prevent this behavior, add the following line to your vimrc
let g:Todo_txt_do_not_map=1
<
6.1 Sort *TodoTxt-Sort*
7.1 Sort *TodoTxt-Sort*
`<LocalLeader>s` : Sort the file by priority
@@ -262,7 +282,7 @@ Possible values are :
+ `notoverdue`: The first task that is not overdue (requires #13)
+ `bottom`: The last line of the buffer
6.2 Priorities *TodoTxt-Priorities*
7.2 Priorities *TodoTxt-Priorities*
`<LocalLeader>j` : Lower the priority of the current line
@@ -274,7 +294,7 @@ Possible values are :
`<LocalLeader>c` : Add the priority (C) to the current line
6.3 Dates *TodoTxt-Dates*
7.3 Dates *TodoTxt-Dates*
`<LocalLeader>d` : Insert the current date
@@ -294,7 +314,7 @@ following to your vimrc:
let g:Todo_txt_prefix_creation_date=1
<
6.4 Done *TodoTxt-Done*
7.4 Done *TodoTxt-Done*
`<LocalLeader>x` : Toggle mark task as done (inserts or remove current
@@ -308,6 +328,6 @@ following to your vimrc:
`<LocalLeader>` is \ by default, so ̀`<LocaLeader>-s` means you type \s
6.5 Format *TodoTxt-format*
7.5 Format *TodoTxt-format*
`<LocalLeader>ff` : Try to fix todo.txt format

View File

@@ -79,6 +79,7 @@ if !exists("g:Todo_txt_do_not_map") || ! g:Todo_txt_do_not_map
" try fix format {{{2
nnoremap <script> <silent> <buffer> <localleader>ff :call todo#FixFormat()<CR>
" increment and decrement due:date {{{2
nmap <localleader>p <Plug>TodotxtIncrementDueDateNormal
vmap <localleader>p <Plug>TodotxtIncrementDueDateVisual
nmap <localleader>P <Plug>TodotxtDecrementDueDateNormal
@@ -94,7 +95,8 @@ endif
" Functions for maps {{{1
function! s:ChangeDueDateWrapper(by_days, repeat_mapping)
call todo#ChangeDueDate(a:by_days, 'd')
call todo#CreateNewRecurrence(0)
call todo#ChangeDueDate(a:by_days, 'd', '')
silent! call repeat#set(a:repeat_mapping, v:count)
endfunction

22
tests/include/setup.vader Normal file
View File

@@ -0,0 +1,22 @@
Execute (Clean up test environment):
Save maplocalleader
let maplocalleader = '\'
function! ReplaceCurrentDates(expected)
if a:expected != ''
execute "silent! %substitute/" . a:expected . "/**EXPECTED**/"
endif
execute "silent! %substitute/" . strftime("%Y-%m-%d") . "/**TODAY**/"
for b:unit in ['D', 'W', 'M', 'Y']
let [s:year, s:month, s:day] = todo#ParseDate(strftime("%Y-%m-%d"))
let [s:year, s:month, s:day] = todo#DateAdd(s:year, s:month, s:day, 2, b:unit)
let s:duedate = printf('%04d', s:year) . '-' . printf('%02d', s:month) . '-' . printf('%02d', s:day)
execute "silent! %substitute/" . s:duedate . "/**TODAY+2" . b:unit . "**/"
endfor
endfunction
Before:
After:
Given:

View File

@@ -0,0 +1,4 @@
Given:
Execute (Restoring test environment):
Restore

View File

@@ -7,6 +7,8 @@
" [Vader](https://github.com/junegunn/vader.vim) is a simple unit testing
" plugin for VIM.
Include: include/setup.vader
" file: syntax/todo.vim {{{1
" syntax match {{{2
@@ -470,7 +472,7 @@ Given todo (Tasks):
Tricky incomplete task x 2017-09-18
Execute (Toggle completed):
:global/./call todo#ToggleMarkAsDone('')
execute "silent %substitute/" . strftime("%Y-%m-%d") . "/**TODAY**/"
:call ReplaceCurrentDates('')
Expect todo (Toggled tasks with today as **TODAY**):
Complete task
2017-09-01 Completed task with a created date
@@ -488,7 +490,7 @@ Expect todo (Toggled tasks with today as **TODAY**):
Execute (Toggle twice):
:global/./call todo#ToggleMarkAsDone('')
:global/./call todo#ToggleMarkAsDone('')
execute "silent %substitute/" . strftime("%Y-%m-%d") . "/**TODAY**/"
:call ReplaceCurrentDates('')
Expect todo (Tasks, completed on today):
x **TODAY** Complete task
x **TODAY** 2017-09-01 Completed task with a created date
@@ -527,7 +529,7 @@ Given todo (Tasks):
Tricky incancelle task x 2017-09-18
Execute (Toggle cancelled):
:global/./call todo#ToggleMarkAsDone('Cancelled')
execute "%substitute/" . strftime("%Y-%m-%d") . "/**TODAY**/"
:call ReplaceCurrentDates('')
Expect todo (Toggled tasks with today as **TODAY**):
Cancelled task
2017-09-01 Cancelledd task with a created date
@@ -545,7 +547,7 @@ Expect todo (Toggled tasks with today as **TODAY**):
Execute (Toggle twice):
:global/./call todo#ToggleMarkAsDone('Cancelled')
:global/./call todo#ToggleMarkAsDone('Cancelled')
execute "%substitute/" . strftime("%Y-%m-%d") . "/**TODAY**/"
:call ReplaceCurrentDates('')
Expect todo (Tasks, cancelled on today):
x **TODAY** Cancelled Cancelled task
x **TODAY** Cancelled 2017-09-01 Cancelledd task with a created date
@@ -953,6 +955,11 @@ Execute (todo#DateAdd, days):
AssertEqual todo#DateAdd(2017, 04, 01, 30, 'd'), [2017, 05, 01]
AssertEqual todo#DateAdd(1900, 01, 01, 1520, 'd'), [1904, 03, 01]
AssertEqual todo#DateAdd(2304, 03, 01,-1520, 'd'), [2300, 01, 01]
Execute (todo#DateAdd, weeks):
AssertEqual todo#DateAdd(2014, 08, 07, 1, 'w'), [2014, 08, 14]
AssertEqual todo#DateAdd(2014, 08, 07, 2, 'w'), [2014, 08, 21]
AssertEqual todo#DateAdd(2014, 08, 04, -3, 'w'), [2014, 07, 14]
AssertEqual todo#DateAdd(2014, 08, 04, -2, 'w'), [2014, 07, 21]
Execute (todo#DateAdd, months):
" Add one month to a date in the middle of the month should keep day of month
AssertEqual todo#DateAdd(2016, 12, 27, 1, 'm'), [2017, 01, 27]
@@ -1023,7 +1030,7 @@ Execute (todo#DateAdd, years):
AssertEqual todo#DateAdd(1915, 11, 30, -5, 'y'), [1910, 11, 30]
" 2012 is a leap year
AssertEqual todo#DateAdd(2011, 02, 05, 3, 'y'), [2014, 02, 05]
Execute (todo#DateAdd, boundaries and validity):
Execute (todo#DateAdd, boundaries, variations and validity):
AssertEqual todo#DateAdd(1800, 01, 01, 1, 'd'), [1900, 01, 02]
AssertEqual todo#DateAdd(1800, 01, 01, -1, 'd'), [1900, 01, 01]
AssertEqual todo#DateAdd(2017, 30, 01, 1, 'd'), [2017, 12, 02]
@@ -1038,6 +1045,15 @@ Execute (todo#DateAdd, boundaries and validity):
AssertEqual todo#DateAdd(2017, 04, 80, 1, 'm'), [2017, 05, 31]
AssertEqual todo#DateAdd(2017, 04, 80, -1, 'm'), [2017, 03, 31]
AssertEqual todo#DateAdd(-1, -1, -1, 1, 'd'), [1900, 01, 02]
" Case
AssertEqual todo#DateAdd(1950, 10, 10, 2, 'D'), [1950, 10, 12]
AssertEqual todo#DateAdd(1950, 10, 10, -2, 'D'), [1950, 10, 08]
AssertEqual todo#DateAdd(1950, 10, 10, 2, 'W'), [1950, 10, 24]
AssertEqual todo#DateAdd(1950, 10, 10, -2, 'W'), [1950, 09, 26]
AssertEqual todo#DateAdd(1950, 10, 10, 2, 'M'), [1950, 12, 10]
AssertEqual todo#DateAdd(1950, 10, 10, -2, 'M'), [1950, 08, 10]
AssertEqual todo#DateAdd(1950, 10, 10, 2, 'Y'), [1952, 10, 10]
AssertEqual todo#DateAdd(1950, 10, 10, -2, 'Y'), [1948, 10, 10]
" People may well use the behaviours below to their advantage, it could be
" useful, we should try to keep this consistent.
AssertEqual todo#DateAdd(0, 0, 0, 1, 'd'), [str2nr(strftime('%Y')), str2nr(strftime('%m')), str2nr(strftime('%d')) + 1]
@@ -1049,7 +1065,7 @@ Execute (todo#DateAdd, boundaries and validity):
AssertEqual todo#DateAdd(1600, 50, 50, 0, 'd'), [1900, 12, 31]
AssertEqual todo#DateAdd(0, 50, 50, 0, 'd'), [str2nr(strftime('%Y')), 12, 31]
" function todo#ChangeDueDate(units, unit_type) {{{2
" function todo#ChangeDueDate(units, unit_type, from_reference) {{{2
Before:
After:
Given todo (Tasks with a bit of everything):
@@ -1076,14 +1092,14 @@ Given todo (Tasks with a bit of everything):
overdue due:2011-11-11- trailing - invalidates the date L21
active due date at very end of line L22 due:2058-02-01
Execute (todo#ChangeDueDate):
:%call todo#ChangeDueDate(1, 'd')
:%call todo#ChangeDueDate(1, 'd', '')
:let [s:year, s:month, s:day] = todo#ParseDate(strftime("%Y-%m-%d"))
:let [s:year, s:month, s:day] = todo#DateAdd(s:year, s:month, s:day, 1, 'd')
:let s:duedate = printf('%04d', s:year) . '-' . printf('%02d', s:month) . '-' . printf('%02d', s:day)
execute "silent %substitute/" . s:duedate . "/**EXPECTDUE**/"
:call ReplaceCurrentDates(s:duedate)
Expect todo (Tasks with due date incremented):
active dUE:2051-01-02 cAsE L01
notdue overdue:2011-11-11 invalid key L02 due:**EXPECTDUE**
notdue overdue:2011-11-11 invalid key L02 due:**EXPECTED**
xoverdue due:2001-02-02 This is not done (must be lower x) L03
x done due:2011-11-11 completed task L04
notdue due: 2011-11-11 space invalidates due: L05
@@ -1095,7 +1111,7 @@ Expect todo (Tasks with due date incremented):
+Project overdue due:2003-02-02 project at start of line L11
notdue due:2011 L12
active DUe:2052-01-02 cAsE L13
notdue @Project L14 due:**EXPECTDUE**
notdue @Project L14 due:**EXPECTED**
active key:value due:2054-01-02 leading key:value L15
overdue due:2011-01-01 L16
notdue due:invalid invalid due date L17
@@ -1105,10 +1121,168 @@ Expect todo (Tasks with due date incremented):
overdue due:2011-11-11- trailing - invalidates the date L21
active due date at very end of line L22 due:2058-02-02
" function todo#CreateNewRecurrence(triggerOnNonStrict) {{{2
Before:
After:
Given todo (Recurring tasks - strict):
L01 unrelated task 1
L02 due:2020-01-02 strict recurring task rec:+5d should be 2020-01-07 after
L03 dUE:2020-01-03 cAsE rEc:+5M should be 2020-06-03 after
rec:+1y L04 task starting with a recur keyword due:2020-01-04 should be 2021-01-04 after
L05 due:2020-01-05 recur at end of line, should be 2020-01-19 after rec:+2w
L06 due:2020-01-06 REC:+1D REC:+1W REC:+1Y select first rec: and then chain should be 2020-01-07, 2020-01-13, 2021-01-06 after
L07 unrelated task 2
Execute (todo#CreateNewRecurrence 1):
:01call todo#CreateNewRecurrence(1)
:AssertEqual 1, line('.')
:02call todo#CreateNewRecurrence(1)
:AssertEqual 2, line('.')
:04call todo#CreateNewRecurrence(1)
:AssertEqual 4, line('.')
:06call todo#CreateNewRecurrence(1)
:AssertEqual 6, line('.')
:08call todo#CreateNewRecurrence(1)
:AssertEqual 8, line('.')
:10call todo#CreateNewRecurrence(1)
:10call todo#CreateNewRecurrence(1)
:10call todo#CreateNewRecurrence(1)
:AssertEqual 10, line('.')
Expect todo (New task and recurrence due date extended):
L01 unrelated task 1
L02 due:2020-01-02 strict recurring task should be 2020-01-07 after
L02 due:2020-01-07 strict recurring task rec:+5d should be 2020-01-07 after
L03 dUE:2020-01-03 cAsE should be 2020-06-03 after
L03 dUE:2020-06-03 cAsE rEc:+5M should be 2020-06-03 after
L04 task starting with a recur keyword due:2020-01-04 should be 2021-01-04 after
rec:+1y L04 task starting with a recur keyword due:2021-01-04 should be 2021-01-04 after
L05 due:2020-01-05 recur at end of line, should be 2020-01-19 after
L05 due:2020-01-19 recur at end of line, should be 2020-01-19 after rec:+2w
L06 due:2020-01-06 select first rec: and then chain should be 2020-01-07, 2020-01-13, 2021-01-06 after
L06 due:2021-01-06 REC:+1Y select first rec: and then chain should be 2020-01-07, 2020-01-13, 2021-01-06 after
L06 due:2020-01-13 REC:+1W REC:+1Y select first rec: and then chain should be 2020-01-07, 2020-01-13, 2021-01-06 after
L06 due:2020-01-07 REC:+1D REC:+1W REC:+1Y select first rec: and then chain should be 2020-01-07, 2020-01-13, 2021-01-06 after
L07 unrelated task 2
Execute (todo#CreateNewRecurrence 0):
" Calling with 0 or 1 for strict recurrence should yield the same results
:01call todo#CreateNewRecurrence(0)
:AssertEqual 1, line('.')
:02call todo#CreateNewRecurrence(0)
:AssertEqual 2, line('.')
:04call todo#CreateNewRecurrence(0)
:AssertEqual 4, line('.')
:06call todo#CreateNewRecurrence(0)
:AssertEqual 6, line('.')
:08call todo#CreateNewRecurrence(0)
:AssertEqual 8, line('.')
:10call todo#CreateNewRecurrence(0)
:10call todo#CreateNewRecurrence(0)
:10call todo#CreateNewRecurrence(0)
:AssertEqual 10, line('.')
Expect todo (New task and recurrence due date extended):
L01 unrelated task 1
L02 due:2020-01-02 strict recurring task should be 2020-01-07 after
L02 due:2020-01-07 strict recurring task rec:+5d should be 2020-01-07 after
L03 dUE:2020-01-03 cAsE should be 2020-06-03 after
L03 dUE:2020-06-03 cAsE rEc:+5M should be 2020-06-03 after
L04 task starting with a recur keyword due:2020-01-04 should be 2021-01-04 after
rec:+1y L04 task starting with a recur keyword due:2021-01-04 should be 2021-01-04 after
L05 due:2020-01-05 recur at end of line, should be 2020-01-19 after
L05 due:2020-01-19 recur at end of line, should be 2020-01-19 after rec:+2w
L06 due:2020-01-06 select first rec: and then chain should be 2020-01-07, 2020-01-13, 2021-01-06 after
L06 due:2021-01-06 REC:+1Y select first rec: and then chain should be 2020-01-07, 2020-01-13, 2021-01-06 after
L06 due:2020-01-13 REC:+1W REC:+1Y select first rec: and then chain should be 2020-01-07, 2020-01-13, 2021-01-06 after
L06 due:2020-01-07 REC:+1D REC:+1W REC:+1Y select first rec: and then chain should be 2020-01-07, 2020-01-13, 2021-01-06 after
L07 unrelated task 2
Given todo (Recurring tasks - non-strict):
L01 unrelated task 1
L02 due:2020-01-02 strict recurring task rec:2d
L03 dUE:2020-01-03 cAsE rEc:2M
rec:2y L04 task starting with a recur keyword due:2020-01-04
L05 due:2020-01-05 recur at end of line rec:2w
L06 due:2020-01-06 REC:2D REC:2W REC:2Y select first rec: and then chain
L07 unrelated task 2
Execute (todo#CreateNewRecurrence 1):
" Calling with 1 on non-strict should trigger recurrence
:01call todo#CreateNewRecurrence(1)
:AssertEqual 1, line('.')
:02call todo#CreateNewRecurrence(1)
:AssertEqual 2, line('.')
:04call todo#CreateNewRecurrence(1)
:AssertEqual 4, line('.')
:06call todo#CreateNewRecurrence(1)
:AssertEqual 6, line('.')
:08call todo#CreateNewRecurrence(1)
:AssertEqual 8, line('.')
:10call todo#CreateNewRecurrence(1)
:10call todo#CreateNewRecurrence(1)
:10call todo#CreateNewRecurrence(1)
:AssertEqual 10, line('.')
:call ReplaceCurrentDates('')
Expect todo (New task and recurrence due date extended):
L01 unrelated task 1
L02 due:2020-01-02 strict recurring task
L02 due:**TODAY+2D** strict recurring task rec:2d
L03 dUE:2020-01-03 cAsE
L03 dUE:**TODAY+2M** cAsE rEc:2M
L04 task starting with a recur keyword due:2020-01-04
rec:2y L04 task starting with a recur keyword due:**TODAY+2Y**
L05 due:2020-01-05 recur at end of line
L05 due:**TODAY+2W** recur at end of line rec:2w
L06 due:2020-01-06 select first rec: and then chain
L06 due:**TODAY+2Y** REC:2Y select first rec: and then chain
L06 due:**TODAY+2W** REC:2W REC:2Y select first rec: and then chain
L06 due:**TODAY+2D** REC:2D REC:2W REC:2Y select first rec: and then chain
L07 unrelated task 2
Execute (todo#CreateNewRecurrence 0):
" Calling with 0 on non-strict should not make any changes
:1call todo#CreateNewRecurrence(0)
:AssertEqual 1, line('.')
:2call todo#CreateNewRecurrence(0)
:AssertEqual 2, line('.')
:3call todo#CreateNewRecurrence(0)
:AssertEqual 3, line('.')
:4call todo#CreateNewRecurrence(0)
:AssertEqual 4, line('.')
:5call todo#CreateNewRecurrence(0)
:AssertEqual 5, line('.')
:6call todo#CreateNewRecurrence(0)
:AssertEqual 6, line('.')
Expect todo (Recurring tasks - non-strict, not modified):
L01 unrelated task 1
L02 due:2020-01-02 strict recurring task rec:2d
L03 dUE:2020-01-03 cAsE rEc:2M
rec:2y L04 task starting with a recur keyword due:2020-01-04
L05 due:2020-01-05 recur at end of line rec:2w
L06 due:2020-01-06 REC:2D REC:2W REC:2Y select first rec: and then chain
L07 unrelated task 2
" file: ftplugin/todo.vim {{{1
" Mappings {{{2
" Mappings: <LocalLeader>x {{{3
Before:
After:
Given todo (Tasks):
Active task
x 2015-01-01 Complete task
Strict recurring task due:2016-01-01 rec:+1m
Non-strict recurring task due:2017-01-01 rec:2d
Do:
\xj\xj\xjj\x
Then:
call ReplaceCurrentDates('')
Expect todo (Tasks toggled complete):
x **TODAY** Active task
Complete task
x **TODAY** Strict recurring task due:2016-01-01
Strict recurring task due:2016-02-01 rec:+1m
x **TODAY** Non-strict recurring task due:2017-01-01
Non-strict recurring task due:**TODAY+2D** rec:2d
" Mappings: <LocalLeader>p / <LocalLeader>P {{{3
Before:
After:
Given todo (Tasks):
@@ -1117,14 +1291,26 @@ Given todo (Tasks):
Third task, postpone -1 day, should result in 2017-02-19 due:2017-02-20
Forth task, postpone -5 day, should result in 2017-02-15 due:2017-02-20
Fifth task, should not be changed from 2017-03-01 due:2017-03-01
Strict recurring task due:2017-01-01 rec:+2d should result in 2017-01-02 and 2017-01-03
Non-strict recurring task due:2017-02-01 rec:1d should result in 2017-02-02
Do (Postpone tasks - normal mode):
\pj5\pj\Pj5\P
\p j
5\p j
\P j
5\P j
j
\p j
j
\p
Expect todo (Postponed tasks):
First task, postpone 1 day, should result in 2017-01-02 due:2017-01-02
Second task, postpone 5 days, should result in 2017-01-06 due:2017-01-06
Third task, postpone -1 day, should result in 2017-02-19 due:2017-02-19
Forth task, postpone -5 day, should result in 2017-02-15 due:2017-02-15
Fifth task, should not be changed from 2017-03-01 due:2017-03-01
Strict recurring task due:2017-01-02 should result in 2017-01-02 and 2017-01-03
Strict recurring task due:2017-01-03 rec:+2d should result in 2017-01-02 and 2017-01-03
Non-strict recurring task due:2017-02-02 rec:1d should result in 2017-02-02
Given todo (Tasks):
Task visual block 1, should result in 2017-01-02 due:2017-01-01 L01
@@ -1149,6 +1335,7 @@ Expect todo (Postponed tasks):
Task visual block 4, should result in 2017-02-15 due:2017-02-15 L08
Fifth task, should not be changed from 2017-03-01 due:2017-03-01 L09
" Mappings: o, O, <Enter> {{{3
Before:
let g:Todo_txt_prefix_creation_date=0
After:
@@ -1173,7 +1360,7 @@ Execute (Open some new lines):
:normal oNew task o
:normal ONew task O
:normal A
New task CR
New task CR
:call ReplaceCurrentDates('')
Expect todo (New task with no creation date):
@@ -1217,4 +1404,6 @@ Execute (Check folding text):
AssertEqual foldtextresult(4), '+- 1 Completed tasks '
"}}}
Include: include/teardown.vader