comparison plugin/lawrencium.vim @ 15:f02e37f395ae

Added ability to add files from the `hg status` window. Added ability to refresh the `hg status` window. Added generation of `hg` commands usage, and used that for a better `:Hg` auto-completion.
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 13 Dec 2011 17:09:37 -0800
parents eab2680e6818
children 724f6db3baa2
comparison
equal deleted inserted replaced
14:eab2680e6818 15:f02e37f395ae
40 elseif has('win32') 40 elseif has('win32')
41 return substitute(a:path, '/', '\\', '') 41 return substitute(a:path, '/', '\\', '')
42 else 42 else
43 return a:path 43 return a:path
44 endif 44 endif
45 endfunction
46
47 " Like tempname() but with some control over the filename.
48 function! s:tempname(name, ...)
49 let l:path = tempname()
50 let l:result = fnamemodify(l:path, ':h') . '/' . a:name . fnamemodify(l:path, ':t')
51 if a:0 > 0
52 let l:result = l:result . a:1
53 endif
54 return l:result
45 endfunction 55 endfunction
46 56
47 " Prints a message if debug tracing is enabled. 57 " Prints a message if debug tracing is enabled.
48 function! s:trace(message, ...) 58 function! s:trace(message, ...)
49 if g:lawrencium_trace || (a:0 && a:1) 59 if g:lawrencium_trace || (a:0 && a:1)
233 function! s:Hg(bang, ...) abort 243 function! s:Hg(bang, ...) abort
234 let l:repo = s:hg_repo() 244 let l:repo = s:hg_repo()
235 let l:output = call(l:repo.RunCommand, a:000, l:repo) 245 let l:output = call(l:repo.RunCommand, a:000, l:repo)
236 if a:bang 246 if a:bang
237 " Open the output of the command in a temp file. 247 " Open the output of the command in a temp file.
238 let l:temp_file = tempname() 248 let l:temp_file = s:tempname('hg-output-', '.txt')
239 execute 'pedit ' . l:temp_file 249 execute 'pedit ' . l:temp_file
240 wincmd p 250 wincmd p
241 call append(0, l:output) 251 call append(0, l:output)
242 else 252 else
243 " Just print out the output of the command. 253 " Just print out the output of the command.
244 echo l:output 254 echo l:output
245 endif 255 endif
246 endfunction 256 endfunction
247 257
248 call s:AddMainCommand("-bang -nargs=* Hg :execute s:Hg(<bang>0, <f-args>)") 258 " Include the generated HG usage file.
259 let s:usage_file = expand("<sfile>:h:h") . "/resources/hg_usage.vim"
260 if filereadable(s:usage_file)
261 execute "source " . s:usage_file
262 else
263 echoerr "Can't find the Mercurial usage file. Auto-completion will be disabled in Lawrencium."
264 endif
265
266 function! s:CompleteHg(ArgLead, CmdLine, CursorPos)
267 " Don't do anything if the usage file was not sourced.
268 if !exists('g:lawrencium_hg_commands') || !exists('g:lawrencium_hg_options')
269 return []
270 endif
271
272 " a:ArgLead seems to be the number 0 when completing a minus '-'.
273 " Gotta find out why...
274 let l:arglead = a:ArgLead
275 if type(a:ArgLead) == type(0)
276 let l:arglead = '-'
277 endif
278
279 " Try completing a global option, before any command name.
280 if a:CmdLine =~# '\v^Hg(\s+\-[a-zA-Z0-9\-_]*)+$'
281 return filter(copy(g:lawrencium_hg_options), "v:val[0:strlen(l:arglead)-1] ==# l:arglead")
282 endif
283
284 " Try completing a command (note that there could be global options before
285 " the command name).
286 if a:CmdLine =~# '\v^Hg\s+(\-[a-zA-Z0-9\-_]+\s+)*[a-zA-Z]+$'
287 echom " - matched command"
288 return filter(keys(g:lawrencium_hg_commands), "v:val[0:strlen(l:arglead)-1] ==# l:arglead")
289 endif
290
291 " Try completing a command's options.
292 let l:cmd = matchstr(a:CmdLine, '\v(^Hg\s+(\-[a-zA-Z0-9\-_]+\s+)*)@<=[a-zA-Z]+')
293 if strlen(l:cmd) > 0
294 echom " - matched command option for " . l:cmd . " with : " . l:arglead
295 endif
296 if strlen(l:cmd) > 0 && l:arglead[0] ==# '-'
297 if has_key(g:lawrencium_hg_commands, l:cmd)
298 " Return both command options and global options together.
299 let l:copts = filter(copy(g:lawrencium_hg_commands[l:cmd]), "v:val[0:strlen(l:arglead)-1] ==# l:arglead")
300 let l:gopts = filter(copy(g:lawrencium_hg_options), "v:val[0:strlen(l:arglead)-1] ==# l:arglead")
301 return l:copts + l:gopts
302 endif
303 endif
304
305 " Just auto-complete with filenames unless it's an option.
306 if l:arglead[0] ==# '-'
307 return []
308 else
309 return s:ListRepoFiles(a:ArgLead, a:CmdLine, a:CursorPos)
310 endfunction
311
312 call s:AddMainCommand("-bang -complete=customlist,s:CompleteHg -nargs=* Hg :execute s:Hg(<bang>0, <f-args>)")
249 313
250 " }}} 314 " }}}
251 315
252 " Hgstatus {{{ 316 " Hgstatus {{{
253 317
259 echo "Nothing modified." 323 echo "Nothing modified."
260 endif 324 endif
261 325
262 " Open a new temp buffer in the preview window, jump to it, 326 " Open a new temp buffer in the preview window, jump to it,
263 " and paste the `hg status` output in there. 327 " and paste the `hg status` output in there.
264 let l:temp_file = tempname() 328 let l:temp_file = s:tempname('hg-status-', '.txt')
265 let l:temp_file = fnamemodify(l:temp_file, ':h') . 'hg-status-' . fnamemodify(l:temp_file, ':t') . '.txt'
266 let l:preview_height = &previewheight 329 let l:preview_height = &previewheight
267 let l:status_lines = split(l:status_text, '\n') 330 let l:status_lines = split(l:status_text, '\n')
268 execute "setlocal previewheight=" . (len(l:status_lines) + 1) 331 execute "setlocal previewheight=" . (len(l:status_lines) + 1)
269 execute "pedit " . l:temp_file 332 execute "pedit " . l:temp_file
270 wincmd p 333 wincmd p
287 nnoremap <buffer> <silent> <C-N> :call search('^[MARC\!\?I ]\s.', 'We')<cr> 350 nnoremap <buffer> <silent> <C-N> :call search('^[MARC\!\?I ]\s.', 'We')<cr>
288 nnoremap <buffer> <silent> <C-P> :call search('^[MARC\!\?I ]\s.','Wbe')<cr> 351 nnoremap <buffer> <silent> <C-P> :call search('^[MARC\!\?I ]\s.','Wbe')<cr>
289 nnoremap <buffer> <silent> <cr> :execute <SID>HgStatus_FileEdit()<cr> 352 nnoremap <buffer> <silent> <cr> :execute <SID>HgStatus_FileEdit()<cr>
290 nnoremap <buffer> <silent> <C-D> :execute <SID>HgStatus_FileDiff(0)<cr> 353 nnoremap <buffer> <silent> <C-D> :execute <SID>HgStatus_FileDiff(0)<cr>
291 nnoremap <buffer> <silent> <C-V> :execute <SID>HgStatus_FileDiff(1)<cr> 354 nnoremap <buffer> <silent> <C-V> :execute <SID>HgStatus_FileDiff(1)<cr>
355 nnoremap <buffer> <silent> <C-A> :execute <SID>HgStatus_FileAdd()<cr>
356 nnoremap <buffer> <silent> <C-R> :execute <SID>HgStatus_Refresh()<cr>
292 nnoremap <buffer> <silent> q :bdelete<cr> 357 nnoremap <buffer> <silent> q :bdelete<cr>
358
359 " Make sure the file is deleted with the buffer.
360 autocmd BufDelete <buffer> call s:HgStatus_CleanUp(expand('<afile>:p'))
361 endfunction
362
363 function! s:HgStatus_CleanUp(path) abort
364 " If the `hg status` output has been saved to disk (e.g. because of a
365 " refresh we did), let's delete it.
366 if filewritable(a:path)
367 call s:trace("Cleaning up status log: " . a:path)
368 call delete(a:path)
369 endif
370 endfunction
371
372 function! s:HgStatus_Refresh() abort
373 " Get the repo and the `hg status` output.
374 let l:repo = s:hg_repo()
375 let l:status_text = l:repo.RunCommand('status')
376
377 " Replace the contents of the current buffer with it, and refresh.
378 echo "Writing to " . expand('%:p')
379 let l:path = expand('%:p')
380 let l:status_lines = split(l:status_text, '\n')
381 call writefile(l:status_lines, l:path)
382 edit
293 endfunction 383 endfunction
294 384
295 function! s:HgStatus_FileEdit() abort 385 function! s:HgStatus_FileEdit() abort
296 " Get the path of the file the cursor is on. 386 " Get the path of the file the cursor is on.
297 let l:filename = s:HgStatus_GetSelectedPath() 387 let l:filename = s:HgStatus_GetSelectedPath()
304 else 394 else
305 execute 'edit ' . l:filename 395 execute 'edit ' . l:filename
306 endif 396 endif
307 endfunction 397 endfunction
308 398
399 function! s:HgStatus_FileAdd() abort
400 " Get the path of the file the cursor is on, and its status.
401 let l:filename = s:HgStatus_GetSelectedPath()
402 let l:status = s:HgStatus_GetSelectedStatus()
403 if l:status !=# '?'
404 echoerr "Not an untracked file: " . l:filename
405 endif
406
407 " Add the file.
408 let l:repo = s:hg_repo()
409 call l:repo.RunCommand('add', l:filename)
410
411 " Refresh the status window.
412 call s:HgStatus_Refresh()
413 endfunction
414
309 function! s:HgStatus_FileDiff(vertical) abort 415 function! s:HgStatus_FileDiff(vertical) abort
310 " Get the path of the file the cursor is on. 416 " Get the path of the file the cursor is on.
311 let l:filename = s:HgStatus_GetSelectedPath() 417 let l:filename = s:HgStatus_GetSelectedPath()
312 418
313 " Go back to the previous window and call HgDiff. 419 " Go back to the previous window and call HgDiff.
318 function! s:HgStatus_GetSelectedPath() abort 424 function! s:HgStatus_GetSelectedPath() abort
319 let l:repo = s:hg_repo() 425 let l:repo = s:hg_repo()
320 let l:line = getline('.') 426 let l:line = getline('.')
321 " Yay, awesome, Vim's regex syntax is fucked up like shit, especially for 427 " Yay, awesome, Vim's regex syntax is fucked up like shit, especially for
322 " look-aheads and look-behinds. See for yourself: 428 " look-aheads and look-behinds. See for yourself:
323 let l:filename = matchstr(l:line, '\v([MARC\!\?I ]\s)@<=.*') 429 let l:filename = matchstr(l:line, '\v(^[MARC\!\?I ]\s)@<=.*')
324 let l:filename = l:repo.GetFullPath(l:filename) 430 let l:filename = l:repo.GetFullPath(l:filename)
325 return l:filename 431 return l:filename
432 endfunction
433
434 function! s:HgStatus_GetSelectedStatus() abort
435 let l:line = getline('.')
436 return matchstr(l:line, '\v^[MARC\!\?I ]')
326 endfunction 437 endfunction
327 438
328 call s:AddMainCommand("Hgstatus :execute s:HgStatus()") 439 call s:AddMainCommand("Hgstatus :execute s:HgStatus()")
329 440
330 " }}} 441 " }}}
416 function! s:HgCommit(bang, vertical) abort 527 function! s:HgCommit(bang, vertical) abort
417 " Get the repo we'll be committing into. 528 " Get the repo we'll be committing into.
418 let l:repo = s:hg_repo() 529 let l:repo = s:hg_repo()
419 530
420 " Open a commit message file. 531 " Open a commit message file.
421 let l:commit_path = tempname() 532 let l:commit_path = s:tempname('hg-editor-', '.txt')
422 let l:split = a:vertical ? 'vsplit' : 'split' 533 let l:split = a:vertical ? 'vsplit' : 'split'
423 execute l:split . ' ' . l:commit_path 534 execute l:split . ' ' . l:commit_path
424 call append(0, ['', '']) 535 call append(0, ['', ''])
425 call append(2, split(s:HgCommit_GenerateMessage(l:repo), '\n')) 536 call append(2, split(s:HgCommit_GenerateMessage(l:repo), '\n'))
426 537
427 " Setup the auto-command that will actually commit on write/exit, 538 " Setup the auto-command that will actually commit on write/exit,
428 " and make the buffer delete itself on exit. 539 " and make the buffer delete itself on exit.
429 let b:mercurial_dir = l:repo.root_dir 540 let b:mercurial_dir = l:repo.root_dir
430 setlocal bufhidden=delete 541 setlocal bufhidden=delete
542 setlocal syntax=hgcommit
431 if a:bang 543 if a:bang
544 autocmd BufDelete <buffer> call s:HgCommit_Execute(expand('<afile>:p'), 0)
545 else
432 autocmd BufDelete <buffer> call s:HgCommit_Execute(expand('<afile>:p'), 1) 546 autocmd BufDelete <buffer> call s:HgCommit_Execute(expand('<afile>:p'), 1)
433 else
434 autocmd BufDelete <buffer> call s:HgCommit_Execute(expand('<afile>:p'), 0)
435 endif 547 endif
436 endfunction 548 endfunction
437 549
438 let s:hg_status_messages = { 550 let s:hg_status_messages = {
439 \'M': 'modified', 551 \'M': 'modified',
468 endfunction 580 endfunction
469 581
470 function! s:HgCommit_Execute(log_file, show_output) abort 582 function! s:HgCommit_Execute(log_file, show_output) abort
471 " Check if the user actually saved a commit message. 583 " Check if the user actually saved a commit message.
472 if !filereadable(a:log_file) 584 if !filereadable(a:log_file)
473 call s:trace("Commit message was not saved... abort commit.") 585 call s:trace("Commit message was not saved... abort commit.", 1)
474 return 586 return
475 endif 587 endif
476 588
477 call s:trace("Committing with log file: " . a:log_file) 589 call s:trace("Committing with log file: " . a:log_file)
478 590