blob: e9df63f8c98d53c959d4ece3ef609156cc5ee8d4 [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>
Bram Moolenaar079ba762021-10-23 12:08:41 +01005" Latest Revision: 2021-10-17
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 Moolenaar079ba762021-10-23 12:08:41 +010058
Bram Moolenaar5c736222010-01-06 20:54:52 +010059 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
Bram Moolenaar57657d82006-04-21 22:12:41 +0000226 let b:undo_ftplugin = "setl com< fo< et< ai<"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000227
Bram Moolenaar071d4272004-06-13 20:20:40 +0000228 setlocal comments=
229 setlocal formatoptions+=t
230 setlocal noexpandtab
Bram Moolenaar42eeac32005-06-29 22:40:58 +0000231 setlocal autoindent
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232
Bram Moolenaar57657d82006-04-21 22:12:41 +0000233 if &textwidth == 0
234 setlocal textwidth=78
235 let b:undo_ftplugin .= " tw<"
236 endif
237
Bram Moolenaar079ba762021-10-23 12:08:41 +0100238 if !exists("no_plugin_maps") && !exists("no_changelog_maps") && exists(":NewChangelogEntry") != 2
239 nnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR>
240 xnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR>
241 command! -buffer -nargs=0 NewChangelogEntry call s:new_changelog_entry('')
242 let b:undo_ftplugin .= " | sil! exe 'nunmap <buffer> <Leader>o'" .
243 \ " | sil! exe 'vunmap <buffer> <Leader>o'" .
244 \ " | sil! delc NewChangelogEntry"
245 endif
246
Bram Moolenaar42eeac32005-06-29 22:40:58 +0000247 let &cpo = s:cpo_save
248 unlet s:cpo_save
Bram Moolenaar071d4272004-06-13 20:20:40 +0000249else
Bram Moolenaar5c736222010-01-06 20:54:52 +0100250 let s:cpo_save = &cpo
251 set cpo&vim
252
Bram Moolenaar079ba762021-10-23 12:08:41 +0100253 if !exists("no_plugin_maps") && !exists("no_changelog_maps")
254 " Add the Changelog opening mapping
255 nnoremap <silent> <Leader>o :call <SID>open_changelog()<CR>
256 let b:undo_ftplugin .= " | silent! exe 'nunmap <buffer> <Leader>o"
257 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000258
259 function! s:open_changelog()
Bram Moolenaar5c736222010-01-06 20:54:52 +0100260 let path = expand('%:p:h')
261 if exists('b:changelog_path')
262 let changelog = b:changelog_path
263 else
264 if exists('b:changelog_name')
265 let name = b:changelog_name
266 else
267 let name = 'ChangeLog'
268 endif
269 while isdirectory(path)
270 let changelog = path . '/' . name
271 if filereadable(changelog)
272 break
273 endif
274 let parent = substitute(path, '/\+[^/]*$', "", "")
275 if path == parent
276 break
277 endif
278 let path = parent
279 endwhile
280 endif
281 if !filereadable(changelog)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000282 return
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +0100284
285 if exists('b:changelog_entry_prefix')
286 let prefix = call(b:changelog_entry_prefix, [])
287 else
Bram Moolenaar4b550b42013-12-15 10:02:33 +0100288 let prefix = substitute(strpart(expand('%:p'), strlen(path)), '^/\+', "", "")
Bram Moolenaar5c736222010-01-06 20:54:52 +0100289 endif
290
291 let buf = bufnr(changelog)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000292 if buf != -1
293 if bufwinnr(buf) != -1
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000294 execute bufwinnr(buf) . 'wincmd w'
Bram Moolenaar57657d82006-04-21 22:12:41 +0000295 else
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000296 execute 'sbuffer' buf
Bram Moolenaar57657d82006-04-21 22:12:41 +0000297 endif
298 else
Bram Moolenaar5c736222010-01-06 20:54:52 +0100299 execute 'split' fnameescape(changelog)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000300 endif
301
Bram Moolenaar5c736222010-01-06 20:54:52 +0100302 call s:new_changelog_entry(prefix)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000303 endfunction
Bram Moolenaar5c736222010-01-06 20:54:52 +0100304
305 let &cpo = s:cpo_save
306 unlet s:cpo_save
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307endif