CLI config/dotfiles
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

561 lines
14 KiB

  1. " plugin to handle the TaskPaper to-do list format
  2. " Language: Taskpaper (http://hogbaysoftware.com/projects/taskpaper)
  3. " Maintainer: David O'Callaghan <david.ocallaghan@cs.tcd.ie>
  4. " URL: https://github.com/davidoc/taskpaper.vim
  5. " Last Change: 2012-03-07
  6. let s:save_cpo = &cpo
  7. set cpo&vim
  8. function! s:add_delete_tag(tag, value, add)
  9. let cur_line = getline(".")
  10. let tag = " @" . a:tag
  11. if a:value != ''
  12. let tag .= "(" . a:value . ")"
  13. endif
  14. " Add tag
  15. if a:add
  16. let new_line = cur_line . tag
  17. call setline(".", new_line)
  18. return 1
  19. endif
  20. " Delete tag
  21. if cur_line =~# '\V' . tag
  22. if a:value != ''
  23. let new_line = substitute(cur_line, '\V' . tag, "", "g")
  24. else
  25. let new_line = substitute(cur_line, '\V' . tag . '\v(\([^)]*\))?',
  26. \ "", "g")
  27. endif
  28. call setline(".", new_line)
  29. return 1
  30. endif
  31. return 0
  32. endfunction
  33. function! taskpaper#add_tag(tag, ...)
  34. let value = a:0 > 0 ? a:1 : input('Value: ')
  35. return s:add_delete_tag(a:tag, value, 1)
  36. endfunction
  37. function! taskpaper#delete_tag(tag, ...)
  38. let value = a:0 > 0 ? a:1 : ''
  39. return s:add_delete_tag(a:tag, value, 0)
  40. endfunction
  41. function! taskpaper#swap_tag(oldtag, newtag)
  42. call taskpaper#delete_tag(a:oldtag)
  43. call taskpaper#add_tag(a:newtag, '')
  44. endfunction
  45. function! taskpaper#swap_tags(oldtags, newtags)
  46. for oldtag in a:oldtags
  47. call taskpaper#delete_tag(oldtag)
  48. endfor
  49. for newtag in a:newtags
  50. call taskpaper#add_tag(newtag, '')
  51. endfor
  52. endfunction
  53. function! taskpaper#toggle_tag(tag, ...)
  54. if !taskpaper#delete_tag(a:tag, '')
  55. let args = a:0 > 0 ? [a:tag, a:1] : [a:tag]
  56. call call("taskpaper#add_tag", args)
  57. endif
  58. endfunction
  59. function! taskpaper#has_tag(tag)
  60. let cur_line = getline(".")
  61. let m = matchstr(cur_line, '@'.a:tag)
  62. if m != ''
  63. return 1
  64. else
  65. return 0
  66. endfunction
  67. function! taskpaper#cycle_tags(...)
  68. let tags_index = 0
  69. let tag_list = a:000
  70. let tag_added = 0
  71. for tag_name in tag_list
  72. let tags_index = tags_index + 1
  73. if tags_index == len(tag_list)
  74. let tags_index = 0
  75. endif
  76. let has_tag = taskpaper#has_tag(tag_name)
  77. if has_tag == 1
  78. let tag_added = 1
  79. call taskpaper#delete_tag(tag_name)
  80. let new_tag = tag_list[tags_index]
  81. if new_tag != ''
  82. call taskpaper#add_tag(new_tag, '')
  83. endif
  84. break
  85. endif
  86. endfor
  87. if tag_added == 0
  88. call taskpaper#add_tag(tag_list[0], '')
  89. endif
  90. endfunction
  91. function! taskpaper#update_tag(tag, ...)
  92. call taskpaper#delete_tag(a:tag, '')
  93. let args = a:0 > 0 ? [a:tag, a:1] : [a:tag]
  94. call call("taskpaper#add_tag", args)
  95. endfunction
  96. function! taskpaper#date()
  97. return strftime(g:task_paper_date_format, localtime())
  98. endfunction
  99. function! taskpaper#complete_project(lead, cmdline, pos)
  100. let lnum = 1
  101. let list = []
  102. let stack = ['']
  103. let depth = 0
  104. while lnum <= line('$')
  105. let line = getline(lnum)
  106. let ml = matchlist(line, '\v\C^\t*(.+):(\s+\@[^ \t(]+(\([^)]*\))?)*$')
  107. if !empty(ml)
  108. let d = len(matchstr(line, '^\t*'))
  109. while d < depth
  110. call remove(stack, -1)
  111. let depth -= 1
  112. endwhile
  113. while d > depth
  114. call add(stack, '')
  115. let depth += 1
  116. endwhile
  117. let stack[d] = ml[1]
  118. let candidate = join(stack, ':')
  119. if candidate =~ '^' . a:lead
  120. call add(list, join(stack, ':'))
  121. endif
  122. endif
  123. let lnum += 1
  124. endwhile
  125. return list
  126. endfunction
  127. function! taskpaper#go_to_project()
  128. let res = input('Project: ', '', 'customlist,taskpaper#complete_project')
  129. if res != ''
  130. call taskpaper#search_project(split(res, ':'))
  131. endif
  132. endfunction
  133. function! taskpaper#next_project()
  134. return search('^\t*\zs.\+:\(\s\+@[^\s(]\+\(([^)]*)\)\?\)*$', 'w')
  135. endfunction
  136. function! taskpaper#previous_project()
  137. return search('^\t*\zs.\+:\(\s\+@[^\s(]\+\(([^)]*)\)\?\)*$', 'bw')
  138. endfunction
  139. function! s:search_project(project, depth, begin, end)
  140. call cursor(a:begin, 1)
  141. return search('\v^\t{' . a:depth . '}\V' . a:project . ':', 'c', a:end)
  142. endfunction
  143. function! taskpaper#search_project(projects)
  144. if empty(a:projects)
  145. return 0
  146. endif
  147. let save_pos = getpos('.')
  148. let begin = 1
  149. let end = line('$')
  150. let depth = 0
  151. for project in a:projects
  152. if !s:search_project(project, depth, begin, end)
  153. call setpos('.', save_pos)
  154. return 0
  155. endif
  156. let begin = line('.')
  157. let end = taskpaper#search_end_of_item(begin)
  158. let depth += 1
  159. endfor
  160. call cursor(begin, 1)
  161. normal! ^
  162. return begin
  163. endfunction
  164. function! taskpaper#search_end_of_item(...)
  165. let lnum = a:0 > 0 ? a:1 : line('.')
  166. let flags = a:0 > 1 ? a:2 : ''
  167. let depth = len(matchstr(getline(lnum), '^\t*'))
  168. let end = lnum
  169. let lnum += 1
  170. while lnum <= line('$')
  171. let line = getline(lnum)
  172. if line =~ '^\s*$'
  173. " Do nothing
  174. elseif depth < len(matchstr(line, '^\t*'))
  175. let end = lnum
  176. else
  177. break
  178. endif
  179. let lnum += 1
  180. endwhile
  181. if flags !~# 'n'
  182. call cursor(end, 0)
  183. normal! ^
  184. endif
  185. return end
  186. endfunction
  187. function! taskpaper#delete(...)
  188. let start = a:0 > 0 ? a:1 : line('.')
  189. let reg = a:0 > 1 ? a:2 : '"'
  190. let kill_indent = a:0 > 2 ? a:3 : 0
  191. let reg_save = ''
  192. if kill_indent && reg =~# '\u'
  193. let reg = tolower(reg)
  194. let reg_save = getreg(reg)
  195. endif
  196. let save_fen = &l:foldenable
  197. setlocal nofoldenable
  198. let depth = len(matchstr(getline(start), '^\t*'))
  199. let end = taskpaper#search_end_of_item(start)
  200. silent execute start . ',' . end . 'delete ' . reg
  201. let &l:foldenable = save_fen
  202. if kill_indent
  203. let pat = '\(^\|\n\)\t\{' . depth . '\}'
  204. let content = substitute(getreg(reg), pat, '\1', 'g')
  205. if reg_save != ''
  206. let content = reg_save . content
  207. endif
  208. call setreg(reg, content)
  209. endif
  210. return end - start + 1
  211. endfunction
  212. function! taskpaper#put(...)
  213. let projects = a:0 > 0 ? a:1 : []
  214. let reg = a:0 > 1 ? a:2 : '"'
  215. let indent = a:0 > 2 ? a:3 : 0
  216. let save_fen = &l:foldenable
  217. setlocal nofoldenable
  218. if !empty(projects) && !taskpaper#search_project(projects)
  219. let &l:foldenable = save_fen
  220. return 0
  221. endif
  222. if indent > 0
  223. let project_depth = len(matchstr(getline('.'), '^\t*'))
  224. let tabs = repeat("\t", project_depth + indent)
  225. else
  226. let tabs = ''
  227. endif
  228. execute 'put' reg
  229. silent execute "'[,']" . 's/^\ze./' . tabs
  230. let &l:foldenable = save_fen
  231. return line("']") - line("'[") + 1
  232. endfunction
  233. function! taskpaper#move(projects, ...)
  234. let lnum = a:0 > 0 ? a:1 : line('.')
  235. let save_fen = &l:foldenable
  236. setlocal nofoldenable
  237. if !taskpaper#search_project(a:projects)
  238. let &l:foldenable = save_fen
  239. return 0
  240. endif
  241. let reg = 'a'
  242. let save_reg = [getreg(reg), getregtype(reg)]
  243. let nlines = taskpaper#delete(lnum, reg, 1)
  244. call taskpaper#put(a:projects, reg, 1)
  245. let &l:foldenable = save_fen
  246. call setreg(reg, save_reg[0], save_reg[1])
  247. if g:task_paper_follow_move == 0
  248. execute lnum
  249. endif
  250. return nlines
  251. endfunction
  252. function! taskpaper#move_to_project()
  253. let res = input('Project: ', '', 'customlist,taskpaper#complete_project')
  254. call taskpaper#move(split(res, ':'))
  255. endfunction
  256. function! taskpaper#update_project()
  257. let indent = matchstr(getline("."), '^\t*')
  258. let depth = len(indent)
  259. let projects = []
  260. for linenr in range(line('.'), 1, -1)
  261. let line = getline(linenr)
  262. let ml = matchlist(line, '\v^\t{0,' . depth . '}([^\t:]+):')
  263. if empty(ml)
  264. continue
  265. endif
  266. let project = ml[1]
  267. if project != ""
  268. call add(projects, project)
  269. let indent = matchstr(line, '^\t*')
  270. let depth = len(indent) - 1
  271. if depth < 0
  272. break
  273. endif
  274. endif
  275. endfor
  276. call taskpaper#update_tag('project', join(reverse(projects), ' / '))
  277. endfunction
  278. function! taskpaper#archive_done()
  279. let archive_start = search('^' . g:task_paper_archive_project . ':', 'cw')
  280. if archive_start == 0
  281. call append('$', g:task_paper_archive_project . ':')
  282. let archive_start = line('$')
  283. let archive_end = 0
  284. else
  285. let archive_end = search('^\S\+:', 'W')
  286. endif
  287. let save_fen = &l:foldenable
  288. let save_reg = [getreg('a'), getregtype('a')]
  289. setlocal nofoldenable
  290. call setreg('a', '')
  291. call cursor(1, 1)
  292. let deleted = 0
  293. while 1
  294. let lnum = search('@done', 'W', archive_start - deleted)
  295. if lnum == 0
  296. break
  297. endif
  298. call taskpaper#update_project()
  299. let deleted += taskpaper#delete(lnum, 'A', 1)
  300. endwhile
  301. if archive_end != 0
  302. call cursor(archive_end, 1)
  303. while 1
  304. let lnum = search('@done', 'W')
  305. if lnum == 0
  306. break
  307. endif
  308. call taskpaper#update_project()
  309. let deleted += taskpaper#delete(lnum, 'A', 1)
  310. endwhile
  311. endif
  312. if deleted != 0
  313. call taskpaper#put([g:task_paper_archive_project], 'a', 1)
  314. else
  315. echo 'No done items.'
  316. endif
  317. let &l:foldenable = save_fen
  318. call setreg('a', save_reg[0], save_reg[1])
  319. return deleted
  320. endfunction
  321. function! taskpaper#fold(lnum, pat, ipat)
  322. let line = getline(a:lnum)
  323. let level = foldlevel(a:lnum)
  324. if line =~? a:pat && (a:ipat == '' || line !~? a:ipat)
  325. return 0
  326. elseif synIDattr(synID(a:lnum, 1, 1), "name") != 'taskpaperProject'
  327. return 1
  328. elseif level != -1
  329. return level
  330. endif
  331. let depth = len(matchstr(getline(a:lnum), '^\t*'))
  332. for lnum in range(a:lnum + 1, line('$'))
  333. let line = getline(lnum)
  334. if depth >= len(matchstr(line, '^\t*'))
  335. break
  336. endif
  337. if line =~? a:pat && (a:ipat == '' || line !~? a:ipat)
  338. return 0
  339. endif
  340. endfor
  341. return 1
  342. endfunction
  343. function! taskpaper#search(...)
  344. let pat = a:0 > 0 ? a:1 : input('Search: ')
  345. let ipat = a:0 > 1 ? a:2 : ''
  346. if pat == ''
  347. return
  348. endif
  349. setlocal foldexpr=taskpaper#fold(v:lnum,pat,ipat)
  350. setlocal foldminlines=0 foldtext=''
  351. setlocal foldmethod=expr foldlevel=0 foldenable
  352. endfunction
  353. function! taskpaper#fold_except_range(lnum, begin, end)
  354. if a:lnum > a:end
  355. return 1
  356. elseif a:lnum >= a:begin
  357. return 0
  358. elseif synIDattr(synID(a:lnum, 1, 1), "name") != 'taskpaperProject'
  359. return 1
  360. elseif level != -1
  361. return level
  362. endif
  363. if a:end <= taskpaper#search_end_of_item(a:lnum, 'n')
  364. return 0
  365. endif
  366. return 1
  367. endfunction
  368. function! taskpaper#focus_project()
  369. let pos = getpos('.')
  370. normal! $
  371. let begin = taskpaper#previous_project()
  372. if begin == 0
  373. call setpos('.', pos)
  374. return
  375. endif
  376. let end = taskpaper#search_end_of_item(begin, 'n')
  377. " Go to the top level project
  378. while taskpaper#previous_project()
  379. if getline('.') =~ '^[^\t]'
  380. break
  381. endif
  382. endwhile
  383. setlocal foldexpr=taskpaper#fold_except_range(v:lnum,begin,end)
  384. setlocal foldminlines=0 foldtext=''
  385. setlocal foldmethod=expr foldlevel=0 foldenable
  386. endfunction
  387. function! taskpaper#search_tag(...)
  388. if a:0 > 0
  389. let tag = a:1
  390. else
  391. let cword = expand('<cword>')
  392. let tag = input('Tag: ', cword =~ '@\k\+' ? cword[1:] : '')
  393. endif
  394. if tag != ''
  395. let ipat = (g:task_paper_search_hide_done == 1)?'\<@done\>':''
  396. call taskpaper#search('\<@' . tag . '\>', ipat)
  397. endif
  398. endfunction
  399. function! taskpaper#_fold_projects(lnum)
  400. if synIDattr(synID(a:lnum, 1, 1), "name") != 'taskpaperProject'
  401. return '='
  402. endif
  403. let line = getline(a:lnum)
  404. let depth = len(matchstr(line, '^\t*'))
  405. return '>' . (depth + 1)
  406. endfunction
  407. function! taskpaper#fold_projects()
  408. setlocal foldexpr=taskpaper#_fold_projects(v:lnum)
  409. setlocal foldminlines=0 foldtext=foldtext()
  410. setlocal foldmethod=expr foldlevel=0 foldenable
  411. endfunction
  412. function! taskpaper#newline()
  413. let lnum = line('.')
  414. let line = getline('.')
  415. if lnum == 1 || line !~ '^\s*$' ||
  416. \ synIDattr(synID(lnum - 1, 1, 1), "name") != 'taskpaperProject'
  417. return ''
  418. endif
  419. let pline = getline(lnum - 1)
  420. let depth = len(matchstr(pline, '^\t*'))
  421. call setline(lnum, repeat("\t", depth + 1) . '- ')
  422. return "\<End>"
  423. endfunction
  424. function! taskpaper#tag_style(...)
  425. if a:0 > 0
  426. let tag_name = a:1
  427. endif
  428. if a:0 > 1
  429. let tag_style = a:2
  430. let tag_style_name = 'taskpaperAutoStyle_' . tag_name
  431. execute 'syn match' tag_style_name '/\s\zs@'.tag_name.'\(([^)]*)\)\?/'
  432. execute 'hi' tag_style_name tag_style
  433. if version < 508
  434. execute 'hi link' tag_style_name tag_style_name
  435. else
  436. execute 'hi def link' tag_style_name tag_style_name
  437. endif
  438. else
  439. echo "No style specified."
  440. return ''
  441. endif
  442. endfunction
  443. function! taskpaper#tag_style_dict(tsd)
  444. for tag_name in keys(a:tsd)
  445. call taskpaper#tag_style(tag_name,a:tsd[tag_name])
  446. endfor
  447. endfunction
  448. let &cpo = s:save_cpo