blob: ee83c79175c52faaf1a554bd4d8f74d0806787f5 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001" Vim filetype plugin file
Bram Moolenaar42eeac32005-06-29 22:40:58 +00002" Language: generic Changelog file
Bram Moolenaar57657d82006-04-21 22:12:41 +00003" Maintainer: Nikolai Weibull <now@bitwi.se>
Bram Moolenaard09acef2012-09-21 14:54:30 +02004" Latest Revision: 2012-08-23
Bram Moolenaar071d4272004-06-13 20:20:40 +00005" Variables:
Bram Moolenaar57657d82006-04-21 22:12:41 +00006" g:changelog_timeformat (deprecated: use g:changelog_dateformat instead) -
Bram Moolenaar42eeac32005-06-29 22:40:58 +00007" description: the timeformat used in ChangeLog entries.
8" default: "%Y-%m-%d".
Bram Moolenaar57657d82006-04-21 22:12:41 +00009" g:changelog_dateformat -
10" description: the format sent to strftime() to generate a date string.
11" default: "%Y-%m-%d".
Bram Moolenaar071d4272004-06-13 20:20:40 +000012" g:changelog_username -
Bram Moolenaar42eeac32005-06-29 22:40:58 +000013" description: the username to use in ChangeLog entries
14" default: try to deduce it from environment variables and system files.
Bram Moolenaar071d4272004-06-13 20:20:40 +000015" Local Mappings:
16" <Leader>o -
Bram Moolenaar42eeac32005-06-29 22:40:58 +000017" adds a new changelog entry for the current user for the current date.
Bram Moolenaar071d4272004-06-13 20:20:40 +000018" Global Mappings:
19" <Leader>o -
Bram Moolenaar42eeac32005-06-29 22:40:58 +000020" 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.
Bram Moolenaar071d4272004-06-13 20:20:40 +000023" 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.
29
30" If 'filetype' isn't "changelog", we must have been to add ChangeLog opener
Bram Moolenaar57657d82006-04-21 22:12:41 +000031if &filetype == 'changelog'
32 if exists('b:did_ftplugin')
Bram Moolenaar071d4272004-06-13 20:20:40 +000033 finish
34 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000035 let b:did_ftplugin = 1
36
Bram Moolenaar42eeac32005-06-29 22:40:58 +000037 let s:cpo_save = &cpo
38 set cpo&vim
Bram Moolenaar071d4272004-06-13 20:20:40 +000039
Bram Moolenaar57657d82006-04-21 22:12:41 +000040 " 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
Bram Moolenaar071d4272004-06-13 20:20:40 +000047 endif
48
Bram Moolenaar5c736222010-01-06 20:54:52 +010049 function! s:username()
50 if exists('g:changelog_username')
51 return g:changelog_username
52 elseif $EMAIL != ""
53 return $EMAIL
54 elseif $EMAIL_ADDRESS != ""
55 return $EMAIL_ADDRESS
Bram Moolenaar071d4272004-06-13 20:20:40 +000056 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +010057
58 let login = s:login()
59 return printf('%s <%s@%s>', s:name(login), login, s:hostname())
60 endfunction
61
62 function! s:login()
63 return s:trimmed_system_with_default('whoami', 'unknown')
64 endfunction
65
66 function! s:trimmed_system_with_default(command, default)
67 return s:first_line(s:system_with_default(a:command, a:default))
68 endfunction
69
70 function! s:system_with_default(command, default)
71 let output = system(a:command)
72 if v:shell_error
73 return default
74 endif
75 return output
76 endfunction
77
78 function! s:first_line(string)
79 return substitute(a:string, '\n.*$', "", "")
80 endfunction
81
82 function! s:name(login)
83 for name in [s:gecos_name(a:login), $NAME, s:capitalize(a:login)]
84 if name != ""
85 return name
86 endif
87 endfor
88 endfunction
89
90 function! s:gecos_name(login)
91 for line in s:try_reading_file('/etc/passwd')
92 if line =~ '^' . a:login . ':'
93 return substitute(s:passwd_field(line, 5), '&', s:capitalize(a:login), "")
94 endif
95 endfor
96 return ""
97 endfunction
98
99 function! s:try_reading_file(path)
100 try
101 return readfile(a:path)
Bram Moolenaar446beb42011-05-10 17:18:44 +0200102 catch
103 return []
Bram Moolenaar5c736222010-01-06 20:54:52 +0100104 endtry
Bram Moolenaar5c736222010-01-06 20:54:52 +0100105 endfunction
106
107 function! s:passwd_field(line, field)
108 let fields = split(a:line, ':', 1)
Bram Moolenaard09acef2012-09-21 14:54:30 +0200109 if len(fields) < a:field
Bram Moolenaar5c736222010-01-06 20:54:52 +0100110 return ""
111 endif
Bram Moolenaard09acef2012-09-21 14:54:30 +0200112 return fields[a:field - 1]
Bram Moolenaar5c736222010-01-06 20:54:52 +0100113 endfunction
114
115 function! s:capitalize(word)
116 return toupper(a:word[0]) . strpart(a:word, 1)
117 endfunction
118
119 function! s:hostname()
120 return s:trimmed_system_with_default('hostname', 'localhost')
121 endfunction
Bram Moolenaar071d4272004-06-13 20:20:40 +0000122
Bram Moolenaar57657d82006-04-21 22:12:41 +0000123 " Format used for new date entries.
124 if !exists('g:changelog_new_date_format')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000125 let g:changelog_new_date_format = "%d %u\n\n\t* %c\n\n"
126 endif
127
Bram Moolenaar57657d82006-04-21 22:12:41 +0000128 " Format used for new entries to current date entry.
129 if !exists('g:changelog_new_entry_format')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000130 let g:changelog_new_entry_format = "\t* %c"
131 endif
132
Bram Moolenaar57657d82006-04-21 22:12:41 +0000133 " Regular expression used to find a given date entry.
134 if !exists('g:changelog_date_entry_search')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000135 let g:changelog_date_entry_search = '^\s*%d\_s*%u'
136 endif
137
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000138 " Regular expression used to find the end of a date entry
139 if !exists('g:changelog_date_end_entry_search')
Bram Moolenaar446cb832008-06-24 21:56:24 +0000140 let g:changelog_date_end_entry_search = '^\s*$'
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000141 endif
142
143
Bram Moolenaar57657d82006-04-21 22:12:41 +0000144 " Substitutes specific items in new date-entry formats and search strings.
145 " Can be done with substitute of course, but unclean, and need \@! then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000146 function! s:substitute_items(str, date, user)
147 let str = a:str
Bram Moolenaar57657d82006-04-21 22:12:41 +0000148 let middles = {'%': '%', 'd': a:date, 'u': a:user, 'c': '{cursor}'}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000149 let i = stridx(str, '%')
150 while i != -1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000151 let inc = 0
152 if has_key(middles, str[i + 1])
153 let mid = middles[str[i + 1]]
154 let str = strpart(str, 0, i) . mid . strpart(str, i + 2)
155 let inc = strlen(mid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000156 endif
Bram Moolenaar57657d82006-04-21 22:12:41 +0000157 let i = stridx(str, '%', i + 1 + inc)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000158 endwhile
159 return str
160 endfunction
161
Bram Moolenaar57657d82006-04-21 22:12:41 +0000162 " Position the cursor once we've done all the funky substitution.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000163 function! s:position_cursor()
164 if search('{cursor}') > 0
Bram Moolenaar57657d82006-04-21 22:12:41 +0000165 let lnum = line('.')
166 let line = getline(lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000167 let cursor = stridx(line, '{cursor}')
Bram Moolenaar57657d82006-04-21 22:12:41 +0000168 call setline(lnum, substitute(line, '{cursor}', '', ''))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000169 endif
170 startinsert!
171 endfunction
172
Bram Moolenaar57657d82006-04-21 22:12:41 +0000173 " Internal function to create a new entry in the ChangeLog.
Bram Moolenaar446beb42011-05-10 17:18:44 +0200174 function! s:new_changelog_entry()
Bram Moolenaar57657d82006-04-21 22:12:41 +0000175 " Deal with 'paste' option.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000176 let save_paste = &paste
177 let &paste = 1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000178 call cursor(1, 1)
179 " Look for an entry for today by our user.
180 let date = strftime(g:changelog_dateformat)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000181 let search = s:substitute_items(g:changelog_date_entry_search, date,
Bram Moolenaar446beb42011-05-10 17:18:44 +0200182 \ s:username())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000183 if search(search) > 0
Bram Moolenaar57657d82006-04-21 22:12:41 +0000184 " Ok, now we look for the end of the date entry, and add an entry.
185 call cursor(nextnonblank(line('.') + 1), 1)
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000186 if search(g:changelog_date_end_entry_search, 'W') > 0
Bram Moolenaar5c736222010-01-06 20:54:52 +0100187 let p = (line('.') == line('$')) ? line('.') : line('.') - 1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000188 else
189 let p = line('.')
190 endif
191 let ls = split(s:substitute_items(g:changelog_new_entry_format, '', ''),
192 \ '\n')
193 call append(p, ls)
194 call cursor(p + 1, 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000195 else
Bram Moolenaar57657d82006-04-21 22:12:41 +0000196 " Flag for removing empty lines at end of new ChangeLogs.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000197 let remove_empty = line('$') == 1
198
Bram Moolenaar57657d82006-04-21 22:12:41 +0000199 " No entry today, so create a date-user header and insert an entry.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000200 let todays_entry = s:substitute_items(g:changelog_new_date_format,
Bram Moolenaar446beb42011-05-10 17:18:44 +0200201 \ date, s:username())
Bram Moolenaar57657d82006-04-21 22:12:41 +0000202 " Make sure we have a cursor positioning.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000203 if stridx(todays_entry, '{cursor}') == -1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000204 let todays_entry = todays_entry . '{cursor}'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000205 endif
206
Bram Moolenaar57657d82006-04-21 22:12:41 +0000207 " Now do the work.
208 call append(0, split(todays_entry, '\n'))
209
210 " Remove empty lines at end of file.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000211 if remove_empty
Bram Moolenaar57657d82006-04-21 22:12:41 +0000212 $-/^\s*$/-1,$delete
Bram Moolenaar071d4272004-06-13 20:20:40 +0000213 endif
214
Bram Moolenaar57657d82006-04-21 22:12:41 +0000215 " Reposition cursor once we're done.
216 call cursor(1, 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217 endif
218
219 call s:position_cursor()
220
221 " And reset 'paste' option
222 let &paste = save_paste
223 endfunction
224
225 if exists(":NewChangelogEntry") != 2
Bram Moolenaar5c736222010-01-06 20:54:52 +0100226 noremap <buffer> <silent> <Leader>o <Esc>:call <SID>new_changelog_entry()<CR>
Bram Moolenaar071d4272004-06-13 20:20:40 +0000227 command! -nargs=0 NewChangelogEntry call s:new_changelog_entry()
228 endif
229
Bram Moolenaar57657d82006-04-21 22:12:41 +0000230 let b:undo_ftplugin = "setl com< fo< et< ai<"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232 setlocal comments=
233 setlocal formatoptions+=t
234 setlocal noexpandtab
Bram Moolenaar42eeac32005-06-29 22:40:58 +0000235 setlocal autoindent
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236
Bram Moolenaar57657d82006-04-21 22:12:41 +0000237 if &textwidth == 0
238 setlocal textwidth=78
239 let b:undo_ftplugin .= " tw<"
240 endif
241
Bram Moolenaar42eeac32005-06-29 22:40:58 +0000242 let &cpo = s:cpo_save
243 unlet s:cpo_save
Bram Moolenaar071d4272004-06-13 20:20:40 +0000244else
Bram Moolenaar5c736222010-01-06 20:54:52 +0100245 let s:cpo_save = &cpo
246 set cpo&vim
247
Bram Moolenaar071d4272004-06-13 20:20:40 +0000248 " Add the Changelog opening mapping
Bram Moolenaar5c736222010-01-06 20:54:52 +0100249 nnoremap <silent> <Leader>o :call <SID>open_changelog()<CR>
Bram Moolenaar071d4272004-06-13 20:20:40 +0000250
251 function! s:open_changelog()
Bram Moolenaar5c736222010-01-06 20:54:52 +0100252 let path = expand('%:p:h')
253 if exists('b:changelog_path')
254 let changelog = b:changelog_path
255 else
256 if exists('b:changelog_name')
257 let name = b:changelog_name
258 else
259 let name = 'ChangeLog'
260 endif
261 while isdirectory(path)
262 let changelog = path . '/' . name
263 if filereadable(changelog)
264 break
265 endif
266 let parent = substitute(path, '/\+[^/]*$', "", "")
267 if path == parent
268 break
269 endif
270 let path = parent
271 endwhile
272 endif
273 if !filereadable(changelog)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000274 return
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +0100276
277 if exists('b:changelog_entry_prefix')
278 let prefix = call(b:changelog_entry_prefix, [])
279 else
280 let prefix = substitute(strpart(expand('%:p'), strlen(path)), '^/\+', "", "") . ':'
281 endif
282 if !empty(prefix)
283 let prefix = ' ' . prefix
284 endif
285
286 let buf = bufnr(changelog)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000287 if buf != -1
288 if bufwinnr(buf) != -1
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000289 execute bufwinnr(buf) . 'wincmd w'
Bram Moolenaar57657d82006-04-21 22:12:41 +0000290 else
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000291 execute 'sbuffer' buf
Bram Moolenaar57657d82006-04-21 22:12:41 +0000292 endif
293 else
Bram Moolenaar5c736222010-01-06 20:54:52 +0100294 execute 'split' fnameescape(changelog)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000295 endif
296
Bram Moolenaar5c736222010-01-06 20:54:52 +0100297 call s:new_changelog_entry(prefix)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000298 endfunction
Bram Moolenaar5c736222010-01-06 20:54:52 +0100299
300 let &cpo = s:cpo_save
301 unlet s:cpo_save
Bram Moolenaar071d4272004-06-13 20:20:40 +0000302endif