1071d4279SBram Moolenaar" Vim filetype plugin file 2071d4279SBram Moolenaar" Language: generic Changelog file 357657d85SBram Moolenaar" Maintainer: Nikolai Weibull <[email protected]> 4*4b550b4cSBram Moolenaar" Latest Revision: 2013-12-15 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 495c73622aSBram Moolenaar function! s:username() 505c73622aSBram Moolenaar if exists('g:changelog_username') 515c73622aSBram Moolenaar return g:changelog_username 525c73622aSBram Moolenaar elseif $EMAIL != "" 535c73622aSBram Moolenaar return $EMAIL 545c73622aSBram Moolenaar elseif $EMAIL_ADDRESS != "" 555c73622aSBram Moolenaar return $EMAIL_ADDRESS 56071d4279SBram Moolenaar endif 57071d4279SBram Moolenaar 585c73622aSBram Moolenaar let login = s:login() 595c73622aSBram Moolenaar return printf('%s <%s@%s>', s:name(login), login, s:hostname()) 605c73622aSBram Moolenaar endfunction 615c73622aSBram Moolenaar 625c73622aSBram Moolenaar function! s:login() 635c73622aSBram Moolenaar return s:trimmed_system_with_default('whoami', 'unknown') 645c73622aSBram Moolenaar endfunction 655c73622aSBram Moolenaar 665c73622aSBram Moolenaar function! s:trimmed_system_with_default(command, default) 675c73622aSBram Moolenaar return s:first_line(s:system_with_default(a:command, a:default)) 685c73622aSBram Moolenaar endfunction 695c73622aSBram Moolenaar 705c73622aSBram Moolenaar function! s:system_with_default(command, default) 715c73622aSBram Moolenaar let output = system(a:command) 725c73622aSBram Moolenaar if v:shell_error 735c73622aSBram Moolenaar return default 74071d4279SBram Moolenaar endif 755c73622aSBram Moolenaar return output 765c73622aSBram Moolenaar endfunction 775c73622aSBram Moolenaar 785c73622aSBram Moolenaar function! s:first_line(string) 795c73622aSBram Moolenaar return substitute(a:string, '\n.*$', "", "") 805c73622aSBram Moolenaar endfunction 815c73622aSBram Moolenaar 825c73622aSBram Moolenaar function! s:name(login) 835c73622aSBram Moolenaar for name in [s:gecos_name(a:login), $NAME, s:capitalize(a:login)] 845c73622aSBram Moolenaar if name != "" 855c73622aSBram Moolenaar return name 8657657d85SBram Moolenaar endif 8757657d85SBram Moolenaar endfor 885c73622aSBram Moolenaar endfunction 89071d4279SBram Moolenaar 905c73622aSBram Moolenaar function! s:gecos_name(login) 915c73622aSBram Moolenaar for line in s:try_reading_file('/etc/passwd') 925c73622aSBram Moolenaar if line =~ '^' . a:login . ':' 935c73622aSBram Moolenaar return substitute(s:passwd_field(line, 5), '&', s:capitalize(a:login), "") 9457657d85SBram Moolenaar endif 955c73622aSBram Moolenaar endfor 965c73622aSBram Moolenaar return "" 975c73622aSBram Moolenaar endfunction 9857657d85SBram Moolenaar 995c73622aSBram Moolenaar function! s:try_reading_file(path) 1005c73622aSBram Moolenaar try 1015c73622aSBram Moolenaar return readfile(a:path) 102446beb48SBram Moolenaar catch 1035c73622aSBram Moolenaar return [] 104446beb48SBram Moolenaar endtry 1055c73622aSBram Moolenaar endfunction 106071d4279SBram Moolenaar 1075c73622aSBram Moolenaar function! s:passwd_field(line, field) 1085c73622aSBram Moolenaar let fields = split(a:line, ':', 1) 109d09acef4SBram Moolenaar if len(fields) < a:field 1105c73622aSBram Moolenaar return "" 111071d4279SBram Moolenaar endif 112d09acef4SBram Moolenaar return fields[a:field - 1] 1135c73622aSBram Moolenaar endfunction 1145c73622aSBram Moolenaar 1155c73622aSBram Moolenaar function! s:capitalize(word) 1165c73622aSBram Moolenaar return toupper(a:word[0]) . strpart(a:word, 1) 1175c73622aSBram Moolenaar endfunction 1185c73622aSBram Moolenaar 1195c73622aSBram Moolenaar function! s:hostname() 1205c73622aSBram Moolenaar return s:trimmed_system_with_default('hostname', 'localhost') 1215c73622aSBram Moolenaar endfunction 122071d4279SBram Moolenaar 12357657d85SBram Moolenaar " Format used for new date entries. 12457657d85SBram Moolenaar if !exists('g:changelog_new_date_format') 125*4b550b4cSBram Moolenaar let g:changelog_new_date_format = "%d %u\n\n\t* %p%c\n\n" 126071d4279SBram Moolenaar endif 127071d4279SBram Moolenaar 12857657d85SBram Moolenaar " Format used for new entries to current date entry. 12957657d85SBram Moolenaar if !exists('g:changelog_new_entry_format') 130*4b550b4cSBram Moolenaar let g:changelog_new_entry_format = "\t* %p%c" 131071d4279SBram Moolenaar endif 132071d4279SBram Moolenaar 13357657d85SBram Moolenaar " Regular expression used to find a given date entry. 13457657d85SBram Moolenaar if !exists('g:changelog_date_entry_search') 135071d4279SBram Moolenaar let g:changelog_date_entry_search = '^\s*%d\_s*%u' 136071d4279SBram Moolenaar endif 137071d4279SBram Moolenaar 1382c7a7638SBram Moolenaar " Regular expression used to find the end of a date entry 1392c7a7638SBram Moolenaar if !exists('g:changelog_date_end_entry_search') 140446cb837SBram Moolenaar let g:changelog_date_end_entry_search = '^\s*$' 1412c7a7638SBram Moolenaar endif 1422c7a7638SBram Moolenaar 1432c7a7638SBram Moolenaar 14457657d85SBram Moolenaar " Substitutes specific items in new date-entry formats and search strings. 14557657d85SBram Moolenaar " Can be done with substitute of course, but unclean, and need \@! then. 146*4b550b4cSBram Moolenaar function! s:substitute_items(str, date, user, prefix) 147071d4279SBram Moolenaar let str = a:str 148*4b550b4cSBram Moolenaar let middles = {'%': '%', 'd': a:date, 'u': a:user, 'p': a:prefix, 'c': '{cursor}'} 149071d4279SBram Moolenaar let i = stridx(str, '%') 150071d4279SBram Moolenaar while i != -1 15157657d85SBram Moolenaar let inc = 0 15257657d85SBram Moolenaar if has_key(middles, str[i + 1]) 15357657d85SBram Moolenaar let mid = middles[str[i + 1]] 15457657d85SBram Moolenaar let str = strpart(str, 0, i) . mid . strpart(str, i + 2) 15557657d85SBram Moolenaar let inc = strlen(mid) 156071d4279SBram Moolenaar endif 15757657d85SBram Moolenaar let i = stridx(str, '%', i + 1 + inc) 158071d4279SBram Moolenaar endwhile 159071d4279SBram Moolenaar return str 160071d4279SBram Moolenaar endfunction 161071d4279SBram Moolenaar 16257657d85SBram Moolenaar " Position the cursor once we've done all the funky substitution. 163071d4279SBram Moolenaar function! s:position_cursor() 164071d4279SBram Moolenaar if search('{cursor}') > 0 16557657d85SBram Moolenaar let lnum = line('.') 16657657d85SBram Moolenaar let line = getline(lnum) 167071d4279SBram Moolenaar let cursor = stridx(line, '{cursor}') 16857657d85SBram Moolenaar call setline(lnum, substitute(line, '{cursor}', '', '')) 169071d4279SBram Moolenaar endif 170071d4279SBram Moolenaar startinsert! 171071d4279SBram Moolenaar endfunction 172071d4279SBram Moolenaar 17357657d85SBram Moolenaar " Internal function to create a new entry in the ChangeLog. 174*4b550b4cSBram Moolenaar function! s:new_changelog_entry(prefix) 17557657d85SBram Moolenaar " Deal with 'paste' option. 176071d4279SBram Moolenaar let save_paste = &paste 177071d4279SBram Moolenaar let &paste = 1 17857657d85SBram Moolenaar call cursor(1, 1) 17957657d85SBram Moolenaar " Look for an entry for today by our user. 18057657d85SBram Moolenaar let date = strftime(g:changelog_dateformat) 181071d4279SBram Moolenaar let search = s:substitute_items(g:changelog_date_entry_search, date, 182*4b550b4cSBram Moolenaar \ s:username(), a:prefix) 183071d4279SBram Moolenaar if search(search) > 0 18457657d85SBram Moolenaar " Ok, now we look for the end of the date entry, and add an entry. 18557657d85SBram Moolenaar call cursor(nextnonblank(line('.') + 1), 1) 1862c7a7638SBram Moolenaar if search(g:changelog_date_end_entry_search, 'W') > 0 1875c73622aSBram Moolenaar let p = (line('.') == line('$')) ? line('.') : line('.') - 1 188071d4279SBram Moolenaar else 18957657d85SBram Moolenaar let p = line('.') 19057657d85SBram Moolenaar endif 191*4b550b4cSBram Moolenaar let ls = split(s:substitute_items(g:changelog_new_entry_format, '', '', a:prefix), 19257657d85SBram Moolenaar \ '\n') 19357657d85SBram Moolenaar call append(p, ls) 19457657d85SBram Moolenaar call cursor(p + 1, 1) 19557657d85SBram Moolenaar else 19657657d85SBram Moolenaar " Flag for removing empty lines at end of new ChangeLogs. 197071d4279SBram Moolenaar let remove_empty = line('$') == 1 198071d4279SBram Moolenaar 19957657d85SBram Moolenaar " No entry today, so create a date-user header and insert an entry. 200071d4279SBram Moolenaar let todays_entry = s:substitute_items(g:changelog_new_date_format, 201*4b550b4cSBram Moolenaar \ date, s:username(), a:prefix) 20257657d85SBram Moolenaar " Make sure we have a cursor positioning. 203071d4279SBram Moolenaar if stridx(todays_entry, '{cursor}') == -1 204071d4279SBram Moolenaar let todays_entry = todays_entry . '{cursor}' 205071d4279SBram Moolenaar endif 206071d4279SBram Moolenaar 20757657d85SBram Moolenaar " Now do the work. 20857657d85SBram Moolenaar call append(0, split(todays_entry, '\n')) 20957657d85SBram Moolenaar 21057657d85SBram Moolenaar " Remove empty lines at end of file. 211071d4279SBram Moolenaar if remove_empty 21257657d85SBram Moolenaar $-/^\s*$/-1,$delete 213071d4279SBram Moolenaar endif 214071d4279SBram Moolenaar 21557657d85SBram Moolenaar " Reposition cursor once we're done. 21657657d85SBram Moolenaar call cursor(1, 1) 217071d4279SBram Moolenaar endif 218071d4279SBram Moolenaar 219071d4279SBram Moolenaar call s:position_cursor() 220071d4279SBram Moolenaar 221071d4279SBram Moolenaar " And reset 'paste' option 222071d4279SBram Moolenaar let &paste = save_paste 223071d4279SBram Moolenaar endfunction 224071d4279SBram Moolenaar 225071d4279SBram Moolenaar if exists(":NewChangelogEntry") != 2 226*4b550b4cSBram Moolenaar noremap <buffer> <silent> <Leader>o <Esc>:call <SID>new_changelog_entry('')<CR> 227*4b550b4cSBram Moolenaar command! -nargs=0 NewChangelogEntry call s:new_changelog_entry('') 228071d4279SBram Moolenaar endif 229071d4279SBram Moolenaar 23057657d85SBram Moolenaar let b:undo_ftplugin = "setl com< fo< et< ai<" 231071d4279SBram Moolenaar 232071d4279SBram Moolenaar setlocal comments= 233071d4279SBram Moolenaar setlocal formatoptions+=t 234071d4279SBram Moolenaar setlocal noexpandtab 23542eeac35SBram Moolenaar setlocal autoindent 236071d4279SBram Moolenaar 23757657d85SBram Moolenaar if &textwidth == 0 23857657d85SBram Moolenaar setlocal textwidth=78 23957657d85SBram Moolenaar let b:undo_ftplugin .= " tw<" 24057657d85SBram Moolenaar endif 24157657d85SBram Moolenaar 24242eeac35SBram Moolenaar let &cpo = s:cpo_save 24342eeac35SBram Moolenaar unlet s:cpo_save 244071d4279SBram Moolenaarelse 2455c73622aSBram Moolenaar let s:cpo_save = &cpo 2465c73622aSBram Moolenaar set cpo&vim 2475c73622aSBram Moolenaar 248071d4279SBram Moolenaar " Add the Changelog opening mapping 2495c73622aSBram Moolenaar nnoremap <silent> <Leader>o :call <SID>open_changelog()<CR> 250071d4279SBram Moolenaar 251071d4279SBram Moolenaar function! s:open_changelog() 2525c73622aSBram Moolenaar let path = expand('%:p:h') 2535c73622aSBram Moolenaar if exists('b:changelog_path') 2545c73622aSBram Moolenaar let changelog = b:changelog_path 2555c73622aSBram Moolenaar else 2565c73622aSBram Moolenaar if exists('b:changelog_name') 2575c73622aSBram Moolenaar let name = b:changelog_name 2585c73622aSBram Moolenaar else 2595c73622aSBram Moolenaar let name = 'ChangeLog' 2605c73622aSBram Moolenaar endif 2615c73622aSBram Moolenaar while isdirectory(path) 2625c73622aSBram Moolenaar let changelog = path . '/' . name 2635c73622aSBram Moolenaar if filereadable(changelog) 2645c73622aSBram Moolenaar break 2655c73622aSBram Moolenaar endif 2665c73622aSBram Moolenaar let parent = substitute(path, '/\+[^/]*$', "", "") 2675c73622aSBram Moolenaar if path == parent 2685c73622aSBram Moolenaar break 2695c73622aSBram Moolenaar endif 2705c73622aSBram Moolenaar let path = parent 2715c73622aSBram Moolenaar endwhile 2725c73622aSBram Moolenaar endif 2735c73622aSBram Moolenaar if !filereadable(changelog) 27457657d85SBram Moolenaar return 27557657d85SBram Moolenaar endif 2765c73622aSBram Moolenaar 2775c73622aSBram Moolenaar if exists('b:changelog_entry_prefix') 2785c73622aSBram Moolenaar let prefix = call(b:changelog_entry_prefix, []) 2795c73622aSBram Moolenaar else 280*4b550b4cSBram Moolenaar let prefix = substitute(strpart(expand('%:p'), strlen(path)), '^/\+', "", "") 2815c73622aSBram Moolenaar endif 2825c73622aSBram Moolenaar 2835c73622aSBram Moolenaar let buf = bufnr(changelog) 28457657d85SBram Moolenaar if buf != -1 28557657d85SBram Moolenaar if bufwinnr(buf) != -1 2862c7a7638SBram Moolenaar execute bufwinnr(buf) . 'wincmd w' 28757657d85SBram Moolenaar else 2882c7a7638SBram Moolenaar execute 'sbuffer' buf 28957657d85SBram Moolenaar endif 290071d4279SBram Moolenaar else 2915c73622aSBram Moolenaar execute 'split' fnameescape(changelog) 292071d4279SBram Moolenaar endif 293071d4279SBram Moolenaar 2945c73622aSBram Moolenaar call s:new_changelog_entry(prefix) 295071d4279SBram Moolenaar endfunction 2965c73622aSBram Moolenaar 2975c73622aSBram Moolenaar let &cpo = s:cpo_save 2985c73622aSBram Moolenaar unlet s:cpo_save 299071d4279SBram Moolenaarendif 300