blob: c871a777ebfbc781d4e4c593fb0087c164eb5a2c [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001" Vim filetype plugin file
Bram Moolenaar42eeac32005-06-29 22:40:58 +00002" Language: generic Changelog file
Bram Moolenaar57657d82006-04-21 22:12:41 +00003" Maintainer: Nikolai Weibull <now@bitwi.se>
Bram Moolenaar483c5d82010-10-20 18:45:33 +02004" Latest Revision: 2010-08-17
Bram Moolenaar071d4272004-06-13 20:20:40 +00005" Variables:
Bram Moolenaar57657d82006-04-21 22:12:41 +00006" g:changelog_timeformat (deprecated: use g:changelog_dateformat instead) -
Bram Moolenaar42eeac32005-06-29 22:40:58 +00007" description: the timeformat used in ChangeLog entries.
8" default: "%Y-%m-%d".
Bram Moolenaar57657d82006-04-21 22:12:41 +00009" g:changelog_dateformat -
10" description: the format sent to strftime() to generate a date string.
11" default: "%Y-%m-%d".
Bram Moolenaar071d4272004-06-13 20:20:40 +000012" g:changelog_username -
Bram Moolenaar42eeac32005-06-29 22:40:58 +000013" description: the username to use in ChangeLog entries
14" default: try to deduce it from environment variables and system files.
Bram Moolenaar071d4272004-06-13 20:20:40 +000015" Local Mappings:
16" <Leader>o -
Bram Moolenaar42eeac32005-06-29 22:40:58 +000017" adds a new changelog entry for the current user for the current date.
Bram Moolenaar071d4272004-06-13 20:20:40 +000018" Global Mappings:
19" <Leader>o -
Bram Moolenaar42eeac32005-06-29 22:40:58 +000020" switches to the ChangeLog buffer opened for the current directory, or
21" opens it in a new buffer if it exists in the current directory. Then
22" it does the same as the local <Leader>o described above.
Bram Moolenaar071d4272004-06-13 20:20:40 +000023" Notes:
24" run 'runtime ftplugin/changelog.vim' to enable the global mapping for
25" changelog files.
26" TODO:
27" should we perhaps open the ChangeLog file even if it doesn't exist already?
28" Problem is that you might end up with ChangeLog files all over the place.
29
30" If 'filetype' isn't "changelog", we must have been to add ChangeLog opener
Bram Moolenaar57657d82006-04-21 22:12:41 +000031if &filetype == 'changelog'
32 if exists('b:did_ftplugin')
Bram Moolenaar071d4272004-06-13 20:20:40 +000033 finish
34 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000035 let b:did_ftplugin = 1
36
Bram Moolenaar42eeac32005-06-29 22:40:58 +000037 let s:cpo_save = &cpo
38 set cpo&vim
Bram Moolenaar071d4272004-06-13 20:20:40 +000039
Bram Moolenaar57657d82006-04-21 22:12:41 +000040 " Set up the format used for dates.
41 if !exists('g:changelog_dateformat')
42 if exists('g:changelog_timeformat')
43 let g:changelog_dateformat = g:changelog_timeformat
44 else
45 let g:changelog_dateformat = "%Y-%m-%d"
46 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000047 endif
48
Bram Moolenaar5c736222010-01-06 20:54:52 +010049 function! s:username()
50 if exists('g:changelog_username')
51 return g:changelog_username
52 elseif $EMAIL != ""
53 return $EMAIL
54 elseif $EMAIL_ADDRESS != ""
55 return $EMAIL_ADDRESS
Bram Moolenaar071d4272004-06-13 20:20:40 +000056 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +010057
58 let login = s:login()
59 return printf('%s <%s@%s>', s:name(login), login, s:hostname())
60 endfunction
61
62 function! s:login()
63 return s:trimmed_system_with_default('whoami', 'unknown')
64 endfunction
65
66 function! s:trimmed_system_with_default(command, default)
67 return s:first_line(s:system_with_default(a:command, a:default))
68 endfunction
69
70 function! s:system_with_default(command, default)
71 let output = system(a:command)
72 if v:shell_error
73 return default
74 endif
75 return output
76 endfunction
77
78 function! s:first_line(string)
79 return substitute(a:string, '\n.*$', "", "")
80 endfunction
81
82 function! s:name(login)
83 for name in [s:gecos_name(a:login), $NAME, s:capitalize(a:login)]
84 if name != ""
85 return name
86 endif
87 endfor
88 endfunction
89
90 function! s:gecos_name(login)
91 for line in s:try_reading_file('/etc/passwd')
92 if line =~ '^' . a:login . ':'
93 return substitute(s:passwd_field(line, 5), '&', s:capitalize(a:login), "")
94 endif
95 endfor
96 return ""
97 endfunction
98
99 function! s:try_reading_file(path)
100 try
101 return readfile(a:path)
102 endtry
103 return []
104 endfunction
105
106 function! s:passwd_field(line, field)
107 let fields = split(a:line, ':', 1)
108 if len(fields) < field
109 return ""
110 endif
111 return fields[field - 1]
112 endfunction
113
114 function! s:capitalize(word)
115 return toupper(a:word[0]) . strpart(a:word, 1)
116 endfunction
117
118 function! s:hostname()
119 return s:trimmed_system_with_default('hostname', 'localhost')
120 endfunction
Bram Moolenaar071d4272004-06-13 20:20:40 +0000121
Bram Moolenaar57657d82006-04-21 22:12:41 +0000122 " Format used for new date entries.
123 if !exists('g:changelog_new_date_format')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000124 let g:changelog_new_date_format = "%d %u\n\n\t* %c\n\n"
125 endif
126
Bram Moolenaar57657d82006-04-21 22:12:41 +0000127 " Format used for new entries to current date entry.
128 if !exists('g:changelog_new_entry_format')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000129 let g:changelog_new_entry_format = "\t* %c"
130 endif
131
Bram Moolenaar57657d82006-04-21 22:12:41 +0000132 " Regular expression used to find a given date entry.
133 if !exists('g:changelog_date_entry_search')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000134 let g:changelog_date_entry_search = '^\s*%d\_s*%u'
135 endif
136
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000137 " Regular expression used to find the end of a date entry
138 if !exists('g:changelog_date_end_entry_search')
Bram Moolenaar446cb832008-06-24 21:56:24 +0000139 let g:changelog_date_end_entry_search = '^\s*$'
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000140 endif
141
142
Bram Moolenaar57657d82006-04-21 22:12:41 +0000143 " Substitutes specific items in new date-entry formats and search strings.
144 " Can be done with substitute of course, but unclean, and need \@! then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000145 function! s:substitute_items(str, date, user)
146 let str = a:str
Bram Moolenaar57657d82006-04-21 22:12:41 +0000147 let middles = {'%': '%', 'd': a:date, 'u': a:user, 'c': '{cursor}'}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000148 let i = stridx(str, '%')
149 while i != -1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000150 let inc = 0
151 if has_key(middles, str[i + 1])
152 let mid = middles[str[i + 1]]
153 let str = strpart(str, 0, i) . mid . strpart(str, i + 2)
154 let inc = strlen(mid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000155 endif
Bram Moolenaar57657d82006-04-21 22:12:41 +0000156 let i = stridx(str, '%', i + 1 + inc)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157 endwhile
158 return str
159 endfunction
160
Bram Moolenaar57657d82006-04-21 22:12:41 +0000161 " Position the cursor once we've done all the funky substitution.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000162 function! s:position_cursor()
163 if search('{cursor}') > 0
Bram Moolenaar57657d82006-04-21 22:12:41 +0000164 let lnum = line('.')
165 let line = getline(lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000166 let cursor = stridx(line, '{cursor}')
Bram Moolenaar57657d82006-04-21 22:12:41 +0000167 call setline(lnum, substitute(line, '{cursor}', '', ''))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000168 endif
169 startinsert!
170 endfunction
171
Bram Moolenaar57657d82006-04-21 22:12:41 +0000172 " Internal function to create a new entry in the ChangeLog.
Bram Moolenaar483c5d82010-10-20 18:45:33 +0200173 function! s:new_changelog_entry(...)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000174 " Deal with 'paste' option.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175 let save_paste = &paste
176 let &paste = 1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000177 call cursor(1, 1)
178 " Look for an entry for today by our user.
179 let date = strftime(g:changelog_dateformat)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000180 let search = s:substitute_items(g:changelog_date_entry_search, date,
Bram Moolenaar57657d82006-04-21 22:12:41 +0000181 \ g:changelog_username)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000182 if search(search) > 0
Bram Moolenaar57657d82006-04-21 22:12:41 +0000183 " Ok, now we look for the end of the date entry, and add an entry.
184 call cursor(nextnonblank(line('.') + 1), 1)
Bram Moolenaar2c7a7632007-05-10 18:19:11 +0000185 if search(g:changelog_date_end_entry_search, 'W') > 0
Bram Moolenaar5c736222010-01-06 20:54:52 +0100186 let p = (line('.') == line('$')) ? line('.') : line('.') - 1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000187 else
188 let p = line('.')
189 endif
190 let ls = split(s:substitute_items(g:changelog_new_entry_format, '', ''),
191 \ '\n')
192 call append(p, ls)
193 call cursor(p + 1, 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000194 else
Bram Moolenaar57657d82006-04-21 22:12:41 +0000195 " Flag for removing empty lines at end of new ChangeLogs.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000196 let remove_empty = line('$') == 1
197
Bram Moolenaar57657d82006-04-21 22:12:41 +0000198 " No entry today, so create a date-user header and insert an entry.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000199 let todays_entry = s:substitute_items(g:changelog_new_date_format,
Bram Moolenaar57657d82006-04-21 22:12:41 +0000200 \ date, g:changelog_username)
201 " Make sure we have a cursor positioning.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000202 if stridx(todays_entry, '{cursor}') == -1
Bram Moolenaar57657d82006-04-21 22:12:41 +0000203 let todays_entry = todays_entry . '{cursor}'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000204 endif
205
Bram Moolenaar57657d82006-04-21 22:12:41 +0000206 " Now do the work.
207 call append(0, split(todays_entry, '\n'))
208
209 " Remove empty lines at end of file.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000210 if remove_empty
Bram Moolenaar57657d82006-04-21 22:12:41 +0000211 $-/^\s*$/-1,$delete
Bram Moolenaar071d4272004-06-13 20:20:40 +0000212 endif
213
Bram Moolenaar57657d82006-04-21 22:12:41 +0000214 " Reposition cursor once we're done.
215 call cursor(1, 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000216 endif
217
218 call s:position_cursor()
219
220 " And reset 'paste' option
221 let &paste = save_paste
222 endfunction
223
224 if exists(":NewChangelogEntry") != 2
Bram Moolenaar5c736222010-01-06 20:54:52 +0100225 noremap <buffer> <silent> <Leader>o <Esc>:call <SID>new_changelog_entry()<CR>
Bram Moolenaar071d4272004-06-13 20:20:40 +0000226 command! -nargs=0 NewChangelogEntry call s:new_changelog_entry()
227 endif
228
Bram Moolenaar57657d82006-04-21 22:12:41 +0000229 let b:undo_ftplugin = "setl com< fo< et< ai<"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000230
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231 setlocal comments=
232 setlocal formatoptions+=t
233 setlocal noexpandtab
Bram Moolenaar42eeac32005-06-29 22:40:58 +0000234 setlocal autoindent
Bram Moolenaar071d4272004-06-13 20:20:40 +0000235
Bram Moolenaar57657d82006-04-21 22:12:41 +0000236 if &textwidth == 0
237 setlocal textwidth=78
238 let b:undo_ftplugin .= " tw<"
239 endif
240
Bram Moolenaar42eeac32005-06-29 22:40:58 +0000241 let &cpo = s:cpo_save
242 unlet s:cpo_save
Bram Moolenaar071d4272004-06-13 20:20:40 +0000243else
Bram Moolenaar5c736222010-01-06 20:54:52 +0100244 let s:cpo_save = &cpo
245 set cpo&vim
246
Bram Moolenaar071d4272004-06-13 20:20:40 +0000247 " Add the Changelog opening mapping
Bram Moolenaar5c736222010-01-06 20:54:52 +0100248 nnoremap <silent> <Leader>o :call <SID>open_changelog()<CR>
Bram Moolenaar071d4272004-06-13 20:20:40 +0000249
250 function! s:open_changelog()
Bram Moolenaar5c736222010-01-06 20:54:52 +0100251 let path = expand('%:p:h')
252 if exists('b:changelog_path')
253 let changelog = b:changelog_path
254 else
255 if exists('b:changelog_name')
256 let name = b:changelog_name
257 else
258 let name = 'ChangeLog'
259 endif
260 while isdirectory(path)
261 let changelog = path . '/' . name
262 if filereadable(changelog)
263 break
264 endif
265 let parent = substitute(path, '/\+[^/]*$', "", "")
266 if path == parent
267 break
268 endif
269 let path = parent
270 endwhile
271 endif
272 if !filereadable(changelog)
Bram Moolenaar57657d82006-04-21 22:12:41 +0000273 return
Bram Moolenaar071d4272004-06-13 20:20:40 +0000274 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +0100275
276 if exists('b:changelog_entry_prefix')
277 let prefix = call(b:changelog_entry_prefix, [])
278 else
279 let prefix = substitute(strpart(expand('%:p'), strlen(path)), '^/\+', "", "") . ':'
280 endif
281 if !empty(prefix)
282 let prefix = ' ' . prefix
283 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