changeset 48:85e39bdd7089

Lots of changes that should have gone in other commits (ugh): - merged changes from @soliman - removed the bang edit command setting. The preview windows is not really well suited for some things, so it's been replaced with a normal split window for `Hg!` and `Hgstatus`. - officialized `Hglog`. - fixed some problems on Windows.
author Ludovic Chabant <ludovic@chabant.com>
date Thu, 08 Nov 2012 13:58:29 -0800
parents 6a4f5200d8da (diff) edc43c59b3b8 (current diff)
children dffb41c2067c
files doc/lawrencium.txt plugin/lawrencium.vim
diffstat 4 files changed, 266 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/doc/lawrencium.txt	Sun Nov 04 16:13:23 2012 +0100
+++ b/doc/lawrencium.txt	Thu Nov 08 13:58:29 2012 -0800
@@ -1,6 +1,23 @@
 *lawrencium.txt*  Mercurial wrapper for VIM
 
 
+            LLLLLLLLLLL                                                      ~
+            L:::::::::L                                                      ~
+            L:::::::::L                                                      ~
+            LL:::::::LL                                                      ~
+              L:::::L              rrrrr   rrrrrrrrr                         ~
+              L:::::L              r::::rrr:::::::::r                        ~
+              L:::::L              r:::::::::::::::::r                       ~
+              L:::::L              rr::::::rrrrr::::::r                      ~
+              L:::::L               r:::::r     r:::::r                      ~
+              L:::::L               r:::::r     rrrrrrr                      ~
+              L:::::L               r:::::r                                  ~
+              L:::::L         LLLLLLr:::::r                                  ~
+            LL:::::::LLLLLLLLL:::::Lr:::::r                                  ~
+            L::::::::::::::::::::::Lr:::::r                                  ~
+            L::::::::::::::::::::::Lr:::::r                                  ~
+            LLLLLLLLLLLLLLLLLLLLLLLLrrrrrrr                                  ~
+
                 LAWRENCIUM REFERENCE MANUAL
                     by Ludovic Chabant
 
@@ -10,6 +27,8 @@
 1.  Introduction            |lawrencium-intro|
 2.  Commands                |lawrencium-commands|
 3.  Status Window           |lawrencium-status-window|
+4.  Log Window              |lawrencium-log-window|
+5.  Global Settings         |lawrencium-global-settings|
 
 
 =============================================================================
@@ -38,14 +57,18 @@
                         file belongs to, and it will auto-complete any
                         standard command or option.
                         
-                        Also, unless the `lawrencium_auto_cd` global is set to
+                        Also, unless the |lawrencium_auto_cd| global is set to
                         `0`, it will temporarily set the current directory to be
                         the root of the repository so that auto-completed
                         filenames work out of the box.
 
                                                 *:Hg!*
 :Hg! {args}             Like |:Hg|, but the output of the command is placed in
-                        a temp file and edited in the |preview-window|.
+                        a temp file and edited in a split window.
+
+                        Once the output is open in a buffer, Lawrencium will
+                        try to set the file type according to the Mercurial
+                        command that was run. See |lawrencium_hg_commands_file_types|.
 
                                                 *:Hgcd*
 :Hgcd[!] {path}         |:cd| relative to the root of the repository.
@@ -60,9 +83,9 @@
 :Hgedit! {file}         |:edit!| {file} relative to the root of the repository.
 
                                                 *:Hgstatus*
-:Hgstatus               Shows the output of 'hg status' in the
-                        |preview-window|. Some extra-commands, along with some
-                        default mappins, are available in this window. See
+:Hgstatus               Shows the output of 'hg status' in a split window.
+                        Some extra-commands, along with some default mappings,
+                        are available in this window. See
                         |lawrencium-status-window|.
 
                                                 *:Hgdiff*
@@ -96,37 +119,48 @@
                         given to prevent a .orig backup file from being
                         created.
 
+                                                *:Hglog*
+:Hglog
+                        Opens the history (log) for the currently edited file
+                        in a split window. Some extra-commands, along with 
+                        some default mappings, are available in this window.
+                        See |lawrencium-log-window|.
+
+                                                *:Hglog_f*
+:Hglog {file}           Same as |:Hglog|, but opens the log for the specified
+                        file instead of the currently edited file.
+
 
 =============================================================================
 3.  Status Window                               *lawrencium-status-window*
 
 The `hg status` window opened by Lawrencium has a few special commands 
