Set TERMINFO before calling tput.
[profile.git] / .vim / plugin / speeddating.vim
1 " speeddating.vim - Use CTRL-A/X to increment dates, times, and more
2 " Maintainer:   Tim Pope
3 " Last Change:  2008 Jan 29
4 " GetLatestVimScripts: 2120 1 :AutoInstall: speeddating.vim
5
6 " Greatly enhanced <C-A>/<C-X>.  Try these keys on various numbers in the
7 " lines below.  You can also give a count (e.g., 4<C-A>).
8
9 " Fri, 31 Dec 1999 23:59:59 +0000
10 " 2008-01-05T04:59:59Z
11 " 1865-04-15
12 " 11/Sep/01
13 " January 14th, 1982
14 " 11:55 AM
15 " 3rd
16 " XXXVIII
17
18 " Try selecting the following lines in visual line mode, positioning the
19 " cursor in the last column (so it's on top of a number), and pressing <C-A>.
20
21 "        I
22 "       II
23 "      III
24 "       IV
25 "        V
26
27 " Also try below to see what happens when the field is missing (alphabetical
28 " characters can be used in visual mode only, as they overlap with roman
29 " numerals):
30
31 " Z
32
33
34
35
36
37
38
39 " The :SpeedDatingFormat command can be used to define custom date and time
40 " formats.  Invoke ":SpeedDatingFormat!" for help.
41 "
42 " Two additional mappings:
43 " d<C-A>  change the timestamp under the cursor to the current time in UTC
44 " d<C-X>  change the timestamp under the cursor to the current local time
45 "
46 " Caveats:
47 " - Completely timezone ignorant.
48 " - Gregorian calendar always used.
49 " - Beginning a format with a digit causes Vim to treat leading digits as a
50 "   count instead.  To work around this escape it with %[] instead (e.g.,
51 "   %[2]0%0y%0m%0d%* is a decent format for DNS serials).
52
53 " Licensed under the same terms as Vim itself.
54
55 " Initialization {{{1
56
57 if exists("g:loaded_speeddating") || &cp || v:version < 700
58     finish
59 endif
60 let g:loaded_speeddating = 1
61
62 let s:cpo_save = &cpo
63 set cpo&vim
64
65 let g:speeddating_handlers = []
66
67 let s:install_dir = expand("<sfile>:p:h:h")
68
69 " }}}1
70 " Utility Functions {{{1
71
72 function! s:function(name)
73     return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
74 endfunction
75
76 " In Vim, -4 % 3 == -1.  Let's return 2 instead.
77 function! s:mod(a,b)
78     if a:a < 0 && a:b > 0 || a:a > 0 && a:b < 0
79         return (a:a % a:b) + a:b
80     else
81         return a:a % a:b
82     endif
83 endfunction
84
85 " In Vim, -4 / 3 == -1.  Let's return -2 instead.
86 function! s:div(a,b)
87     if a:a < 0 && a:b > 0
88         return (a:a-a:b+1)/a:b
89     elseif a:a > 0 && a:b < 0
90         return (a:a-a:b-1)/a:b
91     else
92         return a:a / a:b
93     endif
94 endfunction
95
96 function! s:match(...)
97     let b = call("match",a:000)
98     let e = call("matchend",a:000)
99     let s = call("matchlist",a:000)
100     if s == []
101         let s = ["","","","","","","","","",""]
102     endif
103     return [b,e] + s
104 endfunction
105
106 function! s:findatoffset(string,pattern,offset)
107     let line = a:string
108     let curpos = 0
109     let offset = a:offset
110     while strpart(line,offset,1) == " "
111         let offset += 1
112     endwhile
113     let [start,end,string;caps] = s:match(line,a:pattern,curpos,0)
114     while start >= 0
115         if offset >= start && offset < end
116             break
117         endif
118         let curpos = start + 1
119         let [start,end,string;caps] = s:match(line,a:pattern,curpos,0)
120     endwhile
121     return [start,end,string] + caps
122 endfunction
123
124 function! s:findinline(pattern)
125     return s:findatoffset(getline('.'),a:pattern,col('.')-1)
126 endfunction
127
128 function! s:replaceinline(start,end,new)
129     let line = getline('.')
130     let before_text = strpart(line,0,a:start)
131     let after_text = strpart(line,a:end)
132     " If this generates a warning it will be attached to an ugly backtrace.
133     " No warning at all is preferable to that.
134     silent call setline('.',before_text.a:new.after_text)
135     call setpos("'[",[0,line('.'),strlen(before_text)+1,0])
136     call setpos("']",[0,line('.'),a:start+strlen(a:new),0])
137 endfunction
138
139 " }}}1
140 " Normal Mode {{{1
141
142 function! s:increment(increment)
143     for handler in s:time_handlers + g:speeddating_handlers
144         let pattern = type(handler.regexp) == type(function('tr')) ? handler.regexp() : handler.regexp
145         let [start,end,string;caps] = s:findinline('\C'.pattern)
146         if string != ""
147             let [repl,offset] = handler.increment(string,col('.')-1-start,a:increment)
148             if offset < 0
149                 let offset += strlen(repl) + 1
150             endif
151             if repl != ""
152                 call s:replaceinline(start,end,repl)
153                 call setpos('.',[0,line('.'),start+offset,0])
154                 silent! call repeat#set("\<Plug>SpeedDating" . (a:increment < 0 ? "Down" : "Up"),a:increment < 0 ? -a:increment : a:increment)
155                 return
156             endif
157         endif
158     endfor
159     if a:increment > 0
160         exe "norm! ". a:increment."\<C-A>"
161     else
162         exe "norm! ".-a:increment."\<C-X>"
163     endif
164     silent! call repeat#set("\<Plug>SpeedDating" . (a:increment < 0 ? "Down" : "Up"),a:increment < 0 ? -a:increment : a:increment)
165 endfunction
166
167 " }}}1
168 " Visual Mode {{{1
169
170 function! s:setvirtcol(line,col)
171     call setpos('.',[0,a:line,a:col,0])
172     while virtcol('.') < a:col
173         call setpos('.',[0,a:line,col('.')+1,0])
174     endwhile
175     while virtcol('.') > a:col
176         call setpos('.',[0,a:line,col('.')-1,0])
177     endwhile
178     return col('.') + getpos('.')[3]
179 endfunction
180
181 function! s:chars(string)
182     return strlen(substitute(a:string,'.','.','g'))
183 endfunction
184
185 function! s:incrementstring(string,offset,count)
186     let repl = ""
187     let offset = -1
188     for handler in s:time_handlers + g:speeddating_handlers + s:visual_handlers
189         let pattern = type(handler.regexp) == type(function('tr')) ? handler.regexp() : handler.regexp
190         let [start,end,string;caps] = s:findatoffset(a:string,'\C'.pattern,a:offset)
191         if string != ""
192             let [repl,offset] = handler.increment(string,a:offset,a:count)
193             if repl != ""
194                 break
195             endif
196         endif
197     endfor
198     if offset < 0
199         let offset += strlen(repl) + 1
200     endif
201
202     if repl != ""
203         let before_text = strpart(a:string,0,start)
204         let change = s:chars(repl) - s:chars(string)
205         if change < 0 && before_text !~ '\w$'
206             let offset -= change
207             let repl = repeat(' ',-change) . repl
208         elseif change > 0 && before_text =~ ' $'
209             let before_text = substitute(before_text,' \{1,'.change.'\}$','','')
210             let before_text = substitute(before_text,'\w$','& ','')
211             let start = strlen(before_text)
212         endif
213         let offset += start
214         let repl = before_text.repl.strpart(a:string,end)
215     endif
216     return [repl,offset,start,end]
217 endfunction
218
219 function! s:incrementvisual(count)
220     let ve = &ve
221     set virtualedit=all
222     exe "norm! gv\<Esc>"
223     let vcol = virtcol('.')
224     let lnum = line("'<")
225     let lastrepl = ""
226     call s:setvirtcol(lnum,vcol)
227     call setpos("'[",[0,line("'<"),1,0])
228     while lnum <= line("'>")
229         call s:setvirtcol(lnum,vcol)
230         let [repl,offset,start,end] = s:incrementstring(getline('.'),col('.')-1,a:count)
231         if repl == "" && lastrepl != ""
232             call setpos(".",[0,lnum-1,laststart,0])
233             let start = s:setvirtcol(lnum,virtcol('.'))
234             call setpos(".",[0,lnum-1,lastend,0])
235             let end = s:setvirtcol(lnum,virtcol('.'))
236             call s:setvirtcol(lnum,vcol)
237             if strpart(getline('.'),start,end-start) =~ '^\s*$'
238                 let before_padded = printf("%-".start."s",strpart(getline('.'),0,start))
239                 let tweaked_line  = before_padded.strpart(lastrepl,laststart,lastend-laststart).strpart(getline('.'),end)
240                 let [repl,offset,start,end] = s:incrementstring(tweaked_line,col('.')-1,a:count*(lnum-lastlnum))
241             endif
242         elseif repl != ""
243             let [lastrepl,laststart,lastend,lastlnum] = [repl,start,end,lnum]
244         endif
245         if repl != ""
246             silent call setline('.',repl)
247         endif
248         let lnum += 1
249     endwhile
250     let &ve = ve
251     call setpos("']",[0,line('.'),col('$'),0])
252 endfunction
253
254 " }}}1
255 " Visual Mode Handlers {{{1
256
257 let s:visual_handlers = []
258
259 function! s:numberincrement(string,offset,increment)
260     let n = (a:string + a:increment)
261     if a:string =~# '^0x.*[A-F]'
262         return [printf("0x%X",n),-1]
263     elseif a:string =~# '^0x'
264         return [printf("0x%x",n),-1]
265     elseif a:string =~# '^00*[^0]'
266         return [printf("0%o",n),-1]
267     else
268         return [printf("%d",n),-1]
269     endif
270 endfunction
271
272 let s:visual_handlers += [{'regexp': '-\=\<\%(0x\x\+\|\d\+\)\>', 'increment': s:function("s:numberincrement")}]
273
274 function! s:letterincrement(string,offset,increment)
275     return [nr2char((char2nr(toupper(a:string)) - char2nr('A') + a:increment) % 26 + (a:string =~# '[A-Z]' ? char2nr('A') : char2nr('a'))),-1]
276 endfunction
277
278 let s:visual_handlers += [{'regexp': '\<[A-Za-z]\>', 'increment': s:function("s:letterincrement")}]
279
280 " }}}1
281 " Ordinals {{{1
282
283 function! s:ordinalize(number)
284     let n = a:number
285     let a = n < 0 ? -n : +n
286     if a % 100 == 11 || a % 100 == 12 || a % 100 == 13
287         return n."th"
288     elseif a % 10 == 1
289         return n."st"
290     elseif a % 10 == 2
291         return n."nd"
292     elseif a % 10 == 3
293         return n."rd"
294     else
295         return n."th"
296     endif
297 endfunction
298
299 function! s:ordinalincrement(string,offset,increment)
300     return [s:ordinalize(a:string+a:increment),-1]
301 endfunction
302
303 let g:speeddating_handlers += [{'regexp': '-\=\<\d\+\%(st\|nd\|rd\|th\)\>', 'increment': s:function("s:ordinalincrement")}]
304
305 " }}}1
306 " Roman Numerals {{{1
307
308 " Based on similar functions from VisIncr.vim
309
310 let s:a2r = [[1000, 'm'], [900, 'cm'], [500, 'd'], [400, 'cd'], [100, 'c'],
311             \             [90 , 'xc'], [50 , 'l'], [40 , 'xl'], [10 , 'x'],
312             \             [9  , 'ix'], [5  , 'v'], [4  , 'iv'], [1  , 'i']]
313
314 function! s:roman2arabic(roman)
315     let roman  = tolower(a:roman)
316     let sign   = 1
317     let arabic = 0
318     while roman != ''
319         if roman =~ '^[-n]'
320             let sign = -sign
321         endif
322         for [numbers,letters] in s:a2r
323             if roman =~ '^'.letters
324                 let arabic += sign * numbers
325                 let roman = strpart(roman,strlen(letters)-1)
326                 break
327             endif
328         endfor
329         let roman = strpart(roman,1)
330     endwhile
331
332     return arabic
333 endfunction
334
335 function! s:arabic2roman(arabic)
336   if a:arabic <= 0
337       let arabic = -a:arabic
338       let roman = "n"
339   else
340       let arabic = a:arabic
341       let roman = ""
342   endif
343   for [numbers, letters] in s:a2r
344       let roman .= repeat(letters,arabic/numbers)
345       let arabic = arabic % numbers
346   endfor
347   return roman
348 endfunction
349
350 " }}}1
351 " Time Helpers {{{1
352
353 " approximate
354 let s:offset = strftime("%d",86400)*24+strftime("%H",86400)-48
355
356 let s:days_engl   =["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]
357 let s:days_abbr   =map(range(86400*3+43200-s:offset*3600,86400*12,86400),'strftime("%a",v:val)')[0:6]
358 let s:days_full   =map(range(86400*3+43200-s:offset*3600,86400*12,86400),'strftime("%A",v:val)')[0:6]
359
360 let s:months_engl =["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
361 let s:months_abbr =map(range(86400*2,86400*365,86400*31),'strftime("%b",v:val)')
362 let s:months_full =map(range(86400*2,86400*365,86400*31),'strftime("%B",v:val)')
363
364 function! s:ary2pat(array)
365     return '\%('.join(a:array,'\|').'\)'
366     return '\%('.join(map(copy(a:array),'substitute(v:val,"[[:alpha:]]","[\\u&\\l&]","g")'),'\|').'\)'
367 endfunction
368
369 function! s:initializetime(time)
370     call extend(a:time,{'y':2000,'b':1,'d':0,'h':0,'m':0,'s':0},"keep")
371     if get(a:time,'b','') !~ '^\d*$'
372         let full = index(s:months_full ,a:time.b,0,1) + 1
373         let engl = index(s:months_engl ,a:time.b,0,1) + 1
374         let abbr = index(s:months_abbr ,a:time.b,0,1) + 1
375         if full
376             let a:time.b = full
377         elseif engl
378             let a:time.b = engl
379         elseif abbr
380             let a:time.b = abbr
381         else
382             let a:time.b = 1
383         endif
384     endif
385     if has_key(a:time,'p')
386         let a:time.h = a:time.h % 12
387         if a:time.p ==? "PM"
388             let a:time.h += 12
389         endif
390         call remove(a:time,"p")
391     endif
392     if a:time.y !~ '^\d*$'
393         let a:time.y = s:roman2arabic(a:time.y)
394     elseif a:time.y =~ '^-\=0..'
395         let a:time.y = substitute(a:time.y,'0\+','','')
396     elseif a:time.y < 38 && a:time.y >= 0
397         let a:time.y += 2000
398     elseif a:time.y < 100 && a:time.y >= 0
399         let a:time.y += 1900
400     endif
401     if a:time.d == 0 && has_key(a:time,'w')
402         let full = index(s:days_full ,a:time.w,0,1)
403         let engl = index(s:days_engl ,a:time.w,0,1)
404         let abbr = index(s:days_abbr ,a:time.w,0,1)
405         let any = full > 0 ? full : (engl > 0 ? engl : (abbr > 0 ? abbr : a:time.w))
406         let a:time.d = s:mod(any - s:jd(a:time.y,a:time.b,1),7)
407         call remove(a:time,'w')
408     endif
409     if a:time.d == 0
410         let a:time.d = 1
411     endif
412     return a:time
413 endfunction
414
415 " Julian day (always Gregorian calendar)
416 function! s:jd(year,mon,day)
417     let y = a:year + 4800 - (a:mon <= 2)
418     let m = a:mon + (a:mon <= 2 ? 9 : -3)
419     let jul = a:day + (153*m+2)/5 + s:div(1461*y,4) - 32083
420     return jul - s:div(y,100) + s:div(y,400) + 38
421 endfunction
422
423 function! s:gregorian(jd)
424     let l = a:jd + 68569
425     let n = s:div(4 * l, 146097)
426     let l = l - s:div(146097 * n + 3, 4)
427     let i = ( 4000 * ( l + 1 ) ) / 1461001
428     let l = l - ( 1461 * i ) / 4 + 31
429     let j = ( 80 * l ) / 2447
430     let d = l - ( 2447 * j ) / 80
431     let l = j / 11
432     let m = j + 2 - ( 12 * l )
433     let y = 100 * ( n - 49 ) + i + l
434     return {'y':y,'b':m,'d':d}
435 endfunction
436
437 function! s:normalizetime(time)
438     let a:time.y += s:div(a:time.b-1,12)
439     let a:time.b = s:mod(a:time.b-1,12)+1
440     let seconds = a:time.h * 3600 + a:time.m * 60 + a:time.s
441     let a:time.s = s:mod(seconds,60)
442     let a:time.m = s:mod(s:div(seconds,60),60)
443     let a:time.h = s:mod(s:div(seconds,3600),24)
444     if seconds != 0 || a:time.b != 1 || a:time.d != 1
445         let day = s:gregorian(s:jd(a:time.y,a:time.b,a:time.d)+s:div(seconds,86400))
446         return extend(a:time,day)
447     else
448         return a:time
449     endif
450 endfunction
451
452 function! s:applymodifer(number,modifier,width)
453     if a:modifier == '-'
454         return substitute(a:number,'^0*','','')
455     elseif a:modifier == '_'
456         return printf('%'.a:width.'d',a:number)
457     elseif a:modifier == '^'
458         return toupper(a:number)
459     else
460         return printf('%0'.a:width.'s',a:number)
461     endif
462 endfunction
463
464 function! s:modyear(y)
465     return printf('%02d',s:mod(a:y,100))
466 endfunction
467
468 function! s:strftime(pattern,time)
469     if type(a:time) == type({})
470         let time = s:normalizetime(copy(a:time))
471     else
472         let time = s:normalizetime(s:initializetime({'y':1970,'s':a:time}))
473     endif
474     let time.w = s:mod(s:jd(time.y,time.b,time.d)+1,7)
475     let time.p = time.h
476     let expanded = ""
477     let remaining = a:pattern
478     while remaining != ""
479         if remaining =~ '^%'
480             let modifier = matchstr(remaining,'%\zs[-_0^]\=\ze.')
481             let specifier = matchstr(remaining,'%[-_0^]\=\zs.')
482             let remaining = matchstr(remaining,'%[-_0^]\=.\zs.*')
483             if specifier == '%'
484                 let expanded .= '%'
485             elseif has_key(s:strftime_items,specifier)
486                 let item = s:strftime_items[specifier]
487                 let number = time[item[1]]
488                 if type(item[4]) == type([])
489                     let expanded .= s:applymodifer(item[4][number % len(item[4])],modifier,1)
490                 elseif type(item[4]) == type(function('tr'))
491                     let expanded .= s:applymodifer(call(item[4],[number]),modifier,1)
492                 else
493                     let expanded .= s:applymodifer(number,modifier,item[4])
494                 endif
495             else
496                 let expanded .= '%'.modifier.specifier
497             endif
498         else
499             let expanded .= matchstr(remaining,'[^%]*')
500             let remaining = matchstr(remaining,'[^%]*\zs.*')
501         endif
502     endwhile
503     return expanded
504 endfunction
505
506 " }}}1
507 " Time Handler {{{1
508
509 function! s:timestamp(utc,count)
510     for handler in s:time_handlers
511         let [start,end,string;caps] = s:findinline('\C'.join(handler.groups,''))
512         if string != ""
513             let format = substitute(handler.strftime,'\\\([1-9]\)','\=caps[submatch(1)-1]','g')
514             if a:utc
515                 let newstring = s:strftime(format,localtime()+a:count*60*15)
516             elseif a:count
517                 let newstring = s:strftime(format,localtime()-a:count*60*15)
518             else
519                 let newstring = s:strftime(format,{
520                             \ 'y': strftime('%Y'),
521                             \ 'b': strftime('%m'),
522                             \ 'd': strftime('%d'),
523                             \ 'h': strftime('%H'),
524                             \ 'm': strftime('%M'),
525                             \ 's': strftime('%S')})
526             endif
527             call s:replaceinline(start,end,newstring)
528             call setpos('.',[0,line('.'),start+strlen(newstring),0])
529             silent! call repeat#set("\<Plug>SpeedDatingNow".(a:utc ? "UTC" : "Local"),a:count)
530             return ""
531         endif
532     endfor
533     let [start,end,string;caps] = s:findinline('-\=\<\d\+\>')
534     if string != ""
535         let newstring = localtime() + (a:utc ? 1 : -1) * a:count * 60*15
536         call s:replaceinline(start,end,newstring)
537         call setpos('.',[0,line('.'),start+strlen(newstring),0])
538         silent! call repeat#set("\<Plug>SpeedDatingNow".(a:utc ? "UTC" : "Local"),a:count)
539     endif
540 endfunction
541
542 function! s:dateincrement(string,offset,increment) dict
543     let [start,end,string;caps] = s:match(a:string,'\C'.join(self.groups,''))
544     let string = a:string
545     let offset = a:offset
546     let cursor_capture = 1
547     let idx = 0
548     while idx < len(self.groups)
549         let partial_matchend = matchend(string,join(self.groups[0:idx],''))
550         if partial_matchend > offset
551             break
552         endif
553         let idx += 1
554     endwhile
555     while get(self.targets,idx,"") == " "
556         let idx += 1
557     endwhile
558     while get(self.targets,idx," ") == " "
559         let idx -= 1
560     endwhile
561     let partial_pattern = join(self.groups[0:idx],'')
562     let char = self.targets[idx]
563     let i = 0
564     let time = {}
565     for cap in caps
566         if get(self.reader,i," ") !~ '^\s\=$'
567             let time[self.reader[i]] = substitute(cap,'^\s*','','')
568         endif
569         let i += 1
570     endfor
571     call s:initializetime(time)
572     let time[char] += a:increment
573     let format = substitute(self.strftime,'\\\([1-9]\)','\=caps[submatch(1)-1]','g')
574     let time_string = s:strftime(format,time)
575     return [time_string, matchend(time_string,partial_pattern)]
576 endfunction
577
578 let s:strftime_items = {
579             \ "a": ['d','w',s:ary2pat(s:days_abbr),   'weekday (abbreviation)',s:days_abbr],
580             \ "A": ['d','w',s:ary2pat(s:days_full),   'weekday (full name)',s:days_full],
581             \ "i": ['d','w',s:ary2pat(s:days_engl),   'weekday (English abbr)',s:days_engl],
582             \ "b": ['b','b',s:ary2pat(s:months_abbr), 'month (abbreviation)',[""]+s:months_abbr],
583             \ "B": ['b','b',s:ary2pat(s:months_full), 'month (full name)',[""]+s:months_full],
584             \ "h": ['b','b',s:ary2pat(s:months_engl), 'month (English abbr)',[""]+s:months_engl],
585             \ "d": ['d','d','[ 0-3]\=\d', 'day   (01-31)',2],
586             \ "H": ['h','h','[ 0-2]\=\d', 'hour  (00-23)',2],
587             \ "I": ['h','h','[ 0-2]\=\d', 'hour  (01-12)',['12', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11']],
588             \ "m": ['b','b','[ 0-1]\=\d', 'month (01-12)',2],
589             \ "M": ['m','m','[ 0-5]\=\d', 'minutes',2],
590             \ "o": ['d','d','[ 0-3]\=\d\%(st\|nd\|rd\|th\)','day  (1st-31st)',s:function("s:ordinalize")],
591             \ "P": ['h','p','[ap]m', 'am/pm',repeat(['am'],12) + repeat(['pm'],12)],
592             \ "S": ['s','s','[ 0-5]\=\d', 'seconds',2],
593             \ "v": ['y','y','[ivxlcdmn]\+','year (roman numerals)',s:function("s:arabic2roman")],
594             \ "y": ['y','y','\d\d','year  (00-99)',s:function("s:modyear")],
595             \ "Y": ['y','y','-\=\d\d\d\=\d\=','year',4]}
596
597 function! s:timeregexp() dict
598     return join(self.groups,'')
599 endfunction
600
601 function! s:createtimehandler(format)
602     let pattern = '^\%(%?\=\[.\{-\}\]\|%[-_0^]\=.\|[^%]*\)'
603     let regexp = ['\%(\<\|-\@=\)']
604     let reader = []
605     let targets = [' ']
606     let template = ""
607     let default = ""
608     let remaining = substitute(a:format,'\C%\@<!%p','%^P','g')
609     let group = 0
610     let usergroups = []
611     let userdefaults = []
612     while remaining != ""
613         let fragment  = matchstr(remaining,pattern)
614         let remaining = matchstr(remaining,pattern.'\zs.*')
615         if fragment =~ '^%\*\W'
616             let suffix = '*'
617             let fragment = '%' . strpart(fragment,2)
618         elseif fragment =~ '^%?\W'
619             let suffix = '\='
620             let fragment = '%' . strpart(fragment,2)
621         else
622             let suffix = ''
623         endif
624         let targets += [' ']
625         if fragment =~ '^%' && has_key(s:strftime_items,matchstr(fragment,'.$'))
626             let item = s:strftime_items[matchstr(fragment,'.$')]
627             let modifier = matchstr(fragment,'^%\zs.\ze.$')
628             let targets[-1] = item[0]
629             let reader += [item[1]]
630             if modifier == '^'
631                 let pat = substitute(item[2],'\C\\\@<![[:lower:]]','\u&','g')
632             elseif modifier == '0'
633                 let pat = substitute(item[2],' \|-\@<!\\=','','g')
634             else
635                 let pat = item[2]
636             endif
637             let regexp += ['\('.pat.'\)']
638             let group += 1
639             let template .= fragment
640             let default .= fragment
641         elseif fragment =~ '^%\[.*\]$'
642             let reader += [' ']
643             let regexp += ['\('.matchstr(fragment,'\[.*').suffix.'\)']
644             let group += 1
645             let usergroups += [group]
646             let template .= "\\".group
647             if suffix == ""
648                 let default .= strpart(fragment,2,1)
649                 let userdefaults += [strpart(fragment,2,1)]
650             else
651                 let userdefaults += [""]
652             endif
653         elseif fragment =~ '^%\d'
654             let regexp += ["\\".usergroups[strpart(fragment,1)-1]]
655             let template .= regexp[-1]
656             let default .= userdefaults[strpart(fragment,1)-1]
657         elseif fragment == '%*'
658             if len(regexp) == 1
659                 let regexp = []
660                 let targets = []
661             else
662                 let regexp += ['\(.*\)']
663             endif
664         else
665             let regexp += [fragment]
666             let template .= fragment
667             let default .= fragment
668         endif
669     endwhile
670     if regexp[-1] == '\(.*\)'
671         call remove(regexp,-1)
672         call remove(targets,-1)
673     else
674         let regexp += ['\>']
675     endif
676     return {'source': a:format, 'strftime': template, 'groups': regexp, 'regexp': s:function('s:timeregexp'), 'reader': reader, 'targets': targets, 'default': default, 'increment': s:function('s:dateincrement')}
677 endfunction
678
679 function! s:comparecase(i1, i2)
680     if a:i1 ==? a:i2
681         return a:i1 ==# a:i2 ? 0 : a:i1 ># a:i2 ? 1 : -1
682     else
683         return tolower(a:i1) > tolower(a:i2) ? 1 : -1
684     endif
685 endfunction
686
687 function! s:adddate(master,count,bang)
688     if a:master == ""
689         if a:bang && a:count
690             silent! call remove(s:time_handlers,a:count - 1)
691         elseif a:bang
692             echo "SpeedDatingFormat             List defined formats"
693             echo "SpeedDatingFormat!            This help"
694             echo "SpeedDatingFormat %Y-%m-%d    Add a format"
695             echo "1SpeedDatingFormat %Y-%m-%d   Add a format before first format"
696             echo "SpeedDatingFormat! %Y-%m-%d   Remove a format"
697             echo "1SpeedDatingFormat!           Remove first format"
698             echo " "
699             echo "Expansions:"
700             for key in sort(keys(s:strftime_items),s:function("s:comparecase"))
701                 echo printf("%2s     %-25s %s",'%'.key,s:strftime_items[key][3],s:strftime('%'.key,localtime()))
702             endfor
703             echo '%0x    %x with mandatory leading zeros'
704             echo '%_x    %x with spaces rather than leading zeros'
705             echo '%-x    %x with no leading spaces or zeros'
706             echo '%^x    %x in uppercase'
707             echo '%*     at beginning/end, surpress \</\> respectively'
708             echo '%[..]  any one character         \([..]\)'
709             echo '%?[..] up to one character       \([..]\=\)'
710             echo '%1     character from first collection match \1'
711             echo " "
712             echo "Examples:"
713             echo 'SpeedDatingFormat %m%[/-]%d%1%Y    " American 12/25/2007'
714             echo 'SpeedDatingFormat %d%[/-]%m%1%Y    " European 25/12/2007'
715             echo " "
716             echo "Define formats in ".s:install_dir."/after/plugin/speeddating.vim"
717         elseif a:count
718             echo get(s:time_handlers,a:count-1,{'source':''}).source
719         else
720             let i = 0
721             for handler in s:time_handlers
722                 let i += 1
723                 echo printf("%3d %-32s %-32s",i,handler.source,s:strftime(handler.default,localtime()))
724             endfor
725         endif
726     elseif a:bang
727         call filter(s:time_handlers,'v:val.source != a:master')
728     else
729         let handler = s:createtimehandler(a:master)
730         if a:count
731             call insert(s:time_handlers,handler,a:count - 1)
732         else
733             let s:time_handlers += [handler]
734         endif
735     endif
736 endfunction
737
738 let s:time_handlers = []
739
740 command! -bar -bang -count=0 -nargs=? SpeedDatingFormat :call s:adddate(<q-args>,<count>,<bang>0)
741
742 " }}}1
743 " Default Formats {{{1
744
745 SpeedDatingFormat %a %b %d %H:%M:%S UTC %Y      " default date(1) format
746 SpeedDatingFormat %a %b %d %H:%M:%S %[A-Z]%[A-Z]T %Y
747 SpeedDatingFormat %i, %d %h %Y %H:%M:%S         " RFC822, sans timezone
748 SpeedDatingFormat %i, %h %d, %Y at %I:%M:%S%^P  " mutt default date format
749 SpeedDatingFormat %h %_d %H:%M:%S               " syslog
750 SpeedDatingFormat %Y-%m-%d%[ T_-]%H:%M:%S%?[Z]  " SQL, etc.
751 SpeedDatingFormat %Y-%m-%d
752 SpeedDatingFormat %-I:%M:%S%?[ ]%^P
753 SpeedDatingFormat %-I:%M%?[ ]%^P
754 SpeedDatingFormat %-I%?[ ]%^P
755 SpeedDatingFormat %H:%M:%S
756 SpeedDatingFormat %B %o, %Y
757 SpeedDatingFormat %d%[-/ ]%b%1%y
758 SpeedDatingFormat %d%[-/ ]%b%1%Y                " These three are common in the
759 SpeedDatingFormat %Y %b %d                      " 'Last Change:' headers of
760 SpeedDatingFormat %b %d, %Y                     " Vim runtime files
761 SpeedDatingFormat %^v
762 SpeedDatingFormat %v
763
764 " }}}1
765 " Maps {{{1
766
767 nnoremap <silent> <Plug>SpeedDatingUp   :<C-U>call <SID>increment(v:count1)<CR>
768 nnoremap <silent> <Plug>SpeedDatingDown :<C-U>call <SID>increment(-v:count1)<CR>
769 vnoremap <silent> <Plug>SpeedDatingUp   :<C-U>call <SID>incrementvisual(v:count1)<CR>
770 vnoremap <silent> <Plug>SpeedDatingDown :<C-U>call <SID>incrementvisual(-v:count1)<CR>
771 nnoremap <silent> <Plug>SpeedDatingNowLocal :<C-U>call <SID>timestamp(0,v:count)<CR>
772 nnoremap <silent> <Plug>SpeedDatingNowUTC   :<C-U>call <SID>timestamp(1,v:count)<CR>
773
774 if !exists("g:speeddating_no_mappings") || !g:speeddating_no_mappings
775     nmap  <C-A>     <Plug>SpeedDatingUp
776     nmap  <C-X>     <Plug>SpeedDatingDown
777     xmap  <C-A>     <Plug>SpeedDatingUp
778     xmap  <C-X>     <Plug>SpeedDatingDown
779     nmap d<C-A>     <Plug>SpeedDatingNowUTC
780     nmap d<C-X>     <Plug>SpeedDatingNowLocal
781 endif
782
783 " }}}1
784
785 let &cpo = s:cpo_save
786
787 " vim:set et sw=4 sts=4: