1071d4279SBram Moolenaar" Vim filetype plugin file 2071d4279SBram Moolenaar" Language: generic Changelog file 357657d85SBram Moolenaar" Maintainer: Nikolai Weibull <[email protected]> 4*5c73622aSBram Moolenaar" Latest Revision: 2009-05-25 5071d4279SBram Moolenaar" Variables: 657657d85SBram Moolenaar" g:changelog_timeformat (deprecated: use g:changelog_dateformat instead) - 7071d4279SBram Moolenaar" description: the timeformat used in ChangeLog entries. 8071d4279SBram Moolenaar" default: "%Y-%m-%d". 957657d85SBram Moolenaar" g:changelog_dateformat - 1057657d85SBram Moolenaar" description: the format sent to strftime() to generate a date string. 1157657d85SBram Moolenaar" default: "%Y-%m-%d". 12071d4279SBram Moolenaar" g:changelog_username - 13071d4279SBram Moolenaar" description: the username to use in ChangeLog entries 14071d4279SBram Moolenaar" default: try to deduce it from environment variables and system files. 15071d4279SBram Moolenaar" Local Mappings: 16071d4279SBram Moolenaar" <Leader>o - 17071d4279SBram Moolenaar" adds a new changelog entry for the current user for the current date. 18071d4279SBram Moolenaar" Global Mappings: 19071d4279SBram Moolenaar" <Leader>o - 20071d4279SBram Moolenaar" switches to the ChangeLog buffer opened for the current directory, or 21071d4279SBram Moolenaar" opens it in a new buffer if it exists in the current directory. Then 22071d4279SBram Moolenaar" it does the same as the local <Leader>o described above. 23071d4279SBram Moolenaar" Notes: 24071d4279SBram Moolenaar" run 'runtime ftplugin/changelog.vim' to enable the global mapping for 25071d4279SBram Moolenaar" changelog files. 26071d4279SBram Moolenaar" TODO: 27071d4279SBram Moolenaar" should we perhaps open the ChangeLog file even if it doesn't exist already? 28071d4279SBram Moolenaar" Problem is that you might end up with ChangeLog files all over the place. 29071d4279SBram Moolenaar 30071d4279SBram Moolenaar" If 'filetype' isn't "changelog", we must have been to add ChangeLog opener 3157657d85SBram Moolenaarif &filetype == 'changelog' 3257657d85SBram Moolenaar if exists('b:did_ftplugin') 33071d4279SBram Moolenaar finish 34071d4279SBram Moolenaar endif 35071d4279SBram Moolenaar let b:did_ftplugin = 1 36071d4279SBram Moolenaar 3742eeac35SBram Moolenaar let s:cpo_save = &cpo 3842eeac35SBram Moolenaar set cpo&vim 39071d4279SBram Moolenaar 4057657d85SBram Moolenaar " Set up the format used for dates. 4157657d85SBram Moolenaar if !exists('g:changelog_dateformat') 4257657d85SBram Moolenaar if exists('g:changelog_timeformat') 4357657d85SBram Moolenaar let g:changelog_dateformat = g:changelog_timeformat 4457657d85SBram Moolenaar else 4557657d85SBram Moolenaar let g:changelog_dateformat = "%Y-%m-%d" 4657657d85SBram Moolenaar endif 47071d4279SBram Moolenaar endif 48071d4279SBram Moolenaar 49*5c73622aSBram Moolenaar function! s:username() 50*5c73622aSBram Moolenaar if exists('g:changelog_username') 51*5c73622aSBram Moolenaar return g:changelog_username 52*5c73622aSBram Moolenaar elseif $EMAIL != "" 53*5c73622aSBram Moolenaar return $EMAIL 54*5c73622aSBram Moolenaar elseif $EMAIL_ADDRESS != "" 55*5c73622aSBram Moolenaar return $EMAIL_ADDRESS 56071d4279SBram Moolenaar endif 57071d4279SBram Moolenaar 58*5c73622aSBram Moolenaar let login = s:login() 59*5c73622aSBram Moolenaar return printf('%s <%s@%s>', s:name(login), login, s:hostname()) 60*5c73622aSBram Moolenaar endfunction 61*5c73622aSBram Moolenaar 62*5c73622aSBram Moolenaar function! s:login() 63*5c73622aSBram Moolenaar return s:trimmed_system_with_default('whoami', 'unknown') 64*5c73622aSBram Moolenaar endfunction 65*5c73622aSBram Moolenaar 66*5c73622aSBram Moolenaar function! s:trimmed_system_with_default(command, default) 67*5c73622aSBram Moolenaar return s:first_line(s:system_with_default(a:command, a:default)) 68*5c73622aSBram Moolenaar endfunction 69*5c73622aSBram Moolenaar 70*5c73622aSBram Moolenaar function! s:system_with_default(command, default) 71*5c73622aSBram Moolenaar let output = system(a:command) 72*5c73622aSBram Moolenaar if v:shell_error 73*5c73622aSBram Moolenaar return default 74071d4279SBram Moolenaar endif 75*5c73622aSBram Moolenaar return output 76*5c73622aSBram Moolenaar endfunction 77*5c73622aSBram Moolenaar 78*5c73622aSBram Moolenaar function! s:first_line(string) 79*5c73622aSBram Moolenaar return substitute(a:string, '\n.*$', "", "") 80*5c73622aSBram Moolenaar endfunction 81*5c73622aSBram Moolenaar 82*5c73622aSBram Moolenaar function! s:name(login) 83*5c73622aSBram Moolenaar for name in [s:gecos_name(a:login), $NAME, s:capitalize(a:login)] 84*5c73622aSBram Moolenaar if name != "" 85*5c73622aSBram Moolenaar return name 8657657d85SBram Moolenaar endif 8757657d85SBram Moolenaar endfor 88*5c73622aSBram Moolenaar endfunction 89071d4279SBram Moolenaar 90*5c73622aSBram Moolenaar function! s:gecos_name(login) 91*5c73622aSBram Moolenaar for line in s:try_reading_file('/etc/passwd') 92*5c73622aSBram Moolenaar if line =~ '^' . a:login . ':' 93*5c73622aSBram Moolenaar return substitute(s:passwd_field(line, 5), '&', s:capitalize(a:login), "") 9457657d85SBram Moolenaar endif 95*5c73622aSBram Moolenaar endfor 96*5c73622aSBram Moolenaar return "" 97*5c73622aSBram Moolenaar endfunction 9857657d85SBram Moolenaar 99*5c73622aSBram Moolenaar function! s:try_reading_file(path) 100*5c73622aSBram Moolenaar try 101*5c73622aSBram Moolenaar return readfile(a:path) 102*5c73622aSBram Moolenaar endtry 103*5c73622aSBram Moolenaar return [] 104*5c73622aSBram Moolenaar endfunction 105071d4279SBram Moolenaar 106*5c73622aSBram Moolenaar function! s:passwd_field(line, field) 107*5c73622aSBram Moolenaar let fields = split(a:line, ':', 1) 108*5c73622aSBram Moolenaar if len(fields) < field 109*5c73622aSBram Moolenaar return "" 110071d4279SBram Moolenaar endif 111*5c73622aSBram Moolenaar return fields[field - 1] 112*5c73622aSBram Moolenaar endfunction 113*5c73622aSBram Moolenaar 114*5c73622aSBram Moolenaar function! s:capitalize(word) 115*5c73622aSBram Moolenaar return toupper(a:word[0]) . strpart(a:word, 1) 116*5c73622aSBram Moolenaar endfunction 117*5c73622aSBram Moolenaar 118*5c73622aSBram Moolenaar function! s:hostname() 119*5c73622aSBram Moolenaar return s:trimmed_system_with_default('hostname', 'localhost') 120*5c73622aSBram Moolenaar endfunction 121071d4279SBram Moolenaar 12257657d85SBram Moolenaar " Format used for new date entries. 12357657d85SBram Moolenaar if !exists('g:changelog_new_date_format') 124071d4279SBram Moolenaar let g:changelog_new_date_format = "%d %u\n\n\t* %c\n\n" 125071d4279SBram Moolenaar endif 126071d4279SBram Moolenaar 12757657d85SBram Moolenaar " Format used for new entries to current date entry. 12857657d85SBram Moolenaar if !exists('g:changelog_new_entry_format') 129071d4279SBram Moolenaar let g:changelog_new_entry_format = "\t* %c" 130071d4279SBram Moolenaar endif 131071d4279SBram Moolenaar 13257657d85SBram Moolenaar " Regular expression used to find a given date entry. 13357657d85SBram Moolenaar if !exists('g:changelog_date_entry_search') 134071d4279SBram Moolenaar let g:changelog_date_entry_search = '^\s*%d\_s*%u' 135071d4279SBram Moolenaar endif 136071d4279SBram Moolenaar 1372c7a7638SBram Moolenaar " Regular expression used to find the end of a date entry 1382c7a7638SBram Moolenaar if !exists('g:changelog_date_end_entry_search') 139446cb837SBram Moolenaar let g:changelog_date_end_entry_search = '^\s*$' 1402c7a7638SBram Moolenaar endif 1412c7a7638SBram Moolenaar 1422c7a7638SBram Moolenaar 14357657d85SBram Moolenaar " Substitutes specific items in new date-entry formats and search strings. 14457657d85SBram Moolenaar " Can be done with substitute of course, but unclean, and need \@! then. 145071d4279SBram Moolenaar function! s:substitute_items(str, date, user) 146071d4279SBram Moolenaar let str = a:str 14757657d85SBram Moolenaar let middles = {'%': '%', 'd': a:date, 'u': a:user, 'c': '{cursor}'} 148071d4279SBram Moolenaar let i = stridx(str, '%') 149071d4279SBram Moolenaar while i != -1 15057657d85SBram Moolenaar let inc = 0 15157657d85SBram Moolenaar if has_key(middles, str[i + 1]) 15257657d85SBram Moolenaar let mid = middles[str[i + 1]] 15357657d85SBram Moolenaar let str = strpart(str, 0, i) . mid . strpart(str, i + 2) 15457657d85SBram Moolenaar let inc = strlen(mid) 155071d4279SBram Moolenaar endif 15657657d85SBram Moolenaar let i = stridx(str, '%', i + 1 + inc) 157071d4279SBram Moolenaar endwhile 158071d4279SBram Moolenaar return str 159071d4279SBram Moolenaar endfunction 160071d4279SBram Moolenaar 16157657d85SBram Moolenaar " Position the cursor once we've done all the funky substitution. 162071d4279SBram Moolenaar function! s:position_cursor() 163071d4279SBram Moolenaar if search('{cursor}') > 0 16457657d85SBram Moolenaar let lnum = line('.') 16557657d85SBram Moolenaar let line = getline(lnum) 166071d4279SBram Moolenaar let cursor = stridx(line, '{cursor}') 16757657d85SBram Moolenaar call setline(lnum, substitute(line, '{cursor}', '', '')) 168071d4279SBram Moolenaar endif 169071d4279SBram Moolenaar startinsert! 170071d4279SBram Moolenaar endfunction 171071d4279SBram Moolenaar 17257657d85SBram Moolenaar " Internal function to create a new entry in the ChangeLog. 173071d4279SBram Moolenaar function! s:new_changelog_entry() 17457657d85SBram Moolenaar " Deal with 'paste' option. 175071d4279SBram Moolenaar let save_paste = &paste 176071d4279SBram Moolenaar let &paste = 1 17757657d85SBram Moolenaar call cursor(1, 1) 17857657d85SBram Moolenaar " Look for an entry for today by our user. 17957657d85SBram Moolenaar let date = strftime(g:changelog_dateformat) 180071d4279SBram Moolenaar let search = s:substitute_items(g:changelog_date_entry_search, date, 181071d4279SBram Moolenaar \ g:changelog_username) 182071d4279SBram Moolenaar if search(search) > 0 18357657d85SBram Moolenaar " Ok, now we look for the end of the date entry, and add an entry. 18457657d85SBram Moolenaar call cursor(nextnonblank(line('.') + 1), 1) 1852c7a7638SBram Moolenaar if search(g:changelog_date_end_entry_search, 'W') > 0 186*5c73622aSBram Moolenaar let p = (line('.') == line('$')) ? line('.') : line('.') - 1 187071d4279SBram Moolenaar else 18857657d85SBram Moolenaar let p = line('.') 18957657d85SBram Moolenaar endif 19057657d85SBram Moolenaar let ls = split(s:substitute_items(g:changelog_new_entry_format, '', ''), 19157657d85SBram Moolenaar \ '\n') 19257657d85SBram Moolenaar call append(p, ls) 19357657d85SBram Moolenaar call cursor(p + 1, 1) 19457657d85SBram Moolenaar else 19557657d85SBram Moolenaar " Flag for removing empty lines at end of new ChangeLogs. 196071d4279SBram Moolenaar let remove_empty = line('$') == 1 197071d4279SBram Moolenaar 19857657d85SBram Moolenaar " No entry today, so create a date-user header and insert an entry. 199071d4279SBram Moolenaar let todays_entry = s:substitute_items(g:changelog_new_date_format, 200071d4279SBram Moolenaar \ date, g:changelog_username) 20157657d85SBram Moolenaar " Make sure we have a cursor positioning. 202071d4279SBram Moolenaar if stridx(todays_entry, '{cursor}') == -1 203071d4279SBram Moolenaar let todays_entry = todays_entry . '{cursor}' 204071d4279SBram Moolenaar endif 205071d4279SBram Moolenaar 20657657d85SBram Moolenaar " Now do the work. 20757657d85SBram Moolenaar call append(0, split(todays_entry, '\n')) 20857657d85SBram Moolenaar 20957657d85SBram Moolenaar " Remove empty lines at end of file. 210071d4279SBram Moolenaar if remove_empty 21157657d85SBram Moolenaar $-/^\s*$/-1,$delete 212071d4279SBram Moolenaar endif 213071d4279SBram Moolenaar 21457657d85SBram Moolenaar " Reposition cursor once we're done. 21557657d85SBram Moolenaar call cursor(1, 1) 216071d4279SBram Moolenaar endif 217071d4279SBram Moolenaar 218071d4279SBram Moolenaar call s:position_cursor() 219071d4279SBram Moolenaar 220071d4279SBram Moolenaar " And reset 'paste' option 221071d4279SBram Moolenaar let &paste = save_paste 222071d4279SBram Moolenaar endfunction 223071d4279SBram Moolenaar 224071d4279SBram Moolenaar if exists(":NewChangelogEntry") != 2 225*5c73622aSBram Moolenaar noremap <buffer> <silent> <Leader>o <Esc>:call <SID>new_changelog_entry()<CR> 226071d4279SBram Moolenaar command! -nargs=0 NewChangelogEntry call s:new_changelog_entry() 227071d4279SBram Moolenaar endif 228071d4279SBram Moolenaar 22957657d85SBram Moolenaar let b:undo_ftplugin = "setl com< fo< et< ai<" 230071d4279SBram Moolenaar 231071d4279SBram Moolenaar setlocal comments= 232071d4279SBram Moolenaar setlocal formatoptions+=t 233071d4279SBram Moolenaar setlocal noexpandtab 23442eeac35SBram Moolenaar setlocal autoindent 235071d4279SBram Moolenaar 23657657d85SBram Moolenaar if &textwidth == 0 23757657d85SBram Moolenaar setlocal textwidth=78 23857657d85SBram Moolenaar let b:undo_ftplugin .= " tw<" 23957657d85SBram Moolenaar endif 24057657d85SBram Moolenaar 24142eeac35SBram Moolenaar let &cpo = s:cpo_save 24242eeac35SBram Moolenaar unlet s:cpo_save 243071d4279SBram Moolenaarelse 244*5c73622aSBram Moolenaar let s:cpo_save = &cpo 245*5c73622aSBram Moolenaar set cpo&vim 246*5c73622aSBram Moolenaar 247071d4279SBram Moolenaar " Add the Changelog opening mapping 248*5c73622aSBram Moolenaar nnoremap <silent> <Leader>o :call <SID>open_changelog()<CR> 249071d4279SBram Moolenaar 250071d4279SBram Moolenaar function! s:open_changelog() 251*5c73622aSBram Moolenaar let path = expand('%:p:h') 252*5c73622aSBram Moolenaar if exists('b:changelog_path') 253*5c73622aSBram Moolenaar let changelog = b:changelog_path 254*5c73622aSBram Moolenaar else 255*5c73622aSBram Moolenaar if exists('b:changelog_name') 256*5c73622aSBram Moolenaar let name = b:changelog_name 257*5c73622aSBram Moolenaar else 258*5c73622aSBram Moolenaar let name = 'ChangeLog' 259*5c73622aSBram Moolenaar endif 260*5c73622aSBram Moolenaar while isdirectory(path) 261*5c73622aSBram Moolenaar let changelog = path . '/' . name 262*5c73622aSBram Moolenaar if filereadable(changelog) 263*5c73622aSBram Moolenaar break 264*5c73622aSBram Moolenaar endif 265*5c73622aSBram Moolenaar let parent = substitute(path, '/\+[^/]*$', "", "") 266*5c73622aSBram Moolenaar if path == parent 267*5c73622aSBram Moolenaar break 268*5c73622aSBram Moolenaar endif 269*5c73622aSBram Moolenaar let path = parent 270*5c73622aSBram Moolenaar endwhile 271*5c73622aSBram Moolenaar endif 272*5c73622aSBram Moolenaar if !filereadable(changelog) 27357657d85SBram Moolenaar return 27457657d85SBram Moolenaar endif 275*5c73622aSBram Moolenaar 276*5c73622aSBram Moolenaar if exists('b:changelog_entry_prefix') 277*5c73622aSBram Moolenaar let prefix = call(b:changelog_entry_prefix, []) 278*5c73622aSBram Moolenaar else 279*5c73622aSBram Moolenaar let prefix = substitute(strpart(expand('%:p'), strlen(path)), '^/\+', "", "") . ':' 280*5c73622aSBram Moolenaar endif 281*5c73622aSBram Moolenaar if !empty(prefix) 282*5c73622aSBram Moolenaar let prefix = ' ' . prefix 283*5c73622aSBram Moolenaar endif 284*5c73622aSBram Moolenaar 285*5c73622aSBram Moolenaar let buf = bufnr(changelog) 28657657d85SBram Moolenaar if buf != -1 28757657d85SBram Moolenaar if bufwinnr(buf) != -1 2882c7a7638SBram Moolenaar execute bufwinnr(buf) . 'wincmd w' 28957657d85SBram Moolenaar else 2902c7a7638SBram Moolenaar execute 'sbuffer' buf 29157657d85SBram Moolenaar endif 292071d4279SBram Moolenaar else 293*5c73622aSBram Moolenaar execute 'split' fnameescape(changelog) 294071d4279SBram Moolenaar endif 295071d4279SBram Moolenaar 296*5c73622aSBram Moolenaar call s:new_changelog_entry(prefix) 297071d4279SBram Moolenaar endfunction 298*5c73622aSBram Moolenaar 299*5c73622aSBram Moolenaar let &cpo = s:cpo_save 300*5c73622aSBram Moolenaar unlet s:cpo_save 301071d4279SBram Moolenaarendif 302