-defined in it. Also, unless the global `lawrencium_define_mappings` is set to
+defined in it. Also, unless the global |lawrencium_define_mappings| is set to
 `0`, some commands are mapped to keyboard shortcuts, as detailed in the
 following descriptions.
 
 
                                                 *:Hgstatusedit*
 :Hgstatusedit           Open the file mentioned on the current line.
-                        Mapped to <cr>.
+                        Mapped to <CR>.
 
                                                 *:Hgstatusaddremove*
 :Hgstatusaddremove      Run `hg addremove` on the file mentioned on the
                         current line, or on the lines currently selected in
                         visual selection mode (see |visual-start|).
-                        Mapped to <C-A>.
+                        Mapped to <CTRL-A>.
 
                                                 *:Hgstatusdiff*
 :Hgstatusdiff           Open a diff window on the file mentioned on the
                         current line, between the working directory version
                         and the parent revision version. Similar to running
                         |:Hgdiff| on that file.
-                        Mapped to <C-D>.
+                        Mapped to <CTRL-D>.
 
                                                 *:Hgstatusvdiff*
 :Hgstatusvdiff          Similar to |:Hgstatusdiff|, but use a vertical split.
                         Similar to running |:Hgvdiff| on that file.
-                        Mapped to <C-V>.
+                        Mapped to <CTRL-V>.
 
                                                 *:Hgstatuscommit*
 :Hgstatuscommit         Runs |:Hgcommit| on the files currently mentioned in
@@ -134,7 +168,7 @@
                         option means you can delete lines from the buffer
                         until the remaining filenames are those you want to
                         commit.
-                        Mapped to <C-S>.
+                        Mapped to <CTRL-S>.
 
                                                 *:Hgstatusvcommit*
 :Hgstatusvcommit        Similar to |:Hgstatuscommit|, but use a vertical
@@ -144,7 +178,7 @@
                                                 *:Hgstatusrefresh*
 :Hgstatusrefresh        Refreshes the status window by running `hg status`
                         again.
-                        Mapped to <C-R>.
+                        Mapped to <CTRL-R>.
 
                                                 *:Hgstatusqnew*
 :Hgstatusqnew {patch} {message}.
@@ -167,4 +201,64 @@
    q                    Quit the status window.
 
 
+
+=============================================================================
+4.  Log Window                                  *lawrencium-log-window*
+                        
+The `hg log` window opened by Lawrencium has a few special commands 
+defined in it. Also, unless the global |lawrencium_define_mappings| is set to
+`0`, some commands are mapped to keyboard shortcuts, as detailed in the
+following descriptions.
+
+                                                *:Hglogrevedit*
+:Hglogrevedit           Looks at the revision specified on the current line,
+                        and opens that revision for edit in the previous
+                        window (although as a read-only buffer).
+                        Mapped to |<CR>|
+
+                                                *lawrencium-log-mappings*
+A few other mappings are available in the log window:
+
+    q                   Quit the log window.
+
+
+
+=============================================================================
+5.  Global Settings                             *lawrencium-global-settings*
+
+The following global settings can be defined in your |vimrc| to change the
+default behaviour of Lawrencium.
+
+                                                *lawrencium_hg_executable*
+g:lawrencium_hg_executable
+                        Defines the executable to run when running Mercurial
+                        commands.
+                        Defaults to simply `hg`.
+
+                                                *lawrencium_auto_cd*
+g:lawrencium_auto_cd    Specifies whether the current working directory should 
+                        be set to the repository's root while running |:Hg|
+                        commands so that auto-completion works magically with
+                        repository relative paths.
+                        Defaults to `1`.
+
+                                                *lawrencium_define_mappings*
+g:lawrencium_define_mappings
+                        Specifies whether Lawrencium should define default
+                        keyboard shortcuts.
+                        Defaults to `1`.
+
+                                                *lawrencium_hg_commands_file_types*
+g:lawrencium_hg_commands_file_types
+                        Defines the |file-types| that Lawrencium should use
+                        when editing the output of |:Hg!| based on the
+                        Mercurial command that was used.
+                        
+                        By default, unless overridden, the following commands
+                        map to the following file types:
+
+                        diff                diff
+                        graphlog            graphlog
+
+
  vim:tw=78:et:ft=help:norl:
