diff 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
line wrap: on
line diff
--- a/plugin/lawrencium.vim	Tue Dec 13 07:21:24 2011 -0800
+++ b/plugin/lawrencium.vim	Tue Dec 13 17:09:37 2011 -0800
@@ -44,6 +44,16 @@
     endif
 endfunction
 
+" Like tempname() but with some control over the filename.
+function! s:tempname(name, ...)
+    let l:path = tempname()
+    let l:result = fnamemodify(l:path, ':h') . '/' . a:name . fnamemodify(l:path, ':t')
+    if a:0 > 0
+        let l:result = l:result . a:1
+    endif
+    return l:result
+endfunction
+
 " Prints a message if debug tracing is enabled.
 function! s:trace(message, ...)
    if g:lawrencium_trace || (a:0 && a:1)
@@ -235,7 +245,7 @@
     let l:output = call(l:repo.RunCommand, a:000, l:repo)
     if a:bang
         " Open the output of the command in a temp file.
-        let l:temp_file = tempname()
+        let l:temp_file = s:tempname('hg-output-', '.txt')
         execute 'pedit ' . l:temp_file
         wincmd p
         call append(0, l:output)
@@ -245,7 +255,61 @@
     endif
 endfunction
 
-call s:AddMainCommand("-bang -nargs=* Hg :execute s:Hg(<bang>0, <f-args>)")
+" Include the generated HG usage file.
+let s:usage_file = expand("<sfile>:h:h") . "/resources/hg_usage.vim"
+if filereadable(s:usage_file)
+    execute "source " . s:usage_file
+else
+    echoerr "Can't find the Mercurial usage file. Auto-completion will be disabled in Lawrencium."
+endif
+
+function! s:CompleteHg(ArgLead, CmdLine, CursorPos)
+    " Don't do anything if the usage file was not sourced.
+    if !exists('g:lawrencium_hg_commands') || !exists('g:lawrencium_hg_options')
+        return []
+    endif
+
+    " a:ArgLead seems to be the number 0 when completing a minus '-'.
+    " Gotta find out why...
+    let l:arglead = a:ArgLead
+    if type(a:ArgLead) == type(0)
+        let l:arglead = '-'
+    endif
+
+    " Try completing a global option, before any command name.
+    if a:CmdLine =~# '\v^Hg(\s+\-[a-zA-Z0-9\-_]*)+$'
+        return filter(copy(g:lawrencium_hg_options), "v:val[0:strlen(l:arglead)-1] ==# l:arglead")
+    endif
+
+    " Try completing a command (note that there could be global options before
+    " the command name).
+    if a:CmdLine =~# '\v^Hg\s+(\-[a-zA-Z0-9\-_]+\s+)*[a-zA-Z]+$'
+        echom " - matched command"
+        return filter(keys(g:lawrencium_hg_commands), "v:val[0:strlen(l:arglead)-1] ==# l:arglead")
+    endif
+    
+    " Try completing a command's options.
+    let l:cmd = matchstr(a:CmdLine, '\v(^Hg\s+(\-[a-zA-Z0-9\-_]+\s+)*)@<=[a-zA-Z]+')
+    if strlen(l:cmd) > 0
+        echom " - matched command option for " . l:cmd . " with : " . l:arglead
+    endif
+    if strlen(l:cmd) > 0 && l:arglead[0] ==# '-'
+        if has_key(g:lawrencium_hg_commands, l:cmd)
+            " Return both command options and global options together.
+            let l:copts = filter(copy(g:lawrencium_hg_commands[l:cmd]), "v:val[0:strlen(l:arglead)-1] ==# l:arglead")
+            let l:gopts = filter(copy(g:lawrencium_hg_options), "v:val[0:strlen(l:arglead)-1] ==# l:arglead")
+            return l:copts + l:gopts
+        endif
+    endif
+    
+    " Just auto-complete with filenames unless it's an option.
+    if l:arglead[0] ==# '-'
+        return []
+    else
+        return s:ListRepoFiles(a:ArgLead, a:CmdLine, a:CursorPos)
+endfunction
+
+call s:AddMainCommand("-bang -complete=customlist,s:CompleteHg -nargs=* Hg :execute s:Hg(<bang>0, <f-args>)")
 
 " }}}
 
@@ -261,8 +325,7 @@
 
     " Open a new temp buffer in the preview window, jump to it,
     " and paste the `hg status` output in there.
-    let l:temp_file = tempname()
-    let l:temp_file = fnamemodify(l:temp_file, ':h') . 'hg-status-' . fnamemodify(l:temp_file, ':t') . '.txt'
+    let l:temp_file = s:tempname('hg-status-', '.txt')
     let l:preview_height = &previewheight
     let l:status_lines = split(l:status_text, '\n')
     execute "setlocal previewheight=" . (len(l:status_lines) + 1)
