Mercurial > vim-lawrencium
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 |