comparison plugin/lawrencium.vim @ 45:ea0ae8f6af81

Added `Hglog` command.
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 28 Aug 2012 21:37:53 -0700
parents 95f8e7cb5ca2
children 6a4f5200d8da edc43c59b3b8
comparison
equal deleted inserted replaced
44:95f8e7cb5ca2 45:ea0ae8f6af81
58 let l:result = fnamemodify(l:path, ':h') . '/' . a:name . fnamemodify(l:path, ':t') 58 let l:result = fnamemodify(l:path, ':h') . '/' . a:name . fnamemodify(l:path, ':t')
59 if a:0 > 0 59 if a:0 > 0
60 let l:result = l:result . a:1 60 let l:result = l:result . a:1
61 endif 61 endif
62 return l:result 62 return l:result
63 endfunction
64
65 " Delete a temporary file if it exists.
66 function! s:clean_tempfile(path)
67 if filewritable(a:path)
68 call s:trace("Cleaning up temporary file: " . a:path)
69 call delete(a:path)
70 endif
63 endfunction 71 endfunction
64 72
65 " Prints a message if debug tracing is enabled. 73 " Prints a message if debug tracing is enabled.
66 function! s:trace(message, ...) 74 function! s:trace(message, ...)
67 if g:lawrencium_trace || (a:0 && a:1) 75 if g:lawrencium_trace || (a:0 && a:1)
94 let l:path = fnamemodify(l:path, ':h') 102 let l:path = fnamemodify(l:path, ':h')
95 endwhile 103 endwhile
96 call s:throw("No Mercurial repository found above: " . a:path) 104 call s:throw("No Mercurial repository found above: " . a:path)
97 endfunction 105 endfunction
98 106
107 " Given a Lawrencium path (e.g: 'lawrencium:///repo/root_dir@foo/bar/file.py//34'), extract
108 " the repository root, relative file path and revision number/changeset ID.
109 function! s:parse_lawrencium_path(lawrencium_path)
110 let l:repo_path = a:lawrencium_path
111 if l:repo_path =~? '^lawrencium://'
112 let l:repo_path = strpart(l:repo_path, strlen('lawrencium://'))
113 endif
114
115 let l:root_dir = ''
116 let l:at_idx = stridx(l:repo_path, '@')
117 if l:at_idx >= 0
118 let l:root_dir = strpart(l:repo_path, 0, l:at_idx)
119 let l:repo_path = strpart(l:repo_path, l:at_idx + 1)
120 endif
121
122 let l:rev = matchstr(l:repo_path, '\v//[0-9a-f]+$')
123 if l:rev !=? ''
124 let l:repo_path = strpart(l:repo_path, 0, strlen(l:repo_path) - strlen(l:rev))
125 let l:rev = strpart(l:rev, 2)
126 endif
127
128 let l:result = { 'root': l:root_dir, 'path': l:repo_path, 'rev': l:rev }
129 return l:result
130 endfunction
131
99 " }}} 132 " }}}
100 133
101 " Mercurial Repository {{{ 134 " Mercurial Repository {{{
102 135
103 " Let's define a Mercurial repo 'class' using prototype-based object-oriented 136 " Let's define a Mercurial repo 'class' using prototype-based object-oriented
104 " programming. 137 " programming.
105 " 138 "
106 " The prototype dictionary. 139 " The prototype dictionary.
107 let s:HgRepo = {} 140 let s:HgRepo = {}
108 141
109 " Constructor 142 " Constructor.
110 function! s:HgRepo.New(path) abort 143 function! s:HgRepo.New(path) abort
111 let l:newRepo = copy(self) 144 let l:newRepo = copy(self)
112 let l:newRepo.root_dir = s:find_repo_root(a:path) 145 let l:newRepo.root_dir = s:find_repo_root(a:path)
113 call s:trace("Built new Mercurial repository object at : " . l:newRepo.root_dir) 146 call s:trace("Built new Mercurial repository object at : " . l:newRepo.root_dir)
114 return l:newRepo 147 return l:newRepo
115 endfunction 148 endfunction
116 149
117 " Gets a full path given a repo-relative path 150 " Gets a full path given a repo-relative path.
118 function! s:HgRepo.GetFullPath(path) abort 151 function! s:HgRepo.GetFullPath(path) abort
119 let l:root_dir = self.root_dir 152 let l:root_dir = self.root_dir
120 if a:path =~# '\v^[/\\]' 153 if a:path =~# '\v^[/\\]'
121 let l:root_dir = s:stripslash(l:root_dir) 154 let l:root_dir = s:stripslash(l:root_dir)
122 endif 155 endif
142 let l:strip_len = len(l:root_dir) 175 let l:strip_len = len(l:root_dir)
143 call map(l:matches, 'v:val[l:strip_len : -1]') 176 call map(l:matches, 'v:val[l:strip_len : -1]')
144 return l:matches 177 return l:matches
145 endfunction 178 endfunction
146 179
147 " Runs a Mercurial command in the repo 180 " Gets a full Mercurial command.
148 function! s:HgRepo.RunCommand(command, ...) abort 181 function! s:HgRepo.GetCommand(command, ...) abort
149 " If there's only one argument, and it's a list, then use that as the 182 " If there's only one argument, and it's a list, then use that as the
150 " argument list. 183 " argument list.
151 let l:arg_list = a:000 184 let l:arg_list = a:000
152 if a:0 == 1 && type(a:1) == type([]) 185 if a:0 == 1 && type(a:1) == type([])
153 let l:arg_list = a:1 186 let l:arg_list = a:1
154 endif 187 endif
155 let l:hg_command = g:lawrencium_hg_executable . ' --repository ' . shellescape(s:stripslash(self.root_dir)) 188 let l:hg_command = g:lawrencium_hg_executable . ' --repository ' . shellescape(s:stripslash(self.root_dir))
156 let l:hg_command = l:hg_command . ' ' . a:command . ' ' . join(l:arg_list, ' ') 189 let l:hg_command = l:hg_command . ' ' . a:command . ' ' . join(l:arg_list, ' ')
190 return l:hg_command
191 endfunction
192
193 " Runs a Mercurial command in the repo.
194 function! s:HgRepo.RunCommand(command, ...) abort
195 let l:all_args = [a:command] + a:000
196 let l:hg_command = call(self['GetCommand'], l:all_args, self)
157 call s:trace("Running Mercurial command: " . l:hg_command) 197 call s:trace("Running Mercurial command: " . l:hg_command)
158 return system(l:hg_command) 198 return system(l:hg_command)
159 endfunction 199 endfunction
160 200
161 " Repo cache map 201 " Repo cache map.
162 let s:buffer_repos = {} 202 let s:buffer_repos = {}
163 203
164 " Get a cached repo 204 " Get a cached repo.
165 function! s:hg_repo(...) abort 205 function! s:hg_repo(...) abort
166 " Use the given path, or the mercurial directory of the current buffer. 206 " Use the given path, or the mercurial directory of the current buffer.
167 if a:0 == 0 207 if a:0 == 0
168 if exists('b:mercurial_dir') 208 if exists('b:mercurial_dir')
169 let l:path = b:mercurial_dir 209 let l:path = b:mercurial_dir
400 vnoremap <buffer> <silent> <C-A> :Hgstatusaddremove<cr> 440 vnoremap <buffer> <silent> <C-A> :Hgstatusaddremove<cr>
401 vnoremap <buffer> <silent> <C-S> :Hgstatuscommit<cr> 441 vnoremap <buffer> <silent> <C-S> :Hgstatuscommit<cr>
402 endif 442 endif
403 443
404 " Make sure the file is deleted with the buffer. 444 " Make sure the file is deleted with the buffer.
405 autocmd BufDelete <buffer> call s:HgStatus_CleanUp(expand('<afile>:p')) 445 autocmd BufDelete <buffer> call s:clean_tempfile(expand('<afile>:p'))
406 endfunction
407
408 function! s:HgStatus_CleanUp(path) abort
409 " If the `hg status` output has been saved to disk (e.g. because of a
410 " refresh we did), let's delete it.
411 if filewritable(a:path)
412 call s:trace("Cleaning up status log: " . a:path)
413 call delete(a:path)
414 endif
415 endfunction 446 endfunction
416 447
417 function! s:HgStatus_Refresh() abort 448 function! s:HgStatus_Refresh() abort
418 " Get the repo and the `hg status` output. 449 " Get the repo and the `hg status` output.
419 let l:repo = s:hg_repo() 450 let l:repo = s:hg_repo()
839 endif 870 endif
840 if a:bang 871 if a:bang
841 call insert(l:filenames, '--no-backup', 0) 872 call insert(l:filenames, '--no-backup', 0)
842 endif 873 endif
843 874
844 " Get the repo. 875 " Get the repo and run the command.
845 let l:repo = s:hg_repo() 876 let l:repo = s:hg_repo()
846
847 " Run the command.
848 call l:repo.RunCommand('revert', l:filenames) 877 call l:repo.RunCommand('revert', l:filenames)
849 endfunction 878 endfunction
850 879
851 call s:AddMainCommand("-bang -nargs=* -complete=customlist,s:ListRepoFiles Hgrevert :call s:HgRevert(<bang>0, <f-args>)") 880 call s:AddMainCommand("-bang -nargs=* -complete=customlist,s:ListRepoFiles Hgrevert :call s:HgRevert(<bang>0, <f-args>)")
881
882 " }}}
883
884 " Hglog {{{
885
886 function! s:HgLog(...) abort
887 let l:filepath = expand('%:p')
888 if a:0 == 1
889 let l:filepath = a:1
890 endif
891
892 " Get the repo and get the command.
893 let l:repo = s:hg_repo()
894 let l:log_command = l:repo.GetCommand('log', l:filepath, '--template', '"{rev}\t{desc|firstline}\n"')
895
896 " Open a new temp buffer in the preview window, jump to it,
897 " and paste the `hg log` output in there.
898 let l:temp_file = s:tempname('hg-log-', '.txt')
899 execute "pedit " . l:temp_file
900 wincmd p
901 execute "read !" . escape(l:log_command, '%#\')
902
903 " Setup the buffer correctly: readonly, and with the correct repo linked
904 " to it, and deleted on close.
905 let b:mercurial_dir = l:repo.root_dir
906 let b:mercurial_logged_file = l:filepath
907 setlocal bufhidden=delete
908 setlocal buftype=nofile
909 setlocal syntax=hglog
910
911 " Make commands available.
912 call s:DefineMainCommands()
913
914 " Add some other nice commands and mappings.
915 command! -buffer -nargs=? Hglogrevedit :call s:HgLog_FileRevEdit(<f-args>)
916 if g:lawrencium_define_mappings
917 nnoremap <buffer> <silent> <cr> :Hglogrevedit<cr>
918 nnoremap <buffer> <silent> q :bdelete!<cr>
919 endif
920
921 " Make sure the file is deleted with the buffer.
922 autocmd BufDelete <buffer> call s:clean_tempfile(expand('<afile>:p'))
923 endfunction
924
925 function! s:HgLog_FileRevEdit(...)
926 if a:0 > 0
927 " Revision was given manually.
928 let l:rev = a:1
929 else
930 " Revision should be parsed from the current line in the log.
931 let l:rev = s:HgLog_GetSelectedRev()
932 endif
933 let l:path = 'lawrencium://' . b:mercurial_dir . '@' . b:mercurial_logged_file . '//' . l:rev
934 execute 'edit ' . l:path
935 endfunction
936
937 function! s:HgLog_GetSelectedRev() abort
938 let l:line = getline('.')
939 " Behold, Vim's look-ahead regex syntax again! WTF.
940 let l:rev = matchstr(l:line, '\v^(\d+)(\s)@=')
941 if l:rev == ''
942 call s:throw("Can't parse revision number from line: " . l:line)
943 endif
944 return l:rev
945 endfunction
946
947 call s:AddMainCommand("-nargs=? -complete=customlist,s:ListRepoFiles Hglog :call s:HgLog(<f-args>)")
948
949 " }}}
950
951 " Lawrencium files {{{
952
953 function! s:ReadFileRevision(path) abort
954 let l:comps = s:parse_lawrencium_path(a:path)
955 if l:comps['root'] == ''
956 call s:throw("Can't get repository root from: " . a:path)
957 endif
958 let l:repo = s:HgRepo.New(l:comps['root'])
959 if l:comps['rev'] == ''
960 execute 'read !' . escape(l:repo.GetCommand('cat', l:comps['path']), '%#\')
961 else
962 execute 'read !' . escape(l:repo.GetCommand('cat', '-r', l:comps['rev'], l:comps['path']), '%#\')
963 endif
964 return ''
965 endfunction
966
967 augroup lawrencium_files
968 autocmd!
969 autocmd BufReadCmd lawrencium://**//[0-9a-f][0-9a-f]* exe s:ReadFileRevision(expand('<amatch>'))
970 augroup END
852 971
853 " }}} 972 " }}}
854 973
855 " Autoload Functions {{{ 974 " Autoload Functions {{{
856 975