Mercurial > vim-lawrencium
comparison plugin/lawrencium.vim @ 0:0a5b490dc35d
Initial commit
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Thu, 08 Dec 2011 12:40:55 -0800 |
parents | |
children | b6e4446ed292 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:0a5b490dc35d |
---|---|
1 " lawrencium.vim - A Mercurial wrapper | |
2 " Maintainer: Ludovic Chabant <http://ludovic.chabant.com> | |
3 " Version: 0.1 | |
4 | |
5 " Globals {{{ | |
6 | |
7 if !exists('g:lawrencium_debug') | |
8 let g:lawrencium_debug = 0 | |
9 endif | |
10 | |
11 if (exists('g:loaded_lawrencium') || &cp) && !g:lawrencium_debug | |
12 finish | |
13 endif | |
14 let g:loaded_lawrencium = 1 | |
15 | |
16 if !exists('g:lawrencium_hg_executable') | |
17 let g:lawrencium_hg_executable = 'hg' | |
18 endif | |
19 | |
20 if !exists('g:lawrencium_trace') | |
21 let g:lawrencium_trace = 0 | |
22 endif | |
23 | |
24 if g:lawrencium_debug | |
25 echom "Loaded Lawrencium." | |
26 endif | |
27 | |
28 " }}} | |
29 | |
30 " Utility {{{ | |
31 | |
32 " Strips the ending slash in a path. | |
33 function! s:stripslash(path) | |
34 return fnamemodify(a:path, ':s?[/\\]$??') | |
35 endfunction | |
36 | |
37 " Normalizes the slashes in a path. | |
38 function! s:normalizepath(path) | |
39 if exists('+shellslash') && &shellslash | |
40 return substitute(a:path, '\\', '/', '') | |
41 elseif has('win32') | |
42 return substitute(a:path, '/', '\\', '') | |
43 else | |
44 return a:path | |
45 endif | |
46 endfunction | |
47 | |
48 " Prints a message if debug tracing is enabled. | |
49 function! s:trace(message) | |
50 if g:lawrencium_trace | |
51 let l:message = "lawrencium: " . a:message | |
52 echom l:message | |
53 endif | |
54 endfunction | |
55 | |
56 " Throw a Lawrencium exception message. | |
57 function! s:throw(message) | |
58 let v:errmsg = "lawrencium: " . a:message | |
59 throw v:errmsg | |
60 endfunction | |
61 | |
62 " Finds the repository root given a path inside that repository. | |
63 " Throw an error if not repository is found. | |
64 function! s:find_repo_root(path) | |
65 let l:path = s:stripslash(a:path) | |
66 let l:previous_path = "" | |
67 while l:path != l:previous_path | |
68 if isdirectory(l:path . '/.hg/store') | |
69 return simplify(fnamemodify(l:path, ':p')) | |
70 endif | |
71 let l:previous_path = l:path | |
72 let l:path = fnamemodify(l:path, ':h') | |
73 endwhile | |
74 call s:throw("No Mercurial repository found above: " . a:path) | |
75 endfunction | |
76 | |
77 " }}} | |
78 | |
79 " Mercurial Repository {{{ | |
80 | |
81 " Let's define a Mercurial repo 'class' using prototype-based object-oriented | |
82 " programming. | |
83 " | |
84 " The prototype dictionary. | |
85 let s:HgRepo = {} | |
86 | |
87 " Constructor | |
88 function! s:HgRepo.New(path) abort | |
89 let l:newRepo = copy(self) | |
90 let l:newRepo.root_dir = s:find_repo_root(a:path) | |
91 call s:trace("Built new Mercurial repository object at : " . l:newRepo.root_dir) | |
92 return l:newRepo | |
93 endfunction | |
94 | |
95 " Gets a full path given a repo-relative path | |
96 function! s:HgRepo.GetFullPath(path) abort | |
97 let l:root_dir = self.root_dir | |
98 if a:path =~# '^[/\\]' | |
99 let l:root_dir = s:stripslash(l:root_dir) | |
100 endif | |
101 return l:root_dir . a:path | |
102 endfunction | |
103 | |
104 " Gets a list of files matching a root-relative pattern. | |
105 " If a flag is passed and is TRUE, a slash will be appended to all | |
106 " directories. | |
107 function! s:HgRepo.Glob(pattern, ...) abort | |
108 let l:root_dir = self.root_dir | |
109 if (a:pattern =~# '^[/\\]') | |
110 let l:root_dir = s:stripslash(l:root_dir) | |
111 endif | |
112 let l:matches = split(glob(l:root_dir . a:pattern), '\n') | |
113 if a:0 && a:1 | |
114 for l:idx in range(len(l:matches)) | |
115 if !filereadable(l:matches[l:idx]) | |
116 let l:matches[l:idx] = l:matches[l:idx] . '/' | |
117 endif | |
118 endfor | |
119 endif | |
120 let l:strip_len = len(l:root_dir) | |
121 call map(l:matches, 'v:val[l:strip_len : -1]') | |
122 return l:matches | |
123 endfunction | |
124 | |
125 " Runs a Mercurial command in the repo | |
126 function! s:HgRepo.RunCommand(command, ...) abort | |
127 let l:hg_command = g:lawrencium_hg_executable . ' --repository ' . shellescape(s:stripslash(self.root_dir)) | |
128 let l:hg_command = l:hg_command . ' ' . a:command . ' ' . join(a:000, ' ') | |
129 call s:trace("Running Mercurial command: " . l:hg_command) | |
130 return system(l:hg_command) | |
131 endfunction | |
132 | |
133 " Repo cache map | |
134 let s:buffer_repos = {} | |
135 | |
136 " Get a cached repo | |
137 function! s:hg_repo(...) abort | |
138 " Use the given path, or the mercurial directory of the current buffer. | |
139 if a:0 == 0 | |
140 if exists('b:mercurial_dir') | |
141 let l:path = b:mercurial_dir | |
142 else | |
143 let l:path = s:find_repo_root(expand('%:p')) | |
144 endif | |
145 else | |
146 let l:path = a:1 | |
147 endif | |
148 " Find a cache repo instance, or make a new one. | |
149 if has_key(s:buffer_repos, l:path) | |
150 return get(s:buffer_repos, l:path) | |
151 else | |
152 let l:repo = s:HgRepo.New(l:path) | |
153 let s:buffer_repos[l:path] = l:repo | |
154 return l:repo | |
155 endif | |
156 endfunction | |
157 | |
158 " Sets up the current buffer with Lawrencium commands if it contains a file from a Mercurial repo. | |
159 " If the file is not in a Mercurial repo, just exit silently. | |
160 function! s:setup_buffer_commands() abort | |
161 call s:trace("Scanning buffer '" . bufname('%') . "' for Lawrencium setup...") | |
162 let l:do_setup = 1 | |
163 if exists('b:mercurial_dir') | |
164 if b:mercurial_dir =~# '/^\s*$/' | |
165 unlet b:mercurial_dir | |
166 else | |
167 let l:do_setup = 0 | |
168 endif | |
169 endif | |
170 try | |
171 let l:repo = s:hg_repo() | |
172 catch /^lawrencium\:/ | |
173 return | |
174 endtry | |
175 let b:mercurial_dir = l:repo.root_dir | |
176 if exists('b:mercurial_dir') && l:do_setup | |
177 call s:trace("Setting Mercurial commands for buffer '" . bufname('%')) | |
178 call s:trace(" with repo : " . expand(b:mercurial_dir)) | |
179 silent doautocmd User Lawrencium | |
180 endif | |
181 endfunction | |
182 | |
183 augroup lawrencium_detect | |
184 autocmd! | |
185 autocmd BufNewFile,BufReadPost * call s:setup_buffer_commands() | |
186 autocmd VimEnter * if expand('<amatch>')==''|call s:setup_buffer_commands()|endif | |
187 augroup end | |
188 | |
189 " }}} | |
190 | |
191 " Commands {{{ | |
192 | |
193 let s:main_commands = [] | |
194 | |
195 function! s:AddMainCommand(command) abort | |
196 let s:main_commands += [a:command] | |
197 endfunction | |
198 | |
199 function! s:DefineMainCommands() | |
200 for l:command in s:main_commands | |
201 execute 'command! -buffer ' . l:command | |
202 endfor | |
203 endfunction | |
204 | |
205 augroup lawrencium_main | |
206 autocmd! | |
207 autocmd User Lawrencium call s:DefineMainCommands() | |
208 augroup end | |
209 | |
210 " }}} | |
211 | |
212 " HgExecute {{{ | |
213 | |
214 function! s:HgExecute(...) abort | |
215 let l:repo = s:hg_repo() | |
216 echo call(l:repo.RunCommand, a:000, l:repo) | |
217 endfunction | |
218 | |
219 call s:AddMainCommand("-nargs=* Hg :execute s:HgExecute(<f-args>)") | |
220 | |
221 " }}} | |
222 | |
223 " HgStatus {{{ | |
224 | |
225 function! s:HgStatus() abort | |
226 echo s:hg_repo().RunCommand('status') | |
227 endfunction | |
228 | |
229 call s:AddMainCommand("HgStatus :execute s:HgStatus()") | |
230 | |
231 " }}} | |
232 | |
233 " Hgcd, Hglcd {{{ | |
234 | |
235 function! s:ListRepoDirs(ArgLead, CmdLine, CursorPos) abort | |
236 let l:matches = s:hg_repo().Glob(a:ArgLead . '*/') | |
237 call map(l:matches, 's:normalizepath(v:val)') | |
238 return l:matches | |
239 endfunction | |
240 | |
241 call s:AddMainCommand("-bang -nargs=? -complete=customlist,s:ListRepoDirs Hgcd :cd<bang> `=s:hg_repo().GetFullPath(<q-args>)`") | |
242 call s:AddMainCommand("-bang -nargs=? -complete=customlist,s:ListRepoDirs Hglcd :lcd<bang> `=s:hg_repo().GetFullPath(<q-args>)`") | |
243 | |
244 " }}} | |
245 | |
246 " Hgedit {{{ | |
247 | |
248 function! s:ListRepoFiles(ArgLead, CmdLine, CursorPos) abort | |
249 let l:matches = s:hg_repo().Glob(a:ArgLead . '*', 1) | |
250 call map(l:matches, 's:normalizepath(v:val)') | |
251 return l:matches | |
252 endfunction | |
253 | |
254 call s:AddMainCommand("-bang -nargs=? -complete=customlist,s:ListRepoFiles Hgedit :edit<bang> `=s:hg_repo().GetFullPath(<q-args>)`") | |
255 | |
256 " }}} | |
257 | |
258 " Autoload Functions {{{ | |
259 | |
260 " Prints a summary of the current repo (if any) that's appropriate for | |
261 " displaying on the status line. | |
262 function! lawrencium#statusline(...) | |
263 if !exists('b:mercurial_dir') | |
264 return '' | |
265 endif | |
266 let l:summary = s:hg_repo().RunCommand('summary') | |
267 let l:parent_rev = matchstr(l:summary, 'parent\: \d+\:[0-9a-f]+') | |
268 let l:branch = matchstr(l:summary, 'branch\: [\d\w\-_\.]+') | |
269 return l:branch . ', ' . l:parent_rev | |
270 endfunction | |
271 | |
272 " Rescans the current buffer for setting up Mercurial commands. | |
273 " Passing '1' as the parameter enables debug traces temporarily. | |
274 function! lawrencium#rescan(...) | |
275 if exists('b:mercurial_dir') | |
276 unlet b:mercurial_dir | |
277 endif | |
278 if a:0 && a:1 | |
279 let l:trace_backup = g:lawrencium_trace | |
280 let g:lawrencium_trace = 1 | |
281 endif | |
282 call s:setup_buffer_commands() | |
283 if a:0 && a:1 | |
284 let g:lawrencium_trace = l:trace_backup | |
285 endif | |
286 endfunction | |
287 | |
288 " Enables/disables the debug trace. | |
289 function! lawrencium#debugtrace(...) | |
290 let g:lawrencium_trace = (a:0 == 0 || (a:0 && a:1)) | |
291 echom "Lawrencium debug trace is now " . (g:lawrencium_trace ? "enabled." : "disabled.") | |
292 endfunction | |
293 | |
294 " }}} | |
295 |