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