--- a/plugin/lawrencium.vim	Sun Nov 04 16:13:23 2012 +0100
+++ b/plugin/lawrencium.vim	Thu Nov 08 13:58:29 2012 -0800
@@ -44,14 +44,23 @@
 " Normalizes the slashes in a path.
 function! s:normalizepath(path)
     if exists('+shellslash') && &shellslash
-        return substitute(a:path, '\\', '/', '')
+        return substitute(a:path, '\v/', '\\', 'g')
     elseif has('win32')
-        return substitute(a:path, '/', '\\', '')
+        return substitute(a:path, '\v/', '\\', 'g')
     else
         return a:path
     endif
 endfunction
 
+" Shell-slashes the path (opposite of `normalizepath`).
+function! s:shellslash(path)
+  if exists('+shellslash') && !&shellslash
+    return substitute(a:path, '\v\\', '/', 'g')
+  else
+    return a:path
+  endif
+endfunction
+
 " Like tempname() but with some control over the filename.
 function! s:tempname(name, ...)
     let l:path = tempname()
@@ -107,8 +116,8 @@
 " Given a Lawrencium path (e.g: 'lawrencium:///repo/root_dir@foo/bar/file.py//34'), extract
 " the repository root, relative file path and revision number/changeset ID.
 function! s:parse_lawrencium_path(lawrencium_path)
-    let l:repo_path = a:lawrencium_path
-    if l:repo_path =~? '^lawrencium://'
+    let l:repo_path = s:shellslash(a:lawrencium_path)
+    if l:repo_path =~? '\v^lawrencium://'
         let l:repo_path = strpart(l:repo_path, strlen('lawrencium://'))
     endif
 
@@ -313,9 +322,23 @@
     if a:bang
         " Open the output of the command in a temp file.
         let l:temp_file = s:tempname('hg-output-', '.txt')
-        execute 'pedit ' . l:temp_file
-        wincmd P
+        split
+        execute 'edit ' . l:temp_file
         call append(0, split(l:output, '\n'))
+        call cursor(1, 1)
+
+        " Make it a temp buffer
+        setlocal bufhidden=delete
+        setlocal buftype=nofile
+
+        " Try to find a nice syntax to set given the current command.
+        let l:command_name = s:GetHgCommandName(a:000)
+        if l:command_name != '' && exists('g:lawrencium_hg_commands_file_types')
+            let l:file_type = get(g:lawrencium_hg_commands_file_types, l:command_name, '')
+            if l:file_type != ''
+                execute 'setlocal ft=' . l:file_type
+            endif
+        endif
     else
         " Just print out the output of the command.
         echo l:output
@@ -330,6 +353,12 @@
     call s:error("Can't find the Mercurial usage file. Auto-completion will be disabled in Lawrencium.")
 endif
 
+" Include the command file type mappings.
+let s:file_type_mappings = expand("<sfile>:h:h") . '/resources/hg_command_file_types.vim'
+if filereadable(s:file_type_mappings)
+    execute "source " . s:file_type_mappings
+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')
@@ -351,15 +380,11 @@
     " 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.
@@ -376,6 +401,15 @@
         return s:ListRepoFiles(a:ArgLead, a:CmdLine, a:CursorPos)
 endfunction
 
+function! s:GetHgCommandName(args) abort
+    for l:a in a:args
+        if stridx(l:a, '-') != 0
+            return l:a
+        endif
+    endfor
+    return ''
+endfunction
+
 call s:AddMainCommand("-bang -complete=customlist,s:CompleteHg -nargs=* Hg :call s:Hg(<bang>0, <f-args>)")
 
 " }}}
@@ -390,18 +424,17 @@
         echo "Nothing modified."
     endif
 
-    " Open a new temp buffer in the preview window, jump to it,
+    " Open a new temp buffer in a new window, jump to it,
     " and paste the `hg status` output in there.
     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)
