Mercurial > vim-lawrencium
annotate plugin/lawrencium.vim @ 4:b6e4446ed292
HgStatus now outputs to the location window.
Fixed some commands naming.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Thu, 08 Dec 2011 17:19:52 -0800 |
parents | 0a5b490dc35d |
children | 3a4f9f41a7e2 |
rev | line source |
---|---|
0 | 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 | |
4
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
191 " Main Buffer Commands {{{ |
0 | 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 | |
4
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
211 " Hg {{{ |
0 | 212 |
4
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
213 function! s:Hg(...) abort |
0 | 214 let l:repo = s:hg_repo() |
215 echo call(l:repo.RunCommand, a:000, l:repo) | |
216 endfunction | |
217 | |
4
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
218 call s:AddMainCommand("-nargs=* Hg :execute s:Hg(<f-args>)") |
0 | 219 |
220 " }}} | |
221 | |
4
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
222 " Hgstatus {{{ |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
223 |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
224 let s:hg_status_messages = { |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
225 \'M': 'modified', |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
226 \'A': 'added', |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
227 \'R': 'removed', |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
228 \'C': 'clean', |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
229 \'!': 'missing', |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
230 \'?': 'not tracked', |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
231 \'I': 'ignored', |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
232 \} |
0 | 233 |
234 function! s:HgStatus() abort | |
4
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
235 let l:repo = s:hg_repo() |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
236 let l:status_text = l:repo.RunCommand('status') |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
237 let l:status_lines = split(l:status_text, '\n') |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
238 let l:entries = [] |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
239 for l:line in l:status_lines |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
240 if l:line =~# '^\s*$' |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
241 continue |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
242 endif |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
243 echom "STATUS: " . l:line |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
244 let l:tokens = split(l:line, '\s\+') |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
245 let l:entry = { |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
246 \'type': l:tokens[0], |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
247 \'filename': (l:repo.root_dir . l:tokens[1]), |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
248 \'text': s:hg_status_messages[l:tokens[0]], |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
249 \} |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
250 call add(l:entries, l:entry) |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
251 endfor |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
252 call setloclist(0, l:entries) |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
253 lopen |
0 | 254 endfunction |
255 | |
4
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
256 call s:AddMainCommand("Hgstatus :execute s:HgStatus()") |
0 | 257 |
258 " }}} | |
259 | |
260 " Hgcd, Hglcd {{{ | |
261 | |
262 function! s:ListRepoDirs(ArgLead, CmdLine, CursorPos) abort | |
263 let l:matches = s:hg_repo().Glob(a:ArgLead . '*/') | |
264 call map(l:matches, 's:normalizepath(v:val)') | |
265 return l:matches | |
266 endfunction | |
267 | |
268 call s:AddMainCommand("-bang -nargs=? -complete=customlist,s:ListRepoDirs Hgcd :cd<bang> `=s:hg_repo().GetFullPath(<q-args>)`") | |
269 call s:AddMainCommand("-bang -nargs=? -complete=customlist,s:ListRepoDirs Hglcd :lcd<bang> `=s:hg_repo().GetFullPath(<q-args>)`") | |
270 | |
271 " }}} | |
272 | |
273 " Hgedit {{{ | |
274 | |
275 function! s:ListRepoFiles(ArgLead, CmdLine, CursorPos) abort | |
276 let l:matches = s:hg_repo().Glob(a:ArgLead . '*', 1) | |
277 call map(l:matches, 's:normalizepath(v:val)') | |
278 return l:matches | |
279 endfunction | |
280 | |
281 call s:AddMainCommand("-bang -nargs=? -complete=customlist,s:ListRepoFiles Hgedit :edit<bang> `=s:hg_repo().GetFullPath(<q-args>)`") | |
282 | |
283 " }}} | |
284 | |
4
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
285 " }}} |
b6e4446ed292
HgStatus now outputs to the location window.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
286 |
0 | 287 " Autoload Functions {{{ |
288 | |
289 " Prints a summary of the current repo (if any) that's appropriate for | |
290 " displaying on the status line. | |
291 function! lawrencium#statusline(...) | |
292 if !exists('b:mercurial_dir') | |
293 return '' | |
294 endif | |
295 let l:summary = s:hg_repo().RunCommand('summary') | |
296 let l:parent_rev = matchstr(l:summary, 'parent\: \d+\:[0-9a-f]+') | |
297 let l:branch = matchstr(l:summary, 'branch\: [\d\w\-_\.]+') | |
298 return l:branch . ', ' . l:parent_rev | |
299 endfunction | |
300 | |
301 " Rescans the current buffer for setting up Mercurial commands. | |
302 " Passing '1' as the parameter enables debug traces temporarily. | |
303 function! lawrencium#rescan(...) | |
304 if exists('b:mercurial_dir') | |
305 unlet b:mercurial_dir | |
306 endif | |
307 if a:0 && a:1 | |
308 let l:trace_backup = g:lawrencium_trace | |
309 let g:lawrencium_trace = 1 | |
310 endif | |
311 call s:setup_buffer_commands() | |
312 if a:0 && a:1 | |
313 let g:lawrencium_trace = l:trace_backup | |
314 endif | |
315 endfunction | |
316 | |
317 " Enables/disables the debug trace. | |
318 function! lawrencium#debugtrace(...) | |
319 let g:lawrencium_trace = (a:0 == 0 || (a:0 && a:1)) | |
320 echom "Lawrencium debug trace is now " . (g:lawrencium_trace ? "enabled." : "disabled.") | |
321 endfunction | |
322 | |
323 " }}} | |
324 |