From c0a67266963832f789bf6e8b6249d3aee4d6a3bd Mon Sep 17 00:00:00 2001 From: David Beniamine Date: Fri, 10 Oct 2014 17:38:37 +0200 Subject: [PATCH] CHG: Hierachical sort can now have1 to 3 levels This allow for example to sort the wall list by context, then each context by project and each project by priority. The sort is very flexible, the only constraint is that the sort by priority (if we decide to do it) is the last sort. --- plugin/todo-txt.vim | 115 +++++++++++++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 34 deletions(-) diff --git a/plugin/todo-txt.vim b/plugin/todo-txt.vim index b5fd74e..2faaabe 100644 --- a/plugin/todo-txt.vim +++ b/plugin/todo-txt.vim @@ -1,74 +1,121 @@ " File: todo.txt.vim " Description: Todo.txt sorting plugin -" Author: Leandro Freitas +" Author: David Beniamine " Licence: Vim licence " Website: http://github.com/dbeniamine/todo.txt.vim " Version: 0.3 +" vim: ts=4 sw=4 :help tw=78 cc=80 -" These two variables are parameters for the first and second call to the vim -" sort function +" These two variables are parameters for the successive calls the vim sort " '' means no flags " '! i' means reverse and ignore case " for more information on flags, see :help sort if (! exists("g:Todo_txt_first_level_sort_mode")) - let g:Todo_txt_first_level_sort_mode="i" + let g:Todo_txt_first_level_sort_mode='i' endif if (! exists("g:Todo_txt_second_level_sort_mode")) - let g:Todo_txt_second_level_sort_mode="i" + let g:Todo_txt_second_level_sort_mode='i' +endif +if (! exists("g:Todo_txt_third_level_sort_mode")) + let g:Todo_txt_third_level_sort_mode='i' endif " Sort todo by (first) context -noremap sc :call Todo_txt_TwoLevelsSort('@') +noremap sc :call Todo_txt_HierarchicalSort('@', '', 1) +noremap scp :call Todo_txt_HierarchicalSort('@', '+', 1) " Sort todo by (first) project -noremap sp :call Todo_txt_TwoLevelsSort('+') +noremap sp :call Todo_txt_HierarchicalSort('+', '',1) +noremap spc :call Todo_txt_HierarchicalSort('+', '@',0) -" This is a two level sort designed for todo.txt todo lists +" This is a Hierarchical sort designed for todo.txt todo lists, however it +" might be used for other files types " At the first level, lines are sorted by the word right after the first " occurence of a:symbol, there must be no space between the symbol and the -" word. Therefore, according to todo.txt syntaxt, if +" word. At the second level, the same kind of sort is done based on +" a:symbolsub, is a:symbol==' ', the second sort doesn't occurs +" Therefore, according to todo.txt syntaxt, if " a:symbol is a '+' it sort by the first project " a:symbol is an '@' it sort by the first context -" The second level of sort is done direcetly on the line, so according to -" todo.txt syntax, it means sort by priority -function! Todo_txt_TwoLevelsSort(symbol) - "if the sort modes doesn't start by '!' it must start with a space - let l:sortmode=Todo_txt_InsertSpaceIfNeeded(g:Todo_txt_first_level_sort_mode) - let l:sortmode2=Todo_txt_InsertSpaceIfNeeded(g:Todo_txt_second_level_sort_mode) - - " Count the number of lines - let l:position= getpos(".") - execute "silent normal g\" +" The last level of sort is done directly on the line, so according to +" todo.txt syntax, it means by priority. This sort is done if and only if the +" las argument is not 0 +function! Todo_txt_HierarchicalSort(symbol, symbolsub, dolastsort) if v:statusmsg =~ '--No lines in buffer--' "Empty buffer do nothing return endif + "if the sort modes doesn't start by '!' it must start with a space + let l:sortmode=Todo_txt_InsertSpaceIfNeeded(g:Todo_txt_first_level_sort_mode) + let l:sortmodesub=Todo_txt_InsertSpaceIfNeeded(g:Todo_txt_second_level_sort_mode) + let l:sortmodefinal=Todo_txt_InsertSpaceIfNeeded(g:Todo_txt_third_level_sort_mode) + + " Count the number of lines + let l:position= getpos(".") + execute "silent normal g\" let l:linecount=str2nr(split(v:statusmsg)[7]) " Get all the groups names - let l:curline=0 + let l:groups=GetGroups(a:symbol,0,l:linecount) + + " Sort by groups + execute 'sort'.l:sortmode.' /.\{-}\ze'.a:symbol.'/' + for l:g in l:groups + " Find the beginning of the group + execute '/'.a:symbol.l:g.'.*$' + let l:groupBegin=getpos(".")[1] + " Find the end of the group + silent normal N + let l:groupEnd=getpos(".")[1] + + " I'm too lazy to sort one groups of one line + if(l:groupEnd==l:groupBegin) + continue + endif + if( a:symbolsub!='') + " Sort by subgroups + let l:subgroups=GetGroups(a:symbolsub,l:groupBegin,l:groupEnd) + " Go before the first line of the group + " Sort the group using the second symbol + for l:sg in l:subgroups + " Find the beginning of the subgroup + execute '/'.a:symbol.l:g.'.*'.a:symbolsub.l:sg.'.*$\|'.a:symbolsub.l:sg.'.*'.a:symbol.l:g.'.*$' + let l:subgroupBegin=getpos(".")[1] + " Find the end of the subgroup + silent normal N + let l:subgroupEnd=getpos(".")[1] + " Sort by priority + if a:dolastsort + execute l:subgroupBegin.','.l:subgroupEnd.'sort'.l:sortmodefinal + endif + endfor + else + " Sort by priority + if a:dolastsort + execute l:groupBegin.','.l:groupEnd.'sort'.l:sortmodefinal + endif + endif + endfor + " Restore the cursor position + call setpos('.', position) +endfunction + +" Returns the list of groups starting by a:symbol between lines a:begin and +" a:end +function GetGroups(symbol,begin, end) + let l:curline=a:begin let l:groups=[] - while l:curline <= l:linecount + while l:curline <= a:end let l:curproj=strpart(matchstr(getline(l:curline),a:symbol.'\a*'),1) if l:curproj != "" && index(l:groups,l:curproj) == -1 let l:groups=add(l:groups , l:curproj) endif let l:curline += 1 endwhile - - " Sort by groups - execute 'sort'.l:sortmode.' /.\{-}\ze'.a:symbol.'/' - for l:p in l:groups - execute '/^.\{-}'.a:symbol.l:p.'.*$' - normal ma - normal G - execute '?^.\{-}'.a:symbol.l:p.'.*$' - normal mb - execute "'a,'b sort".l:sortmode2 - endfor - " Restore the cursor position - call setpos('.', position) + return l:groups endfunction +" Insert a space if needed (the first char isn't '!' or ' ') in front of +" sort parameters function! Todo_txt_InsertSpaceIfNeeded(str) let l:c=strpart(a:str,1,1) if( l:c != '!' && l:c !=' ')