blob: 257e9cd9d4222a74a0fb93923eb99bb765343333 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001" Vim filetype plugin file
Bram Moolenaard042dc82015-11-24 19:18:36 +01002" Language: generic Changelog file
3" Maintainer: Martin Florian <marfl@posteo.de>
4" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
5" Latest Revision: 2015-10-25
Bram Moolenaar071d4272004-06-13 20:20:40 +00006" Variables:
Bram Moolenaar57657d82006-04-21 22:12:41 +00007" g:changelog_timeformat (deprecated: use g:changelog_dateformat instead) -
Bram Moolenaar42eeac32005-06-29 22:40:58 +00008" description: the timeformat used in ChangeLog entries.
9" default: "%Y-%m-%d".
Bram Moolenaar57657d82006-04-21 22:12:41 +000010" g:changelog_dateformat -
11" description: the format sent to strftime() to generate a date string.
12" default: "%Y-%m-%d".
Bram Moolenaar071d4272004-06-13 20:20:40 +000013" g:changelog_username -
Bram Moolenaar42eeac32005-06-29 22:40:58 +000014" description: the username to use in ChangeLog entries
15" default: try to deduce it from environment variables and system files.
Bram Moolenaar071d4272004-06-13 20:20:40 +000016" Local Mappings:
17" <Leader>o -
Bram Moolenaar42eeac32005-06-29 22:40:58 +000018" adds a new changelog entry for the current user for the current date.
Bram Moolenaar071d4272004-06-13 20:20:40 +000019" Global Mappings:
20" <Leader>o -
Bram Moolenaar42eeac32005-06-29 22:40:58 +000021" switches to the ChangeLog buffer opened for the current directory, or
22" opens it in a new buffer if it exists in the current directory. Then
23" it does the same as the local <Leader>o described above.
Bram Moolenaar071d4272004-06-13 20:20:40 +000024" Notes:
25" run 'runtime ftplugin/changelog.vim' to enable the global mapping for
26" changelog files.
27" TODO:
28" should we perhaps open the ChangeLog file even if it doesn't exist already?
29" Problem is that you might end up with ChangeLog files all over the place.
30
31" If 'filetype' isn't "changelog", we must have been to add ChangeLog opener
Bram Moolenaar57657d82006-04-21 22:12:41 +000032if &filetype == 'changelog'
33 if exists('b:did_ftplugin')
Bram Moolenaar071d4272004-06-13 20:20:40 +000034 finish
35 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000036 let b:did_ftplugin = 1
37
Bram Moolenaar42eeac32005-06-29 22:40:58 +000038 let s:cpo_save = &cpo
39 set cpo&vim
Bram Moolenaar071d4272004-06-13 20:20:40 +000040
Bram Moolenaar57657d82006-04-21 22:12:41 +000041 " Set up the format used for dates.
42 if !exists('g:changelog_dateformat')
43 if exists('g:changelog_timeformat')
44 let g:changelog_dateformat = g:changelog_timeformat
45 else
46 let g:changelog_dateformat = "%Y-%m-%d"
47 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000048 endif
49
Bram Moolenaar5c736222010-01-06 20:54:52 +010050 function! s:username()
51 if exists('g:changelog_username')
52 return g:changelog_username
53 elseif $EMAIL != ""
54 return $EMAIL
55 elseif $EMAIL_ADDRESS != ""
56 return $EMAIL_ADDRESS
Bram Moolenaar071d4272004-06-13 20:20:40 +000057 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +010058
59 let login = s:login()
60 return printf('%s <%s@%s>', s:name(login), login, s:hostname())
61 endfunction
62
63 function! s:login()
64 return s:trimmed_system_with_default('whoami', 'unknown')
65 endfunction
66
67 function! s:trimmed_system_with_default(command, default)
68 return s:first_line(s:system_with_default(a:command, a:default))
69 endfunction
70
71 function! s:system_with_default(command, default)
72 let output = system(a:command)
73 if v:shell_error
74 return default
75 endif
76 return output
77 endfunction
78
79 function! s:first_line(string)
80 return substitute(a:string, '\n.*$', "", "")
81 endfunction
82
83 function! s:name(login)
84 for name in [s:gecos_name(a:login), $NAME, s:capitalize(a:login)]
85 if name != ""
86 return name
87 endif
88 endfor
89 endfunction
90
91 function! s:gecos_name(login)
92 for line in s:try_reading_file('/etc/passwd')
93 if line =~ '^' . a:login . ':'
94 return substitute(s:passwd_field(line, 5), '&', s:capitalize(a:login), "")
95 endif
96 endfor
97 return ""
98 endfunction
99
100 function! s:try_reading_file(path)
101 try
102 return readfile(a:path)
Bram Moolenaar446beb42011-05-10 17:18:44 +0200103 catch
104 return []
Bram Moolenaar5c736222010-01-06 20:54:52 +0100105 endtry
Bram Moolenaar5c736222010-01-06 20:54:52 +0100106 endfunction
107
108 function! s:passwd_field(line, field)
109 let fields = split(a:line, ':', 1)
Bram Moolenaard09acef2012-09-21 14:54:30 +0200110 if len(fields) < a:field
Bram Moolenaar5c736222010-01-06 20:54:52 +0100111 return ""
112 endif
Bram Moolenaard09acef2012-09-21 14:54:30 +0200113 return fields[a:field - 1]
Bram Moolenaar5c736222010-01-06 20:54:52 +0100114 endfunction
115
116 function! s:capitalize(word)
117 return toupper(a:word[0]) . strpart(a:word, 1)
118 endfunction
119
120 function! s:hostname()
121 return s:trimmed_system_with_default('hostname', 'localhost')
122 endfunction
Bram Moolenaar071d4272004-06-13 20:20:40 +0000123
Bram Moolenaar57657d82006-04-21 22:12:41 +0000124 " Format used for new date entries.
125 if !exists('g:changelog_new_date_format')
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100126 let g:changelog_new_date_format = "%d %u\n\n\t* %p%c\n\n"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000127 endif
128
Bram Moolenaar57657d82006-04-21 22:12:41 +0000129 " Format used for new entries to current date entry.
130 if !exists('g:changelog_new_entry_format')
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100131 let g:changelog_new_entry_format = "\t* %p%c"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000132 endif
133
Bram Moolenaar57657d82006-04-21 22:12:41 +0000134 " Regular expression used to find a given date entry.
135 if !exists('g:changelog_date_entry_search')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000136 let g:changelog_date_entry_search = '^\s*%d\_s*%u'
137 endif
138
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000139 " Regular expression used to find the end of a date entry
140 if !exists('g:changelog_date_end_entry_search')
Bram Moolenaar446cb832008-06-24 21:56:24 +0000141 let g:changelog_date_end_entry_search = '^\s*$'
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000142 endif
143
144
Bram Moolenaar57657d82006-04-21 22:12:41 +0000145 " Substitutes specific items in new date-entry formats and search strings.
146 " Can be done with substitute of course, but unclean, and need \@! then.
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100147 function! s:substitute_items(str, date, user, prefix)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000148 let str = a:str
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100149 let middles = {'%': '%', 'd': a:date, 'u': a:user, 'p': a:prefix, 'c': '{cursor}'}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150 let i = stridx(str, '%')
151 while i != -1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000152 let inc = 0
153 if has_key(middles, str[i + 1])
154 let mid = middles[str[i + 1]]
155 let str = strpart(str, 0, i) . mid . strpart(str, i + 2)
Bram Moolenaar8d043172014-01-23 14:24:41 +0100156 let inc = strlen(mid) - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157 endif
Bram Moolenaar57657d82006-04-21 22:12:41 +0000158 let i = stridx(str, '%', i + 1 + inc)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000159 endwhile
160 return str
161 endfunction
162
Bram Moolenaar57657d82006-04-21 22:12:41 +0000163 " Position the cursor once we've done all the funky substitution.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000164 function! s:position_cursor()
165 if search('{cursor}') > 0
Bram Moolenaar57657d82006-04-21 22:12:41 +0000166 let lnum = line('.')
167 let line = getline(lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000168 let cursor = stridx(line, '{cursor}')
Bram Moolenaar57657d82006-04-21 22:12:41 +0000169 call setline(lnum, substitute(line, '{cursor}', '', ''))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000170 endif
Bram Moolenaard042dc82015-11-24 19:18:36 +0100171 startinsert
Bram Moolenaar071d4272004-06-13 20:20:40 +0000172 endfunction
173
Bram Moolenaar57657d82006-04-21 22:12:41 +0000174 " Internal function to create a new entry in the ChangeLog.
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100175 function! s:new_changelog_entry(prefix)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000176 " Deal with 'paste' option.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000177 let save_paste = &paste
178 let &paste = 1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000179 call cursor(1, 1)
180 " Look for an entry for today by our user.
181 let date = strftime(g:changelog_dateformat)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000182 let search = s:substitute_items(g:changelog_date_entry_search, date,
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100183 \ s:username(), a:prefix)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000184 if search(search) > 0
Bram Moolenaar57657d82006-04-21 22:12:41 +0000185 " Ok, now we look for the end of the date entry, and add an entry.
186 call cursor(nextnonblank(line('.') + 1), 1)
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000187 if search(g:changelog_date_end_entry_search, 'W') > 0
Bram Moolenaar5c736222010-01-06 20:54:52 +0100188 let p = (line('.') == line('$')) ? line('.') : line('.') - 1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000189 else
190 let p = line('.')
191 endif
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100192 let ls = split(s:substitute_items(g:changelog_new_entry_format, '', '', a:prefix),
Bram Moolenaar57657d82006-04-21 22:12:41 +0000193 \ '\n')
194 call append(p, ls)
195 call cursor(p + 1, 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000196 else
Bram Moolenaar57657d82006-04-21 22:12:41 +0000197 " Flag for removing empty lines at end of new ChangeLogs.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000198 let remove_empty = line('$') == 1
199
Bram Moolenaar57657d82006-04-21 22:12:41 +0000200 " No entry today, so create a date-user header and insert an entry.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000201 let todays_entry = s:substitute_items(g:changelog_new_date_format,
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100202 \ date, s:username(), a:prefix)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000203 " Make sure we have a cursor positioning.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000204 if stridx(todays_entry, '{cursor}') == -1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000205 let todays_entry = todays_entry . '{cursor}'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000206 endif
207
Bram Moolenaar57657d82006-04-21 22:12:41 +0000208 " Now do the work.
209 call append(0, split(todays_entry, '\n'))
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100210
Bram Moolenaar57657d82006-04-21 22:12:41 +0000211 " Remove empty lines at end of file.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000212 if remove_empty
Bram Moolenaar57657d82006-04-21 22:12:41 +0000213 $-/^\s*$/-1,$delete
Bram Moolenaar071d4272004-06-13 20:20:40 +0000214 endif
215
Bram Moolenaar57657d82006-04-21 22:12:41 +0000216 " Reposition cursor once we're done.
217 call cursor(1, 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218 endif
219
220 call s:position_cursor()
221
222 " And reset 'paste' option
223 let &paste = save_paste
224 endfunction
225
226 if exists(":NewChangelogEntry") != 2
Bram Moolenaard042dc82015-11-24 19:18:36 +0100227 nnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR>
228 xnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR>
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100229 command! -nargs=0 NewChangelogEntry call s:new_changelog_entry('')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000230 endif
231
Bram Moolenaar57657d82006-04-21 22:12:41 +0000232 let b:undo_ftplugin = "setl com< fo< et< ai<"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000233
Bram Moolenaar071d4272004-06-13 20:20:40 +0000234 setlocal comments=
235 setlocal formatoptions+=t
236 setlocal noexpandtab
Bram Moolenaar42eeac32005-06-29 22:40:58 +0000237 setlocal autoindent
Bram Moolenaar071d4272004-06-13 20:20:40 +0000238
Bram Moolenaar57657d82006-04-21 22:12:41 +0000239 if &textwidth == 0
240 setlocal textwidth=78
241 let b:undo_ftplugin .= " tw<"
242 endif
243
Bram Moolenaar42eeac32005-06-29 22:40:58 +0000244 let &cpo = s:cpo_save
245 unlet s:cpo_save
Bram Moolenaar071d4272004-06-13 20:20:40 +0000246else
Bram Moolenaar5c736222010-01-06 20:54:52 +0100247 let s:cpo_save = &cpo
248 set cpo&vim
249
Bram Moolenaar071d4272004-06-13 20:20:40 +0000250 " Add the Changelog opening mapping
Bram Moolenaar5c736222010-01-06 20:54:52 +0100251 nnoremap <silent> <Leader>o :call <SID>open_changelog()<CR>
Bram Moolenaar071d4272004-06-13 20:20:40 +0000252
253 function! s:open_changelog()
Bram Moolenaar5c736222010-01-06 20:54:52 +0100254 let path = expand('%:p:h')
255 if exists('b:changelog_path')
256 let changelog = b:changelog_path
257 else
258 if exists('b:changelog_name')
259 let name = b:changelog_name
260 else
261 let name = 'ChangeLog'
262 endif
263 while isdirectory(path)
264 let changelog = path . '/' . name
265 if filereadable(changelog)
266 break
267 endif
268 let parent = substitute(path, '/\+[^/]*$', "", "")
269 if path == parent
270 break
271 endif
272 let path = parent
273 endwhile
274 endif
275 if !filereadable(changelog)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000276 return
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +0100278
279 if exists('b:changelog_entry_prefix')
280 let prefix = call(b:changelog_entry_prefix, [])
281 else
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100282 let prefix = substitute(strpart(expand('%:p'), strlen(path)), '^/\+', "", "")
Bram Moolenaar5c736222010-01-06 20:54:52 +0100283 endif
284
285 let buf = bufnr(changelog)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000286 if buf != -1
287 if bufwinnr(buf) != -1
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000288 execute bufwinnr(buf) . 'wincmd w'
Bram Moolenaar57657d82006-04-21 22:12:41 +0000289 else
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000290 execute 'sbuffer' buf
Bram Moolenaar57657d82006-04-21 22:12:41 +0000291 endif
292 else
Bram Moolenaar5c736222010-01-06 20:54:52 +0100293 execute 'split' fnameescape(changelog)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000294 endif
295
Bram Moolenaar5c736222010-01-06 20:54:52 +0100296 call s:new_changelog_entry(prefix)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000297 endfunction
Bram Moolenaar5c736222010-01-06 20:54:52 +0100298
299 let &cpo = s:cpo_save
300 unlet s:cpo_save
Bram Moolenaar071d4272004-06-13 20:20:40 +0000301endif