-    execute "pedit " . l:temp_file
-    wincmd P
+    split
+    execute "setlocal winfixheight"
+    execute "setlocal winheight=" . (len(l:status_lines) + 1)
+    execute "resize " . (len(l:status_lines) + 1)
+    execute "edit " . l:temp_file
     call append(0, l:status_lines)
     call cursor(1, 1)
-    " Make it a nice size. 
-    execute "setlocal previewheight=" . l:preview_height
     " Make sure it's deleted when we exit the window.
     setlocal bufhidden=delete
     
@@ -451,7 +484,6 @@
     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)
@@ -889,16 +921,17 @@
         let l:filepath = a:1
     endif
 
-    " Get the repo and get the command.
+    " Get the repo and run the command.
     let l:repo = s:hg_repo()
-    let l:log_command = l:repo.GetCommand('log', l:filepath, '--template', '"{rev}\t{desc|firstline}\n"')
+    let l:output = l:repo.RunCommand('log', l:filepath, '--template', '"{rev}\t{desc|firstline}\n"')
 
     " Open a new temp buffer in the preview window, jump to it,
     " and paste the `hg log` output in there.
     let l:temp_file = s:tempname('hg-log-', '.txt')
     execute "pedit " . l:temp_file
     wincmd P
-    execute "read !" . escape(l:log_command, '%#\')
+    call append(0, split(l:output, '\n'))
+    call cursor(1, 1)
 
     " Setup the buffer correctly: readonly, and with the correct repo linked
     " to it, and deleted on close.
@@ -918,8 +951,51 @@
         nnoremap <buffer> <silent> q     :bdelete!<cr>
     endif
 
-    " Make sure the file is deleted with the buffer.
-    autocmd BufDelete <buffer> call s:clean_tempfile(expand('<afile>:p'))
+    " Clean up when the log buffer is deleted.
+    autocmd BufDelete <buffer> call s:HgLog_Delete(expand('<afile>:p'))
+endfunction
+
+function! s:HgLog_Delete(path)
+    let l:orignr = winnr()
+    let l:origpath = b:mercurial_logged_file
+    " Delete any other buffer opened by this log.
+    " (buffers with Lawrencium paths that match this repo and filename)
+    for nr in range(1, winnr('$'))
+        let l:br = winbufnr(nr)
+        let l:bpath = bufname(l:br)
+        let l:bpath_comps = s:parse_lawrencium_path(l:bpath)
+        if l:bpath_comps['root'] != ''
+            let l:bpath_root = s:normalizepath(l:bpath_comps['root'])
+            let l:bpath_path = s:normalizepath(l:bpath_comps['path'])
+            if l:bpath_root == b:mercurial_dir && l:bpath_path == b:mercurial_logged_file
+                " Go to that window and switch to the previous buffer
+                " from the buffer with the file revision.
+                " Just switching away should delete the buffer since it
+                " has `bufhidden=delete`.
+                echom "Found buffer in window: ".nr
+                execute nr . 'wincmd w'
+                let l:altbufname = s:shellslash(bufname('#'))
+                if l:altbufname =~# '\v^lawrencium://'
+                    " This is a special Lawrencium buffer... it could be
+                    " a previously shown revision of the file opened with
+                    " this very `Hglog`, which we don't want to switch to.
+                    " Let's just default to editing the original file
+                    " again... not sure what else to do here.
+                    execute 'edit ' . l:origpath
+                else
+                    bprevious
+                endif
+            endif
+        endif
+    endfor
+    " Restore the current window if we switched away.
+    let l:curnr = winnr()
+    if l:curnr != l:orignr
+        execute l:orignr . 'wincmd w'
+    endif
+    
+    " Delete the temp file if it was created somehow.
+    call s:clean_tempfile(a:path)
 endfunction
 
 function! s:HgLog_FileRevEdit(...)
@@ -931,7 +1007,11 @@
         let l:rev = s:HgLog_GetSelectedRev()
     endif
     let l:path = 'lawrencium://' . b:mercurial_dir . '@' . b:mercurial_logged_file . '//' . l:rev
+    let l:path = fnameescape(l:path)
+    wincmd p
     execute 'edit ' . l:path
+    setlocal bufhidden=delete
+    setlocal buftype=nofile
 endfunction
 
 function! s:HgLog_GetSelectedRev() abort
@@ -961,12 +1041,16 @@
     else
         execute 'read !' . escape(l:repo.GetCommand('cat', '-r', l:comps['rev'], l:comps['path']), '%#\')
     endif
+    setlocal readonly
+    setlocal nomodifiable
+    setlocal nomodified
+    setlocal bufhidden=delete
     return ''
 endfunction
 
 augroup lawrencium_files
   autocmd!
-  autocmd BufReadCmd  lawrencium://**//[0-9a-f][0-9a-f]* exe s:ReadFileRevision(expand('<amatch>'))
+  autocmd BufReadCmd  lawrencium://**//[0-9a-f]* exe s:ReadFileRevision(expand('<amatch>'))
 augroup END
 
 " }}}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/hg_command_file_types.vim	Thu Nov 08 13:58:29 2012 -0800
@@ -0,0 +1,9 @@
+" LAWRENCIUM - MERCURIAL COMMANDS FILE TYPES
+"
+if !exists('g:lawrencium_hg_commands_file_types')
+    g:lawrencium_hg_commands_file_types = {}
+endif
+
+let g:lawrencium_hg_commands_file_types.diff = 'diff'
+let g:lawrencium_hg_commands_file_types.glog = 'hggraphlog'
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/syntax/hggraphlog.vim	Thu Nov 08 13:58:29 2012 -0800
@@ -0,0 +1,43 @@
+" Vim syntax file
+" Language:    hg graphlog output
+" Maintainer:  Ludovic Chabant <ludovic@chabant.com>
+" Filenames:   <none>
+
+if exists("b:current_syntax")
+    finish
+endif
+
+syn case match
+
+syn match hggraphlogBranch         /^|\(\( .*\)\|$\)/he=s+1
+syn match hggraphlogBranchMerge    /^|[\\\/]/
+syn match hggraphlogNode           /^o .*/he=s+1
+
+syn match hggraphlogBranch2        / |\(\( .*\)\|$\)/he=s+2          contained containedin=hggraphlogBranch,hggraphlogNode
+syn match hggraphlogBranch2Merge   / |[\\\/]/                        contained containedin=hggraphlogBranch
+syn match hggraphlogNode2          / o .*/he=s+2                     contained containedin=hggraphlogBranch
+
+syn match hggraphlogBranch3        / | |\(\( .*\)\|$\)/ms=s+3,he=s+4 contained containedin=hggraphlogBranch2,hggraphlogNode2
+syn match hggraphlogBranch3Merge   / | |[\\\/]/ms=s+3                contained containedin=hggraphlogBranch2
+syn match hggraphlogNode3          / | o .*/ms=s+3,he=s+4            contained containedin=hggraphlogBranch2
+
+syn match hggraphlogBranch4        / | | |\(\( .*\)\|$\)/ms=s+5,he=s+6 contained containedin=hggraphlogBranch3,hggraphlogNode3
+syn match hggraphlogBranch4Merge   / | | |[\\\/]/ms=s+5                contained containedin=hggraphlogBranch3
+syn match hggraphlogNode4          / | | o .*/ms=s+5,he=s+6            contained containedin=hggraphlogBranch3
+
+syn match hggraphlogHead        /^@\s/he=e-1
+
+hi def link hggraphlogBranch    hlLevel1
+hi def link hggraphlogBranchMerge hlLevel1
+hi def link hggraphlogNode      hlLevel1
+hi def link hggraphlogBranch2   hlLevel2
+hi def link hggraphlogBranch2Merge hlLevel2
+hi def link hggraphlogNode2     hlLevel2
+hi def link hggraphlogBranch3   hlLevel3
+hi def link hggraphlogBranch3Merge hlLevel3
+hi def link hggraphlogNode3     hlLevel3
+hi def link hggraphlogBranch4   hlLevel4
+hi def link hggraphlogBranch4Merge hlLevel4
+hi def link hggraphlogNode4     hlLevel4
+hi def link hggraphlogHead      PreProc
+