codekingpro/portable-devtools
114k
1" Vim filetype plugin file2" Language: generic Changelog file3" Maintainer: Martin Florian <marfl@posteo.de>4" Previous Maintainer: Nikolai Weibull <now@bitwi.se>5" Latest Revision: 2021-10-176" Variables:7" g:changelog_timeformat (deprecated: use g:changelog_dateformat instead) -8" description: the timeformat used in ChangeLog entries.9" default: "%Y-%m-%d".10" g:changelog_dateformat -11" description: the format sent to strftime() to generate a date string.12" default: "%Y-%m-%d".13" g:changelog_username -14" description: the username to use in ChangeLog entries15" default: try to deduce it from environment variables and system files.16" Local Mappings:17" <Leader>o -18" adds a new changelog entry for the current user for the current date.19" Global Mappings:20" <Leader>o -21" switches to the ChangeLog buffer opened for the current directory, or22" opens it in a new buffer if it exists in the current directory. Then23" it does the same as the local <Leader>o described above.24" Notes:25" run 'runtime ftplugin/changelog.vim' to enable the global mapping for26" 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 opener32if &filetype == 'changelog'33 if exists('b:did_ftplugin')34 finish35 endif36 let b:did_ftplugin = 137 38 let s:cpo_save = &cpo39 set cpo&vim40 41 " 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_timeformat45 else46 let g:changelog_dateformat = "%Y-%m-%d"47 endif48 endif49 50 function! s:username()51 if exists('g:changelog_username')52 return g:changelog_username53 elseif $EMAIL != ""54 return $EMAIL55 elseif $EMAIL_ADDRESS != ""56 return $EMAIL_ADDRESS57 endif58 let s:default_login = 'unknown'59 60 " Disabled by default for security reasons.61 if dist#vim#IsSafeExecutable('changelog', 'whoami')62 let login = s:login()63 else64 let login = s:default_login65 endif66 return printf('%s <%s@%s>', s:name(login), login, s:hostname())67 endfunction68 69 function! s:login()70 return s:trimmed_system_with_default('whoami', s:default_login)71 endfunction72 73 function! s:trimmed_system_with_default(command, default)74 return s:first_line(s:system_with_default(a:command, a:default))75 endfunction76 77 function! s:system_with_default(command, default)78 let output = system(a:command)79 if v:shell_error80 return a:default81 endif82 return output83 endfunction84 85 function! s:first_line(string)86 return substitute(a:string, '\n.*$', "", "")87 endfunction88 89 function! s:name(login)90 for name in [s:gecos_name(a:login), $NAME, s:capitalize(a:login)]91 if name != ""92 return name93 endif94 endfor95 endfunction96 97 function! s:gecos_name(login)98 for line in s:try_reading_file('/etc/passwd')99 if line =~ '^' . a:login . ':'100 return substitute(s:passwd_field(line, 5), '&', s:capitalize(a:login), "")101 endif102 endfor103 return ""104 endfunction105 106 function! s:try_reading_file(path)107 try108 return readfile(a:path)109 catch110 return []111 endtry112 endfunction113 114 function! s:passwd_field(line, field)115 let fields = split(a:line, ':', 1)116 if len(fields) < a:field117 return ""118 endif119 return fields[a:field - 1]120 endfunction121 122 function! s:capitalize(word)123 return toupper(a:word[0]) . strpart(a:word, 1)124 endfunction125 126 function! s:hostname()127 return s:trimmed_system_with_default('hostname', 'localhost')128 endfunction129 130 " Format used for new date entries.131 if !exists('g:changelog_new_date_format')132 let g:changelog_new_date_format = "%d %u\n\n\t* %p%c\n\n"133 endif134 135 " Format used for new entries to current date entry.136 if !exists('g:changelog_new_entry_format')137 let g:changelog_new_entry_format = "\t* %p%c"138 endif139 140 " Regular expression used to find a given date entry.141 if !exists('g:changelog_date_entry_search')142 let g:changelog_date_entry_search = '^\s*%d\_s*%u'143 endif144 145 " Regular expression used to find the end of a date entry146 if !exists('g:changelog_date_end_entry_search')147 let g:changelog_date_end_entry_search = '^\s*$'148 endif149 150 151 " Substitutes specific items in new date-entry formats and search strings.152 " Can be done with substitute of course, but unclean, and need \@! then.153 function! s:substitute_items(str, date, user, prefix)154 let str = a:str155 let middles = {'%': '%', 'd': a:date, 'u': a:user, 'p': a:prefix, 'c': '{cursor}'}156 let i = stridx(str, '%')157 while i != -1158 let inc = 0159 if has_key(middles, str[i + 1])160 let mid = middles[str[i + 1]]161 let str = strpart(str, 0, i) . mid . strpart(str, i + 2)162 let inc = strlen(mid) - 1163 endif164 let i = stridx(str, '%', i + 1 + inc)165 endwhile166 return str167 endfunction168 169 " Position the cursor once we've done all the funky substitution.170 function! s:position_cursor()171 if search('{cursor}') > 0172 let lnum = line('.')173 let line = getline(lnum)174 let cursor = stridx(line, '{cursor}')175 call setline(lnum, substitute(line, '{cursor}', '', ''))176 endif177 startinsert178 endfunction179 180 " Internal function to create a new entry in the ChangeLog.181 function! s:new_changelog_entry(prefix)182 " Deal with 'paste' option.183 let save_paste = &paste184 let &paste = 1185 call cursor(1, 1)186 " Look for an entry for today by our user.187 let date = strftime(g:changelog_dateformat)188 let search = s:substitute_items(g:changelog_date_entry_search, date,189 \ s:username(), a:prefix)190 if search(search) > 0191 " Ok, now we look for the end of the date entry, and add an entry.192 call cursor(nextnonblank(line('.') + 1), 1)193 if search(g:changelog_date_end_entry_search, 'W') > 0194 let p = (line('.') == line('$')) ? line('.') : line('.') - 1195 else196 let p = line('.')197 endif198 let ls = split(s:substitute_items(g:changelog_new_entry_format, '', '', a:prefix),199 \ '\n')200 call append(p, ls)201 call cursor(p + 1, 1)202 else203 " Flag for removing empty lines at end of new ChangeLogs.204 let remove_empty = line('$') == 1205 206 " No entry today, so create a date-user header and insert an entry.207 let todays_entry = s:substitute_items(g:changelog_new_date_format,208 \ date, s:username(), a:prefix)209 " Make sure we have a cursor positioning.210 if stridx(todays_entry, '{cursor}') == -1211 let todays_entry = todays_entry . '{cursor}'212 endif213 214 " Now do the work.215 call append(0, split(todays_entry, '\n'))216 217 " Remove empty lines at end of file.218 if remove_empty219 $-/^\s*$/-1,$delete220 endif221 222 " Reposition cursor once we're done.223 call cursor(1, 1)224 endif225 226 call s:position_cursor()227 228 " And reset 'paste' option229 let &paste = save_paste230 endfunction231 232 let b:undo_ftplugin = "setl com< fo< et< ai<"233 234 setlocal comments=235 setlocal formatoptions+=t236 setlocal noexpandtab237 setlocal autoindent238 239 if &textwidth == 0240 setlocal textwidth=78241 let b:undo_ftplugin .= " tw<"242 endif243 244 if !exists("no_plugin_maps") && !exists("no_changelog_maps") && exists(":NewChangelogEntry") != 2245 nnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR>246 xnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR>247 command! -buffer -nargs=0 NewChangelogEntry call s:new_changelog_entry('')248 let b:undo_ftplugin .= " | sil! exe 'nunmap <buffer> <Leader>o'" .249 \ " | sil! exe 'vunmap <buffer> <Leader>o'" .250 \ " | sil! delc NewChangelogEntry"251 endif252 253 let &cpo = s:cpo_save254 unlet s:cpo_save255else256 let s:cpo_save = &cpo257 set cpo&vim258 259 if !exists("no_plugin_maps") && !exists("no_changelog_maps")260 " Add the Changelog opening mapping261 nnoremap <silent> <Leader>o :call <SID>open_changelog()<CR>262 let b:undo_ftplugin .= " | silent! exe 'nunmap <buffer> <Leader>o"263 endif264 265 function! s:open_changelog()266 let path = expand('%:p:h')267 if exists('b:changelog_path')268 let changelog = b:changelog_path269 else270 if exists('b:changelog_name')271 let name = b:changelog_name272 else273 let name = 'ChangeLog'274 endif275 while isdirectory(path)276 let changelog = path . '/' . name277 if filereadable(changelog)278 break279 endif280 let parent = substitute(path, '/\+[^/]*$', "", "")281 if path == parent282 break283 endif284 let path = parent285 endwhile286 endif287 if !filereadable(changelog)288 return289 endif290 291 if exists('b:changelog_entry_prefix')292 let prefix = call(b:changelog_entry_prefix, [])293 else294 let prefix = substitute(strpart(expand('%:p'), strlen(path)), '^/\+', "", "")295 endif296 297 let buf = bufnr(changelog)298 if buf != -1299 if bufwinnr(buf) != -1300 execute bufwinnr(buf) . 'wincmd w'301 else302 execute 'sbuffer' buf303 endif304 else305 execute 'split' fnameescape(changelog)306 endif307 308 call s:new_changelog_entry(prefix)309 endfunction310 311 let &cpo = s:cpo_save312 unlet s:cpo_save313endif314 