@@ -289,7 +352,34 @@
     nnoremap <buffer> <silent> <cr>  :execute <SID>HgStatus_FileEdit()<cr>
     nnoremap <buffer> <silent> <C-D> :execute <SID>HgStatus_FileDiff(0)<cr>
     nnoremap <buffer> <silent> <C-V> :execute <SID>HgStatus_FileDiff(1)<cr>
+    nnoremap <buffer> <silent> <C-A> :execute <SID>HgStatus_FileAdd()<cr>
+    nnoremap <buffer> <silent> <C-R> :execute <SID>HgStatus_Refresh()<cr>
     nnoremap <buffer> <silent> q     :bdelete<cr>
+
+    " Make sure the file is deleted with the buffer.
+    autocmd BufDelete <buffer> call s:HgStatus_CleanUp(expand('<afile>:p'))
+endfunction
+
+function! s:HgStatus_CleanUp(path) abort
+    " If the `hg status` output has been saved to disk (e.g. because of a
+    " refresh we did), let's delete it.
+    if filewritable(a:path)
+        call s:trace("Cleaning up status log: " . a:path)
+        call delete(a:path)
+    endif
+endfunction
+
+function! s:HgStatus_Refresh() abort
+    " Get the repo and the `hg status` output.
+    let l:repo = s:hg_repo()
+    let l:status_text = l:repo.RunCommand('status')
+
+    " Replace the contents of the current buffer with it, and refresh.
+    echo "Writing to " . expand('%:p')
+    let l:path = expand('%:p')
+    let l:status_lines = split(l:status_text, '\n')
+    call writefile(l:status_lines, l:path)
+    edit
 endfunction
 
 function! s:HgStatus_FileEdit() abort
@@ -306,6 +396,22 @@
     endif
 endfunction
 
+function! s:HgStatus_FileAdd() abort
+    " Get the path of the file the cursor is on, and its status.
+    let l:filename = s:HgStatus_GetSelectedPath()
+    let l:status = s:HgStatus_GetSelectedStatus()
+    if l:status !=# '?'
+        echoerr "Not an untracked file: " . l:filename
+    endif
+
+    " Add the file.
+    let l:repo = s:hg_repo()
+    call l:repo.RunCommand('add', l:filename)
+
+    " Refresh the status window.
+    call s:HgStatus_Refresh()
+endfunction
+
 function! s:HgStatus_FileDiff(vertical) abort
     " Get the path of the file the cursor is on.
     let l:filename = s:HgStatus_GetSelectedPath()
@@ -320,11 +426,16 @@
     let l:line = getline('.')
     " Yay, awesome, Vim's regex syntax is fucked up like shit, especially for
     " look-aheads and look-behinds. See for yourself:
-    let l:filename = matchstr(l:line, '\v([MARC\!\?I ]\s)@<=.*')
+    let l:filename = matchstr(l:line, '\v(^[MARC\!\?I ]\s)@<=.*')
     let l:filename = l:repo.GetFullPath(l:filename)
     return l:filename
 endfunction
 
+function! s:HgStatus_GetSelectedStatus() abort
+    let l:line = getline('.')
+    return matchstr(l:line, '\v^[MARC\!\?I ]')
+endfunction
+
 call s:AddMainCommand("Hgstatus :execute s:HgStatus()")
 
 " }}}
@@ -418,7 +529,7 @@
     let l:repo = s:hg_repo()
 
     " Open a commit message file.
-    let l:commit_path = tempname()
+    let l:commit_path = s:tempname('hg-editor-', '.txt')
     let l:split = a:vertical ? 'vsplit' : 'split'
     execute l:split . ' ' . l:commit_path
     call append(0, ['', ''])
@@ -428,10 +539,11 @@
     " and make the buffer delete itself on exit.
     let b:mercurial_dir = l:repo.root_dir
     setlocal bufhidden=delete
+    setlocal syntax=hgcommit
     if a:bang
+        autocmd BufDelete <buffer> call s:HgCommit_Execute(expand('<afile>:p'), 0)
+    else
         autocmd BufDelete <buffer> call s:HgCommit_Execute(expand('<afile>:p'), 1)
-    else
-        autocmd BufDelete <buffer> call s:HgCommit_Execute(expand('<afile>:p'), 0)
     endif
 endfunction
 
@@ -470,7 +582,7 @@
 function! s:HgCommit_Execute(log_file, show_output) abort
     " Check if the user actually saved a commit message.
     if !filereadable(a:log_file)
-        call s:trace("Commit message was not saved... abort commit.")
+        call s:trace("Commit message was not saved... abort commit.", 1)
         return
     endif