diff --git a/README.markdown b/README.markdown index d0d93ee..da7aaf6 100644 --- a/README.markdown +++ b/README.markdown @@ -28,6 +28,13 @@ ## Release notes ++ V0.8.1 Incorporates yet antoher Fretep work : highlithing for tasks due today. + ++ v0.8 Incorporates Fretep's work on overdue dates (PR#13 and PR#16) witch +removes python dependency, allow to control the cursor position after a sort by +todo (see (sort)[#sort] and/or issue #15) and fixes bug when sorting a file +containing only lines with due:date (issue #14). + + v0.7.6 Incorporates [Sietse's work](https://github.com/sietse/todo.txt-vim/commit/57d45200c8b033d31c9191ee0eb0711c801cdb1d) to make cancel and mark as done mapping repeatable using [vim-repeat](https://github.com/tpope/vim-repeat). + v0.7.5 Incorporates [Fievel's work](https://github.com/fievel/todo.txt-vim/commit/0863e1434e9a89ace06c4856b6cb32ba9906e3de) to make overduedates work on python3. + v0.7.4 includes the overduedate support from Guilherme Victal (see pull @@ -215,6 +222,19 @@ prevent this behavior, add the following line to your vimrc + `-sd` : Sort the file by due-date. Entries with a due date appears sorted by at the beginning of the file, the rest of the file is not modified. +When you sort by due dates, at the end of the sort, your cursor will be placed +at the top of the file. This behavior can be set with the following global +variable : + + let g:TodoTxtSortDueDateCursorPos = "top" + +Possible values are : + ++ `top` (default): The first line of the buffer, i.e. your most outstanding task ++ `lastdue`: The last task with a due:date set ++ `notoverdue`: The first task that is not overdue (requires #13) ++ `bottom`: The last line of the buffer + ### Priorities + `j` : Lower the priority of the current line diff --git a/autoload/todo.vim b/autoload/todo.vim index 9e21886..88a5e5e 100644 --- a/autoload/todo.vim +++ b/autoload/todo.vim @@ -167,22 +167,50 @@ function! todo#Sort() endfunction function! todo#SortDue() - silent! %s/\([dD][uU][eE]:\d\{4}\)-\(\d\{2}\)-\(\d\{2}\)/\1\2\3/g - " Sort adding entries with due dates add the beginning - sort n /[dD][uU][eE]:/ - " Count the number of lines - silent normal gg - execute "/[dD][uU][eE]:" - let l:first=getpos(".")[1] - silent normal N - let l:last=getpos(".")[1] - let l:diff=l:last-l:first+1 + " Check how many lines have a due:date on them + let l:tasksWithDueDate = 0 + silent! %global/\v\c^[^x].*/let l:tasksWithDueDate += 1 + if l:tasksWithDueDate == 0 + " No tasks with a due:date: No need to modify the buffer at all + " Also means we don't need to cater for no matches on searches below + return + endif + " FIXME: There is a small chance that due:\d{8} might legitimately exist in the buffer + " We modify due:yyyy-mm-dd to yyyymmdd which would then mean we would alter the buffer + " in an unexpected way, altering user data. Not sure how to deal with this at the moment. + " I'm going to throw an exception, and if this is a problem we can revisit. + silent %global/\v\c/throw "Text matching 'due:\\d\\{8\\}' exists in the buffer, this function cannot sort your buffer" + " Turn the due:date from due:yyyy-mm-dd to due:yyyymmdd so we can do a numeric sort + silent! %substitute/\v<(due:\d{4})\-(\d{2})\-(\d{2})>/\1\2\3/ei + " Sort all the lines with due: by numeric yyyymmdd, they will end up in ascending order at the bottom of the buffer + sort in /^[^x].*\ 1 + " ...but only if the whole file didn't get sorted. + execute l:firstLineWithDue . ",$move 0" + endif + " Change the due:yyyymmdd back to due:yyyy-mm-dd. + silent! %substitute/\v<(due:\d{4})(\d{2})(\d{2})>/\1-\2-\3/ei + " Cursor is now on the last task with a due:date + " Let's check a global for a user preference on the cursor position. + if exists("g:TodoTxtSortDueDateCursorPos") + if g:TodoTxtSortDueDateCursorPos ==? "top" + normal gg + elseif g:TodoTxtSortDueDateCursorPos ==? "lastdue" + " Nothing to do + elseif g:TodoTxtSortDueDateCursorPos ==? "notoverdue" + " Let's try to put the cursor on the first non-overdue task + let l:overduePat = todo#GetDateRegexForPastDates() + execute ":silent! ?\\v?+1" + elseif g:TodoTxtSortDueDateCursorPos ==? "bottom" + silent normal G + endif + else + " Default: Top of the document + normal gg + endif " TODO: add time sorting (YYYY-MM-DD HH:MM) endfunction diff --git a/doc/todo.txt b/doc/todo.txt index 123cc24..7242fb2 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -31,6 +31,13 @@ Table of Contents *TodoTxt-Contents* ~ =============================================================================== 1. Release notes *TodoTxt-ReleaseNotes* ~ +V0.8.1 Incorporates yet antoher Fretep work : highlithing for tasks due today. + +v0.8 Incorporates Fretep's work on overdue dates (PR#13 and PR#16) witch removes +python dependency, allow to control the cursor position after a sort by todo +(see |TodoTxt-Sort| and/or issue #15) and fixes bug when sorting a file +containing only lines with due:date (issue #14). + v0.7.6 Incorporates [Sietse's work](https://github.com/sietse/todo.txt-vim/commit/57d45200c8b033d31c9191ee0eb0711c801cdb1d) to make cancel and mark as done mapping repeatable using [vim-repeat](https://github.com/tpope/vim-repeat). v0.7.5 Incorporates Fievel's work @@ -230,6 +237,19 @@ prevent this behavior, add the following line to your vimrc `-sd` : Sort the file by due-date. Entries with a due date appears sorted by at the beginning of the file, the rest of the file is not modified. +When you sort by due dates, at the end of the sort, your cursor will be placed +at the top of the file. This behavior can be set with the following global +variable : + + let g:TodoTxtSortDueDateCursorPos = "top" + +Possible values are : + ++ `top` (default): The first line of the buffer, i.e. your most outstanding task ++ `lastdue`: The last task with a due:date set ++ `notoverdue`: The first task that is not overdue (requires #13) ++ `bottom`: The last line of the buffer + 6.2 Priorities *TodoTxt-Priorities* `j` : Lower the priority of the current line diff --git a/ftplugin/todo.vim b/ftplugin/todo.vim index 85e2d8c..e4cd090 100644 --- a/ftplugin/todo.vim +++ b/ftplugin/todo.vim @@ -12,7 +12,7 @@ set cpo&vim if exists("g:Todo_txt_loaded") finish else - let g:Todo_txt_loaded=0.7.6 + let g:Todo_txt_loaded=0.8.1 endif " General options {{{1 diff --git a/syntax/python/dateregex/dateregex/__init__.py b/syntax/python/dateregex/dateregex/__init__.py deleted file mode 100644 index 6864127..0000000 --- a/syntax/python/dateregex/dateregex/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# File: __init__.py -# Author: Guilherme Victal -# Description: Dateregex library entry point -# License: Vim license -# Website: http://github.com/freitass/todo.txt-vim -# Version: 0.1 - -from dateregex.after import regex_date_after -from dateregex.before import regex_date_before diff --git a/syntax/python/dateregex/dateregex/after.py b/syntax/python/dateregex/dateregex/after.py deleted file mode 100644 index 1e4027a..0000000 --- a/syntax/python/dateregex/dateregex/after.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# File: after.py -# Author: Guilherme Victal -# Description: Generates regexes after a certain date -# License: Vim license -# Website: http://github.com/freitass/todo.txt-vim -# Version: 0.1 - -from datetime import date, timedelta, MAXYEAR - - -def _year_regex_after(year): - if int(year) > MAXYEAR: - return None - - year_regex = r'(\d+\d{%s}' % len(year) - for idx, digit in enumerate(year): - if digit != '9': - regex = '|' + year[0:idx] - regex += '9' if digit == '8' else '[%s-9]' % str(int(digit) + 1) - if idx < len(year) - 1: - regex += '\d{%s}' % (len(year) - (idx + 1)) - year_regex += regex - - year_regex += ')' - return '-'.join((year_regex, r'\d{2}', r'\d{2}')) - - -def _month_regex_after(year, month): - if month == '12': - return None - - digit1, digit2 = month - if digit1 == '1': - month_regex = r'12' if month == '11' else r'1[12]' - else: - month_regex = r'1[0-2]' - if digit2 != '9': - if digit2 == '8': - month_regex = r'(' + month_regex + r'|09)' - else: - month_regex = r'(' + month_regex + r'|0[%s-9])' - month_regex = month_regex % str(int(digit2) + 1) - return '-'.join((year, month_regex, r'\d{2}')) - -def _day_regex_after(year, month, day): - last_month_day = str((date(int(year), (int(month) + 1) % 12, 1) + - date.resolution).day) - if day == last_month_day: - return None - day_regex = r'(' - digit1, digit2 = day - last_digit1, last_digit2 = last_month_day - if digit1 == last_digit1: - day_regex = last_month_day if int(digit2) == int(last_digit2) - 1 else last_digit1 + r'[%s-%s]' % (str(int(digit2) + 1), last_digit2) - else: - day_regex = r'(' - day_regex += last_digit1 if int(digit1) == int(last_digit1) - 1 else r'[%s-%s]' % (str(int(digit1) + 1), last_digit1) - day_regex +=r'\d' - if digit2 < '9': - day_regex += '|' + digit1 - day_regex += '9' if digit2 == '8' else r'[%s-9]' % str(int(digit2) + 1) - - day_regex += ')' - return '-'.join((year, month, day_regex)) - - -def regex_date_after(given_date): - year, month, day = given_date.isoformat().split('-') - - year_regex = _year_regex_after(year) - month_regex = _month_regex_after(year, month) - day_regex = _day_regex_after(year, month, day) - - date_regex = '(' + year_regex if year_regex else '(' - date_regex += ('|' + month_regex) if month_regex else '' - date_regex += ('|' + day_regex) if day_regex else '' - date_regex += ')' - return date_regex - - -def __main(): - import re - date_regex = regex_date_after(date(1999,12,31)) - print(date_regex) - pattern = re.compile(date_regex) - - - d = date.today() + date.resolution - assert pattern.match(date.strftime(d, '%Y-%m-%d')) is not None - print(date.strftime(d, '%Y-%m-%d') + ' is okay') - d += date.resolution - -if __name__ == '__main__': - __main() diff --git a/syntax/python/dateregex/dateregex/before.py b/syntax/python/dateregex/dateregex/before.py deleted file mode 100644 index a2c7d83..0000000 --- a/syntax/python/dateregex/dateregex/before.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# File: before.py -# Author: Guilherme Victal -# Description: Generates regexes before a certain date -# License: Vim license -# Website: http://github.com/freitass/todo.txt-vim -# Version: 0.1 - -from datetime import date, timedelta, MINYEAR - -def _year_regex_before(year): - if int(year) <= MINYEAR: - return None - year_regex = r'(' - year_regex += r'\d{1,%s}' % str(len(year) - 1) if len(year) > 1 else '' - for idx, digit in enumerate(year): - if digit != '0': - regex = '|' + year[0:idx] - regex += '0' if digit == '1' else '[0-%s]' % str(int(digit) - 1) - if idx < len(year) - 1: - regex += '\d{%s}' % (len(year) - (idx + 1)) - year_regex += regex - - year_regex += ')' - return '-'.join((year_regex, r'\d{2}', r'\d{2}')) - -def _month_regex_before(year, month): - if month == '01': - return None - - digit1, digit2 = month - if digit1 == '0': - month_regex = '01' if month == '02' else r'0[1-%s]' % str(int(digit2) - 1) - elif month == '10': - month_regex = r'0\d' - elif month == '11': - month_regex = r'(0\d|10)' - else: - month_regex = r'(0\d|1[01])' - - return '-'.join((year, month_regex, r'\d{2}')) - -def _day_regex_before(year, month, day): - if day == '01': - return None - last_month_day = str((date(int(year), int(month) % 12 + 1, 1) + - date.resolution).day) - last_digit1, last_digit2 = last_month_day - - digit1, digit2 = day - if digit1 == '0': - day_regex = '01' if day == '02' else r'0[1-%s]' % str(int(digit2) - 1) - else: - day_regex = r'(' - day_regex += '0' if digit1 == '1' else r'[0-%s]' % str(int(digit1) - 1) - day_regex += r'\d' - if digit2 != '0': - day_regex += '|' - day_regex += digit1 - day_regex += '0' if digit2 == '1' else r'[0-%s]' % str(int(digit2) - 1) - day_regex += ')' - - return '-'.join((year, month, day_regex)) - - - - -def regex_date_before(given_date): - year, month, day = given_date.isoformat().split('-') - - year_regex = _year_regex_before(year) - month_regex = _month_regex_before(year, month) - day_regex = _day_regex_before(year, month, day) - - date_regex = '(' + year_regex if year_regex else '(' - date_regex += ('|' + month_regex) if month_regex else '' - date_regex += ('|' + day_regex) if day_regex else '' - date_regex += ')' - return date_regex diff --git a/syntax/python/todo.py b/syntax/python/todo.py deleted file mode 100644 index e928026..0000000 --- a/syntax/python/todo.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- -# File: todo.py -# Description: Todo.txt overdue date syntax script -# License: Vim license -# Website: http://github.com/freitass/todo.txt-vim -# Version: 0.1 - -import vim -import os -import sys -from datetime import date - -dateregex_dir = os.path.join(vim.eval('s:script_dir'), 'dateregex') -if os.path.isdir(dateregex_dir): - sys.path.insert(0, dateregex_dir) - -def add_due_date_syntax_highlight(): - try: - from dateregex import regex_date_before - except ImportError: - print("dateregex module not found. Overdue dates won't be highlighted") - return - - regex = regex_date_before(date.today()) - regex = r'(^|<)due:%s(>|$)' % regex - - vim.command("syntax match OverDueDate '\\v%s'" % regex) - vim.command("highlight default link OverDueDate Error") - -add_due_date_syntax_highlight() diff --git a/syntax/todo.vim b/syntax/todo.vim index fe61f36..c9943d8 100644 --- a/syntax/todo.vim +++ b/syntax/todo.vim @@ -10,37 +10,42 @@ if exists("b:current_syntax") endif syntax match TodoDone '^[xX]\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext -syntax match TodoPriorityA '^([aA])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityB '^([bB])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityC '^([cC])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityD '^([dD])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityE '^([eE])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityF '^([fF])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityG '^([gG])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityH '^([hH])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityI '^([iI])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityJ '^([jJ])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityK '^([kK])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityL '^([lL])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityM '^([mM])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityN '^([nN])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityO '^([oO])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityP '^([pP])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityQ '^([qQ])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityR '^([rR])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityS '^([sS])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityT '^([tT])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityU '^([uU])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityV '^([vV])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityW '^([wW])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityX '^([xX])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityY '^([yY])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate -syntax match TodoPriorityZ '^([zZ])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,OverDueDate +syntax match TodoPriorityA '^([aA])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityB '^([bB])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityC '^([cC])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityD '^([dD])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityE '^([eE])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityF '^([fF])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityG '^([gG])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityH '^([hH])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityI '^([iI])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityJ '^([jJ])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityK '^([kK])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityL '^([lL])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityM '^([mM])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityN '^([nN])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityO '^([oO])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityP '^([pP])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityQ '^([qQ])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityR '^([rR])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityS '^([sS])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityT '^([tT])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityU '^([uU])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityV '^([vV])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityW '^([wW])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityX '^([xX])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityY '^([yY])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate +syntax match TodoPriorityZ '^([zZ])\s.\+$' contains=TodoKey,TodoDate,TodoProject,TodoContext,TodoDueToday,TodoOverDueDate syntax match TodoDate '\d\{2,4\}-\d\{2\}-\d\{2\}' contains=NONE -syntax match TodoKey '\S*\S:\S\S*' contains=TodoDate +syntax match TodoKey '\S*\S:\S\S*' contains=TodoDate syntax match TodoProject '\(^\|\W\)+[^[:blank:]]\+' contains=NONE syntax match TodoContext '\(^\|\W\)@[^[:blank:]]\+' contains=NONE +let s:todayDate=strftime('%Y\-%m\-%d') +"TODO: Figure out how to allow stop the following line being highlighted inside TodoDone +"execute 'syntax match TodoDueToday /\v\c<(due:)@4<=' . s:todayDate . '/ contains=NONE contained' +execute 'syntax match TodoDueToday /\v\c:p:h') -let s:script_dir = b:curdir . "/python/" -if has('python3') - execute "py3file " . s:script_dir. "todo.py" -elseif has('python') - execute "pyfile " . s:script_dir. "todo.py" -endif +function! todo#GetDateRegexForPastDates(...) + " Build a RegExp to match all dates prior to a reference date. + " + " Optionally accepts a (year, month, day) for the date, otherwise assumes the + " reference date is the current date. + " + " In the end, the RegExp will look something like: + " =todo#GetDateRegexForPastDates(2017, 09, 15) + " \v(([01]\d{3}|200\d|201[0-6])\-\d{2}\-\d{2}|(2017\-(0[0-8])\-\d{2})|(2017\-09\-0\d)|(2017\-09\-1[0-4])) + " + " We split the RegExp into a few alternation groups: + " 1. All dates prior to 2000, dates before this are not supported + " 2. All previous decades for the reference date century + " 3. The current decade up to the year prior to the reference year + " 4. All months for the reference year up to the end of the previous month + " 5. Days of the month part 1. + " 6. Days of the month part 2. + " + " Will not work on reference dates past 2099, or before 2000. + " + " Invalid months and days are not checked, i.e. 2015-14-67 will match. + " + " Years must be 4 digits. + " + + " Get the reference date + let l:day=strftime("%d") + let l:month=strftime("%m") + let l:year=strftime("%Y") + if a:0 >= 1 + let l:year=a:1 + endif + if a:0 >= 2 + let l:month=a:2 + endif + if a:0 >= 3 + let l:day=a:3 + endif + + " Use very magic mode, and start an alternation + let l:overdueRex = '\v(' + + " PART 1: 0000-1999 + " This sucker is static and won't change to year 3000. I'm not coding for the year 3000. + let l:overdueRex = l:overdueRex . '([01]\d{3}' + + " PART 2. All previous decades for the reference date century + " i.e. for 2017: "200\d", for 2035: "20[0-2]\d" + " for 2000: skip + let l:decade = strpart(l:year, 2, 1) " i.e. the 1 from 2017 + if l:decade > 0 + let l:overdueRex = l:overdueRex . '|20' + if l:decade > 1 + let l:overdueRex = l:overdueRex . '[0-' . (l:decade - 1) . ']' + else + let l:overdueRex = l:overdueRex . '0' + endif + let l:overdueRex = l:overdueRex . '\d' + endif + + " PART 3: This decade, to previous year + " i.e. for 2017: "201[0-6]", for 2035: "203[0-4]", for 2000: skip + let l:y = strpart(l:year, 3, 1) " Last digit of the year, i.e. 7 for 2017 + if l:y > 0 + if l:y > 1 + let l:overdueRex = l:overdueRex . '|20' . l:decade . '[0-' . (l:y - 1) . ']' + else + let l:overdueRex = l:overdueRex . '|20' . l:decade . '0' + endif + endif + let l:overdueRex = l:overdueRex . ')\-\d{2}\-\d{2}|' + + " PART 4: All months to the end of the previous month + " i.e. for a date of 2017-09-07, "2017-(0[1-8])-\d{2}" + " for 2017-11-30: "2017-(0\d|1[0-1])-\d{2}" + " for 2017-01-20: skip + " This only applies if the reference date is not in January + if l:month > 1 + let l:overdueRex = l:overdueRex . '(' . l:year . '\-(0' + if l:month > 10 + let l:overdueRex = l:overdueRex . '\d|1' + endif + let l:y = strpart(printf('%02d', l:month), 1, 1) " Second digit of the month + let l:overdueRex = l:overdueRex . '[0-' . (l:y - 1) . '])\-\d{2})|' + endif + + " PART 5. Days of the month part 1. + " i.e. for 2017-09-07: skip + " for 2017-12-29: "2017-12-[0-1]\d" + let l:y = strpart(printf('%02d', l:day), 0, 1) " First digit of the day + if l:y > 0 + if l:y > 1 + let l:overdueRex = l:overdueRex . '(' . l:year . '\-' . printf('%02d', l:month) . '\-[0-' . (l:y - 1) . ']\d)|' + else + let l:overdueRex = l:overdueRex . '(' . l:year . '\-' . printf('%02d', l:month) . '\-0\d)|' + endif + endif + + " PART 6. Days of the month part 2. + " i.e. for 2017-09-07: "2017-09-0[0-6]" + " for 2017-12-29: "2017-12-2[0-8]" + let l:y = strpart(printf('%02d', l:day), 0, 1) " First digit of the day + let l:overdueRex = l:overdueRex . '(' . l:year . '\-' . printf('%02d', l:month) . '\-' . l:y + let l:y = strpart(printf('%02d', l:day), 1, 1) " Last digit of the day + if l:y > 0 + let l:overdueRex = l:overdueRex . '[0-' . (l:y - 1) . ']' + else + let l:overdueRex = l:overdueRex . '0' + endif + let l:overdueRex = l:overdueRex . ')' + + let l:overdueRex = l:overdueRex . ')' + + return l:overdueRex +endfunction + +execute 'syntax match TodoOverDueDate /\v\c/' +highlight default link TodoOverDueDate Error let b:current_syntax = "todo"