Merge branch 'vim' into feat/code-check
[vim_extended.git] / runtime / ftplugin / changelog.vim
blob924d35daedc990ece9091c46a8ef9a2093f35112
1 " Vim filetype plugin file
2 " Language:         generic Changelog file
3 " Maintainer:       Nikolai Weibull <now@bitwi.se>
4 " Latest Revision:  2007-05-21
5 " Variables:
6 "   g:changelog_timeformat (deprecated: use g:changelog_dateformat instead) -
7 "       description: the timeformat used in ChangeLog entries.
8 "       default: "%Y-%m-%d".
9 "   g:changelog_dateformat -
10 "       description: the format sent to strftime() to generate a date string.
11 "       default: "%Y-%m-%d".
12 "   g:changelog_username -
13 "       description: the username to use in ChangeLog entries
14 "       default: try to deduce it from environment variables and system files.
15 " Local Mappings:
16 "   <Leader>o -
17 "       adds a new changelog entry for the current user for the current date.
18 " Global Mappings:
19 "   <Leader>o -
20 "       switches to the ChangeLog buffer opened for the current directory, or
21 "       opens it in a new buffer if it exists in the current directory.  Then
22 "       it does the same as the local <Leader>o described above.
23 " Notes:
24 "   run 'runtime ftplugin/changelog.vim' to enable the global mapping for
25 "   changelog files.
26 " TODO:
27 "  should we perhaps open the ChangeLog file even if it doesn't exist already?
28 "  Problem is that you might end up with ChangeLog files all over the place.
30 " If 'filetype' isn't "changelog", we must have been to add ChangeLog opener
31 if &filetype == 'changelog'
32   if exists('b:did_ftplugin')
33     finish
34   endif
35   let b:did_ftplugin = 1
37   let s:cpo_save = &cpo
38   set cpo&vim
40   " Set up the format used for dates.
41   if !exists('g:changelog_dateformat')
42     if exists('g:changelog_timeformat')
43       let g:changelog_dateformat = g:changelog_timeformat
44     else
45       let g:changelog_dateformat = "%Y-%m-%d"
46     endif
47   endif
49   " Try to figure out a reasonable username of the form:
50   "   Full Name <user@host>.
51   if !exists('g:changelog_username')
52     if exists('$EMAIL') && $EMAIL != ''
53       let g:changelog_username = $EMAIL
54     elseif exists('$EMAIL_ADDRESS') && $EMAIL_ADDRESS != ''
55       " This is some Debian junk if I remember correctly.
56       let g:changelog_username = $EMAIL_ADDRESS
57     else
58       " Get the users login name.
59       let login = system('whoami')
60       if v:shell_error
61         let login = 'unknown'
62       else
63         let newline = stridx(login, "\n")
64         if newline != -1
65           let login = strpart(login, 0, newline)
66         endif
67       endif
69       " Try to get the full name from gecos field in /etc/passwd.
70       if filereadable('/etc/passwd')
71         for line in readfile('/etc/passwd')
72           if line =~ '^' . login
73             let name = substitute(line,'^\%([^:]*:\)\{4}\([^:]*\):.*$','\1','')
74             " Only keep stuff before the first comma.
75             let comma = stridx(name, ',')
76             if comma != -1
77               let name = strpart(name, 0, comma)
78             endif
79             " And substitute & in the real name with the login of our user.
80             let amp = stridx(name, '&')
81             if amp != -1
82               let name = strpart(name, 0, amp) . toupper(login[0]) .
83                        \ strpart(login, 1) . strpart(name, amp + 1)
84             endif
85           endif
86         endfor
87       endif
89       " If we haven't found a name, try to gather it from other places.
90       if !exists('name')
91         " Maybe the environment has something of interest.
92         if exists("$NAME")
93           let name = $NAME
94         else
95           " No? well, use the login name and capitalize first
96           " character.
97           let name = toupper(login[0]) . strpart(login, 1)
98         endif
99       endif
101       " Get our hostname.
102       let hostname = system('hostname')
103       if v:shell_error
104         let hostname = 'localhost'
105       else
106         let newline = stridx(hostname, "\n")
107         if newline != -1
108           let hostname = strpart(hostname, 0, newline)
109         endif
110       endif
112       " And finally set the username.
113       let g:changelog_username = name . '  <' . login . '@' . hostname . '>'
114     endif
115   endif
117   " Format used for new date entries.
118   if !exists('g:changelog_new_date_format')
119     let g:changelog_new_date_format = "%d  %u\n\n\t* %c\n\n"
120   endif
122   " Format used for new entries to current date entry.
123   if !exists('g:changelog_new_entry_format')
124     let g:changelog_new_entry_format = "\t* %c"
125   endif
127   " Regular expression used to find a given date entry.
128   if !exists('g:changelog_date_entry_search')
129     let g:changelog_date_entry_search = '^\s*%d\_s*%u'
130   endif
132   " Regular expression used to find the end of a date entry
133   if !exists('g:changelog_date_end_entry_search')
134     let g:changelog_date_end_entry_search = '^\s*$'
135   endif
138   " Substitutes specific items in new date-entry formats and search strings.
139   " Can be done with substitute of course, but unclean, and need \@! then.
140   function! s:substitute_items(str, date, user)
141     let str = a:str
142     let middles = {'%': '%', 'd': a:date, 'u': a:user, 'c': '{cursor}'}
143     let i = stridx(str, '%')
144     while i != -1
145       let inc = 0
146       if has_key(middles, str[i + 1])
147         let mid = middles[str[i + 1]]
148         let str = strpart(str, 0, i) . mid . strpart(str, i + 2)
149         let inc = strlen(mid)
150       endif
151       let i = stridx(str, '%', i + 1 + inc)
152     endwhile
153     return str
154   endfunction
156   " Position the cursor once we've done all the funky substitution.
157   function! s:position_cursor()
158     if search('{cursor}') > 0
159       let lnum = line('.')
160       let line = getline(lnum)
161       let cursor = stridx(line, '{cursor}')
162       call setline(lnum, substitute(line, '{cursor}', '', ''))
163     endif
164     startinsert!
165   endfunction
167   " Internal function to create a new entry in the ChangeLog.
168   function! s:new_changelog_entry()
169     " Deal with 'paste' option.
170     let save_paste = &paste
171     let &paste = 1
172     call cursor(1, 1)
173     " Look for an entry for today by our user.
174     let date = strftime(g:changelog_dateformat)
175     let search = s:substitute_items(g:changelog_date_entry_search, date,
176                                   \ g:changelog_username)
177     if search(search) > 0
178       " Ok, now we look for the end of the date entry, and add an entry.
179       call cursor(nextnonblank(line('.') + 1), 1)
180       if search(g:changelog_date_end_entry_search, 'W') > 0
181         let p = line('.') - 1
182       else
183         let p = line('.')
184       endif
185       let ls = split(s:substitute_items(g:changelog_new_entry_format, '', ''),
186                    \ '\n')
187       call append(p, ls)
188       call cursor(p + 1, 1)
189     else
190       " Flag for removing empty lines at end of new ChangeLogs.
191       let remove_empty = line('$') == 1
193       " No entry today, so create a date-user header and insert an entry.
194       let todays_entry = s:substitute_items(g:changelog_new_date_format,
195                                           \ date, g:changelog_username)
196       " Make sure we have a cursor positioning.
197       if stridx(todays_entry, '{cursor}') == -1
198         let todays_entry = todays_entry . '{cursor}'
199       endif
201       " Now do the work.
202       call append(0, split(todays_entry, '\n'))
203       
204       " Remove empty lines at end of file.
205       if remove_empty
206         $-/^\s*$/-1,$delete
207       endif
209       " Reposition cursor once we're done.
210       call cursor(1, 1)
211     endif
213     call s:position_cursor()
215     " And reset 'paste' option
216     let &paste = save_paste
217   endfunction
219   if exists(":NewChangelogEntry") != 2
220     map <buffer> <silent> <Leader>o <Esc>:call <SID>new_changelog_entry()<CR>
221     command! -nargs=0 NewChangelogEntry call s:new_changelog_entry()
222   endif
224   let b:undo_ftplugin = "setl com< fo< et< ai<"
226   setlocal comments=
227   setlocal formatoptions+=t
228   setlocal noexpandtab
229   setlocal autoindent
231   if &textwidth == 0
232     setlocal textwidth=78
233     let b:undo_ftplugin .= " tw<"
234   endif
236   let &cpo = s:cpo_save
237   unlet s:cpo_save
238 else
239   " Add the Changelog opening mapping
240   nmap <silent> <Leader>o :call <SID>open_changelog()<CR>
242   function! s:open_changelog()
243     if !filereadable('ChangeLog')
244       return
245     endif
246     let buf = bufnr('ChangeLog')
247     if buf != -1
248       if bufwinnr(buf) != -1
249         execute bufwinnr(buf) . 'wincmd w'
250       else
251         execute 'sbuffer' buf
252       endif
253     else
254       split ChangeLog
255     endif
257     call s:new_changelog_entry()
258   endfunction
259 endif