Split plugin file into multiple extensions.
A => autoload/lawrencium.vim +810 -0
@@ 0,0 1,810 @@ 
+
+" Path Utility {{{
+
+" Strips the ending slash in a path.
+function! lawrencium#stripslash(path)
+    return fnamemodify(a:path, ':s?[/\\]$??')
+endfunction
+
+" Returns whether a path is absolute.
+function! lawrencium#isabspath(path)
+    return a:path =~# '\v^(\w\:)?[/\\]'
+endfunction
+
+" Normalizes the slashes in a path.
+function! lawrencium#normalizepath(path)
+    if exists('+shellslash') && &shellslash
+        return substitute(a:path, '\v/', '\\', 'g')
+    elseif has('win32')
+        return substitute(a:path, '\v/', '\\', 'g')
+    else
+        return a:path
+    endif
+endfunction
+
+" Shell-slashes the path (opposite of `normalizepath`).
+function! lawrencium#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! lawrencium#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
+
+" Delete a temporary file if it exists.
+function! lawrencium#clean_tempfile(path)
+    if filewritable(a:path)
+        call lawrencium#trace("Cleaning up temporary file: " . a:path)
+        call delete(a:path)
+    endif
+endfunction
+
+" }}}
+
+" Logging {{{
+
+" Prints a message if debug tracing is enabled.
+function! lawrencium#trace(message, ...)
+   if g:lawrencium_trace || (a:0 && a:1)
+       let l:message = "lawrencium: " . a:message
+       echom l:message
+   endif
+endfunction
+
+" Prints an error message with 'lawrencium error' prefixed to it.
+function! lawrencium#error(message)
+    echom "lawrencium error: " . a:message
+endfunction
+
+" Throw a Lawrencium exception message.
+function! lawrencium#throw(message)
+    let v:errmsg = "lawrencium: " . a:message
+    throw v:errmsg
+endfunction
+
+" }}}
+
+" Repositories {{{
+
+" Finds the repository root given a path inside that repository.
+" Throw an error if not repository is found.
+function! lawrencium#find_repo_root(path)
+    let l:path = lawrencium#stripslash(a:path)
+    let l:previous_path = ""
+    while l:path != l:previous_path
+        if isdirectory(l:path . '/.hg')
+            return lawrencium#normalizepath(simplify(fnamemodify(l:path, ':p')))
+        endif
+        let l:previous_path = l:path
+        let l:path = fnamemodify(l:path, ':h')
+    endwhile
+    call lawrencium#throw("No Mercurial repository found above: " . a:path)
+endfunction
+
+" Given a Lawrencium path (e.g: 'lawrencium:///repo/root_dir//foo/bar/file.py//rev=34'), extract
+" the repository root, relative file path and revision number/changeset ID.
+"
+" If a second argument exists, it must be:
+" - `relative`: to make the file path relative to the repository root.
+" - `absolute`: to make the file path absolute.
+"
+function! lawrencium#parse_lawrencium_path(lawrencium_path, ...)
+    let l:repo_path = lawrencium#shellslash(a:lawrencium_path)
+    let l:repo_path = substitute(l:repo_path, '\\ ', ' ', 'g')
+    if l:repo_path =~? '\v^lawrencium://'
+        let l:repo_path = strpart(l:repo_path, strlen('lawrencium://'))
+    endif
+
+    let l:root_dir = ''
+    let l:at_idx = stridx(l:repo_path, '//')
+    if l:at_idx >= 0
+        let l:root_dir = strpart(l:repo_path, 0, l:at_idx)
+        let l:repo_path = strpart(l:repo_path, l:at_idx + 2)
+    endif
+
+    let l:value = ''
+    let l:action = ''
+    let l:actionidx = stridx(l:repo_path, '//')
+    if l:actionidx >= 0
+        let l:action = strpart(l:repo_path, l:actionidx + 2)
+        let l:repo_path = strpart(l:repo_path, 0, l:actionidx)
+
+        let l:equalidx = stridx(l:action, '=')
+        if l:equalidx >= 0
+            let l:value = strpart(l:action, l:equalidx + 1)
+            let l:action = strpart(l:action, 0, l:equalidx)
+        endif
+    endif
+
+    if a:0 > 0
+        execute 'cd! ' . fnameescape(l:root_dir)
+        if a:1 == 'relative'
+            let l:repo_path = fnamemodify(l:repo_path, ':.')
+        elseif a:1 == 'absolute'
+            let l:repo_path = fnamemodify(l:repo_path, ':p')
+        endif
+        execute 'cd! -'
+    endif
+    
+    let l:result = { 'root': l:root_dir, 'path': l:repo_path, 'action': l:action, 'value': l:value }
+    return l:result
+endfunction
+
+" Clean up all the 'HG:' lines from a commit message, and see if there's
+" any message left (Mercurial does this automatically, usually, but
+" apparently not when you feed it a log file...).
+function! lawrencium#clean_commit_file(log_file) abort
+    let l:lines = readfile(a:log_file)
+    call filter(l:lines, "v:val !~# '\\v^HG:'")
+    if len(filter(copy(l:lines), "v:val !~# '\\v^\\s*$'")) == 0
+        return 0
+    endif
+    call writefile(l:lines, a:log_file)
+    return 1
+endfunction
+
+" }}}
+
+" Vim Utility {{{
+
+" Finds a window whose displayed buffer has a given variable
+" set to the given value.
+function! lawrencium#find_buffer_window(varname, varvalue) abort
+    for wnr in range(1, winnr('$'))
+        let l:bnr = winbufnr(wnr)
+        if getbufvar(l:bnr, a:varname) == a:varvalue
+            return l:wnr
+        endif
+    endfor
+    return -1
+endfunction
+
+" Opens a buffer in a way that makes it easy to delete it later:
+" - if the about-to-be previous buffer doesn't have a given variable,
+"   just open the new buffer.
+" - if the about-to-be previous buffer has a given variable, open the
+"   new buffer with the `keepalt` option to make it so that the
+"   actual previous buffer (returned by things like `bufname('#')`)
+"   is the original buffer that was there before the first deletable
+"   buffer was opened.
+function! lawrencium#edit_deletable_buffer(varname, varvalue, path) abort
+    let l:edit_cmd = 'edit '
+    if getbufvar('%', a:varname) != ''
+        let l:edit_cmd = 'keepalt edit '
+    endif
+    execute l:edit_cmd . fnameescape(a:path)
+    call setbufvar('%', a:varname, a:varvalue)
+endfunction
+
+" Deletes all buffers that have a given variable set to a given value.
+" For each buffer, if it is not shown in any window, it will be just deleted.
+" If it is shown in a window, that window will be switched to the alternate
+" buffer before the buffer is deleted, unless the `lawrencium_quit_on_delete`
+" variable is set to `1`, in which case the window is closed too.
+function! lawrencium#delete_dependency_buffers(varname, varvalue) abort
+    let l:cur_winnr = winnr()
+    for bnr in range(1, bufnr('$'))
+        if getbufvar(bnr, a:varname) == a:varvalue
+            " Delete this buffer if it is not shown in any window.
+            " Otherwise, display the alternate buffer before deleting
+            " it so the window is not closed.
+            let l:bwnr = bufwinnr(bnr)
+            if l:bwnr < 0 || getbufvar(bnr, 'lawrencium_quit_on_delete') == 1
+                if bufloaded(l:bnr)
+                    call lawrencium#trace("Deleting dependency buffer " . bnr)
+                    execute "bdelete! " . bnr
+                else
+                    call lawrencium#trace("Dependency buffer " . bnr . " is already unladed.")
+                endif
+            else
+                execute l:bwnr . "wincmd w"
+                " TODO: better handle case where there's no previous/alternate buffer?
+                let l:prev_bnr = bufnr('#')
+                if l:prev_bnr > 0 && bufloaded(l:prev_bnr)
+                    execute "buffer " . l:prev_bnr
+                    if bufloaded(l:bnr)
+                        call lawrencium#trace("Deleting dependency buffer " . bnr . " after switching to " . l:prev_bnr . " in window " . l:bwnr)
+                        execute "bdelete! " . bnr
+                    else
+                        call lawrencium#trace("Dependency buffer " . bnr . " is unladed after switching to " . l:prev_bnr)
+                    endif
+                else
+                    call lawrencium#trace("Deleting dependency buffer " . bnr . " and window.")
+                    bdelete!
+                endif
+            endif
+        endif
+    endfor
+    if l:cur_winnr != winnr()
+        call lawrencium#trace("Returning to window " . l:cur_winnr)
+        execute l:cur_winnr . "wincmd w"
+    endif
+endfunction
+
+" }}}
+
+" Mercurial Repository Object {{{
+
+" Let's define a Mercurial repo 'class' using prototype-based object-oriented
+" programming.
+"
+" The prototype dictionary.
+let s:HgRepo = {}
+
+" Constructor.
+function! s:HgRepo.New(path) abort
+    let l:newRepo = copy(self)
+    let l:newRepo.root_dir = lawrencium#find_repo_root(a:path)
+    call lawrencium#trace("Built new Mercurial repository object at : " . l:newRepo.root_dir)
+    return l:newRepo
+endfunction
+
+" Gets a full path given a repo-relative path.
+function! s:HgRepo.GetFullPath(path) abort
+    let l:root_dir = self.root_dir
+    if lawrencium#isabspath(a:path)
+        call lawrencium#throw("Expected relative path, got absolute path: " . a:path)
+    endif
+    return lawrencium#normalizepath(l:root_dir . a:path)
+endfunction
+
+" Gets a repo-relative path given any path.
+function! s:HgRepo.GetRelativePath(path) abort
+    execute 'lcd! ' . fnameescape(self.root_dir)
+    let l:relative_path = fnamemodify(a:path, ':.')
+    execute 'lcd! -'
+    return l:relative_path
+endfunction
+
+" Gets, and optionally creates, a temp folder for some operation in the `.hg`
+" directory.
+function! s:HgRepo.GetTempDir(path, ...) abort
+    let l:tmp_dir = self.GetFullPath('.hg/lawrencium/' . a:path)
+    if !isdirectory(l:tmp_dir)
+        if a:0 > 0 && !a:1
+            return ''
+        endif
+        call mkdir(l:tmp_dir, 'p')
+    endif
+    return l:tmp_dir
+endfunction
+
+" Gets a list of files matching a root-relative pattern.
+" If a flag is passed and is TRUE, a slash will be appended to all
+" directories.
+function! s:HgRepo.Glob(pattern, ...) abort
+    let l:root_dir = self.root_dir
+    if (a:pattern =~# '\v^[/\\]')
+        let l:root_dir = lawrencium#stripslash(l:root_dir)
+    endif
+    let l:matches = split(glob(l:root_dir . a:pattern), '\n')
+    if a:0 && a:1
+        for l:idx in range(len(l:matches))
+            if !filereadable(l:matches[l:idx])
+                let l:matches[l:idx] = l:matches[l:idx] . '/'
+            endif
+        endfor
+    endif
+    let l:strip_len = len(l:root_dir)
+    call map(l:matches, 'v:val[l:strip_len : -1]')
+    return l:matches
+endfunction
+
+" Gets a full Mercurial command.
+function! s:HgRepo.GetCommand(command, ...) abort
+    " If there's only one argument, and it's a list, then use that as the
+    " argument list.
+    let l:arg_list = a:000
+    if a:0 == 1 && type(a:1) == type([])
+        let l:arg_list = a:1
+    endif
+    let l:prev_shellslash = &shellslash
+    setlocal noshellslash
+    let l:hg_command = g:lawrencium_hg_executable . ' --repository ' . shellescape(lawrencium#stripslash(self.root_dir))
+    let l:hg_command = l:hg_command . ' ' . a:command
+    for l:arg in l:arg_list
+        let l:hg_command = l:hg_command . ' ' . shellescape(l:arg)
+    endfor
+    if l:prev_shellslash
+        setlocal shellslash
+    endif
+    return l:hg_command
+endfunction
+
+" Runs a Mercurial command in the repo.
+function! s:HgRepo.RunCommand(command, ...) abort
+    let l:all_args = [1, a:command] + a:000
+    return call(self['RunCommandEx'], l:all_args, self)
+endfunction
+
+function! s:HgRepo.RunCommandEx(plain_mode, command, ...) abort
+    let l:prev_hgplain = $HGPLAIN
+    if a:plain_mode
+        let $HGPLAIN = 'true'
+    endif
+    let l:all_args = [a:command] + a:000
+    let l:hg_command = call(self['GetCommand'], l:all_args, self)
+    call lawrencium#trace("Running Mercurial command: " . l:hg_command)
+    let l:cmd_out = system(l:hg_command)
+    if a:plain_mode
+        let $HGPLAIN = l:prev_hgplain
+    endif
+    return l:cmd_out
+endfunction
+
+" Runs a Mercurial command in the repo and reads its output into the current
+" buffer.
+function! s:HgRepo.ReadCommandOutput(command, ...) abort
+    function! s:PutOutputIntoBuffer(command_line)
+        let l:was_buffer_empty = (line('$') == 1 && getline(1) == '')
+        execute '0read!' . escape(a:command_line, '%#\')
+        if l:was_buffer_empty  " (Always true?)
+            " '0read' inserts before the cursor, leaving a blank line which
+            " needs to be deleted... but if there are folds in this thing, we
+            " must open them all first otherwise we could delete the whole
+            " contents of the last fold (since Vim may close them all by
+            " default).
+            normal! zRG"_dd
+        endif
+    endfunction
+
+    let l:all_args = [a:command] + a:000
+    let l:hg_command = call(self['GetCommand'], l:all_args, self)
+    call lawrencium#trace("Running Mercurial command: " . l:hg_command)
+    call s:PutOutputIntoBuffer(l:hg_command)
+endfunction
+
+" Build a Lawrencium path for the given file and action.
+" By default, the given path will be made relative to the repository root,
+" unless '0' is passed as the 4th argument.
+function! s:HgRepo.GetLawrenciumPath(path, action, value, ...) abort
+    let l:path = a:path
+    if a:0 == 0 || !a:1
+        let l:path = self.GetRelativePath(a:path)
+    endif
+    let l:path = fnameescape(l:path)
+    let l:result = 'lawrencium://' . lawrencium#stripslash(self.root_dir) . '//' . l:path
+    if a:action !=? ''
+        let l:result  = l:result . '//' . a:action
+        if a:value !=? ''
+            let l:result = l:result . '=' . a:value
+        endif
+    endif
+    return l:result
+endfunction
+
+" Repo cache map.
+let s:buffer_repos = {}
+
+" Get a cached repo.
+function! lawrencium#hg_repo(...) abort
+    " Use the given path, or the mercurial directory of the current buffer.
+    if a:0 == 0
+        if exists('b:mercurial_dir')
+            let l:path = b:mercurial_dir
+        else
+            let l:path = lawrencium#find_repo_root(expand('%:p'))
+        endif
+    else
+        let l:path = a:1
+    endif
+    " Find a cache repo instance, or make a new one.
+    if has_key(s:buffer_repos, l:path)
+        return get(s:buffer_repos, l:path)
+    else
+        let l:repo = s:HgRepo.New(l:path)
+        let s:buffer_repos[l:path] = l:repo
+        return l:repo
+    endif
+endfunction
+
+" }}}
+
+" Buffer Object {{{
+
+" The prototype dictionary.
+let s:Buffer = {}
+
+" Constructor.
+function! s:Buffer.New(number) dict abort
+    let l:newBuffer = copy(self)
+    let l:newBuffer.nr = a:number
+    let l:newBuffer.var_backup = {}
+    let l:newBuffer.cmd_names = {}
+    let l:newBuffer.on_delete = []
+    let l:newBuffer.on_winleave = []
+    let l:newBuffer.on_unload = []
+    execute 'augroup lawrencium_buffer_' . a:number
+    execute '  autocmd!'
+    execute '  autocmd BufDelete <buffer=' . a:number . '> call s:buffer_on_delete(' . a:number . ')'
+    execute 'augroup end'
+    call lawrencium#trace("Built new buffer object for buffer: " . a:number)
+    return l:newBuffer
+endfunction
+
+function! s:Buffer.GetName(...) dict abort
+    let l:name = bufname(self.nr)
+    if a:0 > 0
+        let l:name = fnamemodify(l:name, a:1)
+    endif
+    return l:name
+endfunction
+
+function! s:Buffer.GetVar(var) dict abort
+    return getbufvar(self.nr, a:var)
+endfunction
+
+function! s:Buffer.SetVar(var, value) dict abort
+    if !has_key(self.var_backup, a:var)
+        let self.var_backup[a:var] = getbufvar(self.nr, a:var)
+    endif
+    return setbufvar(self.nr, a:var, a:value)
+endfunction
+
+function! s:Buffer.RestoreVars() dict abort
+    for key in keys(self.var_backup)
+        setbufvar(self.nr, key, self.var_backup[key])
+    endfor
+endfunction
+
+function! s:Buffer.DefineCommand(name, ...) dict abort
+    if a:0 == 0
+        call lawrencium#throw("Not enough parameters for s:Buffer.DefineCommands()")
+    endif
+    if a:0 == 1
+        let l:flags = ''
+        let l:cmd = a:1
+    else
+        let l:flags = a:1
+        let l:cmd = a:2
+    endif
+    if has_key(self.cmd_names, a:name)
+        call lawrencium#throw("Command '".a:name."' is already defined in buffer ".self.nr)
+    endif
+    if bufnr('%') != self.nr
+        call lawrencium#throw("You must move to buffer ".self.nr."first before defining local commands")
+    endif
+    let self.cmd_names[a:name] = 1
+    let l:real_flags = ''
+    if type(l:flags) == type('')
+        let l:real_flags = l:flags
+    endif
+    execute 'command -buffer '.l:real_flags.' '.a:name.' '.l:cmd
+endfunction
+
+function! s:Buffer.DeleteCommand(name) dict abort
+    if !has_key(self.cmd_names, a:name)
+        call lawrencium#throw("Command '".a:name."' has not been defined in buffer ".self.nr)
+    endif
+    if bufnr('%') != self.nr
+        call lawrencium#throw("You must move to buffer ".self.nr."first before deleting local commands")
+    endif
+    execute 'delcommand '.a:name
+    call remove(self.cmd_names, a:name)
+endfunction
+
+function! s:Buffer.DeleteCommands() dict abort
+    if bufnr('%') != self.nr
+        call lawrencium#throw("You must move to buffer ".self.nr."first before deleting local commands")
+    endif
+    for name in keys(self.cmd_names)
+        execute 'delcommand '.name
+    endfor
+    let self.cmd_names = {}
+endfunction
+
+function! s:Buffer.MoveToFirstWindow() dict abort
+    let l:win_nr = bufwinnr(self.nr)
+    if l:win_nr < 0
+        if a:0 > 0 && a:1 == 0
+            return 0
+        endif
+        call lawrencium#throw("No windows currently showing buffer ".self.nr)
+    endif
+    execute l:win_nr.'wincmd w'
+    return 1
+endfunction
+
+function! s:Buffer.OnDelete(cmd) dict abort
+    call lawrencium#trace("Adding BufDelete callback for buffer " . self.nr . ": " . a:cmd)
+    call add(self.on_delete, a:cmd)
+endfunction
+
+function! s:Buffer.OnWinLeave(cmd) dict abort
+    if len(self.on_winleave) == 0
+        call lawrencium#trace("Adding BufWinLeave auto-command on buffer " . self.nr)
+        execute 'augroup lawrencium_buffer_' . self.nr . '_winleave'
+        execute '  autocmd!'
+        execute '  autocmd BufWinLeave <buffer=' . self.nr . '> call s:buffer_on_winleave(' . self.nr .')'
+        execute 'augroup end'
+    endif
+    call lawrencium#trace("Adding BufWinLeave callback for buffer " . self.nr . ": " . a:cmd)
+    call add(self.on_winleave, a:cmd)
+endfunction
+
+function! s:Buffer.OnUnload(cmd) dict abort
+    if len(self.on_unload) == 0
+        call lawrencium#trace("Adding BufUnload auto-command on buffer " . self.nr)
+        execute 'augroup lawrencium_buffer_' . self.nr . '_unload'
+        execute '  autocmd!'
+        execute '  autocmd BufUnload <buffer=' . self.nr . '> call s:buffer_on_unload(' . self.nr . ')'
+        execute 'augroup end'
+    endif
+    call lawrencium#trace("Adding BufUnload callback for buffer " . self.nr . ": " . a:cmd)
+    call add(self.on_unload, a:cmd)
+endfunction
+
+let s:buffer_objects = {}
+
+" Get a buffer instance for the specified buffer number, or the
+" current buffer if nothing is specified.
+function! lawrencium#buffer_obj(...) abort
+    let l:bufnr = a:0 ? a:1 : bufnr('%')
+    if !has_key(s:buffer_objects, l:bufnr)
+        let s:buffer_objects[l:bufnr] = s:Buffer.New(l:bufnr)
+    endif
+    return s:buffer_objects[l:bufnr]
+endfunction
+
+" Execute all the "on delete" callbacks.
+function! s:buffer_on_delete(number) abort
+    let l:bufobj = s:buffer_objects[a:number]
+    call lawrencium#trace("Calling BufDelete callbacks on buffer " . l:bufobj.nr)
+    for cmd in l:bufobj.on_delete
+        call lawrencium#trace(" [" . cmd . "]")
+        execute cmd
+    endfor
+    call lawrencium#trace("Deleted buffer object " . l:bufobj.nr)
+    call remove(s:buffer_objects, l:bufobj.nr)
+    execute 'augroup lawrencium_buffer_' . l:bufobj.nr
+    execute '  autocmd!'
+    execute 'augroup end'
+endfunction
+
+" Execute all the "on winleave" callbacks.
+function! s:buffer_on_winleave(number) abort
+    let l:bufobj = s:buffer_objects[a:number]
+    call lawrencium#trace("Calling BufWinLeave callbacks on buffer " . l:bufobj.nr)
+    for cmd in l:bufobj.on_winleave
+        call lawrencium#trace(" [" . cmd . "]")
+        execute cmd
+    endfor
+    execute 'augroup lawrencium_buffer_' . l:bufobj.nr . '_winleave'
+    execute '  autocmd!'
+    execute 'augroup end'
+endfunction
+
+" Execute all the "on unload" callbacks.
+function! s:buffer_on_unload(number) abort
+    let l:bufobj = s:buffer_objects[a:number]
+    call lawrencium#trace("Calling BufUnload callbacks on buffer " . l:bufobj.nr)
+    for cmd in l:bufobj.on_unload
+        call lawrencium#trace(" [" . cmd . "]")
+        execute cmd
+    endfor
+    execute 'augroup lawrencium_buffer_' . l:bufobj.nr . '_unload'
+    execute '  autocmd!'
+    execute 'augroup end'
+endfunction
+
+" }}}
+
+" Buffer Commands Management {{{
+
+" Store the commands for Lawrencium-enabled buffers so that we can add them in
+" batch when we need to.
+let s:main_commands = []
+
+function! lawrencium#add_command(command) abort
+    let s:main_commands += [a:command]
+endfunction
+
+function! lawrencium#define_commands()
+    for l:command in s:main_commands
+        execute 'command! -buffer ' . l:command
+    endfor
+endfunction
+
+augroup lawrencium_main
+    autocmd!
+    autocmd User Lawrencium call lawrencium#define_commands()
+augroup end
+
+" Sets up the current buffer with Lawrencium commands if it contains a file from a Mercurial repo.
+" If the file is not in a Mercurial repo, just exit silently.
+function! lawrencium#setup_buffer_commands() abort
+    call lawrencium#trace("Scanning buffer '" . bufname('%') . "' for Lawrencium setup...")
+    let l:do_setup = 1
+    if exists('b:mercurial_dir')
+        if b:mercurial_dir =~# '\v^\s*$'
+            unlet b:mercurial_dir
+        else
+            let l:do_setup = 0
+        endif
+    endif
+    try
+        let l:repo = lawrencium#hg_repo()
+    catch /^lawrencium\:/
+        return
+    endtry
+    let b:mercurial_dir = l:repo.root_dir
+    if exists('b:mercurial_dir') && l:do_setup
+        call lawrencium#trace("Setting Mercurial commands for buffer '" . bufname('%'))
+        call lawrencium#trace("  with repo : " . expand(b:mercurial_dir))
+        silent doautocmd User Lawrencium
+    endif
+endfunction
+
+" }}}
+
+" Commands Auto-Complete {{{
+
+" Auto-complete function for commands that take repo-relative file paths.
+function! lawrencium#list_repo_files(ArgLead, CmdLine, CursorPos) abort
+    let l:matches = lawrencium#hg_repo().Glob(a:ArgLead . '*', 1)
+    call map(l:matches, 'lawrencium#normalizepath(v:val)')
+    return l:matches
+endfunction
+
+" Auto-complete function for commands that take repo-relative directory paths.
+function! lawrencium#list_repo_dirs(ArgLead, CmdLine, CursorPos) abort
+    let l:matches = lawrencium#hg_repo().Glob(a:ArgLead . '*/')
+    call map(l:matches, 'lawrencium#normalizepath(v:val)')
+    return l:matches
+endfunction
+
+" }}}
+
+" Lawrencium Files {{{
+
+" Generic read
+let s:lawrencium_file_readers = {}
+let s:lawrencium_file_customoptions = {}
+
+function! lawrencium#add_reader(action, callback, ...) abort
+    if has_key(s:lawrencium_file_readers, a:action)
+        call lawrencium#throw("Lawrencium file '".a:action."' has alredy been registered.")
+    endif
+    let s:lawrencium_file_readers[a:action] = function(a:callback)
+    if a:0 && a:1
+        let s:lawrencium_file_customoptions[a:action] = 1
+    endif
+endfunction
+
+function! lawrencium#read_lawrencium_file(path) abort
+    call lawrencium#trace("Reading Lawrencium file: " . a:path)
+    let l:path_parts = lawrencium#parse_lawrencium_path(a:path)
+    if l:path_parts['root'] == ''
+        call lawrencium#throw("Can't get repository root from: " . a:path)
+    endif
+    if !has_key(s:lawrencium_file_readers, l:path_parts['action'])
+        call lawrencium#throw("No registered reader for action: " . l:path_parts['action'])
+    endif
+
+    " Call the registered reader.
+    let l:repo = lawrencium#hg_repo(l:path_parts['root'])
+    let l:full_path = l:repo.root_dir . l:path_parts['path']
+    let LawrenciumFileReader = s:lawrencium_file_readers[l:path_parts['action']]
+    call LawrenciumFileReader(l:repo, l:path_parts, l:full_path)
+
+    " Setup the new buffer.
+    if !has_key(s:lawrencium_file_customoptions, l:path_parts['action'])
+        setlocal readonly
+        setlocal nomodified
+        setlocal bufhidden=delete
+        setlocal buftype=nofile
+    endif
+    goto
+
+    " Remember the real Lawrencium path, because Vim can fuck up the slashes
+    " on Windows.
+    let b:lawrencium_path = a:path
+
+    " Remember the repo it belongs to and make
+    " the Lawrencium commands available.
+    let b:mercurial_dir = l:repo.root_dir
+    call lawrencium#define_commands()
+
+    return ''
+endfunction
+
+function! lawrencium#write_lawrencium_file(path) abort
+    call lawrencium#trace("Writing Lawrencium file: " . a:path)
+endfunction
+
+" }}}
+
+" Statusline {{{
+
+" Prints a summary of the current repo (if any) that's appropriate for
+" displaying on the status line.
+function! lawrencium#statusline(...)
+    if !exists('b:mercurial_dir')
+        return ''
+    endif
+    let l:repo = lawrencium#hg_repo()
+    let l:prefix = (a:0 > 0 ? a:1 : '')
+    let l:suffix = (a:0 > 1 ? a:2 : '')
+    let l:branch = 'default'
+    let l:branch_file = l:repo.GetFullPath('.hg/branch')
+    if filereadable(l:branch_file)
+        let l:branch = readfile(l:branch_file)[0]
+    endif
+    let l:bookmarks = ''
+    let l:bookmarks_file = l:repo.GetFullPath('.hg/bookmarks.current')
+    if filereadable(l:bookmarks_file)
+        let l:bookmarks = join(readfile(l:bookmarks_file), ', ')
+    endif
+    let l:line = l:prefix . l:branch
+    if strlen(l:bookmarks) > 0
+        let l:line = l:line . ' - ' . l:bookmarks
+    endif
+    let l:line = l:line . l:suffix
+    return l:line
+endfunction
+
+" }}}
+
+" Miscellaneous User Functions {{{
+
+" Rescans the current buffer for setting up Mercurial commands.
+" Passing '1' as the parameter enables debug traces temporarily.
+function! lawrencium#rescan(...)
+    if exists('b:mercurial_dir')
+        unlet b:mercurial_dir
+    endif
+    if a:0 && a:1
+        let l:trace_backup = g:lawrencium_trace
+        let g:lawrencium_trace = 1
+    endif
+    call lawrencium#setup_buffer_commands()
+    if a:0 && a:1
+        let g:lawrencium_trace = l:trace_backup
+    endif
+endfunction
+
+" Enables/disables the debug trace.
+function! lawrencium#debugtrace(...)
+    let g:lawrencium_trace = (a:0 == 0 || (a:0 && a:1))
+    echom "Lawrencium debug trace is now " . (g:lawrencium_trace ? "enabled." : "disabled.")
+endfunction
+
+" }}}
+
+" Setup {{{
+
+function! lawrencium#init() abort
+    let s:builtin_exts = [
+                \'lawrencium#addremove',
+                \'lawrencium#annotate',
+                \'lawrencium#cat',
+                \'lawrencium#commit',
+                \'lawrencium#diff',
+                \'lawrencium#hg',
+                \'lawrencium#log',
+                \'lawrencium#mq',
+                \'lawrencium#record',
+                \'lawrencium#revert',
+                \'lawrencium#status',
+                \'lawrencium#vimutils'
+                \]
+    let s:user_exts = copy(g:lawrencium_extensions)
+    let s:exts = s:builtin_exts + s:user_exts
+    for ext in s:exts
+        call lawrencium#trace("Initializing Lawrencium extension " . ext)
+        execute ('call ' . ext . '#init()')
+    endfor
+endfunction
+
+" }}}
+

          
A => autoload/lawrencium/addremove.vim +23 -0
@@ 0,0 1,23 @@ 
+
+function! lawrencium#addremove#init() abort
+    call lawrencium#add_command("-bang -nargs=* -complete=customlist,lawrencium#list_repo_files Hgremove :call lawrencium#addremove#HgRemove(<bang>0, <f-args>)")
+endfunction
+
+function! lawrencium#addremove#HgRemove(bang, ...) abort
+    " Get the files to remove.
+    let l:filenames = a:000
+    if a:0 == 0
+        let l:filenames = [ expand('%:p') ]
+    endif
+    if a:bang
+        call insert(l:filenames, '--force', 0)
+    endif
+
+    " Get the repo and run the command.
+    let l:repo = lawrencium#hg_repo()
+    call l:repo.RunCommand('rm', l:filenames)
+
+    " Re-edit the file to see the change.
+    edit
+endfunction
+

          
A => autoload/lawrencium/annotate.vim +154 -0
@@ 0,0 1,154 @@ 
+
+function! lawrencium#annotate#init() abort
+    call lawrencium#add_command("-bang -nargs=? -complete=customlist,lawrencium#list_repo_files Hgannotate :call lawrencium#annotate#HgAnnotate(<bang>0, 0, <f-args>)")
+    call lawrencium#add_command("-bang -nargs=? -complete=customlist,lawrencium#list_repo_files Hgwannotate :call lawrencium#annotate#HgAnnotate(<bang>0, 1, <f-args>)")
+
+    call lawrencium#add_reader('annotate', 'lawrencium#annotate#read')
+endfunction
+
+function! lawrencium#annotate#read(repo, path_parts, full_path) abort
+    let l:cmd_args = ['-c', '-n', '-u', '-d', '-q']
+    if a:path_parts['value'] == 'v=1'
+        call insert(l:cmd_args, '-v', 0)
+    endif
+    call add(l:cmd_args, a:full_path)
+    call a:repo.ReadCommandOutput('annotate', l:cmd_args)
+endfunction
+
+function! lawrencium#annotate#HgAnnotate(bang, verbose, ...) abort
+    " Open the file to annotate if needed.
+    if a:0 > 0
+        call lawrencium#vimutils#HgEdit(a:bang, a:1)
+    endif
+
+    " Get the Lawrencium path for the annotated file.
+    let l:path = expand('%:p')
+    let l:bufnr = bufnr('%')
+    let l:repo = lawrencium#hg_repo()
+    let l:value = a:verbose ? 'v=1' : ''
+    let l:annotation_path = l:repo.GetLawrenciumPath(l:path, 'annotate', l:value)
+    
+    " Check if we're trying to annotate something with local changes.
+    let l:has_local_edits = 0
+    let l:path_status = l:repo.RunCommand('status', l:path)
+    if l:path_status != ''
+        call lawrencium#trace("Found local edits for '" . l:path . "'. Will annotate parent revision.")
+        let l:has_local_edits = 1
+    endif
+    
+    if l:has_local_edits
+        " Just open the output of the command.
+        echom "Local edits found, will show the annotations for the parent revision."
+        execute 'edit ' . fnameescape(l:annotation_path)
+        setlocal nowrap nofoldenable
+        setlocal filetype=hgannotate
+    else
+        " Store some info about the current buffer.
+        let l:cur_topline = line('w0') + &scrolloff
+        let l:cur_line = line('.')
+        let l:cur_wrap = &wrap
+        let l:cur_foldenable = &foldenable
+
+        " Open the annotated file in a split buffer on the left, after
+        " having disabled wrapping and folds on the current file.
+        " Make both windows scroll-bound.
+        setlocal scrollbind nowrap nofoldenable
+        execute 'keepalt leftabove vsplit ' . fnameescape(l:annotation_path)
+        setlocal nonumber
+        setlocal scrollbind nowrap nofoldenable foldcolumn=0
+        setlocal filetype=hgannotate
+
+        " When the annotated buffer is deleted, restore the settings we
+        " changed on the current buffer, and go back to that buffer.
+        let l:annotate_buffer = lawrencium#buffer_obj()
+        call l:annotate_buffer.OnDelete('execute bufwinnr(' . l:bufnr . ') . "wincmd w"')
+        call l:annotate_buffer.OnDelete('setlocal noscrollbind')
+        if l:cur_wrap
+            call l:annotate_buffer.OnDelete('setlocal wrap')
+        endif
+        if l:cur_foldenable
+            call l:annotate_buffer.OnDelete('setlocal foldenable')
+        endif
+
+        " Go to the line we were at in the source buffer when we
+        " opened the annotation window.
+        execute l:cur_topline
+        normal! zt
+        execute l:cur_line
+        syncbind
+
+        " Set the correct window width for the annotations.
+        if a:verbose
+            let l:last_token = match(getline('.'), '\v\d{4}:\s')
+            let l:token_end = 5
+        else
+            let l:last_token = match(getline('.'), '\v\d{2}:\s')
+            let l:token_end = 3
+        endif
+        if l:last_token < 0
+            echoerr "Can't find the end of the annotation columns."
+        else
+            let l:column_count = l:last_token + l:token_end + g:lawrencium_annotate_width_offset
+            execute "vertical resize " . l:column_count
+            setlocal winfixwidth
+        endif
+    endif
+
+    " Make the annotate buffer a Lawrencium buffer.
+    let b:mercurial_dir = l:repo.root_dir
+    let b:lawrencium_annotated_path = l:path
+    let b:lawrencium_annotated_bufnr = l:bufnr
+    call s:DefineMainCommands()
+
+    " Add some other nice commands and mappings.
+    command! -buffer Hgannotatediffsum :call s:HgAnnotate_DiffSummary()
+    command! -buffer Hgannotatelog     :call s:HgAnnotate_DiffSummary(1)
+    if g:lawrencium_define_mappings
+        nnoremap <buffer> <silent> <cr> :Hgannotatediffsum<cr>
+        nnoremap <buffer> <silent> <leader><cr> :Hgannotatelog<cr>
+    endif
+
+    " Clean up when the annotate buffer is deleted.
+    let l:bufobj = lawrencium#buffer_obj()
+    call l:bufobj.OnDelete('call s:HgAnnotate_Delete(' . l:bufobj.nr . ')')
+endfunction
+
+function! s:HgAnnotate_Delete(bufnr) abort
+    if g:lawrencium_auto_close_buffers
+        call s:delete_dependency_buffers('lawrencium_diff_for', a:bufnr)
+    endif
+endfunction
+
+function! s:HgAnnotate_DiffSummary(...) abort
+    " Get the path for the diff of the revision specified under the cursor.
+    let l:line = getline('.')
+    let l:rev_hash = matchstr(l:line, '\v[a-f0-9]{12}')
+    let l:log = (a:0 > 0 ? a:1 : 0)
+
+    " Get the Lawrencium path for the diff, and the buffer object for the
+    " annotation.
+    let l:repo = lawrencium#hg_repo()
+    if l:log
+      let l:path = l:repo.GetLawrenciumPath(b:lawrencium_annotated_path, 'logpatch', l:rev_hash)
+    else
+      let l:path = l:repo.GetLawrenciumPath(b:lawrencium_annotated_path, 'diff', l:rev_hash)
+    endif
+    let l:annotate_buffer = lawrencium#buffer_obj()
+
+    " Find a window already displaying diffs for this annotation.
+    let l:diff_winnr = s:find_buffer_window('lawrencium_diff_for', l:annotate_buffer.nr)
+    if l:diff_winnr == -1
+        " Not found... go back to the main source buffer and open a bottom 
+        " split with the diff for the specified revision.
+        execute bufwinnr(b:lawrencium_annotated_bufnr) . 'wincmd w'
+        execute 'rightbelow split ' . fnameescape(l:path)
+        let b:lawrencium_diff_for = l:annotate_buffer.nr
+        let b:lawrencium_quit_on_delete = 1
+    else
+        " Found! Use that window to open the diff.
+        execute l:diff_winnr . 'wincmd w'
+        execute 'edit ' . fnameescape(l:path)
+        let b:lawrencium_diff_for = l:annotate_buffer.nr
+    endif
+endfunction
+

          
A => autoload/lawrencium/cat.vim +14 -0
@@ 0,0 1,14 @@ 
+
+function! lawrencium#cat#init() abort
+    call lawrencium#add_reader('rev', 'lawrencium#cat#read')
+endfunction
+
+function! lawrencium#cat#read(repo, path_parts, full_path) abort
+    let l:rev = a:path_parts['value']
+    if l:rev == ''
+        call a:repo.ReadCommandOutput('cat', a:full_path)
+    else
+        call a:repo.ReadCommandOutput('cat', '-r', l:rev, a:full_path)
+    endif
+endfunction
+

          
A => autoload/lawrencium/commit.vim +141 -0
@@ 0,0 1,141 @@ 
+
+function! lawrencium#commit#init() abort
+    call lawrencium#add_command("-bang -nargs=* -complete=customlist,lawrencium#list_repo_files Hgcommit :call lawrencium#commit#HgCommit(<bang>0, 0, 0, <f-args>)")
+    call lawrencium#add_command("-bang -nargs=* -complete=customlist,lawrencium#list_repo_files Hgvcommit :call lawrencium#commit#HgCommit(<bang>0, 1, 0, <f-args>)")
+endfunction
+
+function! lawrencium#commit#HgCommit(bang, vertical, callback, ...) abort
+    " Get the repo we'll be committing into.
+    let l:repo = lawrencium#hg_repo()
+
+    " Get the list of files to commit.
+    " It can either be several files passed as extra parameters, or an
+    " actual list passed as the first extra parameter.
+    let l:filenames = []
+    if a:0
+        let l:filenames = a:000
+        if a:0 == 1 && type(a:1) == type([])
+            let l:filenames = a:1
+        endif
+    endif
+
+    " Open a commit message file.
+    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, ['', ''])
+    call append(2, split(s:HgCommit_GenerateMessage(l:repo, l:filenames), '\n'))
+    call cursor(1, 1)
+
+    " Setup the auto-command that will actually commit on write/exit,
+    " and make the buffer delete itself on exit.
+    let b:mercurial_dir = l:repo.root_dir
+    let b:lawrencium_commit_files = l:filenames
+    if type(a:callback) == type([])
+        let b:lawrencium_commit_pre_callback = a:callback[0]
+        let b:lawrencium_commit_post_callback = a:callback[1]
+        let b:lawrencium_commit_abort_callback = a:callback[2]
+    else
+        let b:lawrencium_commit_pre_callback = 0
+        let b:lawrencium_commit_post_callback = a:callback
+        let b:lawrencium_commit_abort_callback = 0
+    endif
+    setlocal bufhidden=delete
+    setlocal filetype=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)
+    endif
+    " Make commands available.
+    call lawrencium#define_commands()
+endfunction
+
+let s:hg_status_messages = { 
+    \'M': 'modified',
+    \'A': 'added',
+    \'R': 'removed',
+    \'C': 'clean',
+    \'!': 'missing',
+    \'?': 'not tracked',
+    \'I': 'ignored',
+    \' ': '',
+    \}
+
+function! s:HgCommit_GenerateMessage(repo, filenames) abort
+    let l:msg  = "HG: Enter commit message. Lines beginning with 'HG:' are removed.\n"
+    let l:msg .= "HG: Leave message empty to abort commit.\n"
+    let l:msg .= "HG: Write and quit buffer to proceed.\n"
+    let l:msg .= "HG: --\n"
+    let l:msg .= "HG: user: " . split(a:repo.RunCommand('showconfig ui.username'), '\n')[0] . "\n"
+    let l:msg .= "HG: branch '" . split(a:repo.RunCommand('branch'), '\n')[0] . "'\n"
+
+    execute 'lcd ' . fnameescape(a:repo.root_dir)
+    if len(a:filenames)
+        let l:status_lines = split(a:repo.RunCommand('status', a:filenames), "\n")
+    else
+        let l:status_lines = split(a:repo.RunCommand('status'), "\n")
+    endif
+    for l:line in l:status_lines
+        if l:line ==# ''
+            continue
+        endif
+        let l:type = matchstr(l:line, '\v^[MARC\!\?I ]')
+        let l:path = l:line[2:]
+        let l:msg .= "HG: " . s:hg_status_messages[l:type] . ' ' . l:path . "\n"
+    endfor
+
+    return l:msg
+endfunction
+
+function! s:HgCommit_Execute(log_file, show_output) abort
+    " Check if the user actually saved a commit message.
+    if !filereadable(a:log_file)
+        call lawrencium#error("abort: Commit message not saved")
+        if exists('b:lawrencium_commit_abort_callback') &&
+                    \type(b:lawrencium_commit_abort_callback) == type("") &&
+                    \b:lawrencium_commit_abort_callback != ''
+            call lawrencium#trace("Executing abort callback: ".b:lawrencium_commit_abort_callback)
+            execute b:lawrencium_commit_abort_callback
+        endif
+        return
+    endif
+
+    " Execute a pre-callback if there is one.
+    if exists('b:lawrencium_commit_pre_callback') &&
+                \type(b:lawrencium_commit_pre_callback) == type("") &&
+                \b:lawrencium_commit_pre_callback != ''
+        call lawrencium#trace("Executing pre callback: ".b:lawrencium_commit_pre_callback)
+        execute b:lawrencium_commit_pre_callback
+    endif
+
+    call lawrencium#trace("Committing with log file: " . a:log_file)
+
+    " Clean all the 'HG: ' lines.
+    let l:is_valid = s:clean_commit_file(a:log_file)
+    if !l:is_valid
+        call lawrencium#error("abort: Empty commit message")
+        return
+    endif
+
+    " Get the repo and commit with the given message.
+    let l:repo = lawrencium#hg_repo()
+    let l:hg_args = ['-l', a:log_file]
+    call extend(l:hg_args, b:lawrencium_commit_files)
+    let l:output = l:repo.RunCommand('commit', l:hg_args)
+    if a:show_output && l:output !~# '\v%^\s*%$'
+        call lawrencium#trace("Output from hg commit:", 1)
+        for l:output_line in split(l:output, '\n')
+            echom l:output_line
+        endfor
+    endif
+
+    " Execute a post-callback if there is one.
+    if exists('b:lawrencium_commit_post_callback') &&
+                \type(b:lawrencium_commit_post_callback) == type("") &&
+                \b:lawrencium_commit_post_callback != ''
+        call lawrencium#trace("Executing post callback: ".b:lawrencium_commit_post_callback)
+        execute b:lawrencium_commit_post_callback
+    endif
+endfunction
+

          
A => autoload/lawrencium/diff.vim +292 -0
@@ 0,0 1,292 @@ 
+
+function! lawrencium#diff#init() abort
+    call lawrencium#add_command("-nargs=* Hgdiff :call lawrencium#diff#HgDiff('%:p', 0, <f-args>)")
+    call lawrencium#add_command("-nargs=* Hgvdiff :call lawrencium#diff#HgDiff('%:p', 1, <f-args>)")
+    call lawrencium#add_command("-nargs=* Hgtabdiff :call lawrencium#diff#HgDiff('%:p', 2, <f-args>)")
+
+    call lawrencium#add_command("-nargs=* Hgdiffsum       :call lawrencium#diff#HgDiffSummary('%:p', 0, <f-args>)")
+    call lawrencium#add_command("-nargs=* Hgdiffsumsplit  :call lawrencium#diff#HgDiffSummary('%:p', 1, <f-args>)")
+    call lawrencium#add_command("-nargs=* Hgvdiffsumsplit :call lawrencium#diff#HgDiffSummary('%:p', 2, <f-args>)")
+    call lawrencium#add_command("-nargs=* Hgtabdiffsum    :call lawrencium#diff#HgDiffSummary('%:p', 3, <f-args>)")
+
+    call lawrencium#add_reader('diff', 'lawrencium#diff#read')
+endfunction
+
+function! lawrencium#diff#read(repo, path_parts, full_path) abort
+    let l:diffargs = []
+    let l:commaidx = stridx(a:path_parts['value'], ',')
+    if l:commaidx > 0
+        let l:rev1 = strpart(a:path_parts['value'], 0, l:commaidx)
+        let l:rev2 = strpart(a:path_parts['value'], l:commaidx + 1)
+        if l:rev1 == '-'
+            let l:diffargs = [ '-r', l:rev2 ]
+        elseif l:rev2 == '-'
+            let l:diffargs = [ '-r', l:rev1 ]
+        else
+            let l:diffargs = [ '-r', l:rev1, '-r', l:rev2 ]
+        endif
+    elseif a:path_parts['value'] != ''
+        let l:diffargs = [ '-c', a:path_parts['value'] ]
+    else
+        let l:diffargs = []
+    endif
+    if a:path_parts['path'] != '' && a:path_parts['path'] != '.'
+        call add(l:diffargs, a:full_path)
+    endif
+    call a:repo.ReadCommandOutput('diff', l:diffargs)
+    setlocal filetype=diff
+    setlocal nofoldenable
+endfunction
+
+function! lawrencium#diff#HgDiff(filename, split, ...) abort
+    " Default revisions to diff: the working directory (null string) 
+    " and the parent of the working directory (using Mercurial's revsets syntax).
+    " Otherwise, use the 1 or 2 revisions specified as extra parameters.
+    let l:rev1 = 'p1()'
+    let l:rev2 = ''
+    if a:0 == 1
+        if type(a:1) == type([])
+            if len(a:1) >= 2
+                let l:rev1 = a:1[0]
+                let l:rev2 = a:1[1]
+            elseif len(a:1) == 1
+                let l:rev1 = a:1[0]
+            endif
+        else
+            let l:rev1 = a:1
+        endif
+    elseif a:0 == 2
+        let l:rev1 = a:1
+        let l:rev2 = a:2
+    endif
+
+    " Get the current repo, and expand the given filename in case it contains
+    " fancy filename modifiers.
+    let l:repo = lawrencium#hg_repo()
+    let l:path = expand(a:filename)
+    let l:diff_id = localtime()
+    call lawrencium#trace("Diff'ing '".l:rev1."' and '".l:rev2."' on file: ".l:path)
+
+    " Get the first file and open it.
+    let l:cleanupbufnr = -1
+    if l:rev1 == ''
+        if a:split == 2
+            " Don't use `tabedit` here because if `l:path` is the same as
+            " the current path, it will also reload the buffer in the current
+            " tab/window for some reason, which causes all state to be lost
+            " (all folds get collapsed again, cursor is moved to start, etc.)
+            tabnew
+            let l:cleanupbufnr = bufnr('%')
+            execute 'edit ' . fnameescape(l:path)
+        else
+            if bufexists(l:path)
+                execute 'buffer ' . fnameescape(l:path)
+            else
+                execute 'edit ' . fnameescape(l:path)
+            endif
+        endif
+        " Make it part of the diff group.
+        call s:HgDiff_DiffThis(l:diff_id)
+    else
+        let l:rev_path = l:repo.GetLawrenciumPath(l:path, 'rev', l:rev1)
+        if a:split == 2
+            " See comments above about avoiding `tabedit`.
+            tabnew
+            let l:cleanupbufnr = bufnr('%')
+        endif
+        execute 'edit ' . fnameescape(l:rev_path)
+        " Make it part of the diff group.
+        call s:HgDiff_DiffThis(l:diff_id)
+    endif
+    if l:cleanupbufnr >= 0 && bufloaded(l:cleanupbufnr)
+        execute 'bdelete ' . l:cleanupbufnr
+    endif
+
+    " Get the second file and open it too.
+    " Don't use `diffsplit` because it will set `&diff` before we get a chance
+    " to save a bunch of local settings that we will want to restore later.
+    let l:diffsplit = 'split'
+    if a:split >= 1
+        let l:diffsplit = 'vsplit'
+    endif
+    if l:rev2 == ''
+        execute l:diffsplit . ' ' . fnameescape(l:path)
+    else
+        let l:rev_path = l:repo.GetLawrenciumPath(l:path, 'rev', l:rev2)
+        execute l:diffsplit . ' ' . fnameescape(l:rev_path)
+    endif
+    call s:HgDiff_DiffThis(l:diff_id)
+endfunction
+
+function! lawrencium#diff#HgDiffThis(diff_id)
+    call s:HgDiff_DiffThis(a:diff_id)
+endfunction
+
+function! s:HgDiff_DiffThis(diff_id) abort
+    " Store some commands to run when we exit diff mode.
+    " It's needed because `diffoff` reverts those settings to their default
+    " values, instead of their previous ones.
+    if &diff
+        call lawrencium#throw("Calling diffthis too late on a buffer!")
+        return
+    endif
+    call lawrencium#trace('Enabling diff mode on ' . bufname('%'))
+    let w:lawrencium_diffoff = {}
+    let w:lawrencium_diffoff['&diff'] = 0
+    let w:lawrencium_diffoff['&wrap'] = &l:wrap
+    let w:lawrencium_diffoff['&scrollopt'] = &l:scrollopt
+    let w:lawrencium_diffoff['&scrollbind'] = &l:scrollbind
+    let w:lawrencium_diffoff['&cursorbind'] = &l:cursorbind
+    let w:lawrencium_diffoff['&foldmethod'] = &l:foldmethod
+    let w:lawrencium_diffoff['&foldcolumn'] = &l:foldcolumn
+    let w:lawrencium_diffoff['&foldenable'] = &l:foldenable
+    let w:lawrencium_diff_id = a:diff_id
+    diffthis
+    autocmd BufWinLeave <buffer> call s:HgDiff_CleanUp()
+endfunction
+
+function! s:HgDiff_DiffOff(...) abort
+    " Get the window name (given as a paramter, or current window).
+    let l:nr = a:0 ? a:1 : winnr()
+
+    " Run the commands we saved in `HgDiff_DiffThis`, or just run `diffoff`.
+    let l:backup = getwinvar(l:nr, 'lawrencium_diffoff')
+    if type(l:backup) == type({}) && len(l:backup) > 0
+        call lawrencium#trace('Disabling diff mode on ' . l:nr)
+        for key in keys(l:backup)
+            call setwinvar(l:nr, key, l:backup[key])
+        endfor
+        call setwinvar(l:nr, 'lawrencium_diffoff', {})
+    else
+        call lawrencium#trace('Disabling diff mode on ' . l:nr . ' (but no true restore)')
+        diffoff
+    endif
+endfunction
+
+function! s:HgDiff_GetDiffWindows(diff_id) abort
+    let l:result = []
+    for nr in range(1, winnr('$'))
+        if getwinvar(nr, '&diff') && getwinvar(nr, 'lawrencium_diff_id') == a:diff_id
+            call add(l:result, nr)
+        endif
+    endfor
+    return l:result
+endfunction
+
+function! s:HgDiff_CleanUp() abort
+    " If we're not leaving one of our diff window, do nothing.
+    if !&diff || !exists('w:lawrencium_diff_id')
+        return
+    endif
+
+    " If there will be only one diff window left (plus the one we're leaving),
+    " turn off diff in it and restore its local settings.
+    let l:nrs = s:HgDiff_GetDiffWindows(w:lawrencium_diff_id)
+    if len(l:nrs) <= 2
+        call lawrencium#trace('Disabling diff mode in ' . len(l:nrs) . ' windows.')
+        for nr in l:nrs
+            if getwinvar(nr, '&diff')
+                call s:HgDiff_DiffOff(nr)
+            endif
+        endfor
+    else
+        call lawrencium#trace('Still ' . len(l:nrs) . ' diff windows open.')
+    endif
+endfunction
+
+function! lawrencium#diff#HgDiffSummary(filename, present_args, ...) abort
+    " Default revisions to diff: the working directory (null string) 
+    " and the parent of the working directory (using Mercurial's revsets syntax).
+    " Otherwise, use the 1 or 2 revisions specified as extra parameters.
+    let l:revs = ''
+    if a:0 == 1
+        if type(a:1) == type([])
+            if len(a:1) >= 2
+                let l:revs = a:1[0] . ',' . a:1[1]
+            elseif len(a:1) == 1
+                let l:revs = a:1[0]
+            endif
+        else
+            let l:revs = a:1
+        endif
+    elseif a:0 >= 2
+        let l:revs = a:1 . ',' . a:2
+    endif
+
+    " Get the current repo, and expand the given filename in case it contains
+    " fancy filename modifiers.
+    let l:repo = lawrencium#hg_repo()
+    let l:path = expand(a:filename)
+    call lawrencium#trace("Diff'ing revisions: '".l:revs."' on file: ".l:path)
+    let l:special = l:repo.GetLawrenciumPath(l:path, 'diff', l:revs)
+
+    " Build the correct edit command, and switch to the correct window, based
+    " on the presentation arguments we got.
+    if type(a:present_args) == type(0)
+        " Just got a split mode.
+        let l:valid_args = {'split_mode': a:present_args}
+    else
+        " Got complex args.
+        let l:valid_args = a:present_args
+    endif
+
+    " First, see if we should reuse an existing window based on some buffer
+    " variable.
+    let l:target_winnr = -1
+    let l:split = get(l:valid_args, 'split_mode', 0)
+    let l:reuse_id = get(l:valid_args, 'reuse_id', '')
+    let l:avoid_id = get(l:valid_args, 'avoid_win', -1)
+    if l:reuse_id != ''
+        let l:target_winnr = lawrencium#find_buffer_window(l:reuse_id, 1)
+        if l:target_winnr > 0 && l:split != 3
+            " Unless we'll be opening in a new tab, don't split anymore, since
+            " we found the exact window we wanted.
+            let l:split = 0
+        endif
+        call lawrencium#trace("Looking for window with '".l:reuse_id."', found: ".l:target_winnr)
+    endif
+    " If we didn't find anything, see if we should use the current or previous
+    " window.
+    if l:target_winnr <= 0
+        let l:use_prev_win = get(l:valid_args, 'use_prev_win', 0)
+        if l:use_prev_win
+            let l:target_winnr = winnr('#')
+            call lawrencium#trace("Will use previous window: ".l:target_winnr)
+        endif
+    endif
+    " And let's see if we have a window we should actually avoid.
+    if l:avoid_id >= 0 && 
+                \(l:target_winnr == l:avoid_id ||
+                \(l:target_winnr <= 0 && winnr() == l:avoid_id))
+        for wnr in range(1, winnr('$'))
+            if wnr != l:avoid_id
+                call lawrencium#trace("Avoiding using window ".l:avoid_id.
+                            \", now using: ".wnr)
+                let l:target_winnr = wnr
+                break
+            endif
+        endfor
+    endif
+    " Now let's see what kind of split we want to use, if any.
+    let l:cmd = 'edit '
+    if l:split == 1
+        let l:cmd = 'rightbelow split '
+    elseif l:split == 2
+        let l:cmd = 'rightbelow vsplit '
+    elseif l:split == 3
+        let l:cmd = 'tabedit '
+    endif
+    
+    " All good now, proceed.
+    if l:target_winnr > 0
+        execute l:target_winnr . "wincmd w"
+    endif
+    execute 'keepalt ' . l:cmd . fnameescape(l:special)
+
+    " Set the reuse ID if we had one.
+    if l:reuse_id != ''
+        call lawrencium#trace("Setting reuse ID '".l:reuse_id."' on buffer: ".bufnr('%'))
+        call setbufvar('%', l:reuse_id, 1)
+    endif
+endfunction
+

          
A => autoload/lawrencium/hg.vim +108 -0
@@ 0,0 1,108 @@ 
+
+function lawrencium#hg#init() abort
+    call lawrencium#add_command("-bang -complete=customlist,lawrencium#hg#CompleteHg -nargs=* Hg :call lawrencium#hg#Hg(<bang>0, <f-args>)")
+endfunction
+
+function! lawrencium#hg#Hg(bang, ...) abort
+    let l:repo = lawrencium#hg_repo()
+    if g:lawrencium_auto_cd
+        " Temporary set the current directory to the root of the repo
+        " to make auto-completed paths work magically.
+        execute 'cd! ' . fnameescape(l:repo.root_dir)
+    endif
+    let l:output = call(l:repo.RunCommandEx, [0] + a:000, l:repo)
+    if g:lawrencium_auto_cd
+        execute 'cd! -'
+    endif
+    silent doautocmd User HgCmdPost
+    if a:bang
+        " Open the output of the command in a temp file.
+        let l:temp_file = lawrencium#tempname('hg-output-', '.txt')
+        split
+        execute 'edit ' . fnameescape(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
+    endif
+endfunction
+
+" Include the generated HG usage file.
+let s:usage_file = expand("<sfile>:h:h:h") . "/resources/hg_usage.vim"
+if filereadable(s:usage_file)
+    execute "source " . fnameescape(s:usage_file)
+else
+    call lawrencium#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 " . fnameescape(s:file_type_mappings)
+endif
+
+function! lawrencium#hg#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]+$'
+        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 && 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 lawrencium#list_repo_files(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
+

          
A => autoload/lawrencium/log.vim +203 -0
@@ 0,0 1,203 @@ 
+
+function! lawrencium#log#init() abort
+    call lawrencium#add_command("Hglogthis  :call lawrencium#log#HgLog(0, '%:p')")
+    call lawrencium#add_command("Hgvlogthis :call lawrencium#log#HgLog(1, '%:p')")
+    call lawrencium#add_command("-nargs=* -complete=customlist,lawrencium#list_repo_files Hglog  :call lawrencium#log#HgLog(0, <f-args>)")
+    call lawrencium#add_command("-nargs=* -complete=customlist,lawrencium#list_repo_files Hgvlog  :call lawrencium#log#HgLog(1, <f-args>)")
+
+    call lawrencium#add_reader("log", "lawrencium#log#read")
+    call lawrencium#add_reader("logpatch", "lawrencium#log#read_patch")
+endfunction
+
+let s:log_style_file = expand("<sfile>:h:h") . "/resources/hg_log.style"
+
+function! lawrencium#log#read(repo, path_parts, full_path) abort
+    let l:log_opts = join(split(a:path_parts['value'], ','))
+    let l:log_cmd = "log " . l:log_opts
+
+    if a:path_parts['path'] == ''
+        call a:repo.ReadCommandOutput(l:log_cmd, '--style', s:log_style_file)
+    else
+        call a:repo.ReadCommandOutput(l:log_cmd, '--style', s:log_style_file, a:full_path)
+    endif
+    setlocal filetype=hglog
+endfunction
+
+function! lawrencium#log#read_patch(repo, path_parts, full_path) abort
+    let l:log_cmd = 'log --patch --verbose --rev ' . a:path_parts['value']
+
+    if a:path_parts['path'] == ''
+        call a:repo.ReadCommandOutput(l:log_cmd)
+    else
+        call a:repo.ReadCommandOutput(l:log_cmd, a:full_path)
+    endif
+    setlocal filetype=diff
+endfunction
+
+function! lawrencium#log#HgLog(vertical, ...) abort
+    " Get the file or directory to get the log from.
+    " (empty string is for the whole repository)
+    let l:repo = lawrencium#hg_repo()
+    if a:0 > 0 && matchstr(a:1, '\v-*') == ""
+        let l:path = l:repo.GetRelativePath(expand(a:1))
+    else
+        let l:path = ''
+    endif
+
+    " Get the Lawrencium path for this `hg log`,
+    " open it in a preview window and jump to it.
+    if a:0 > 0 && l:path != ""
+      let l:log_opts = join(a:000[1:-1], ',')
+    else
+      let l:log_opts = join(a:000, ',')
+    endif
+
+    let l:log_path = l:repo.GetLawrenciumPath(l:path, 'log', l:log_opts)
+    if a:vertical
+        execute 'vertical pedit ' . fnameescape(l:log_path)
+    else
+        execute 'pedit ' . fnameescape(l:log_path)
+    endif
+    wincmd P
+
+    " Add some other nice commands and mappings.
+    let l:is_file = (l:path != '' && filereadable(l:repo.GetFullPath(l:path)))
+    command! -buffer -nargs=* Hglogdiffsum    :call s:HgLog_DiffSummary(1, <f-args>)
+    command! -buffer -nargs=* Hglogvdiffsum   :call s:HgLog_DiffSummary(2, <f-args>)
+    command! -buffer -nargs=* Hglogtabdiffsum :call s:HgLog_DiffSummary(3, <f-args>)
+    command! -buffer -nargs=+ -complete=file Hglogexport :call s:HgLog_ExportPatch(<f-args>)
+    if l:is_file
+        command! -buffer Hglogrevedit          :call s:HgLog_FileRevEdit()
+        command! -buffer -nargs=* Hglogdiff    :call s:HgLog_Diff(0, <f-args>)
+        command! -buffer -nargs=* Hglogvdiff   :call s:HgLog_Diff(1, <f-args>)
+        command! -buffer -nargs=* Hglogtabdiff :call s:HgLog_Diff(2, <f-args>)
+    endif
+
+    if g:lawrencium_define_mappings
+        nnoremap <buffer> <silent> <C-U> :Hglogdiffsum<cr>
+        nnoremap <buffer> <silent> <C-H> :Hglogvdiffsum<cr>
+        nnoremap <buffer> <silent> <cr>  :Hglogvdiffsum<cr>
+        nnoremap <buffer> <silent> q     :bdelete!<cr>
+        if l:is_file
+            nnoremap <buffer> <silent> <C-E>  :Hglogrevedit<cr>
+            nnoremap <buffer> <silent> <C-D>  :Hglogtabdiff<cr>
+            nnoremap <buffer> <silent> <C-V>  :Hglogvdiff<cr>
+        endif
+    endif
+
+    " Clean up when the log buffer is deleted.
+    let l:bufobj = lawrencium#buffer_obj()
+    call l:bufobj.OnDelete('call s:HgLog_Delete(' . l:bufobj.nr . ')')
+endfunction
+
+function! s:HgLog_Delete(bufnr)
+    if g:lawrencium_auto_close_buffers
+        call lawrencium#delete_dependency_buffers('lawrencium_diff_for', a:bufnr)
+        call lawrencium#delete_dependency_buffers('lawrencium_rev_for', a:bufnr)
+    endif
+endfunction
+
+function! s:HgLog_FileRevEdit()
+    let l:repo = lawrencium#hg_repo()
+    let l:bufobj = lawrencium#buffer_obj()
+    let l:rev = s:HgLog_GetSelectedRev()
+    let l:log_path = lawrencium#parse_lawrencium_path(l:bufobj.GetName())
+    let l:path = l:repo.GetLawrenciumPath(l:log_path['path'], 'rev', l:rev)
+
+    " Go to the window we were in before going in the log window,
+    " and open the revision there.
+    wincmd p
+    call lawrencium#edit_deletable_buffer('lawrencium_rev_for', l:bufobj.nr, l:path)
+endfunction
+
+function! s:HgLog_Diff(split, ...) abort
+    let l:revs = []
+    if a:0 >= 2
+        let l:revs = [a:1, a:2]
+    elseif a:0 == 1
+        let l:revs = ['p1('.a:1.')', a:1]
+    else
+        let l:sel = s:HgLog_GetSelectedRev()
+        let l:revs = ['p1('.l:sel.')', l:sel]
+    endif
+
+    let l:repo = lawrencium#hg_repo()
+    let l:bufobj = lawrencium#buffer_obj()
+    let l:log_path = lawrencium#parse_lawrencium_path(l:bufobj.GetName())
+    let l:path = l:repo.GetFullPath(l:log_path['path'])
+
+    " Go to the window we were in before going to the log window,
+    " and open the split diff there.
+    if a:split < 2
+        wincmd p
+    endif
+    call lawrencium#diff#HgDiff(l:path, a:split, l:revs)
+endfunction
+
+function! s:HgLog_DiffSummary(split, ...) abort
+    let l:revs = []
+    if a:0 >= 2
+        let l:revs = [a:1, a:2]
+    elseif a:0 == 1
+        let l:revs = [a:1]
+    else
+        let l:revs = [s:HgLog_GetSelectedRev()]
+    endif
+
+    let l:repo = lawrencium#hg_repo()
+    let l:bufobj = lawrencium#buffer_obj()
+    let l:log_path = lawrencium#parse_lawrencium_path(l:bufobj.GetName())
+    let l:path = l:repo.GetFullPath(l:log_path['path'])
+
+    " Go to the window we were in before going in the log window,
+    " and split for the diff summary from there.
+    let l:reuse_id = 'lawrencium_diffsum_for_' . bufnr('%')
+    let l:split_prev_win = (a:split < 3)
+    let l:args = {'reuse_id': l:reuse_id, 'use_prev_win': l:split_prev_win,
+                \'split_mode': a:split}
+    call lawrencium#diff#HgDiffSummary(l:path, l:args, l:revs)
+endfunction
+
+function! s:HgLog_GetSelectedRev(...) abort
+    if a:0 == 1
+        let l:line = getline(a:1)
+    else
+        let l:line = getline('.')
+    endif
+    " Behold, Vim's look-ahead regex syntax again! WTF.
+    let l:rev = matchstr(l:line, '\v^(\d+)(\:)@=')
+    if l:rev == ''
+        call lawrencium#throw("Can't parse revision number from line: " . l:line)
+    endif
+    return l:rev
+endfunction
+
+function! s:HgLog_ExportPatch(...) abort
+    let l:patch_name = a:1
+    if !empty($HG_EXPORT_PATCH_DIR)
+        " Use the patch dir only if user has specified a relative path
+        if has('win32')
+            let l:is_patch_relative = (matchstr(l:patch_name, '\v^([a-zA-Z]:)?\\') == "")
+        else
+            let l:is_patch_relative = (matchstr(l:patch_name, '\v^/') == "")
+        endif
+        if l:is_patch_relative
+            let l:patch_name = lawrencium#normalizepath(
+                lawrencium#stripslash($HG_EXPORT_PATCH_DIR) . "/" . l:patch_name)
+        endif
+    endif
+
+    if a:0 == 2
+        let l:rev = a:2
+    else
+        let l:rev = s:HgLog_GetSelectedRev()
+    endif
+
+    let l:repo = lawrencium#hg_repo()
+    let l:export_args = ['-o', l:patch_name, '-r', l:rev]
+
+    call l:repo.RunCommand('export', l:export_args)
+
+    echom "Created patch: " . l:patch_name
+endfunction
+

          
A => autoload/lawrencium/mq.vim +126 -0
@@ 0,0 1,126 @@ 
+
+function! lawrencium#mq#init() abort
+    call lawrencium#add_command("Hgqseries call lawrencium#mq#HgQSeries()")
+
+    call lawrencium#add_reader('qseries', "lawrencium#mq#read")
+endfunction
+
+function! lawrencium#mq#read(repo, path_parts, full_path) abort
+    let l:names = split(a:repo.RunCommand('qseries'), '\n')
+    let l:head = split(a:repo.RunCommand('qapplied', '-s'), '\n')
+    let l:tail = split(a:repo.RunCommand('qunapplied', '-s'), '\n')
+
+    let l:idx = 0
+    let l:curbuffer = bufname('%')
+    for line in l:head
+        call setbufvar(l:curbuffer, 'lawrencium_patchname_' . (l:idx + 1), l:names[l:idx])
+        call append(l:idx, "*" . line)
+        let l:idx = l:idx + 1
+    endfor
+    for line in l:tail
+        call setbufvar(l:curbuffer, 'lawrencium_patchname_' . (l:idx + 1), l:names[l:idx])
+        call append(l:idx, line)
+        let l:idx = l:idx + 1
+    endfor
+    call setbufvar(l:curbuffer, 'lawrencium_patchname_top', l:names[len(l:head) - 1])
+    set filetype=hgqseries
+endfunction
+
+function! lawrencium#mq#HgQSeries() abort
+    " Open the MQ series in the preview window and jump to it.
+    let l:repo = lawrencium#hg_repo()
+    let l:path = l:repo.GetLawrenciumPath('', 'qseries', '')
+    execute 'pedit ' . fnameescape(l:path)
+    wincmd P
+
+    " Make the series buffer a Lawrencium buffer.
+    let b:mercurial_dir = l:repo.root_dir
+    call lawrencium#define_commands()
+
+    " Add some commands and mappings.
+    command! -buffer Hgqseriesgoto                  :call s:HgQSeries_Goto()
+    command! -buffer Hgqserieseditmessage           :call s:HgQSeries_EditMessage()
+    command! -buffer -nargs=+ Hgqseriesrename       :call s:HgQSeries_Rename(<f-args>)
+    if g:lawrencium_define_mappings
+        nnoremap <buffer> <silent> <C-g> :Hgqseriesgoto<cr>
+        nnoremap <buffer> <silent> <C-e> :Hgqserieseditmessage<cr>
+        nnoremap <buffer> <silent> q     :bdelete!<cr>
+    endif
+endfunction
+
+function! s:HgQSeries_GetCurrentPatchName() abort
+    let l:pos = getpos('.')
+    return getbufvar('%', 'lawrencium_patchname_' . l:pos[1])
+endfunction
+
+function! s:HgQSeries_Goto() abort
+    let l:repo = lawrencium#hg_repo()
+    let l:patchname = s:HgQSeries_GetCurrentPatchName()
+    if len(l:patchname) == 0
+        call lawrencium#error("No patch to go to here.")
+        return
+    endif
+    call l:repo.RunCommand('qgoto', l:patchname)
+    edit
+endfunction
+
+function! s:HgQSeries_Rename(...) abort
+    let l:repo = lawrencium#hg_repo()
+    let l:current_name = s:HgQSeries_GetCurrentPatchName()
+    if len(l:current_name) == 0
+        call lawrencium#error("No patch to rename here.")
+        return
+    endif
+    let l:new_name = '"' . join(a:000, ' ') . '"'
+    call l:repo.RunCommand('qrename', l:current_name, l:new_name)
+    edit
+endfunction
+
+function! s:HgQSeries_EditMessage() abort
+    let l:repo = lawrencium#hg_repo()
+    let l:patchname = getbufvar('%', 'lawrencium_patchname_top')
+    if len(l:patchname) == 0
+        call lawrencium#error("No patch to edit here.")
+        return
+    endif
+    let l:current = split(l:repo.RunCommand('qheader', l:patchname), '\n')
+
+    " Open a temp file to write the commit message.
+    let l:temp_file = lawrencium#tempname('hg-qrefedit-', '.txt')
+    split
+    execute 'edit ' . fnameescape(l:temp_file)
+    call append(0, 'HG: Enter the new commit message for patch "' . l:patchname . '" here.\n')
+    call append(0, '')
+    call append(0, l:current)
+    call cursor(1, 1)
+
+    " Make it a temp buffer that will actually change the commit message
+    " when it is saved and closed.
+    let b:mercurial_dir = l:repo.root_dir
+    let b:lawrencium_patchname = l:patchname
+    setlocal bufhidden=delete
+    setlocal filetype=hgcommit
+    autocmd BufDelete <buffer> call s:HgQSeries_EditMessage_Execute(expand('<afile>:p'))
+
+    call lawrencium#define_commands()
+endfunction
+
+function! s:HgQSeries_EditMessage_Execute(log_file) abort
+    if !filereadable(a:log_file)
+        call lawrencium#error("abort: Commit message not saved")
+        return
+    endif
+
+    " Clean all the 'HG:' lines.
+    let l:is_valid = lawrencium#clean_commit_file(a:log_file)
+    if !l:is_valid
+        call lawrencium#error("abort: Empty commit message")
+        return
+    endif
+
+    " Get the repo and edit the given patch.
+    let l:repo = lawrencium#hg_repo()
+    let l:hg_args = ['-s', '-l', a:log_file]
+    call l:repo.RunCommand('qref', l:hg_args)
+endfunction
+

          
A => autoload/lawrencium/record.vim +161 -0
@@ 0,0 1,161 @@ 
+
+function! lawrencium#record#init() abort
+    call lawrencium#add_command("Hgrecord call lawrencium#record#HgRecord(0)")
+    call lawrencium#add_command("Hgvrecord call lawrencium#record#HgRecord(1)")
+endfunction
+
+function! lawrencium#record#HgRecord(split) abort
+    let l:repo = lawrencium#hg_repo()
+    let l:orig_buf = lawrencium#buffer_obj()
+    let l:tmp_path = l:orig_buf.GetName(':p') . '~record'
+    let l:diff_id = localtime()
+
+    " Start diffing on the current file, enable some commands.
+    call l:orig_buf.DefineCommand('Hgrecordabort', ':call s:HgRecord_Abort()')
+    call l:orig_buf.DefineCommand('Hgrecordcommit', ':call s:HgRecord_Execute()')
+    call lawrencium#diff#HgDiffThis(l:diff_id)
+    setlocal foldmethod=diff
+
+    " Split the window and open the parent revision in the right or bottom
+    " window. Keep the current buffer in the left or top window... we're going
+    " to 'move' those changes into the parent revision.
+    let l:cmd = 'keepalt rightbelow split '
+    if a:split == 1
+        let l:cmd = 'keepalt rightbelow vsplit '
+    endif
+    let l:rev_path = l:repo.GetLawrenciumPath(expand('%:p'), 'rev', '')
+    execute l:cmd . fnameescape(l:rev_path)
+
+    " This new buffer with the parent revision is set as a Lawrencium buffer.
+    " Let's save it to an actual file and reopen it like that (somehow we
+    " could probably do it with `:saveas` instead but we'd need to reset a
+    " bunch of other buffer settings, and Vim weirdly creates another backup
+    " buffer when you do that).
+    execute 'keepalt write! ' . fnameescape(l:tmp_path)
+    execute 'keepalt edit! ' . fnameescape(l:tmp_path)
+    setlocal bufhidden=delete
+    let b:mercurial_dir = l:repo.root_dir
+    let b:lawrencium_record_for = l:orig_buf.GetName(':p')
+    let b:lawrencium_record_other_nr = l:orig_buf.nr
+    let b:lawrencium_record_commit_split = !a:split
+    call setbufvar(l:orig_buf.nr, 'lawrencium_record_for', '%')
+    call setbufvar(l:orig_buf.nr, 'lawrencium_record_other_nr', bufnr('%'))
+
+    " Hookup the commit and abort commands.
+    let l:rec_buf = lawrencium#buffer_obj()
+    call l:rec_buf.OnDelete('call s:HgRecord_Execute()')
+    call l:rec_buf.DefineCommand('Hgrecordcommit', ':quit')
+    call l:rec_buf.DefineCommand('Hgrecordabort', ':call s:HgRecord_Abort()')
+    call lawrencium#define_commands()
+
+    " Make it the other part of the diff.
+    call lawrencium#diff#HgDiffThis(l:diff_id)
+    setlocal foldmethod=diff
+    call l:rec_buf.SetVar('&filetype', l:orig_buf.GetVar('&filetype'))
+    call l:rec_buf.SetVar('&fileformat', l:orig_buf.GetVar('&fileformat'))
+
+    if g:lawrencium_record_start_in_working_buffer
+        wincmd p
+    endif
+endfunction
+
+function! s:HgRecord_Execute() abort
+    if exists('b:lawrencium_record_abort')
+        " Abort flag is set, let's just cleanup.
+        let l:buf_nr = b:lawrencium_record_for == '%' ? bufnr('%') :
+                    \b:lawrencium_record_other_nr
+        call s:HgRecord_CleanUp(l:buf_nr)
+        call lawrencium#error("abort: User requested aborting the record operation.")
+        return
+    endif
+
+    if !exists('b:lawrencium_record_for')
+        call lawrencium#throw("This doesn't seem like a record buffer, something's wrong!")
+    endif
+    if b:lawrencium_record_for == '%'
+        " Switch to the 'recording' buffer's window.
+        let l:buf_obj = lawrencium#buffer_obj(b:lawrencium_record_other_nr)
+        call l:buf_obj.MoveToFirstWindow()
+    endif
+
+    " Setup the commit operation.
+    let l:split = b:lawrencium_record_commit_split
+    let l:working_bufnr = b:lawrencium_record_other_nr
+    let l:working_path = fnameescape(b:lawrencium_record_for)
+    let l:record_path = fnameescape(expand('%:p'))
+    let l:callbacks = [
+                \'call s:HgRecord_PostExecutePre('.l:working_bufnr.', "'.
+                    \escape(l:working_path, '\').'", "'.
+                    \escape(l:record_path, '\').'")',
+                \'call s:HgRecord_PostExecutePost('.l:working_bufnr.', "'.
+                    \escape(l:working_path, '\').'")',
+                \'call s:HgRecord_PostExecuteAbort('.l:working_bufnr.', "'.
+                    \escape(l:record_path, '\').'")'
+                \]
+    call lawrencium#trace("Starting commit flow with callbacks: ".string(l:callbacks))
+    call lawrencium#commit#HgCommit(0, l:split, l:callbacks, b:lawrencium_record_for)
+endfunction
+
+function! s:HgRecord_PostExecutePre(working_bufnr, working_path, record_path) abort
+    " Just before committing, we switch the original file with the record
+    " file... we'll restore things in the post-callback below.
+    " We also switch on 'autoread' temporarily on the working buffer so that
+    " we don't have an annoying popup in gVim.
+    if has('dialog_gui')
+        call setbufvar(a:working_bufnr, '&autoread', 1)
+    endif
+    call lawrencium#trace("Backuping original file: ".a:working_path)
+    silent call rename(a:working_path, a:working_path.'~working')
+    call lawrencium#trace("Committing recorded changes using: ".a:record_path)
+    silent call rename(a:record_path, a:working_path)
+    sleep 200m
+endfunction
+
+function! s:HgRecord_PostExecutePost(working_bufnr, working_path) abort
+    " Recover the back-up file from underneath the buffer.
+    call lawrencium#trace("Recovering original file: ".a:working_path)
+    silent call rename(a:working_path.'~working', a:working_path)
+
+    " Clean up!
+    call s:HgRecord_CleanUp(a:working_bufnr)
+
+    " Restore default 'autoread'.
+    if has('dialog_gui')
+        set autoread<
+    endif
+endfunction
+
+function! s:HgRecord_PostExecuteAbort(working_bufnr, record_path) abort
+    call s:HgRecord_CleanUp(a:working_bufnr)
+    call lawrencium#trace("Delete discarded record file: ".a:record_path)
+    silent call delete(a:record_path)
+endfunction
+
+function! s:HgRecord_Abort() abort
+    if b:lawrencium_record_for == '%'
+        " We're in the working directory buffer. Switch to the 'recording'
+        " buffer and quit.
+        let l:buf_obj = lawrencium#buffer_obj(b:lawrencium_record_other_nr)
+        call l:buf_obj.MoveToFirstWindow()
+    endif
+    " We're now in the 'recording' buffer... set the abort flag and quit,
+    " which will run the execution (it will early out and clean things up).
+    let b:lawrencium_record_abort = 1
+    quit!
+endfunction
+
+function! s:HgRecord_CleanUp(buf_nr) abort
+    " Get in the original buffer and clean the local commands/variables.
+    let l:buf_obj = lawrencium#buffer_obj(a:buf_nr)
+    call l:buf_obj.MoveToFirstWindow()
+    if !exists('b:lawrencium_record_for') || b:lawrencium_record_for != '%'
+        call lawrencium#throw("Cleaning up something else than the original buffer ".
+                \"for a record operation. That's suspiciously incorrect! ".
+                \"Aborting.")
+    endif
+    call l:buf_obj.DeleteCommand('Hgrecordabort')
+    call l:buf_obj.DeleteCommand('Hgrecordcommit')
+    unlet b:lawrencium_record_for
+    unlet b:lawrencium_record_other_nr
+endfunction
+

          
A => autoload/lawrencium/revert.vim +23 -0
@@ 0,0 1,23 @@ 
+
+function! lawrencium#revert#init() abort
+    call lawrencium#add_command("-bang -nargs=* -complete=customlist,lawrencium#list_repo_files Hgrevert :call lawrencium#revert#HgRevert(<bang>0, <f-args>)")
+endfunction
+
+function! lawrencium#revert#HgRevert(bang, ...) abort
+    " Get the files to revert.
+    let l:filenames = a:000
+    if a:0 == 0
+        let l:filenames = [ expand('%:p') ]
+    endif
+    if a:bang
+        call insert(l:filenames, '--no-backup', 0)
+    endif
+
+    " Get the repo and run the command.
+    let l:repo = lawrencium#hg_repo()
+    call l:repo.RunCommand('revert', l:filenames)
+
+    " Re-edit the file to see the change.
+    edit
+endfunction
+

          
A => autoload/lawrencium/status.vim +288 -0
@@ 0,0 1,288 @@ 
+
+function! lawrencium#status#init() abort
+    call lawrencium#add_command("Hgstatus :call lawrencium#status#HgStatus()")
+
+    call lawrencium#add_reader('status', "lawrencium#status#read", 1)
+endfunction
+
+function! lawrencium#status#read(repo, path_parts, full_path) abort
+    if a:path_parts['path'] == ''
+        call a:repo.ReadCommandOutput('status')
+    else
+        call a:repo.ReadCommandOutput('status', a:full_path)
+    endif
+    setlocal nomodified
+    setlocal filetype=hgstatus
+    setlocal bufhidden=delete
+    setlocal buftype=nofile
+endfunction
+
+function! lawrencium#status#HgStatus() abort
+    " Get the repo and the Lawrencium path for `hg status`.
+    let l:repo = lawrencium#hg_repo()
+    let l:status_path = l:repo.GetLawrenciumPath('', 'status', '')
+
+    " Open the Lawrencium buffer in a new split window of the right size.
+    if g:lawrencium_status_win_split_above
+      execute "keepalt leftabove split " . fnameescape(l:status_path)
+    else
+      execute "keepalt rightbelow split " . fnameescape(l:status_path)
+    endif
+    
+    if (line('$') == 1 && getline(1) == '')
+        " Buffer is empty, which means there are not changes...
+        " Quit and display a message.
+        " TODO: figure out why the first `echom` doesn't show when alone.
+        bdelete
+        echom "Nothing was modified."
+        echom ""
+        return
+    endif
+
+    execute "setlocal winfixheight"
+    if !g:lawrencium_status_win_split_even
+      execute "setlocal winheight=" . (line('$') + 1)
+      execute "resize " . (line('$') + 1)
+    endif
+
+    " Add some nice commands.
+    command! -buffer          Hgstatusedit          :call s:HgStatus_FileEdit(0)
+    command! -buffer          Hgstatusdiff          :call s:HgStatus_Diff(0)
+    command! -buffer          Hgstatusvdiff         :call s:HgStatus_Diff(1)
+    command! -buffer          Hgstatustabdiff       :call s:HgStatus_Diff(2)
+    command! -buffer          Hgstatusdiffsum       :call s:HgStatus_DiffSummary(1)
+    command! -buffer          Hgstatusvdiffsum      :call s:HgStatus_DiffSummary(2)
+    command! -buffer          Hgstatustabdiffsum    :call s:HgStatus_DiffSummary(3)
+    command! -buffer          Hgstatusrefresh       :call s:HgStatus_Refresh()
+    command! -buffer -range -bang Hgstatusrevert    :call s:HgStatus_Revert(<line1>, <line2>, <bang>0)
+    command! -buffer -range   Hgstatusaddremove     :call s:HgStatus_AddRemove(<line1>, <line2>)
+    command! -buffer -range=% -bang Hgstatuscommit  :call s:HgStatus_Commit(<line1>, <line2>, <bang>0, 0)
+    command! -buffer -range=% -bang Hgstatusvcommit :call s:HgStatus_Commit(<line1>, <line2>, <bang>0, 1)
+    command! -buffer -range=% -nargs=+ Hgstatusqnew :call s:HgStatus_QNew(<line1>, <line2>, <f-args>)
+    command! -buffer -range=% Hgstatusqrefresh      :call s:HgStatus_QRefresh(<line1>, <line2>)
+
+    " Add some handy mappings.
+    if g:lawrencium_define_mappings
+        nnoremap <buffer> <silent> <cr>  :Hgstatusedit<cr>
+        nnoremap <buffer> <silent> <C-N> :call search('^[MARC\!\?I ]\s.', 'We')<cr>
+        nnoremap <buffer> <silent> <C-P> :call search('^[MARC\!\?I ]\s.', 'Wbe')<cr>
+        nnoremap <buffer> <silent> <C-D> :Hgstatustabdiff<cr>
+        nnoremap <buffer> <silent> <C-V> :Hgstatusvdiff<cr>
+        nnoremap <buffer> <silent> <C-U> :Hgstatusdiffsum<cr>
+        nnoremap <buffer> <silent> <C-H> :Hgstatusvdiffsum<cr>
+        nnoremap <buffer> <silent> <C-A> :Hgstatusaddremove<cr>
+        nnoremap <buffer> <silent> <C-S> :Hgstatuscommit<cr>
+        nnoremap <buffer> <silent> <C-R> :Hgstatusrefresh<cr>
+        nnoremap <buffer> <silent> q     :bdelete!<cr>
+
+        vnoremap <buffer> <silent> <C-A> :Hgstatusaddremove<cr>
+        vnoremap <buffer> <silent> <C-S> :Hgstatuscommit<cr>
+    endif
+endfunction
+
+function! s:HgStatus_Refresh(...) abort
+    if a:0 > 0
+        let l:win_nr = bufwinnr(a:1)
+        call lawrencium#trace("Switching back to status window ".l:win_nr)
+        if l:win_nr < 0
+            call lawrencium#throw("Can't find the status window anymore!")
+        endif
+        execute l:win_nr . 'wincmd w'
+        " Delete everything in the buffer, and re-read the status into it.
+        " TODO: In theory I would only have to do `edit` like below when we're
+        " already in the window, but for some reason Vim just goes bonkers and
+        " weird shit happens. I have no idea why, hence the work-around here
+        " to bypass the whole `BufReadCmd` auto-command altogether, and just
+        " edit the buffer in place.
+        normal! ggVGd
+        call lawrencium#read_lawrencium_file(b:lawrencium_path)
+        return
+    endif
+
+    " Just re-edit the buffer, it will reload the contents by calling
+    " the matching Mercurial command.
+    edit
+endfunction
+
+function! s:HgStatus_FileEdit(newtab) abort
+    " Get the path of the file the cursor is on.
+    let l:filename = s:HgStatus_GetSelectedFile()
+
+    let l:cleanupbufnr = -1
+    if a:newtab == 0
+        " If the file is already open in a window, jump to that window.
+        " Otherwise, jump to the previous window and open it there.
+        for nr in range(1, winnr('$'))
+            let l:br = winbufnr(nr)
+            let l:bpath = fnamemodify(bufname(l:br), ':p')
+            if l:bpath ==# l:filename
+                execute nr . 'wincmd w'
+                return
+            endif
+        endfor
+        wincmd p
+    else
+        " Just open a new tab so we can edit the file there.
+        " We don't use `tabedit` because it messes up the current window
+        " if it happens to be the same file.
+        " We'll just have to clean up the default empty buffer created.
+        tabnew
+        let l:cleanupbufnr = bufnr('%')
+    endif
+    execute 'edit ' . fnameescape(l:filename)
+    if l:cleanupbufnr >= 0
+        execute 'bdelete ' . l:cleanupbufnr
+    endif
+endfunction
+
+function! s:HgStatus_AddRemove(linestart, lineend) abort
+    " Get the selected filenames.
+    let l:filenames = s:HgStatus_GetSelectedFiles(a:linestart, a:lineend, ['!', '?'])
+    if len(l:filenames) == 0
+        call lawrencium#error("No files to add or remove in selection or current line.")
+        return
+    endif
+
+    " Run `addremove` on those paths.
+    let l:repo = lawrencium#hg_repo()
+    call l:repo.RunCommand('addremove', l:filenames)
+
+    " Refresh the status window.
+    call s:HgStatus_Refresh()
+endfunction
+
+function! s:HgStatus_Revert(linestart, lineend, bang) abort
+    " Get the selected filenames.
+    let l:filenames = s:HgStatus_GetSelectedFiles(a:linestart, a:lineend, ['M', 'A', 'R'])
+    if len(l:filenames) == 0
+        call lawrencium#error("No files to revert in selection or current line.")
+        return
+    endif
+
+    " Run `revert` on those paths.
+    " If the bang modifier is specified, revert with no backup.
+    let l:repo = lawrencium#hg_repo()
+    if a:bang
+        call insert(l:filenames, '-C', 0)
+    endif
+    call l:repo.RunCommand('revert', l:filenames)
+
+    " Refresh the status window.
+    call s:HgStatus_Refresh()
+endfunction
+
+function! s:HgStatus_Commit(linestart, lineend, bang, vertical) abort
+    " Get the selected filenames.
+    let l:filenames = s:HgStatus_GetSelectedFiles(a:linestart, a:lineend, ['M', 'A', 'R'])
+    if len(l:filenames) == 0
+        call lawrencium#error("No files to commit in selection or file.")
+        return
+    endif
+
+    " Run `Hgcommit` on those paths.
+    let l:buf_nr = bufnr('%')
+    let l:callback = 'call s:HgStatus_Refresh('.l:buf_nr.')'
+    call lawrencium#commit#HgCommit(a:bang, a:vertical, l:callback, l:filenames)
+endfunction
+
+function! s:HgStatus_Diff(split) abort
+    " Open the file and run `Hgdiff` on it.
+    " We also need to translate the split mode for it... if we already
+    " opened the file in a new tab, `HgDiff` only needs to do a vertical
+    " split (i.e. split=1).
+    let l:newtab = 0
+    let l:hgdiffsplit = a:split
+    if a:split == 2
+        let l:newtab = 1
+        let l:hgdiffsplit = 1
+    endif
+    call s:HgStatus_FileEdit(l:newtab)
+    call lawrencium#diff#HgDiff('%:p', l:hgdiffsplit)
+endfunction
+
+function! s:HgStatus_DiffSummary(split) abort
+    " Get the path of the file the cursor is on.
+    let l:path = s:HgStatus_GetSelectedFile()
+    " Reuse the same diff summary window
+    let l:reuse_id = 'lawrencium_diffsum_for_' . bufnr('%')
+    let l:split_prev_win = (a:split < 3)
+    let l:args = {'reuse_id': l:reuse_id, 'use_prev_win': l:split_prev_win,
+                \'avoid_win': winnr(), 'split_mode': a:split}
+    call lawrencium#diff#HgDiffSummary(l:path, l:args)
+endfunction
+
+function! s:HgStatus_QNew(linestart, lineend, patchname, ...) abort
+    " Get the selected filenames.
+    let l:filenames = s:HgStatus_GetSelectedFiles(a:linestart, a:lineend, ['M', 'A', 'R'])
+    if len(l:filenames) == 0
+        call lawrencium#error("No files in selection or file to create patch.")
+        return
+    endif
+
+    " Run `Hg qnew` on those paths.
+    let l:repo = lawrencium#hg_repo()
+    call insert(l:filenames, a:patchname, 0)
+    if a:0 > 0
+        call insert(l:filenames, '-m', 0)
+        let l:message = '"' . join(a:000, ' ') . '"'
+        call insert(l:filenames, l:message, 1)
+    endif
+    call l:repo.RunCommand('qnew', l:filenames)
+
+    " Refresh the status window.
+    call s:HgStatus_Refresh()
+endfunction
+
+function! s:HgStatus_QRefresh(linestart, lineend) abort
+    " Get the selected filenames.
+    let l:filenames = s:HgStatus_GetSelectedFiles(a:linestart, a:lineend, ['M', 'A', 'R'])
+    if len(l:filenames) == 0
+        call lawrencium#error("No files in selection or file to refresh the patch.")
+        return
+    endif
+
+    " Run `Hg qrefresh` on those paths.
+    let l:repo = lawrencium#hg_repo()
+    call insert(l:filenames, '-s', 0)
+    call l:repo.RunCommand('qrefresh', l:filenames)
+
+    " Refresh the status window.
+    call s:HgStatus_Refresh()
+endfunction
+
+
+function! s:HgStatus_GetSelectedFile() abort
+    let l:filenames = s:HgStatus_GetSelectedFiles()
+    return l:filenames[0]
+endfunction
+
+function! s:HgStatus_GetSelectedFiles(...) abort
+    if a:0 >= 2
+        let l:lines = getline(a:1, a:2)
+    else
+        let l:lines = []
+        call add(l:lines, getline('.'))
+    endif
+    let l:filenames = []
+    let l:repo = lawrencium#hg_repo()
+    for line in l:lines
+        if a:0 >= 3
+            let l:status = s:HgStatus_GetFileStatus(line)
+            if index(a:3, l:status) < 0
+                continue
+            endif
+        endif
+        " 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 = l:repo.GetFullPath(l:filename)
+        call add(l:filenames, l:filename)
+    endfor
+    return l:filenames
+endfunction
+
+function! s:HgStatus_GetFileStatus(...) abort
+    let l:line = a:0 ? a:1 : getline('.')
+    return matchstr(l:line, '\v^[MARC\!\?I ]')
+endfunction
+

          
A => autoload/lawrencium/vimutils.vim +45 -0
@@ 0,0 1,45 @@ 
+
+function! lawrencium#vimutils#init() abort
+    call lawrencium#add_command("-bang -nargs=1 -complete=customlist,lawrencium#list_repo_files Hgedit :call lawrencium#vimutils#HgEdit(<bang>0, <f-args>)")
+
+    call lawrencium#add_command("-bang -nargs=? -complete=customlist,lawrencium#list_repo_dirs Hgcd :cd<bang> `=lawrencium#hg_repo().GetFullPath(<q-args>)`")
+    call lawrencium#add_command("-bang -nargs=? -complete=customlist,lawrencium#list_repo_dirs Hglcd :lcd<bang> `=lawrencium#hg_repo().GetFullPath(<q-args>)`")
+    
+    call lawrencium#add_command("-bang -nargs=+ -complete=customlist,lawrencium#list_repo_files Hgvimgrep :call lawrencium#vimutils#HgVimGrep(<bang>0, <f-args>)")
+endfunction
+
+" Hgedit {{{
+
+function! lawrencium#vimutils#HgEdit(bang, filename) abort
+    let l:full_path = lawrencium#hg_repo().GetFullPath(a:filename)
+    if a:bang
+        execute "edit! " . fnameescape(l:full_path)
+    else
+        execute "edit " . fnameescape(l:full_path)
+    endif
+endfunction
+
+" }}}
+
+" Hgvimgrep {{{
+
+function! lawrencium#vimutils#HgVimGrep(bang, pattern, ...) abort
+    let l:repo = lawrencium#hg_repo()
+    let l:file_paths = []
+    if a:0 > 0
+        for ff in a:000
+            let l:full_ff = l:repo.GetFullPath(ff)
+            call add(l:file_paths, l:full_ff)
+        endfor
+    else
+        call add(l:file_paths, l:repo.root_dir . "**")
+    endif
+    if a:bang
+        execute "vimgrep! " . a:pattern . " " . join(l:file_paths, " ")
+    else
+        execute "vimgrep " . a:pattern . " " . join(l:file_paths, " ")
+    endif
+endfunction
+
+" }}}
+

          
M plugin/lawrencium.vim +10 -2345
@@ 53,2362 53,27 @@ if !exists('g:lawrencium_record_start_in
     let g:lawrencium_record_start_in_working_buffer = 0
 endif
 
-" }}}
-
-" Utility {{{
-
-" Strips the ending slash in a path.
-function! s:stripslash(path)
-    return fnamemodify(a:path, ':s?[/\\]$??')
-endfunction
-
-" Returns whether a path is absolute.
-function! s:isabspath(path)
-    return a:path =~# '\v^(\w\:)?[/\\]'
-endfunction
-
-" Normalizes the slashes in a path.
-function! s:normalizepath(path)
-    if exists('+shellslash') && &shellslash
-        return substitute(a:path, '\v/', '\\', 'g')
-    elseif has('win32')
-        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()
-    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
-
-" Delete a temporary file if it exists.
-function! s:clean_tempfile(path)
-    if filewritable(a:path)
-        call s:trace("Cleaning up temporary file: " . a:path)
-        call delete(a:path)
-    endif
-endfunction
-
-" Prints a message if debug tracing is enabled.
-function! s:trace(message, ...)
-   if g:lawrencium_trace || (a:0 && a:1)
-       let l:message = "lawrencium: " . a:message
-       echom l:message
-   endif
-endfunction
-
-" Prints an error message with 'lawrencium error' prefixed to it.
-function! s:error(message)
-    echom "lawrencium error: " . a:message
-endfunction
-
-" Throw a Lawrencium exception message.
-function! s:throw(message)
-    let v:errmsg = "lawrencium: " . a:message
-    throw v:errmsg
-endfunction
-
-" Finds the repository root given a path inside that repository.
-" Throw an error if not repository is found.
-function! s:find_repo_root(path)
-    let l:path = s:stripslash(a:path)
-    let l:previous_path = ""
-    while l:path != l:previous_path
-        if isdirectory(l:path . '/.hg')
-            return s:normalizepath(simplify(fnamemodify(l:path, ':p')))
-        endif
-        let l:previous_path = l:path
-        let l:path = fnamemodify(l:path, ':h')
-    endwhile
-    call s:throw("No Mercurial repository found above: " . a:path)
-endfunction
-
-" Given a Lawrencium path (e.g: 'lawrencium:///repo/root_dir//foo/bar/file.py//rev=34'), extract
-" the repository root, relative file path and revision number/changeset ID.
-"
-" If a second argument exists, it must be:
-" - `relative`: to make the file path relative to the repository root.
-" - `absolute`: to make the file path absolute.
-"
-function! s:parse_lawrencium_path(lawrencium_path, ...)
-    let l:repo_path = s:shellslash(a:lawrencium_path)
-    let l:repo_path = substitute(l:repo_path, '\\ ', ' ', 'g')
-    if l:repo_path =~? '\v^lawrencium://'
-        let l:repo_path = strpart(l:repo_path, strlen('lawrencium://'))
-    endif
-
-    let l:root_dir = ''
-    let l:at_idx = stridx(l:repo_path, '//')
-    if l:at_idx >= 0
-        let l:root_dir = strpart(l:repo_path, 0, l:at_idx)
-        let l:repo_path = strpart(l:repo_path, l:at_idx + 2)
-    endif
-
-    let l:value = ''
-    let l:action = ''
-    let l:actionidx = stridx(l:repo_path, '//')
-    if l:actionidx >= 0
-        let l:action = strpart(l:repo_path, l:actionidx + 2)
-        let l:repo_path = strpart(l:repo_path, 0, l:actionidx)
-
-        let l:equalidx = stridx(l:action, '=')
-        if l:equalidx >= 0
-            let l:value = strpart(l:action, l:equalidx + 1)
-            let l:action = strpart(l:action, 0, l:equalidx)
-        endif
-    endif
-
-    if a:0 > 0
-        execute 'cd! ' . fnameescape(l:root_dir)
-        if a:1 == 'relative'
-            let l:repo_path = fnamemodify(l:repo_path, ':.')
-        elseif a:1 == 'absolute'
-            let l:repo_path = fnamemodify(l:repo_path, ':p')
-        endif
-        execute 'cd! -'
-    endif
-    
-    let l:result = { 'root': l:root_dir, 'path': l:repo_path, 'action': l:action, 'value': l:value }
-    return l:result
-endfunction
-
-" Finds a window whose displayed buffer has a given variable
-" set to the given value.
-function! s:find_buffer_window(varname, varvalue) abort
-    for wnr in range(1, winnr('$'))
-        let l:bnr = winbufnr(wnr)
-        if getbufvar(l:bnr, a:varname) == a:varvalue
-            return l:wnr
-        endif
-    endfor
-    return -1
-endfunction
-
-" Opens a buffer in a way that makes it easy to delete it later:
-" - if the about-to-be previous buffer doesn't have a given variable,
-"   just open the new buffer.
-" - if the about-to-be previous buffer has a given variable, open the
-"   new buffer with the `keepalt` option to make it so that the
-"   actual previous buffer (returned by things like `bufname('#')`)
-"   is the original buffer that was there before the first deletable
-"   buffer was opened.
-function! s:edit_deletable_buffer(varname, varvalue, path) abort
-    let l:edit_cmd = 'edit '
-    if getbufvar('%', a:varname) != ''
-        let l:edit_cmd = 'keepalt edit '
-    endif
-    execute l:edit_cmd . fnameescape(a:path)
-    call setbufvar('%', a:varname, a:varvalue)
-endfunction
-
-" Deletes all buffers that have a given variable set to a given value.
-" For each buffer, if it is not shown in any window, it will be just deleted.
-" If it is shown in a window, that window will be switched to the alternate
-" buffer before the buffer is deleted, unless the `lawrencium_quit_on_delete`
-" variable is set to `1`, in which case the window is closed too.
-function! s:delete_dependency_buffers(varname, varvalue) abort
-    let l:cur_winnr = winnr()
-    for bnr in range(1, bufnr('$'))
-        if getbufvar(bnr, a:varname) == a:varvalue
-            " Delete this buffer if it is not shown in any window.
-            " Otherwise, display the alternate buffer before deleting
-            " it so the window is not closed.
-            let l:bwnr = bufwinnr(bnr)
-            if l:bwnr < 0 || getbufvar(bnr, 'lawrencium_quit_on_delete') == 1
-                if bufloaded(l:bnr)
-                    call s:trace("Deleting dependency buffer " . bnr)
-                    execute "bdelete! " . bnr
-                else
-                    call s:trace("Dependency buffer " . bnr . " is already unladed.")
-                endif
-            else
-                execute l:bwnr . "wincmd w"
-                " TODO: better handle case where there's no previous/alternate buffer?
-                let l:prev_bnr = bufnr('#')
-                if l:prev_bnr > 0 && bufloaded(l:prev_bnr)
-                    execute "buffer " . l:prev_bnr
-                    if bufloaded(l:bnr)
-                        call s:trace("Deleting dependency buffer " . bnr . " after switching to " . l:prev_bnr . " in window " . l:bwnr)
-                        execute "bdelete! " . bnr
-                    else
-                        call s:trace("Dependency buffer " . bnr . " is unladed after switching to " . l:prev_bnr)
-                    endif
-                else
-                    call s:trace("Deleting dependency buffer " . bnr . " and window.")
-                    bdelete!
-                endif
-            endif
-        endif
-    endfor
-    if l:cur_winnr != winnr()
-        call s:trace("Returning to window " . l:cur_winnr)
-        execute l:cur_winnr . "wincmd w"
-    endif
-endfunction
-
-" Clean up all the 'HG:' lines from a commit message, and see if there's
-" any message left (Mercurial does this automatically, usually, but
-" apparently not when you feed it a log file...).
-function! s:clean_commit_file(log_file) abort
-    let l:lines = readfile(a:log_file)
-    call filter(l:lines, "v:val !~# '\\v^HG:'")
-    if len(filter(copy(l:lines), "v:val !~# '\\v^\\s*$'")) == 0
-        return 0
-    endif
-    call writefile(l:lines, a:log_file)
-    return 1
-endfunction
+if !exists('g:lawrencium_extensions')
+    let g:lawrencium_extensions = []
+endif
 
 " }}}
 
-" Mercurial Repository Object {{{
-
-" Let's define a Mercurial repo 'class' using prototype-based object-oriented
-" programming.
-"
-" The prototype dictionary.
-let s:HgRepo = {}
-
-" Constructor.
-function! s:HgRepo.New(path) abort
-    let l:newRepo = copy(self)
-    let l:newRepo.root_dir = s:find_repo_root(a:path)
-    call s:trace("Built new Mercurial repository object at : " . l:newRepo.root_dir)
-    return l:newRepo
-endfunction
-
-" Gets a full path given a repo-relative path.
-function! s:HgRepo.GetFullPath(path) abort
-    let l:root_dir = self.root_dir
-    if s:isabspath(a:path)
-        call s:throw("Expected relative path, got absolute path: " . a:path)
-    endif
-    return s:normalizepath(l:root_dir . a:path)
-endfunction
-
-" Gets a repo-relative path given any path.
-function! s:HgRepo.GetRelativePath(path) abort
-    execute 'lcd! ' . fnameescape(self.root_dir)
-    let l:relative_path = fnamemodify(a:path, ':.')
-    execute 'lcd! -'
-    return l:relative_path
-endfunction
-
-" Gets, and optionally creates, a temp folder for some operation in the `.hg`
-" directory.
-function! s:HgRepo.GetTempDir(path, ...) abort
-    let l:tmp_dir = self.GetFullPath('.hg/lawrencium/' . a:path)
-    if !isdirectory(l:tmp_dir)
-        if a:0 > 0 && !a:1
-            return ''
-        endif
-        call mkdir(l:tmp_dir, 'p')
-    endif
-    return l:tmp_dir
-endfunction
-
-" Gets a list of files matching a root-relative pattern.
-" If a flag is passed and is TRUE, a slash will be appended to all
-" directories.
-function! s:HgRepo.Glob(pattern, ...) abort
-    let l:root_dir = self.root_dir
-    if (a:pattern =~# '\v^[/\\]')
-        let l:root_dir = s:stripslash(l:root_dir)
-    endif
-    let l:matches = split(glob(l:root_dir . a:pattern), '\n')
-    if a:0 && a:1
-        for l:idx in range(len(l:matches))
-            if !filereadable(l:matches[l:idx])
-                let l:matches[l:idx] = l:matches[l:idx] . '/'
-            endif
-        endfor
-    endif
-    let l:strip_len = len(l:root_dir)
-    call map(l:matches, 'v:val[l:strip_len : -1]')
-    return l:matches
-endfunction
-
-" Gets a full Mercurial command.
-function! s:HgRepo.GetCommand(command, ...) abort
-    " If there's only one argument, and it's a list, then use that as the
-    " argument list.
-    let l:arg_list = a:000
-    if a:0 == 1 && type(a:1) == type([])
-        let l:arg_list = a:1
-    endif
-    let l:prev_shellslash = &shellslash
-    setlocal noshellslash
-    let l:hg_command = g:lawrencium_hg_executable . ' --repository ' . shellescape(s:stripslash(self.root_dir))
-    let l:hg_command = l:hg_command . ' ' . a:command
-    for l:arg in l:arg_list
-        let l:hg_command = l:hg_command . ' ' . shellescape(l:arg)
-    endfor
-    if l:prev_shellslash
-        setlocal shellslash
-    endif
-    return l:hg_command
-endfunction
-
-" Runs a Mercurial command in the repo.
-function! s:HgRepo.RunCommand(command, ...) abort
-    let l:all_args = [1, a:command] + a:000
-    return call(self['RunCommandEx'], l:all_args, self)
-endfunction
+" Setup {{{
 
-function! s:HgRepo.RunCommandEx(plain_mode, command, ...) abort
-    let l:prev_hgplain = $HGPLAIN
-    if a:plain_mode
-        let $HGPLAIN = 'true'
-    endif
-    let l:all_args = [a:command] + a:000
-    let l:hg_command = call(self['GetCommand'], l:all_args, self)
-    call s:trace("Running Mercurial command: " . l:hg_command)
-    let l:cmd_out = system(l:hg_command)
-    if a:plain_mode
-        let $HGPLAIN = l:prev_hgplain
-    endif
-    return l:cmd_out
-endfunction
-
-" Runs a Mercurial command in the repo and reads its output into the current
-" buffer.
-function! s:HgRepo.ReadCommandOutput(command, ...) abort
-    function! s:PutOutputIntoBuffer(command_line)
-        let l:was_buffer_empty = (line('$') == 1 && getline(1) == '')
-        execute '0read!' . escape(a:command_line, '%#\')
-        if l:was_buffer_empty  " (Always true?)
-            " '0read' inserts before the cursor, leaving a blank line which
-            " needs to be deleted... but if there are folds in this thing, we
-            " must open them all first otherwise we could delete the whole
-            " contents of the last fold (since Vim may close them all by
-            " default).
-            normal! zRG"_dd
-        endif
-    endfunction
-
-    let l:all_args = [a:command] + a:000
-    let l:hg_command = call(self['GetCommand'], l:all_args, self)
-    call s:trace("Running Mercurial command: " . l:hg_command)
-    call s:PutOutputIntoBuffer(l:hg_command)
-endfunction
-
-" Build a Lawrencium path for the given file and action.
-" By default, the given path will be made relative to the repository root,
-" unless '0' is passed as the 4th argument.
-function! s:HgRepo.GetLawrenciumPath(path, action, value, ...) abort
-    let l:path = a:path
-    if a:0 == 0 || !a:1
-        let l:path = self.GetRelativePath(a:path)
-    endif
-    let l:path = fnameescape(l:path)
-    let l:result = 'lawrencium://' . s:stripslash(self.root_dir) . '//' . l:path
-    if a:action !=? ''
-        let l:result  = l:result . '//' . a:action
-        if a:value !=? ''
-            let l:result = l:result . '=' . a:value
-        endif
-    endif
-    return l:result
-endfunction
-
-" Repo cache map.
-let s:buffer_repos = {}
-
-" Get a cached repo.
-function! s:hg_repo(...) abort
-    " Use the given path, or the mercurial directory of the current buffer.
-    if a:0 == 0
-        if exists('b:mercurial_dir')
-            let l:path = b:mercurial_dir
-        else
-            let l:path = s:find_repo_root(expand('%:p'))
-        endif
-    else
-        let l:path = a:1
-    endif
-    " Find a cache repo instance, or make a new one.
-    if has_key(s:buffer_repos, l:path)
-        return get(s:buffer_repos, l:path)
-    else
-        let l:repo = s:HgRepo.New(l:path)
-        let s:buffer_repos[l:path] = l:repo
-        return l:repo
-    endif
-endfunction
-
-" Sets up the current buffer with Lawrencium commands if it contains a file from a Mercurial repo.
-" If the file is not in a Mercurial repo, just exit silently.
-function! s:setup_buffer_commands() abort
-    call s:trace("Scanning buffer '" . bufname('%') . "' for Lawrencium setup...")
-    let l:do_setup = 1
-    if exists('b:mercurial_dir')
-        if b:mercurial_dir =~# '\v^\s*$'
-            unlet b:mercurial_dir
-        else
-            let l:do_setup = 0
-        endif
-    endif
-    try
-        let l:repo = s:hg_repo()
-    catch /^lawrencium\:/
-        return
-    endtry
-    let b:mercurial_dir = l:repo.root_dir
-    if exists('b:mercurial_dir') && l:do_setup
-        call s:trace("Setting Mercurial commands for buffer '" . bufname('%'))
-        call s:trace("  with repo : " . expand(b:mercurial_dir))
-        silent doautocmd User Lawrencium
-    endif
-endfunction
+call lawrencium#init()
 
 augroup lawrencium_detect
     autocmd!
-    autocmd BufNewFile,BufReadPost *     call s:setup_buffer_commands()
-    autocmd VimEnter               *     if expand('<amatch>')==''|call s:setup_buffer_commands()|endif
+    autocmd BufNewFile,BufReadPost *     call lawrencium#setup_buffer_commands()
+    autocmd VimEnter               *     if expand('<amatch>')==''|call lawrencium#setup_buffer_commands()|endif
 augroup end
 
-" }}}
-
-" Buffer Object {{{
-
-" The prototype dictionary.
-let s:Buffer = {}
-
-" Constructor.
-function! s:Buffer.New(number) dict abort
-    let l:newBuffer = copy(self)
-    let l:newBuffer.nr = a:number
-    let l:newBuffer.var_backup = {}
-    let l:newBuffer.cmd_names = {}
-    let l:newBuffer.on_delete = []
-    let l:newBuffer.on_winleave = []
-    let l:newBuffer.on_unload = []
-    execute 'augroup lawrencium_buffer_' . a:number
-    execute '  autocmd!'
-    execute '  autocmd BufDelete <buffer=' . a:number . '> call s:buffer_on_delete(' . a:number . ')'
-    execute 'augroup end'
-    call s:trace("Built new buffer object for buffer: " . a:number)
-    return l:newBuffer
-endfunction
-
-function! s:Buffer.GetName(...) dict abort
-    let l:name = bufname(self.nr)
-    if a:0 > 0
-        let l:name = fnamemodify(l:name, a:1)
-    endif
-    return l:name
-endfunction
-
-function! s:Buffer.GetVar(var) dict abort
-    return getbufvar(self.nr, a:var)
-endfunction
-
-function! s:Buffer.SetVar(var, value) dict abort
-    if !has_key(self.var_backup, a:var)
-        let self.var_backup[a:var] = getbufvar(self.nr, a:var)
-    endif
-    return setbufvar(self.nr, a:var, a:value)
-endfunction
-
-function! s:Buffer.RestoreVars() dict abort
-    for key in keys(self.var_backup)
-        setbufvar(self.nr, key, self.var_backup[key])
-    endfor
-endfunction
-
-function! s:Buffer.DefineCommand(name, ...) dict abort
-    if a:0 == 0
-        call s:throw("Not enough parameters for s:Buffer.DefineCommands()")
-    endif
-    if a:0 == 1
-        let l:flags = ''
-        let l:cmd = a:1
-    else
-        let l:flags = a:1
-        let l:cmd = a:2
-    endif
-    if has_key(self.cmd_names, a:name)
-        call s:throw("Command '".a:name."' is already defined in buffer ".self.nr)
-    endif
-    if bufnr('%') != self.nr
-        call s:throw("You must move to buffer ".self.nr."first before defining local commands")
-    endif
-    let self.cmd_names[a:name] = 1
-    let l:real_flags = ''
-    if type(l:flags) == type('')
-        let l:real_flags = l:flags
-    endif
-    execute 'command -buffer '.l:real_flags.' '.a:name.' '.l:cmd
-endfunction
-
-function! s:Buffer.DeleteCommand(name) dict abort
-    if !has_key(self.cmd_names, a:name)
-        call s:throw("Command '".a:name."' has not been defined in buffer ".self.nr)
-    endif
-    if bufnr('%') != self.nr
-        call s:throw("You must move to buffer ".self.nr."first before deleting local commands")
-    endif
-    execute 'delcommand '.a:name
-    call remove(self.cmd_names, a:name)
-endfunction
-
-function! s:Buffer.DeleteCommands() dict abort
-    if bufnr('%') != self.nr
-        call s:throw("You must move to buffer ".self.nr."first before deleting local commands")
-    endif
-    for name in keys(self.cmd_names)
-        execute 'delcommand '.name
-    endfor
-    let self.cmd_names = {}
-endfunction
-
-function! s:Buffer.MoveToFirstWindow() dict abort
-    let l:win_nr = bufwinnr(self.nr)
-    if l:win_nr < 0
-        if a:0 > 0 && a:1 == 0
-            return 0
-        endif
-        call s:throw("No windows currently showing buffer ".self.nr)
-    endif
-    execute l:win_nr.'wincmd w'
-    return 1
-endfunction
-
-function! s:Buffer.OnDelete(cmd) dict abort
-    call s:trace("Adding BufDelete callback for buffer " . self.nr . ": " . a:cmd)
-    call add(self.on_delete, a:cmd)
-endfunction
-
-function! s:Buffer.OnWinLeave(cmd) dict abort
-    if len(self.on_winleave) == 0
-        call s:trace("Adding BufWinLeave auto-command on buffer " . self.nr)
-        execute 'augroup lawrencium_buffer_' . self.nr . '_winleave'
-        execute '  autocmd!'
-        execute '  autocmd BufWinLeave <buffer=' . self.nr . '> call s:buffer_on_winleave(' . self.nr .')'
-        execute 'augroup end'
-    endif
-    call s:trace("Adding BufWinLeave callback for buffer " . self.nr . ": " . a:cmd)
-    call add(self.on_winleave, a:cmd)
-endfunction
-
-function! s:Buffer.OnUnload(cmd) dict abort
-    if len(self.on_unload) == 0
-        call s:trace("Adding BufUnload auto-command on buffer " . self.nr)
-        execute 'augroup lawrencium_buffer_' . self.nr . '_unload'
-        execute '  autocmd!'
-        execute '  autocmd BufUnload <buffer=' . self.nr . '> call s:buffer_on_unload(' . self.nr . ')'
-        execute 'augroup end'
-    endif
-    call s:trace("Adding BufUnload callback for buffer " . self.nr . ": " . a:cmd)
-    call add(self.on_unload, a:cmd)
-endfunction
-
-let s:buffer_objects = {}
-
-" Get a buffer instance for the specified buffer number, or the
-" current buffer if nothing is specified.
-function! s:buffer_obj(...) abort
-    let l:bufnr = a:0 ? a:1 : bufnr('%')
-    if !has_key(s:buffer_objects, l:bufnr)
-        let s:buffer_objects[l:bufnr] = s:Buffer.New(l:bufnr)
-    endif
-    return s:buffer_objects[l:bufnr]
-endfunction
-
-" Execute all the "on delete" callbacks.
-function! s:buffer_on_delete(number) abort
-    let l:bufobj = s:buffer_objects[a:number]
-    call s:trace("Calling BufDelete callbacks on buffer " . l:bufobj.nr)
-    for cmd in l:bufobj.on_delete
-        call s:trace(" [" . cmd . "]")
-        execute cmd
-    endfor
-    call s:trace("Deleted buffer object " . l:bufobj.nr)
-    call remove(s:buffer_objects, l:bufobj.nr)
-    execute 'augroup lawrencium_buffer_' . l:bufobj.nr
-    execute '  autocmd!'
-    execute 'augroup end'
-endfunction
-
-" Execute all the "on winleave" callbacks.
-function! s:buffer_on_winleave(number) abort
-    let l:bufobj = s:buffer_objects[a:number]
-    call s:trace("Calling BufWinLeave callbacks on buffer " . l:bufobj.nr)
-    for cmd in l:bufobj.on_winleave
-        call s:trace(" [" . cmd . "]")
-        execute cmd
-    endfor
-    execute 'augroup lawrencium_buffer_' . l:bufobj.nr . '_winleave'
-    execute '  autocmd!'
-    execute 'augroup end'
-endfunction
-
-" Execute all the "on unload" callbacks.
-function! s:buffer_on_unload(number) abort
-    let l:bufobj = s:buffer_objects[a:number]
-    call s:trace("Calling BufUnload callbacks on buffer " . l:bufobj.nr)
-    for cmd in l:bufobj.on_unload
-        call s:trace(" [" . cmd . "]")
-        execute cmd
-    endfor
-    execute 'augroup lawrencium_buffer_' . l:bufobj.nr . '_unload'
-    execute '  autocmd!'
-    execute 'augroup end'
-endfunction
-
-" }}}
-
-" Lawrencium Files {{{
-
-" Read revision (`hg cat`)
-function! s:read_lawrencium_rev(repo, path_parts, full_path) abort
-    let l:rev = a:path_parts['value']
-    if l:rev == ''
-        call a:repo.ReadCommandOutput('cat', a:full_path)
-    else
-        call a:repo.ReadCommandOutput('cat', '-r', l:rev, a:full_path)
-    endif
-endfunction
-
-" Status (`hg status`)
-function! s:read_lawrencium_status(repo, path_parts, full_path) abort
-    if a:path_parts['path'] == ''
-        call a:repo.ReadCommandOutput('status')
-    else
-        call a:repo.ReadCommandOutput('status', a:full_path)
-    endif
-    setlocal nomodified
-    setlocal filetype=hgstatus
-    setlocal bufhidden=delete
-    setlocal buftype=nofile
-endfunction
-
-" Log (`hg log`)
-let s:log_style_file = expand("<sfile>:h:h") . "/resources/hg_log.style"
-
-function! s:read_lawrencium_log(repo, path_parts, full_path) abort
-    let l:log_opts = join(split(a:path_parts['value'], ','))
-    let l:log_cmd = "log " . l:log_opts
-
-    if a:path_parts['path'] == ''
-        call a:repo.ReadCommandOutput(l:log_cmd, '--style', s:log_style_file)
-    else
-        call a:repo.ReadCommandOutput(l:log_cmd, '--style', s:log_style_file, a:full_path)
-    endif
-    setlocal filetype=hglog
-endfunction
-
-function! s:read_lawrencium_logpatch(repo, path_parts, full_path) abort
-    let l:log_cmd = 'log --patch --verbose --rev ' . a:path_parts['value']
-
-    if a:path_parts['path'] == ''
-        call a:repo.ReadCommandOutput(l:log_cmd)
-    else
-        call a:repo.ReadCommandOutput(l:log_cmd, a:full_path)
-    endif
-    setlocal filetype=diff
-endfunction
-
-" Diff revisions (`hg diff`)
-function! s:read_lawrencium_diff(repo, path_parts, full_path) abort
-    let l:diffargs = []
-    let l:commaidx = stridx(a:path_parts['value'], ',')
-    if l:commaidx > 0
-        let l:rev1 = strpart(a:path_parts['value'], 0, l:commaidx)
-        let l:rev2 = strpart(a:path_parts['value'], l:commaidx + 1)
-        if l:rev1 == '-'
-            let l:diffargs = [ '-r', l:rev2 ]
-        elseif l:rev2 == '-'
-            let l:diffargs = [ '-r', l:rev1 ]
-        else
-            let l:diffargs = [ '-r', l:rev1, '-r', l:rev2 ]
-        endif
-    elseif a:path_parts['value'] != ''
-        let l:diffargs = [ '-c', a:path_parts['value'] ]
-    else
-        let l:diffargs = []
-    endif
-    if a:path_parts['path'] != '' && a:path_parts['path'] != '.'
-        call add(l:diffargs, a:full_path)
-    endif
-    call a:repo.ReadCommandOutput('diff', l:diffargs)
-    setlocal filetype=diff
-    setlocal nofoldenable
-endfunction
-
-" Annotate file
-function! s:read_lawrencium_annotate(repo, path_parts, full_path) abort
-    let l:cmd_args = ['-c', '-n', '-u', '-d', '-q']
-    if a:path_parts['value'] == 'v=1'
-        call insert(l:cmd_args, '-v', 0)
-    endif
-    call add(l:cmd_args, a:full_path)
-    call a:repo.ReadCommandOutput('annotate', l:cmd_args)
-endfunction
-
-" MQ series
-function! s:read_lawrencium_qseries(repo, path_parts, full_path) abort
-    let l:names = split(a:repo.RunCommand('qseries'), '\n')
-    let l:head = split(a:repo.RunCommand('qapplied', '-s'), '\n')
-    let l:tail = split(a:repo.RunCommand('qunapplied', '-s'), '\n')
-
-    let l:idx = 0
-    let l:curbuffer = bufname('%')
-    for line in l:head
-        call setbufvar(l:curbuffer, 'lawrencium_patchname_' . (l:idx + 1), l:names[l:idx])
-        call append(l:idx, "*" . line)
-        let l:idx = l:idx + 1
-    endfor
-    for line in l:tail
-        call setbufvar(l:curbuffer, 'lawrencium_patchname_' . (l:idx + 1), l:names[l:idx])
-        call append(l:idx, line)
-        let l:idx = l:idx + 1
-    endfor
-    call setbufvar(l:curbuffer, 'lawrencium_patchname_top', l:names[len(l:head) - 1])
-    set filetype=hgqseries
-endfunction
-
-" Generic read
-let s:lawrencium_file_readers = {
-            \'rev': function('s:read_lawrencium_rev'),
-            \'log': function('s:read_lawrencium_log'),
-            \'logpatch': function('s:read_lawrencium_logpatch'),
-            \'diff': function('s:read_lawrencium_diff'),
-            \'status': function('s:read_lawrencium_status'),
-            \'annotate': function('s:read_lawrencium_annotate'),
-            \'qseries': function('s:read_lawrencium_qseries')
-            \}
-let s:lawrencium_file_customoptions = {
-            \'status': 1
-            \}
-
-function! s:ReadLawrenciumFile(path) abort
-    call s:trace("Reading Lawrencium file: " . a:path)
-    let l:path_parts = s:parse_lawrencium_path(a:path)
-    if l:path_parts['root'] == ''
-        call s:throw("Can't get repository root from: " . a:path)
-    endif
-    if !has_key(s:lawrencium_file_readers, l:path_parts['action'])
-        call s:throw("No registered reader for action: " . l:path_parts['action'])
-    endif
-
-    " Call the registered reader.
-    let l:repo = s:hg_repo(l:path_parts['root'])
-    let l:full_path = l:repo.root_dir . l:path_parts['path']
-    let LawrenciumFileReader = s:lawrencium_file_readers[l:path_parts['action']]
-    call LawrenciumFileReader(l:repo, l:path_parts, l:full_path)
-
-    " Setup the new buffer.
-    if !has_key(s:lawrencium_file_customoptions, l:path_parts['action'])
-        setlocal readonly
-        setlocal nomodified
-        setlocal bufhidden=delete
-        setlocal buftype=nofile
-    endif
-    goto
-
-    " Remember the real Lawrencium path, because Vim can fuck up the slashes
-    " on Windows.
-    let b:lawrencium_path = a:path
-
-    " Remember the repo it belongs to and make
-    " the Lawrencium commands available.
-    let b:mercurial_dir = l:repo.root_dir
-    call s:DefineMainCommands()
-
-    return ''
-endfunction
-
-function! s:WriteLawrenciumFile(path) abort
-    call s:trace("Writing Lawrencium file: " . a:path)
-endfunction
-
 augroup lawrencium_files
-  autocmd!
-  autocmd BufReadCmd  lawrencium://**//**//* exe s:ReadLawrenciumFile(expand('<amatch>'))
-  autocmd BufWriteCmd lawrencium://**//**//* exe s:WriteLawrenciumFile(expand('<amatch>'))
+    autocmd!
+    autocmd BufReadCmd  lawrencium://**//**//* exe lawrencium#read_lawrencium_file(expand('<amatch>'))
+    autocmd BufWriteCmd lawrencium://**//**//* exe lawrencium#write_lawrencium_file(expand('<amatch>'))
 augroup END
 
 " }}}
 
-" Buffer Commands Management {{{
-
-" Store the commands for Lawrencium-enabled buffers so that we can add them in
-" batch when we need to.
-let s:main_commands = []
-
-function! s:AddMainCommand(command) abort
-    let s:main_commands += [a:command]
-endfunction
-
-function! s:DefineMainCommands()
-    for l:command in s:main_commands
-        execute 'command! -buffer ' . l:command
-    endfor
-endfunction
-
-augroup lawrencium_main
-    autocmd!
-    autocmd User Lawrencium call s:DefineMainCommands()
-augroup end
-
-" }}}
-
-" Commands Auto-Complete {{{
-
-" Auto-complete function for commands that take repo-relative file paths.
-function! s:ListRepoFiles(ArgLead, CmdLine, CursorPos) abort
-    let l:matches = s:hg_repo().Glob(a:ArgLead . '*', 1)
-    call map(l:matches, 's:normalizepath(v:val)')
-    return l:matches
-endfunction
-
-" Auto-complete function for commands that take repo-relative directory paths.
-function! s:ListRepoDirs(ArgLead, CmdLine, CursorPos) abort
-    let l:matches = s:hg_repo().Glob(a:ArgLead . '*/')
-    call map(l:matches, 's:normalizepath(v:val)')
-    return l:matches
-endfunction
-
-" }}}
-
-" Hg {{{
-
-function! s:Hg(bang, ...) abort
-    let l:repo = s:hg_repo()
-    if g:lawrencium_auto_cd
-        " Temporary set the current directory to the root of the repo
-        " to make auto-completed paths work magically.
-        execute 'cd! ' . fnameescape(l:repo.root_dir)
-    endif
-    let l:output = call(l:repo.RunCommandEx, [0] + a:000, l:repo)
-    if g:lawrencium_auto_cd
-        execute 'cd! -'
-    endif
-    silent doautocmd User HgCmdPost
-    if a:bang
-        " Open the output of the command in a temp file.
-        let l:temp_file = s:tempname('hg-output-', '.txt')
-        split
-        execute 'edit ' . fnameescape(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
-    endif
-endfunction
-
-" 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 " . fnameescape(s:usage_file)
-else
-    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 " . fnameescape(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')
-        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]+$'
-        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 && 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
-
-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>)")
-
-" }}}
-
-" Hgstatus {{{
-
-function! s:HgStatus() abort
-    " Get the repo and the Lawrencium path for `hg status`.
-    let l:repo = s:hg_repo()
-    let l:status_path = l:repo.GetLawrenciumPath('', 'status', '')
-
-    " Open the Lawrencium buffer in a new split window of the right size.
-    if g:lawrencium_status_win_split_above
-      execute "keepalt leftabove split " . fnameescape(l:status_path)
-    else
-      execute "keepalt rightbelow split " . fnameescape(l:status_path)
-    endif
-    
-    if (line('$') == 1 && getline(1) == '')
-        " Buffer is empty, which means there are not changes...
-        " Quit and display a message.
-        " TODO: figure out why the first `echom` doesn't show when alone.
-        bdelete
-        echom "Nothing was modified."
-        echom ""
-        return
-    endif
-
-    execute "setlocal winfixheight"
-    if !g:lawrencium_status_win_split_even
-      execute "setlocal winheight=" . (line('$') + 1)
-      execute "resize " . (line('$') + 1)
-    endif
-
-    " Add some nice commands.
-    command! -buffer          Hgstatusedit          :call s:HgStatus_FileEdit(0)
-    command! -buffer          Hgstatusdiff          :call s:HgStatus_Diff(0)
-    command! -buffer          Hgstatusvdiff         :call s:HgStatus_Diff(1)
-    command! -buffer          Hgstatustabdiff       :call s:HgStatus_Diff(2)
-    command! -buffer          Hgstatusdiffsum       :call s:HgStatus_DiffSummary(1)
-    command! -buffer          Hgstatusvdiffsum      :call s:HgStatus_DiffSummary(2)
-    command! -buffer          Hgstatustabdiffsum    :call s:HgStatus_DiffSummary(3)
-    command! -buffer          Hgstatusrefresh       :call s:HgStatus_Refresh()
-    command! -buffer -range -bang Hgstatusrevert    :call s:HgStatus_Revert(<line1>, <line2>, <bang>0)
-    command! -buffer -range   Hgstatusaddremove     :call s:HgStatus_AddRemove(<line1>, <line2>)
-    command! -buffer -range=% -bang Hgstatuscommit  :call s:HgStatus_Commit(<line1>, <line2>, <bang>0, 0)
-    command! -buffer -range=% -bang Hgstatusvcommit :call s:HgStatus_Commit(<line1>, <line2>, <bang>0, 1)
-    command! -buffer -range=% -nargs=+ Hgstatusqnew :call s:HgStatus_QNew(<line1>, <line2>, <f-args>)
-    command! -buffer -range=% Hgstatusqrefresh      :call s:HgStatus_QRefresh(<line1>, <line2>)
-
-    " Add some handy mappings.
-    if g:lawrencium_define_mappings
-        nnoremap <buffer> <silent> <cr>  :Hgstatusedit<cr>
-        nnoremap <buffer> <silent> <C-N> :call search('^[MARC\!\?I ]\s.', 'We')<cr>
-        nnoremap <buffer> <silent> <C-P> :call search('^[MARC\!\?I ]\s.', 'Wbe')<cr>
-        nnoremap <buffer> <silent> <C-D> :Hgstatustabdiff<cr>
-        nnoremap <buffer> <silent> <C-V> :Hgstatusvdiff<cr>
-        nnoremap <buffer> <silent> <C-U> :Hgstatusdiffsum<cr>
-        nnoremap <buffer> <silent> <C-H> :Hgstatusvdiffsum<cr>
-        nnoremap <buffer> <silent> <C-A> :Hgstatusaddremove<cr>
-        nnoremap <buffer> <silent> <C-S> :Hgstatuscommit<cr>
-        nnoremap <buffer> <silent> <C-R> :Hgstatusrefresh<cr>
-        nnoremap <buffer> <silent> q     :bdelete!<cr>
-
-        vnoremap <buffer> <silent> <C-A> :Hgstatusaddremove<cr>
-        vnoremap <buffer> <silent> <C-S> :Hgstatuscommit<cr>
-    endif
-endfunction
-
-function! s:HgStatus_Refresh(...) abort
-    if a:0 > 0
-        let l:win_nr = bufwinnr(a:1)
-        call s:trace("Switching back to status window ".l:win_nr)
-        if l:win_nr < 0
-            call s:throw("Can't find the status window anymore!")
-        endif
-        execute l:win_nr . 'wincmd w'
-        " Delete everything in the buffer, and re-read the status into it.
-        " TODO: In theory I would only have to do `edit` like below when we're
-        " already in the window, but for some reason Vim just goes bonkers and
-        " weird shit happens. I have no idea why, hence the work-around here
-        " to bypass the whole `BufReadCmd` auto-command altogether, and just
-        " edit the buffer in place.
-        normal! ggVGd
-        call s:ReadLawrenciumFile(b:lawrencium_path)
-        return
-    endif
-
-    " Just re-edit the buffer, it will reload the contents by calling
-    " the matching Mercurial command.
-    edit
-endfunction
-
-function! s:HgStatus_FileEdit(newtab) abort
-    " Get the path of the file the cursor is on.
-    let l:filename = s:HgStatus_GetSelectedFile()
-
-    let l:cleanupbufnr = -1
-    if a:newtab == 0
-        " If the file is already open in a window, jump to that window.
-        " Otherwise, jump to the previous window and open it there.
-        for nr in range(1, winnr('$'))
-            let l:br = winbufnr(nr)
-            let l:bpath = fnamemodify(bufname(l:br), ':p')
-            if l:bpath ==# l:filename
-                execute nr . 'wincmd w'
-                return
-            endif
-        endfor
-        wincmd p
-    else
-        " Just open a new tab so we can edit the file there.
-        " We don't use `tabedit` because it messes up the current window
-        " if it happens to be the same file.
-        " We'll just have to clean up the default empty buffer created.
-        tabnew
-        let l:cleanupbufnr = bufnr('%')
-    endif
-    execute 'edit ' . fnameescape(l:filename)
-    if l:cleanupbufnr >= 0
-        execute 'bdelete ' . l:cleanupbufnr
-    endif
-endfunction
-
-function! s:HgStatus_AddRemove(linestart, lineend) abort
-    " Get the selected filenames.
-    let l:filenames = s:HgStatus_GetSelectedFiles(a:linestart, a:lineend, ['!', '?'])
-    if len(l:filenames) == 0
-        call s:error("No files to add or remove in selection or current line.")
-        return
-    endif
-
-    " Run `addremove` on those paths.
-    let l:repo = s:hg_repo()
-    call l:repo.RunCommand('addremove', l:filenames)
-
-    " Refresh the status window.
-    call s:HgStatus_Refresh()
-endfunction
-
-function! s:HgStatus_Revert(linestart, lineend, bang) abort
-    " Get the selected filenames.
-    let l:filenames = s:HgStatus_GetSelectedFiles(a:linestart, a:lineend, ['M', 'A', 'R'])
-    if len(l:filenames) == 0
-        call s:error("No files to revert in selection or current line.")
-        return
-    endif
-
-    " Run `revert` on those paths.
-    " If the bang modifier is specified, revert with no backup.
-    let l:repo = s:hg_repo()
-    if a:bang
-        call insert(l:filenames, '-C', 0)
-    endif
-    call l:repo.RunCommand('revert', l:filenames)
-
-    " Refresh the status window.
-    call s:HgStatus_Refresh()
-endfunction
-
-function! s:HgStatus_Commit(linestart, lineend, bang, vertical) abort
-    " Get the selected filenames.
-    let l:filenames = s:HgStatus_GetSelectedFiles(a:linestart, a:lineend, ['M', 'A', 'R'])
-    if len(l:filenames) == 0
-        call s:error("No files to commit in selection or file.")
-        return
-    endif
-
-    " Run `Hgcommit` on those paths.
-    let l:buf_nr = bufnr('%')
-    let l:callback = 'call s:HgStatus_Refresh('.l:buf_nr.')'
-    call s:HgCommit(a:bang, a:vertical, l:callback, l:filenames)
-endfunction
-
-function! s:HgStatus_Diff(split) abort
-    " Open the file and run `Hgdiff` on it.
-    " We also need to translate the split mode for it... if we already
-    " opened the file in a new tab, `HgDiff` only needs to do a vertical
-    " split (i.e. split=1).
-    let l:newtab = 0
-    let l:hgdiffsplit = a:split
-    if a:split == 2
-        let l:newtab = 1
-        let l:hgdiffsplit = 1
-    endif
-    call s:HgStatus_FileEdit(l:newtab)
-    call s:HgDiff('%:p', l:hgdiffsplit)
-endfunction
-
-function! s:HgStatus_DiffSummary(split) abort
-    " Get the path of the file the cursor is on.
-    let l:path = s:HgStatus_GetSelectedFile()
-    " Reuse the same diff summary window
-    let l:reuse_id = 'lawrencium_diffsum_for_' . bufnr('%')
-    let l:split_prev_win = (a:split < 3)
-    let l:args = {'reuse_id': l:reuse_id, 'use_prev_win': l:split_prev_win,
-                \'avoid_win': winnr(), 'split_mode': a:split}
-    call s:HgDiffSummary(l:path, l:args)
-endfunction
-
-function! s:HgStatus_QNew(linestart, lineend, patchname, ...) abort
-    " Get the selected filenames.
-    let l:filenames = s:HgStatus_GetSelectedFiles(a:linestart, a:lineend, ['M', 'A', 'R'])
-    if len(l:filenames) == 0
-        call s:error("No files in selection or file to create patch.")
-        return
-    endif
-
-    " Run `Hg qnew` on those paths.
-    let l:repo = s:hg_repo()
-    call insert(l:filenames, a:patchname, 0)
-    if a:0 > 0
-        call insert(l:filenames, '-m', 0)
-        let l:message = '"' . join(a:000, ' ') . '"'
-        call insert(l:filenames, l:message, 1)
-    endif
-    call l:repo.RunCommand('qnew', l:filenames)
-
-    " Refresh the status window.
-    call s:HgStatus_Refresh()
-endfunction
-
-function! s:HgStatus_QRefresh(linestart, lineend) abort
-    " Get the selected filenames.
-    let l:filenames = s:HgStatus_GetSelectedFiles(a:linestart, a:lineend, ['M', 'A', 'R'])
-    if len(l:filenames) == 0
-        call s:error("No files in selection or file to refresh the patch.")
-        return
-    endif
-
-    " Run `Hg qrefresh` on those paths.
-    let l:repo = s:hg_repo()
-    call insert(l:filenames, '-s', 0)
-    call l:repo.RunCommand('qrefresh', l:filenames)
-
-    " Refresh the status window.
-    call s:HgStatus_Refresh()
-endfunction
-
-
-function! s:HgStatus_GetSelectedFile() abort
-    let l:filenames = s:HgStatus_GetSelectedFiles()
-    return l:filenames[0]
-endfunction
-
-function! s:HgStatus_GetSelectedFiles(...) abort
-    if a:0 >= 2
-        let l:lines = getline(a:1, a:2)
-    else
-        let l:lines = []
-        call add(l:lines, getline('.'))
-    endif
-    let l:filenames = []
-    let l:repo = s:hg_repo()
-    for line in l:lines
-        if a:0 >= 3
-            let l:status = s:HgStatus_GetFileStatus(line)
-            if index(a:3, l:status) < 0
-                continue
-            endif
-        endif
-        " 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 = l:repo.GetFullPath(l:filename)
-        call add(l:filenames, l:filename)
-    endfor
-    return l:filenames
-endfunction
-
-function! s:HgStatus_GetFileStatus(...) abort
-    let l:line = a:0 ? a:1 : getline('.')
-    return matchstr(l:line, '\v^[MARC\!\?I ]')
-endfunction
-
-call s:AddMainCommand("Hgstatus :call s:HgStatus()")
-
-" }}}
-
-" Hgcd, Hglcd {{{
-
-call s:AddMainCommand("-bang -nargs=? -complete=customlist,s:ListRepoDirs Hgcd :cd<bang> `=s:hg_repo().GetFullPath(<q-args>)`")
-call s:AddMainCommand("-bang -nargs=? -complete=customlist,s:ListRepoDirs Hglcd :lcd<bang> `=s:hg_repo().GetFullPath(<q-args>)`")
-
-" }}}
-
-" Hgedit {{{
-
-function! s:HgEdit(bang, filename) abort
-    let l:full_path = s:hg_repo().GetFullPath(a:filename)
-    if a:bang
-        execute "edit! " . fnameescape(l:full_path)
-    else
-        execute "edit " . fnameescape(l:full_path)
-    endif
-endfunction
-
-call s:AddMainCommand("-bang -nargs=1 -complete=customlist,s:ListRepoFiles Hgedit :call s:HgEdit(<bang>0, <f-args>)")
-
-" }}}
-
-" Hgvimgrep {{{
-
-function! s:HgVimGrep(bang, pattern, ...) abort
-    let l:repo = s:hg_repo()
-    let l:file_paths = []
-    if a:0 > 0
-        for ff in a:000
-            let l:full_ff = l:repo.GetFullPath(ff)
-            call add(l:file_paths, l:full_ff)
-        endfor
-    else
-        call add(l:file_paths, l:repo.root_dir . "**")
-    endif
-    if a:bang
-        execute "vimgrep! " . a:pattern . " " . join(l:file_paths, " ")
-    else
-        execute "vimgrep " . a:pattern . " " . join(l:file_paths, " ")
-    endif
-endfunction
-
-call s:AddMainCommand("-bang -nargs=+ -complete=customlist,s:ListRepoFiles Hgvimgrep :call s:HgVimGrep(<bang>0, <f-args>)")
-
-" }}}
-
-" Hgdiff, Hgvdiff, Hgtabdiff {{{
-
-function! s:HgDiff(filename, split, ...) abort
-    " Default revisions to diff: the working directory (null string) 
-    " and the parent of the working directory (using Mercurial's revsets syntax).
-    " Otherwise, use the 1 or 2 revisions specified as extra parameters.
-    let l:rev1 = 'p1()'
-    let l:rev2 = ''
-    if a:0 == 1
-        if type(a:1) == type([])
-            if len(a:1) >= 2
-                let l:rev1 = a:1[0]
-                let l:rev2 = a:1[1]
-            elseif len(a:1) == 1
-                let l:rev1 = a:1[0]
-            endif
-        else
-            let l:rev1 = a:1
-        endif
-    elseif a:0 == 2
-        let l:rev1 = a:1
-        let l:rev2 = a:2
-    endif
-
-    " Get the current repo, and expand the given filename in case it contains
-    " fancy filename modifiers.
-    let l:repo = s:hg_repo()
-    let l:path = expand(a:filename)
-    let l:diff_id = localtime()
-    call s:trace("Diff'ing '".l:rev1."' and '".l:rev2."' on file: ".l:path)
-
-    " Get the first file and open it.
-    let l:cleanupbufnr = -1
-    if l:rev1 == ''
-        if a:split == 2
-            " Don't use `tabedit` here because if `l:path` is the same as
-            " the current path, it will also reload the buffer in the current
-            " tab/window for some reason, which causes all state to be lost
-            " (all folds get collapsed again, cursor is moved to start, etc.)
-            tabnew
-            let l:cleanupbufnr = bufnr('%')
-            execute 'edit ' . fnameescape(l:path)
-        else
-            if bufexists(l:path)
-                execute 'buffer ' . fnameescape(l:path)
-            else
-                execute 'edit ' . fnameescape(l:path)
-            endif
-        endif
-        " Make it part of the diff group.
-        call s:HgDiff_DiffThis(l:diff_id)
-    else
-        let l:rev_path = l:repo.GetLawrenciumPath(l:path, 'rev', l:rev1)
-        if a:split == 2
-            " See comments above about avoiding `tabedit`.
-            tabnew
-            let l:cleanupbufnr = bufnr('%')
-        endif
-        execute 'edit ' . fnameescape(l:rev_path)
-        " Make it part of the diff group.
-        call s:HgDiff_DiffThis(l:diff_id)
-    endif
-    if l:cleanupbufnr >= 0 && bufloaded(l:cleanupbufnr)
-        execute 'bdelete ' . l:cleanupbufnr
-    endif
-
-    " Get the second file and open it too.
-    " Don't use `diffsplit` because it will set `&diff` before we get a chance
-    " to save a bunch of local settings that we will want to restore later.
-    let l:diffsplit = 'split'
-    if a:split >= 1
-        let l:diffsplit = 'vsplit'
-    endif
-    if l:rev2 == ''
-        execute l:diffsplit . ' ' . fnameescape(l:path)
-    else
-        let l:rev_path = l:repo.GetLawrenciumPath(l:path, 'rev', l:rev2)
-        execute l:diffsplit . ' ' . fnameescape(l:rev_path)
-    endif
-    call s:HgDiff_DiffThis(l:diff_id)
-endfunction
-
-function! s:HgDiff_DiffThis(diff_id) abort
-    " Store some commands to run when we exit diff mode.
-    " It's needed because `diffoff` reverts those settings to their default
-    " values, instead of their previous ones.
-    if &diff
-        call s:throw("Calling diffthis too late on a buffer!")
-        return
-    endif
-    call s:trace('Enabling diff mode on ' . bufname('%'))
-    let w:lawrencium_diffoff = {}
-    let w:lawrencium_diffoff['&diff'] = 0
-    let w:lawrencium_diffoff['&wrap'] = &l:wrap
-    let w:lawrencium_diffoff['&scrollopt'] = &l:scrollopt
-    let w:lawrencium_diffoff['&scrollbind'] = &l:scrollbind
-    let w:lawrencium_diffoff['&cursorbind'] = &l:cursorbind
-    let w:lawrencium_diffoff['&foldmethod'] = &l:foldmethod
-    let w:lawrencium_diffoff['&foldcolumn'] = &l:foldcolumn
-    let w:lawrencium_diffoff['&foldenable'] = &l:foldenable
-    let w:lawrencium_diff_id = a:diff_id
-    diffthis
-    autocmd BufWinLeave <buffer> call s:HgDiff_CleanUp()
-endfunction
-
-function! s:HgDiff_DiffOff(...) abort
-    " Get the window name (given as a paramter, or current window).
-    let l:nr = a:0 ? a:1 : winnr()
-
-    " Run the commands we saved in `HgDiff_DiffThis`, or just run `diffoff`.
-    let l:backup = getwinvar(l:nr, 'lawrencium_diffoff')
-    if type(l:backup) == type({}) && len(l:backup) > 0
-        call s:trace('Disabling diff mode on ' . l:nr)
-        for key in keys(l:backup)
-            call setwinvar(l:nr, key, l:backup[key])
-        endfor
-        call setwinvar(l:nr, 'lawrencium_diffoff', {})
-    else
-        call s:trace('Disabling diff mode on ' . l:nr . ' (but no true restore)')
-        diffoff
-    endif
-endfunction
-
-function! s:HgDiff_GetDiffWindows(diff_id) abort
-    let l:result = []
-    for nr in range(1, winnr('$'))
-        if getwinvar(nr, '&diff') && getwinvar(nr, 'lawrencium_diff_id') == a:diff_id
-            call add(l:result, nr)
-        endif
-    endfor
-    return l:result
-endfunction
-
-function! s:HgDiff_CleanUp() abort
-    " If we're not leaving one of our diff window, do nothing.
-    if !&diff || !exists('w:lawrencium_diff_id')
-        return
-    endif
-
-    " If there will be only one diff window left (plus the one we're leaving),
-    " turn off diff in it and restore its local settings.
-    let l:nrs = s:HgDiff_GetDiffWindows(w:lawrencium_diff_id)
-    if len(l:nrs) <= 2
-        call s:trace('Disabling diff mode in ' . len(l:nrs) . ' windows.')
-        for nr in l:nrs
-            if getwinvar(nr, '&diff')
-                call s:HgDiff_DiffOff(nr)
-            endif
-        endfor
-    else
-        call s:trace('Still ' . len(l:nrs) . ' diff windows open.')
-    endif
-endfunction
-
-call s:AddMainCommand("-nargs=* Hgdiff :call s:HgDiff('%:p', 0, <f-args>)")
-call s:AddMainCommand("-nargs=* Hgvdiff :call s:HgDiff('%:p', 1, <f-args>)")
-call s:AddMainCommand("-nargs=* Hgtabdiff :call s:HgDiff('%:p', 2, <f-args>)")
-
-" }}}
-
-" Hgdiffsum, Hgdiffsumsplit, Hgvdiffsumsplit, Hgtabdiffsum {{{
-
-function! s:HgDiffSummary(filename, present_args, ...) abort
-    " Default revisions to diff: the working directory (null string) 
-    " and the parent of the working directory (using Mercurial's revsets syntax).
-    " Otherwise, use the 1 or 2 revisions specified as extra parameters.
-    let l:revs = ''
-    if a:0 == 1
-        if type(a:1) == type([])
-            if len(a:1) >= 2
-                let l:revs = a:1[0] . ',' . a:1[1]
-            elseif len(a:1) == 1
-                let l:revs = a:1[0]
-            endif
-        else
-            let l:revs = a:1
-        endif
-    elseif a:0 >= 2
-        let l:revs = a:1 . ',' . a:2
-    endif
-
-    " Get the current repo, and expand the given filename in case it contains
-    " fancy filename modifiers.
-    let l:repo = s:hg_repo()
-    let l:path = expand(a:filename)
-    call s:trace("Diff'ing revisions: '".l:revs."' on file: ".l:path)
-    let l:special = l:repo.GetLawrenciumPath(l:path, 'diff', l:revs)
-
-    " Build the correct edit command, and switch to the correct window, based
-    " on the presentation arguments we got.
-    if type(a:present_args) == type(0)
-        " Just got a split mode.
-        let l:valid_args = {'split_mode': a:present_args}
-    else
-        " Got complex args.
-        let l:valid_args = a:present_args
-    endif
-
-    " First, see if we should reuse an existing window based on some buffer
-    " variable.
-    let l:target_winnr = -1
-    let l:split = get(l:valid_args, 'split_mode', 0)
-    let l:reuse_id = get(l:valid_args, 'reuse_id', '')
-    let l:avoid_id = get(l:valid_args, 'avoid_win', -1)
-    if l:reuse_id != ''
-        let l:target_winnr = s:find_buffer_window(l:reuse_id, 1)
-        if l:target_winnr > 0 && l:split != 3
-            " Unless we'll be opening in a new tab, don't split anymore, since
-            " we found the exact window we wanted.
-            let l:split = 0
-        endif
-        call s:trace("Looking for window with '".l:reuse_id."', found: ".l:target_winnr)
-    endif
-    " If we didn't find anything, see if we should use the current or previous
-    " window.
-    if l:target_winnr <= 0
-        let l:use_prev_win = get(l:valid_args, 'use_prev_win', 0)
-        if l:use_prev_win
-            let l:target_winnr = winnr('#')
-            call s:trace("Will use previous window: ".l:target_winnr)
-        endif
-    endif
-    " And let's see if we have a window we should actually avoid.
-    if l:avoid_id >= 0 && 
-                \(l:target_winnr == l:avoid_id ||
-                \(l:target_winnr <= 0 && winnr() == l:avoid_id))
-        for wnr in range(1, winnr('$'))
-            if wnr != l:avoid_id
-                call s:trace("Avoiding using window ".l:avoid_id.
-                            \", now using: ".wnr)
-                let l:target_winnr = wnr
-                break
-            endif
-        endfor
-    endif
-    " Now let's see what kind of split we want to use, if any.
-    let l:cmd = 'edit '
-    if l:split == 1
-        let l:cmd = 'rightbelow split '
-    elseif l:split == 2
-        let l:cmd = 'rightbelow vsplit '
-    elseif l:split == 3
-        let l:cmd = 'tabedit '
-    endif
-    
-    " All good now, proceed.
-    if l:target_winnr > 0
-        execute l:target_winnr . "wincmd w"
-    endif
-    execute 'keepalt ' . l:cmd . fnameescape(l:special)
-
-    " Set the reuse ID if we had one.
-    if l:reuse_id != ''
-        call s:trace("Setting reuse ID '".l:reuse_id."' on buffer: ".bufnr('%'))
-        call setbufvar('%', l:reuse_id, 1)
-    endif
-endfunction
-
-call s:AddMainCommand("-nargs=* Hgdiffsum       :call s:HgDiffSummary('%:p', 0, <f-args>)")
-call s:AddMainCommand("-nargs=* Hgdiffsumsplit  :call s:HgDiffSummary('%:p', 1, <f-args>)")
-call s:AddMainCommand("-nargs=* Hgvdiffsumsplit :call s:HgDiffSummary('%:p', 2, <f-args>)")
-call s:AddMainCommand("-nargs=* Hgtabdiffsum    :call s:HgDiffSummary('%:p', 3, <f-args>)")
-
-" }}}
-
-" Hgcommit {{{
-
-function! s:HgCommit(bang, vertical, callback, ...) abort
-    " Get the repo we'll be committing into.
-    let l:repo = s:hg_repo()
-
-    " Get the list of files to commit.
-    " It can either be several files passed as extra parameters, or an
-    " actual list passed as the first extra parameter.
-    let l:filenames = []
-    if a:0
-        let l:filenames = a:000
-        if a:0 == 1 && type(a:1) == type([])
-            let l:filenames = a:1
-        endif
-    endif
-
-    " Open a commit message file.
-    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, ['', ''])
-    call append(2, split(s:HgCommit_GenerateMessage(l:repo, l:filenames), '\n'))
-    call cursor(1, 1)
-
-    " Setup the auto-command that will actually commit on write/exit,
-    " and make the buffer delete itself on exit.
-    let b:mercurial_dir = l:repo.root_dir
-    let b:lawrencium_commit_files = l:filenames
-    if type(a:callback) == type([])
-        let b:lawrencium_commit_pre_callback = a:callback[0]
-        let b:lawrencium_commit_post_callback = a:callback[1]
-        let b:lawrencium_commit_abort_callback = a:callback[2]
-    else
-        let b:lawrencium_commit_pre_callback = 0
-        let b:lawrencium_commit_post_callback = a:callback
-        let b:lawrencium_commit_abort_callback = 0
-    endif
-    setlocal bufhidden=delete
-    setlocal filetype=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)
-    endif
-    " Make commands available.
-    call s:DefineMainCommands()
-endfunction
-
-let s:hg_status_messages = { 
-    \'M': 'modified',
-    \'A': 'added',
-    \'R': 'removed',
-    \'C': 'clean',
-    \'!': 'missing',
-    \'?': 'not tracked',
-    \'I': 'ignored',
-    \' ': '',
-    \}
-
-function! s:HgCommit_GenerateMessage(repo, filenames) abort
-    let l:msg  = "HG: Enter commit message. Lines beginning with 'HG:' are removed.\n"
-    let l:msg .= "HG: Leave message empty to abort commit.\n"
-    let l:msg .= "HG: Write and quit buffer to proceed.\n"
-    let l:msg .= "HG: --\n"
-    let l:msg .= "HG: user: " . split(a:repo.RunCommand('showconfig ui.username'), '\n')[0] . "\n"
-    let l:msg .= "HG: branch '" . split(a:repo.RunCommand('branch'), '\n')[0] . "'\n"
-
-    execute 'lcd ' . fnameescape(a:repo.root_dir)
-    if len(a:filenames)
-        let l:status_lines = split(a:repo.RunCommand('status', a:filenames), "\n")
-    else
-        let l:status_lines = split(a:repo.RunCommand('status'), "\n")
-    endif
-    for l:line in l:status_lines
-        if l:line ==# ''
-            continue
-        endif
-        let l:type = matchstr(l:line, '\v^[MARC\!\?I ]')
-        let l:path = l:line[2:]
-        let l:msg .= "HG: " . s:hg_status_messages[l:type] . ' ' . l:path . "\n"
-    endfor
-
-    return l:msg
-endfunction
-
-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:error("abort: Commit message not saved")
-        if exists('b:lawrencium_commit_abort_callback') &&
-                    \type(b:lawrencium_commit_abort_callback) == type("") &&
-                    \b:lawrencium_commit_abort_callback != ''
-            call s:trace("Executing abort callback: ".b:lawrencium_commit_abort_callback)
-            execute b:lawrencium_commit_abort_callback
-        endif
-        return
-    endif
-
-    " Execute a pre-callback if there is one.
-    if exists('b:lawrencium_commit_pre_callback') &&
-                \type(b:lawrencium_commit_pre_callback) == type("") &&
-                \b:lawrencium_commit_pre_callback != ''
-        call s:trace("Executing pre callback: ".b:lawrencium_commit_pre_callback)
-        execute b:lawrencium_commit_pre_callback
-    endif
-
-    call s:trace("Committing with log file: " . a:log_file)
-
-    " Clean all the 'HG: ' lines.
-    let l:is_valid = s:clean_commit_file(a:log_file)
-    if !l:is_valid
-        call s:error("abort: Empty commit message")
-        return
-    endif
-
-    " Get the repo and commit with the given message.
-    let l:repo = s:hg_repo()
-    let l:hg_args = ['-l', a:log_file]
-    call extend(l:hg_args, b:lawrencium_commit_files)
-    let l:output = l:repo.RunCommand('commit', l:hg_args)
-    if a:show_output && l:output !~# '\v%^\s*%$'
-        call s:trace("Output from hg commit:", 1)
-        for l:output_line in split(l:output, '\n')
-            echom l:output_line
-        endfor
-    endif
-
-    " Execute a post-callback if there is one.
-    if exists('b:lawrencium_commit_post_callback') &&
-                \type(b:lawrencium_commit_post_callback) == type("") &&
-                \b:lawrencium_commit_post_callback != ''
-        call s:trace("Executing post callback: ".b:lawrencium_commit_post_callback)
-        execute b:lawrencium_commit_post_callback
-    endif
-endfunction
-
-call s:AddMainCommand("-bang -nargs=* -complete=customlist,s:ListRepoFiles Hgcommit :call s:HgCommit(<bang>0, 0, 0, <f-args>)")
-call s:AddMainCommand("-bang -nargs=* -complete=customlist,s:ListRepoFiles Hgvcommit :call s:HgCommit(<bang>0, 1, 0, <f-args>)")
-
-" }}}
-
-" Hgrevert {{{
-
-function! s:HgRevert(bang, ...) abort
-    " Get the files to revert.
-    let l:filenames = a:000
-    if a:0 == 0
-        let l:filenames = [ expand('%:p') ]
-    endif
-    if a:bang
-        call insert(l:filenames, '--no-backup', 0)
-    endif
-
-    " Get the repo and run the command.
-    let l:repo = s:hg_repo()
-    call l:repo.RunCommand('revert', l:filenames)
-
-    " Re-edit the file to see the change.
-    edit
-endfunction
-
-call s:AddMainCommand("-bang -nargs=* -complete=customlist,s:ListRepoFiles Hgrevert :call s:HgRevert(<bang>0, <f-args>)")
-
-" }}}
-
-" Hgremove {{{
-
-function! s:HgRemove(bang, ...) abort
-    " Get the files to remove.
-    let l:filenames = a:000
-    if a:0 == 0
-        let l:filenames = [ expand('%:p') ]
-    endif
-    if a:bang
-        call insert(l:filenames, '--force', 0)
-    endif
-
-    " Get the repo and run the command.
-    let l:repo = s:hg_repo()
-    call l:repo.RunCommand('rm', l:filenames)
-
-    " Re-edit the file to see the change.
-    edit
-endfunction
-
-call s:AddMainCommand("-bang -nargs=* -complete=customlist,s:ListRepoFiles Hgremove :call s:HgRemove(<bang>0, <f-args>)")
-
-" }}}
-
-" Hglog, Hglogthis {{{
-
-function! s:HgLog(vertical, ...) abort
-    " Get the file or directory to get the log from.
-    " (empty string is for the whole repository)
-    let l:repo = s:hg_repo()
-    if a:0 > 0 && matchstr(a:1, '\v-*') == ""
-        let l:path = l:repo.GetRelativePath(expand(a:1))
-    else
-        let l:path = ''
-    endif
-
-    " Get the Lawrencium path for this `hg log`,
-    " open it in a preview window and jump to it.
-    if a:0 > 0 && l:path != ""
-      let l:log_opts = join(a:000[1:-1], ',')
-    else
-      let l:log_opts = join(a:000, ',')
-    endif
-
-    let l:log_path = l:repo.GetLawrenciumPath(l:path, 'log', l:log_opts)
-    if a:vertical
-        execute 'vertical pedit ' . fnameescape(l:log_path)
-    else
-        execute 'pedit ' . fnameescape(l:log_path)
-    endif
-    wincmd P
-
-    " Add some other nice commands and mappings.
-    let l:is_file = (l:path != '' && filereadable(l:repo.GetFullPath(l:path)))
-    command! -buffer -nargs=* Hglogdiffsum    :call s:HgLog_DiffSummary(1, <f-args>)
-    command! -buffer -nargs=* Hglogvdiffsum   :call s:HgLog_DiffSummary(2, <f-args>)
-    command! -buffer -nargs=* Hglogtabdiffsum :call s:HgLog_DiffSummary(3, <f-args>)
-    command! -buffer -nargs=+ -complete=file Hglogexport :call s:HgLog_ExportPatch(<f-args>)
-    if l:is_file
-        command! -buffer Hglogrevedit          :call s:HgLog_FileRevEdit()
-        command! -buffer -nargs=* Hglogdiff    :call s:HgLog_Diff(0, <f-args>)
-        command! -buffer -nargs=* Hglogvdiff   :call s:HgLog_Diff(1, <f-args>)
-        command! -buffer -nargs=* Hglogtabdiff :call s:HgLog_Diff(2, <f-args>)
-    endif
-
-    if g:lawrencium_define_mappings
-        nnoremap <buffer> <silent> <C-U> :Hglogdiffsum<cr>
-        nnoremap <buffer> <silent> <C-H> :Hglogvdiffsum<cr>
-        nnoremap <buffer> <silent> <cr>  :Hglogvdiffsum<cr>
-        nnoremap <buffer> <silent> q     :bdelete!<cr>
-        if l:is_file
-            nnoremap <buffer> <silent> <C-E>  :Hglogrevedit<cr>
-            nnoremap <buffer> <silent> <C-D>  :Hglogtabdiff<cr>
-            nnoremap <buffer> <silent> <C-V>  :Hglogvdiff<cr>
-        endif
-    endif
-
-    " Clean up when the log buffer is deleted.
-    let l:bufobj = s:buffer_obj()
-    call l:bufobj.OnDelete('call s:HgLog_Delete(' . l:bufobj.nr . ')')
-endfunction
-
-function! s:HgLog_Delete(bufnr)
-    if g:lawrencium_auto_close_buffers
-        call s:delete_dependency_buffers('lawrencium_diff_for', a:bufnr)
-        call s:delete_dependency_buffers('lawrencium_rev_for', a:bufnr)
-    endif
-endfunction
-
-function! s:HgLog_FileRevEdit()
-    let l:repo = s:hg_repo()
-    let l:bufobj = s:buffer_obj()
-    let l:rev = s:HgLog_GetSelectedRev()
-    let l:log_path = s:parse_lawrencium_path(l:bufobj.GetName())
-    let l:path = l:repo.GetLawrenciumPath(l:log_path['path'], 'rev', l:rev)
-
-    " Go to the window we were in before going in the log window,
-    " and open the revision there.
-    wincmd p
-    call s:edit_deletable_buffer('lawrencium_rev_for', l:bufobj.nr, l:path)
-endfunction
-
-function! s:HgLog_Diff(split, ...) abort
-    let l:revs = []
-    if a:0 >= 2
-        let l:revs = [a:1, a:2]
-    elseif a:0 == 1
-        let l:revs = ['p1('.a:1.')', a:1]
-    else
-        let l:sel = s:HgLog_GetSelectedRev()
-        let l:revs = ['p1('.l:sel.')', l:sel]
-    endif
-
-    let l:repo = s:hg_repo()
-    let l:bufobj = s:buffer_obj()
-    let l:log_path = s:parse_lawrencium_path(l:bufobj.GetName())
-    let l:path = l:repo.GetFullPath(l:log_path['path'])
-
-    " Go to the window we were in before going to the log window,
-    " and open the split diff there.
-    if a:split < 2
-        wincmd p
-    endif
-    call s:HgDiff(l:path, a:split, l:revs)
-endfunction
-
-function! s:HgLog_DiffSummary(split, ...) abort
-    let l:revs = []
-    if a:0 >= 2
-        let l:revs = [a:1, a:2]
-    elseif a:0 == 1
-        let l:revs = [a:1]
-    else
-        let l:revs = [s:HgLog_GetSelectedRev()]
-    endif
-
-    let l:repo = s:hg_repo()
-    let l:bufobj = s:buffer_obj()
-    let l:log_path = s:parse_lawrencium_path(l:bufobj.GetName())
-    let l:path = l:repo.GetFullPath(l:log_path['path'])
-
-    " Go to the window we were in before going in the log window,
-    " and split for the diff summary from there.
-    let l:reuse_id = 'lawrencium_diffsum_for_' . bufnr('%')
-    let l:split_prev_win = (a:split < 3)
-    let l:args = {'reuse_id': l:reuse_id, 'use_prev_win': l:split_prev_win,
-                \'split_mode': a:split}
-    call s:HgDiffSummary(l:path, l:args, l:revs)
-endfunction
-
-function! s:HgLog_GetSelectedRev(...) abort
-    if a:0 == 1
-        let l:line = getline(a:1)
-    else
-        let l:line = getline('.')
-    endif
-    " Behold, Vim's look-ahead regex syntax again! WTF.
-    let l:rev = matchstr(l:line, '\v^(\d+)(\:)@=')
-    if l:rev == ''
-        call s:throw("Can't parse revision number from line: " . l:line)
-    endif
-    return l:rev
-endfunction
-
-function! s:HgLog_ExportPatch(...) abort
-    let l:patch_name = a:1
-    if !empty($HG_EXPORT_PATCH_DIR)
-        " Use the patch dir only if user has specified a relative path
-        if has('win32')
-            let l:is_patch_relative = (matchstr(l:patch_name, '\v^([a-zA-Z]:)?\\') == "")
-        else
-            let l:is_patch_relative = (matchstr(l:patch_name, '\v^/') == "")
-        endif
-        if l:is_patch_relative
-            let l:patch_name = s:normalizepath(
-                s:stripslash($HG_EXPORT_PATCH_DIR) . "/" . l:patch_name)
-        endif
-    endif
-
-    if a:0 == 2
-        let l:rev = a:2
-    else
-        let l:rev = s:HgLog_GetSelectedRev()
-    endif
-
-    let l:repo = s:hg_repo()
-    let l:export_args = ['-o', l:patch_name, '-r', l:rev]
-
-    call l:repo.RunCommand('export', l:export_args)
-
-    echom "Created patch: " . l:patch_name
-endfunction
-
-call s:AddMainCommand("Hglogthis  :call s:HgLog(0, '%:p')")
-call s:AddMainCommand("Hgvlogthis :call s:HgLog(1, '%:p')")
-call s:AddMainCommand("-nargs=* -complete=customlist,s:ListRepoFiles Hglog  :call s:HgLog(0, <f-args>)")
-call s:AddMainCommand("-nargs=* -complete=customlist,s:ListRepoFiles Hgvlog  :call s:HgLog(1, <f-args>)")
-
-" }}}
-
-" Hgannotate, Hgwannotate {{{
-
-function! s:HgAnnotate(bang, verbose, ...) abort
-    " Open the file to annotate if needed.
-    if a:0 > 0
-        call s:HgEdit(a:bang, a:1)
-    endif
-
-    " Get the Lawrencium path for the annotated file.
-    let l:path = expand('%:p')
-    let l:bufnr = bufnr('%')
-    let l:repo = s:hg_repo()
-    let l:value = a:verbose ? 'v=1' : ''
-    let l:annotation_path = l:repo.GetLawrenciumPath(l:path, 'annotate', l:value)
-    
-    " Check if we're trying to annotate something with local changes.
-    let l:has_local_edits = 0
-    let l:path_status = l:repo.RunCommand('status', l:path)
-    if l:path_status != ''
-        call s:trace("Found local edits for '" . l:path . "'. Will annotate parent revision.")
-        let l:has_local_edits = 1
-    endif
-    
-    if l:has_local_edits
-        " Just open the output of the command.
-        echom "Local edits found, will show the annotations for the parent revision."
-        execute 'edit ' . fnameescape(l:annotation_path)
-        setlocal nowrap nofoldenable
-        setlocal filetype=hgannotate
-    else
-        " Store some info about the current buffer.
-        let l:cur_topline = line('w0') + &scrolloff
-        let l:cur_line = line('.')
-        let l:cur_wrap = &wrap
-        let l:cur_foldenable = &foldenable
-
-        " Open the annotated file in a split buffer on the left, after
-        " having disabled wrapping and folds on the current file.
-        " Make both windows scroll-bound.
-        setlocal scrollbind nowrap nofoldenable
-        execute 'keepalt leftabove vsplit ' . fnameescape(l:annotation_path)
-        setlocal nonumber
-        setlocal scrollbind nowrap nofoldenable foldcolumn=0
-        setlocal filetype=hgannotate
-
-        " When the annotated buffer is deleted, restore the settings we
-        " changed on the current buffer, and go back to that buffer.
-        let l:annotate_buffer = s:buffer_obj()
-        call l:annotate_buffer.OnDelete('execute bufwinnr(' . l:bufnr . ') . "wincmd w"')
-        call l:annotate_buffer.OnDelete('setlocal noscrollbind')
-        if l:cur_wrap
-            call l:annotate_buffer.OnDelete('setlocal wrap')
-        endif
-        if l:cur_foldenable
-            call l:annotate_buffer.OnDelete('setlocal foldenable')
-        endif
-
-        " Go to the line we were at in the source buffer when we
-        " opened the annotation window.
-        execute l:cur_topline
-        normal! zt
-        execute l:cur_line
-        syncbind
-
-        " Set the correct window width for the annotations.
-        if a:verbose
-            let l:last_token = match(getline('.'), '\v\d{4}:\s')
-            let l:token_end = 5
-        else
-            let l:last_token = match(getline('.'), '\v\d{2}:\s')
-            let l:token_end = 3
-        endif
-        if l:last_token < 0
-            echoerr "Can't find the end of the annotation columns."
-        else
-            let l:column_count = l:last_token + l:token_end + g:lawrencium_annotate_width_offset
-            execute "vertical resize " . l:column_count
-            setlocal winfixwidth
-        endif
-    endif
-
-    " Make the annotate buffer a Lawrencium buffer.
-    let b:mercurial_dir = l:repo.root_dir
-    let b:lawrencium_annotated_path = l:path
-    let b:lawrencium_annotated_bufnr = l:bufnr
-    call s:DefineMainCommands()
-
-    " Add some other nice commands and mappings.
-    command! -buffer Hgannotatediffsum :call s:HgAnnotate_DiffSummary()
-    command! -buffer Hgannotatelog     :call s:HgAnnotate_DiffSummary(1)
-    if g:lawrencium_define_mappings
-        nnoremap <buffer> <silent> <cr> :Hgannotatediffsum<cr>
-        nnoremap <buffer> <silent> <leader><cr> :Hgannotatelog<cr>
-    endif
-
-    " Clean up when the annotate buffer is deleted.
-    let l:bufobj = s:buffer_obj()
-    call l:bufobj.OnDelete('call s:HgAnnotate_Delete(' . l:bufobj.nr . ')')
-endfunction
-
-function! s:HgAnnotate_Delete(bufnr) abort
-    if g:lawrencium_auto_close_buffers
-        call s:delete_dependency_buffers('lawrencium_diff_for', a:bufnr)
-    endif
-endfunction
-
-function! s:HgAnnotate_DiffSummary(...) abort
-    " Get the path for the diff of the revision specified under the cursor.
-    let l:line = getline('.')
-    let l:rev_hash = matchstr(l:line, '\v[a-f0-9]{12}')
-    let l:log = (a:0 > 0 ? a:1 : 0)
-
-    " Get the Lawrencium path for the diff, and the buffer object for the
-    " annotation.
-    let l:repo = s:hg_repo()
-    if l:log
-      let l:path = l:repo.GetLawrenciumPath(b:lawrencium_annotated_path, 'logpatch', l:rev_hash)
-    else
-      let l:path = l:repo.GetLawrenciumPath(b:lawrencium_annotated_path, 'diff', l:rev_hash)
-    endif
-    let l:annotate_buffer = s:buffer_obj()
-
-    " Find a window already displaying diffs for this annotation.
-    let l:diff_winnr = s:find_buffer_window('lawrencium_diff_for', l:annotate_buffer.nr)
-    if l:diff_winnr == -1
-        " Not found... go back to the main source buffer and open a bottom 
-        " split with the diff for the specified revision.
-        execute bufwinnr(b:lawrencium_annotated_bufnr) . 'wincmd w'
-        execute 'rightbelow split ' . fnameescape(l:path)
-        let b:lawrencium_diff_for = l:annotate_buffer.nr
-        let b:lawrencium_quit_on_delete = 1
-    else
-        " Found! Use that window to open the diff.
-        execute l:diff_winnr . 'wincmd w'
-        execute 'edit ' . fnameescape(l:path)
-        let b:lawrencium_diff_for = l:annotate_buffer.nr
-    endif
-endfunction
-
-call s:AddMainCommand("-bang -nargs=? -complete=customlist,s:ListRepoFiles Hgannotate :call s:HgAnnotate(<bang>0, 0, <f-args>)")
-call s:AddMainCommand("-bang -nargs=? -complete=customlist,s:ListRepoFiles Hgwannotate :call s:HgAnnotate(<bang>0, 1, <f-args>)")
-
-" }}}
-
-" Hgqseries {{{
-
-function! s:HgQSeries() abort
-    " Open the MQ series in the preview window and jump to it.
-    let l:repo = s:hg_repo()
-    let l:path = l:repo.GetLawrenciumPath('', 'qseries', '')
-    execute 'pedit ' . fnameescape(l:path)
-    wincmd P
-
-    " Make the series buffer a Lawrencium buffer.
-    let b:mercurial_dir = l:repo.root_dir
-    call s:DefineMainCommands()
-
-    " Add some commands and mappings.
-    command! -buffer Hgqseriesgoto                  :call s:HgQSeries_Goto()
-    command! -buffer Hgqserieseditmessage           :call s:HgQSeries_EditMessage()
-    command! -buffer -nargs=+ Hgqseriesrename       :call s:HgQSeries_Rename(<f-args>)
-    if g:lawrencium_define_mappings
-        nnoremap <buffer> <silent> <C-g> :Hgqseriesgoto<cr>
-        nnoremap <buffer> <silent> <C-e> :Hgqserieseditmessage<cr>
-        nnoremap <buffer> <silent> q     :bdelete!<cr>
-    endif
-endfunction
-
-function! s:HgQSeries_GetCurrentPatchName() abort
-    let l:pos = getpos('.')
-    return getbufvar('%', 'lawrencium_patchname_' . l:pos[1])
-endfunction
-
-function! s:HgQSeries_Goto() abort
-    let l:repo = s:hg_repo()
-    let l:patchname = s:HgQSeries_GetCurrentPatchName()
-    if len(l:patchname) == 0
-        call s:error("No patch to go to here.")
-        return
-    endif
-    call l:repo.RunCommand('qgoto', l:patchname)
-    edit
-endfunction
-
-function! s:HgQSeries_Rename(...) abort
-    let l:repo = s:hg_repo()
-    let l:current_name = s:HgQSeries_GetCurrentPatchName()
-    if len(l:current_name) == 0
-        call s:error("No patch to rename here.")
-        return
-    endif
-    let l:new_name = '"' . join(a:000, ' ') . '"'
-    call l:repo.RunCommand('qrename', l:current_name, l:new_name)
-    edit
-endfunction
-
-function! s:HgQSeries_EditMessage() abort
-    let l:repo = s:hg_repo()
-    let l:patchname = getbufvar('%', 'lawrencium_patchname_top')
-    if len(l:patchname) == 0
-        call s:error("No patch to edit here.")
-        return
-    endif
-    let l:current = split(l:repo.RunCommand('qheader', l:patchname), '\n')
-
-    " Open a temp file to write the commit message.
-    let l:temp_file = s:tempname('hg-qrefedit-', '.txt')
-    split
-    execute 'edit ' . fnameescape(l:temp_file)
-    call append(0, 'HG: Enter the new commit message for patch "' . l:patchname . '" here.\n')
-    call append(0, '')
-    call append(0, l:current)
-    call cursor(1, 1)
-
-    " Make it a temp buffer that will actually change the commit message
-    " when it is saved and closed.
-    let b:mercurial_dir = l:repo.root_dir
-    let b:lawrencium_patchname = l:patchname
-    setlocal bufhidden=delete
-    setlocal filetype=hgcommit
-    autocmd BufDelete <buffer> call s:HgQSeries_EditMessage_Execute(expand('<afile>:p'))
-
-    call s:DefineMainCommands()
-endfunction
-
-function! s:HgQSeries_EditMessage_Execute(log_file) abort
-    if !filereadable(a:log_file)
-        call s:error("abort: Commit message not saved")
-        return
-    endif
-
-    " Clean all the 'HG:' lines.
-    let l:is_valid = s:clean_commit_file(a:log_file)
-    if !l:is_valid
-        call s:error("abort: Empty commit message")
-        return
-    endif
-
-    " Get the repo and edit the given patch.
-    let l:repo = s:hg_repo()
-    let l:hg_args = ['-s', '-l', a:log_file]
-    call l:repo.RunCommand('qref', l:hg_args)
-endfunction
-
-call s:AddMainCommand("Hgqseries call s:HgQSeries()")
-
-" }}}
-
-" Hgrecord {{{
-
-function! s:HgRecord(split) abort
-    let l:repo = s:hg_repo()
-    let l:orig_buf = s:buffer_obj()
-    let l:tmp_path = l:orig_buf.GetName(':p') . '~record'
-    let l:diff_id = localtime()
-
-    " Start diffing on the current file, enable some commands.
-    call l:orig_buf.DefineCommand('Hgrecordabort', ':call s:HgRecord_Abort()')
-    call l:orig_buf.DefineCommand('Hgrecordcommit', ':call s:HgRecord_Execute()')
-    call s:HgDiff_DiffThis(l:diff_id)
-    setlocal foldmethod=diff
-
-    " Split the window and open the parent revision in the right or bottom
-    " window. Keep the current buffer in the left or top window... we're going
-    " to 'move' those changes into the parent revision.
-    let l:cmd = 'keepalt rightbelow split '
-    if a:split == 1
-        let l:cmd = 'keepalt rightbelow vsplit '
-    endif
-    let l:rev_path = l:repo.GetLawrenciumPath(expand('%:p'), 'rev', '')
-    execute l:cmd . fnameescape(l:rev_path)
-
-    " This new buffer with the parent revision is set as a Lawrencium buffer.
-    " Let's save it to an actual file and reopen it like that (somehow we
-    " could probably do it with `:saveas` instead but we'd need to reset a
-    " bunch of other buffer settings, and Vim weirdly creates another backup
-    " buffer when you do that).
-    execute 'keepalt write! ' . fnameescape(l:tmp_path)
-    execute 'keepalt edit! ' . fnameescape(l:tmp_path)
-    setlocal bufhidden=delete
-    let b:mercurial_dir = l:repo.root_dir
-    let b:lawrencium_record_for = l:orig_buf.GetName(':p')
-    let b:lawrencium_record_other_nr = l:orig_buf.nr
-    let b:lawrencium_record_commit_split = !a:split
-    call setbufvar(l:orig_buf.nr, 'lawrencium_record_for', '%')
-    call setbufvar(l:orig_buf.nr, 'lawrencium_record_other_nr', bufnr('%'))
-
-    " Hookup the commit and abort commands.
-    let l:rec_buf = s:buffer_obj()
-    call l:rec_buf.OnDelete('call s:HgRecord_Execute()')
-    call l:rec_buf.DefineCommand('Hgrecordcommit', ':quit')
-    call l:rec_buf.DefineCommand('Hgrecordabort', ':call s:HgRecord_Abort()')
-    call s:DefineMainCommands()
-
-    " Make it the other part of the diff.
-    call s:HgDiff_DiffThis(l:diff_id)
-    setlocal foldmethod=diff
-    call l:rec_buf.SetVar('&filetype', l:orig_buf.GetVar('&filetype'))
-    call l:rec_buf.SetVar('&fileformat', l:orig_buf.GetVar('&fileformat'))
-
-    if g:lawrencium_record_start_in_working_buffer
-        wincmd p
-    endif
-endfunction
-
-function! s:HgRecord_Execute() abort
-    if exists('b:lawrencium_record_abort')
-        " Abort flag is set, let's just cleanup.
-        let l:buf_nr = b:lawrencium_record_for == '%' ? bufnr('%') :
-                    \b:lawrencium_record_other_nr
-        call s:HgRecord_CleanUp(l:buf_nr)
-        call s:error("abort: User requested aborting the record operation.")
-        return
-    endif
-
-    if !exists('b:lawrencium_record_for')
-        call s:throw("This doesn't seem like a record buffer, something's wrong!")
-    endif
-    if b:lawrencium_record_for == '%'
-        " Switch to the 'recording' buffer's window.
-        let l:buf_obj = s:buffer_obj(b:lawrencium_record_other_nr)
-        call l:buf_obj.MoveToFirstWindow()
-    endif
-
-    " Setup the commit operation.
-    let l:split = b:lawrencium_record_commit_split
-    let l:working_bufnr = b:lawrencium_record_other_nr
-    let l:working_path = fnameescape(b:lawrencium_record_for)
-    let l:record_path = fnameescape(expand('%:p'))
-    let l:callbacks = [
-                \'call s:HgRecord_PostExecutePre('.l:working_bufnr.', "'.
-                    \escape(l:working_path, '\').'", "'.
-                    \escape(l:record_path, '\').'")',
-                \'call s:HgRecord_PostExecutePost('.l:working_bufnr.', "'.
-                    \escape(l:working_path, '\').'")',
-                \'call s:HgRecord_PostExecuteAbort('.l:working_bufnr.', "'.
-                    \escape(l:record_path, '\').'")'
-                \]
-    call s:trace("Starting commit flow with callbacks: ".string(l:callbacks))
-    call s:HgCommit(0, l:split, l:callbacks, b:lawrencium_record_for)
-endfunction
-
-function! s:HgRecord_PostExecutePre(working_bufnr, working_path, record_path) abort
-    " Just before committing, we switch the original file with the record
-    " file... we'll restore things in the post-callback below.
-    " We also switch on 'autoread' temporarily on the working buffer so that
-    " we don't have an annoying popup in gVim.
-    if has('dialog_gui')
-        call setbufvar(a:working_bufnr, '&autoread', 1)
-    endif
-    call s:trace("Backuping original file: ".a:working_path)
-    silent call rename(a:working_path, a:working_path.'~working')
-    call s:trace("Committing recorded changes using: ".a:record_path)
-    silent call rename(a:record_path, a:working_path)
-    sleep 200m
-endfunction
-
-function! s:HgRecord_PostExecutePost(working_bufnr, working_path) abort
-    " Recover the back-up file from underneath the buffer.
-    call s:trace("Recovering original file: ".a:working_path)
-    silent call rename(a:working_path.'~working', a:working_path)
-
-    " Clean up!
-    call s:HgRecord_CleanUp(a:working_bufnr)
-
-    " Restore default 'autoread'.
-    if has('dialog_gui')
-        set autoread<
-    endif
-endfunction
-
-function! s:HgRecord_PostExecuteAbort(working_bufnr, record_path) abort
-    call s:HgRecord_CleanUp(a:working_bufnr)
-    call s:trace("Delete discarded record file: ".a:record_path)
-    silent call delete(a:record_path)
-endfunction
-
-function! s:HgRecord_Abort() abort
-    if b:lawrencium_record_for == '%'
-        " We're in the working directory buffer. Switch to the 'recording'
-        " buffer and quit.
-        let l:buf_obj = s:buffer_obj(b:lawrencium_record_other_nr)
-        call l:buf_obj.MoveToFirstWindow()
-    endif
-    " We're now in the 'recording' buffer... set the abort flag and quit,
-    " which will run the execution (it will early out and clean things up).
-    let b:lawrencium_record_abort = 1
-    quit!
-endfunction
-
-function! s:HgRecord_CleanUp(buf_nr) abort
-    " Get in the original buffer and clean the local commands/variables.
-    let l:buf_obj = s:buffer_obj(a:buf_nr)
-    call l:buf_obj.MoveToFirstWindow()
-    if !exists('b:lawrencium_record_for') || b:lawrencium_record_for != '%'
-        call s:throw("Cleaning up something else than the original buffer ".
-                \"for a record operation. That's suspiciously incorrect! ".
-                \"Aborting.")
-    endif
-    call l:buf_obj.DeleteCommand('Hgrecordabort')
-    call l:buf_obj.DeleteCommand('Hgrecordcommit')
-    unlet b:lawrencium_record_for
-    unlet b:lawrencium_record_other_nr
-endfunction
-
-call s:AddMainCommand("Hgrecord call s:HgRecord(0)")
-call s:AddMainCommand("Hgvrecord call s:HgRecord(1)")
-
-" }}}
-
-" Autoload Functions {{{
-
-" Prints a summary of the current repo (if any) that's appropriate for
-" displaying on the status line.
-function! lawrencium#statusline(...)
-    if !exists('b:mercurial_dir')
-        return ''
-    endif
-    let l:repo = s:hg_repo()
-    let l:prefix = (a:0 > 0 ? a:1 : '')
-    let l:suffix = (a:0 > 1 ? a:2 : '')
-    let l:branch = 'default'
-    let l:branch_file = l:repo.GetFullPath('.hg/branch')
-    if filereadable(l:branch_file)
-        let l:branch = readfile(l:branch_file)[0]
-    endif
-    let l:bookmarks = ''
-    let l:bookmarks_file = l:repo.GetFullPath('.hg/bookmarks.current')
-    if filereadable(l:bookmarks_file)
-        let l:bookmarks = join(readfile(l:bookmarks_file), ', ')
-    endif
-    let l:line = l:prefix . l:branch
-    if strlen(l:bookmarks) > 0
-        let l:line = l:line . ' - ' . l:bookmarks
-    endif
-    let l:line = l:line . l:suffix
-    return l:line
-endfunction
-
-" Rescans the current buffer for setting up Mercurial commands.
-" Passing '1' as the parameter enables debug traces temporarily.
-function! lawrencium#rescan(...)
-    if exists('b:mercurial_dir')
-        unlet b:mercurial_dir
-    endif
-    if a:0 && a:1
-        let l:trace_backup = g:lawrencium_trace
-        let g:lawrencium_trace = 1
-    endif
-    call s:setup_buffer_commands()
-    if a:0 && a:1
-        let g:lawrencium_trace = l:trace_backup
-    endif
-endfunction
-
-" Enables/disables the debug trace.
-function! lawrencium#debugtrace(...)
-    let g:lawrencium_trace = (a:0 == 0 || (a:0 && a:1))
-    echom "Lawrencium debug trace is now " . (g:lawrencium_trace ? "enabled." : "disabled.")
-endfunction
-
-" }}}
-