comparison autoload/lawrencium.vim @ 139:065625e1bb31

Split plugin file into multiple extensions.
author Ludovic Chabant <ludovic@chabant.com>
date Mon, 13 Jun 2016 09:32:34 -0700
parents
children 4d5f4233b04e
comparison
equal deleted inserted replaced
138:a2d823c82e5f 139:065625e1bb31
1
2 " Path Utility {{{
3
4 " Strips the ending slash in a path.
5 function! lawrencium#stripslash(path)
6 return fnamemodify(a:path, ':s?[/\\]$??')
7 endfunction
8
9 " Returns whether a path is absolute.
10 function! lawrencium#isabspath(path)
11 return a:path =~# '\v^(\w\:)?[/\\]'
12 endfunction
13
14 " Normalizes the slashes in a path.
15 function! lawrencium#normalizepath(path)
16 if exists('+shellslash') && &shellslash
17 return substitute(a:path, '\v/', '\\', 'g')
18 elseif has('win32')
19 return substitute(a:path, '\v/', '\\', 'g')
20 else
21 return a:path
22 endif
23 endfunction
24
25 " Shell-slashes the path (opposite of `normalizepath`).
26 function! lawrencium#shellslash(path)
27 if exists('+shellslash') && !&shellslash
28 return substitute(a:path, '\v\\', '/', 'g')
29 else
30 return a:path
31 endif
32 endfunction
33
34 " Like tempname() but with some control over the filename.
35 function! lawrencium#tempname(name, ...)
36 let l:path = tempname()
37 let l:result = fnamemodify(l:path, ':h') . '/' . a:name . fnamemodify(l:path, ':t')
38 if a:0 > 0
39 let l:result = l:result . a:1
40 endif
41 return l:result
42 endfunction
43
44 " Delete a temporary file if it exists.
45 function! lawrencium#clean_tempfile(path)
46 if filewritable(a:path)
47 call lawrencium#trace("Cleaning up temporary file: " . a:path)
48 call delete(a:path)
49 endif
50 endfunction
51
52 " }}}
53
54 " Logging {{{
55
56 " Prints a message if debug tracing is enabled.
57 function! lawrencium#trace(message, ...)
58 if g:lawrencium_trace || (a:0 && a:1)
59 let l:message = "lawrencium: " . a:message
60 echom l:message
61 endif
62 endfunction
63
64 " Prints an error message with 'lawrencium error' prefixed to it.
65 function! lawrencium#error(message)
66 echom "lawrencium error: " . a:message
67 endfunction
68
69 " Throw a Lawrencium exception message.
70 function! lawrencium#throw(message)
71 let v:errmsg = "lawrencium: " . a:message
72 throw v:errmsg
73 endfunction
74
75 " }}}
76
77 " Repositories {{{
78
79 " Finds the repository root given a path inside that repository.
80 " Throw an error if not repository is found.
81 function! lawrencium#find_repo_root(path)
82 let l:path = lawrencium#stripslash(a:path)
83 let l:previous_path = ""
84 while l:path != l:previous_path
85 if isdirectory(l:path . '/.hg')
86 return lawrencium#normalizepath(simplify(fnamemodify(l:path, ':p')))
87 endif
88 let l:previous_path = l:path
89 let l:path = fnamemodify(l:path, ':h')
90 endwhile
91 call lawrencium#throw("No Mercurial repository found above: " . a:path)
92 endfunction
93
94 " Given a Lawrencium path (e.g: 'lawrencium:///repo/root_dir//foo/bar/file.py//rev=34'), extract
95 " the repository root, relative file path and revision number/changeset ID.
96 "
97 " If a second argument exists, it must be:
98 " - `relative`: to make the file path relative to the repository root.
99 " - `absolute`: to make the file path absolute.
100 "
101 function! lawrencium#parse_lawrencium_path(lawrencium_path, ...)
102 let l:repo_path = lawrencium#shellslash(a:lawrencium_path)
103 let l:repo_path = substitute(l:repo_path, '\\ ', ' ', 'g')
104 if l:repo_path =~? '\v^lawrencium://'
105 let l:repo_path = strpart(l:repo_path, strlen('lawrencium://'))
106 endif
107
108 let l:root_dir = ''
109 let l:at_idx = stridx(l:repo_path, '//')
110 if l:at_idx >= 0
111 let l:root_dir = strpart(l:repo_path, 0, l:at_idx)
112 let l:repo_path = strpart(l:repo_path, l:at_idx + 2)
113 endif
114
115 let l:value = ''
116 let l:action = ''
117 let l:actionidx = stridx(l:repo_path, '//')
118 if l:actionidx >= 0
119 let l:action = strpart(l:repo_path, l:actionidx + 2)
120 let l:repo_path = strpart(l:repo_path, 0, l:actionidx)
121
122 let l:equalidx = stridx(l:action, '=')
123 if l:equalidx >= 0
124 let l:value = strpart(l:action, l:equalidx + 1)
125 let l:action = strpart(l:action, 0, l:equalidx)
126 endif
127 endif
128
129 if a:0 > 0
130 execute 'cd! ' . fnameescape(l:root_dir)
131 if a:1 == 'relative'
132 let l:repo_path = fnamemodify(l:repo_path, ':.')
133 elseif a:1 == 'absolute'
134 let l:repo_path = fnamemodify(l:repo_path, ':p')
135 endif
136 execute 'cd! -'
137 endif
138
139 let l:result = { 'root': l:root_dir, 'path': l:repo_path, 'action': l:action, 'value': l:value }
140 return l:result
141 endfunction
142
143 " Clean up all the 'HG:' lines from a commit message, and see if there's
144 " any message left (Mercurial does this automatically, usually, but
145 " apparently not when you feed it a log file...).
146 function! lawrencium#clean_commit_file(log_file) abort
147 let l:lines = readfile(a:log_file)
148 call filter(l:lines, "v:val !~# '\\v^HG:'")
149 if len(filter(copy(l:lines), "v:val !~# '\\v^\\s*$'")) == 0
150 return 0
151 endif
152 call writefile(l:lines, a:log_file)
153 return 1
154 endfunction
155
156 " }}}
157
158 " Vim Utility {{{
159
160 " Finds a window whose displayed buffer has a given variable
161 " set to the given value.
162 function! lawrencium#find_buffer_window(varname, varvalue) abort
163 for wnr in range(1, winnr('$'))
164 let l:bnr = winbufnr(wnr)
165 if getbufvar(l:bnr, a:varname) == a:varvalue
166 return l:wnr
167 endif
168 endfor
169 return -1
170 endfunction
171
172 " Opens a buffer in a way that makes it easy to delete it later:
173 " - if the about-to-be previous buffer doesn't have a given variable,
174 " just open the new buffer.
175 " - if the about-to-be previous buffer has a given variable, open the
176 " new buffer with the `keepalt` option to make it so that the
177 " actual previous buffer (returned by things like `bufname('#')`)
178 " is the original buffer that was there before the first deletable
179 " buffer was opened.
180 function! lawrencium#edit_deletable_buffer(varname, varvalue, path) abort
181 let l:edit_cmd = 'edit '
182 if getbufvar('%', a:varname) != ''
183 let l:edit_cmd = 'keepalt edit '
184 endif
185 execute l:edit_cmd . fnameescape(a:path)
186 call setbufvar('%', a:varname, a:varvalue)
187 endfunction
188
189 " Deletes all buffers that have a given variable set to a given value.
190 " For each buffer, if it is not shown in any window, it will be just deleted.
191 " If it is shown in a window, that window will be switched to the alternate
192 " buffer before the buffer is deleted, unless the `lawrencium_quit_on_delete`
193 " variable is set to `1`, in which case the window is closed too.
194 function! lawrencium#delete_dependency_buffers(varname, varvalue) abort
195 let l:cur_winnr = winnr()
196 for bnr in range(1, bufnr('$'))
197 if getbufvar(bnr, a:varname) == a:varvalue
198 " Delete this buffer if it is not shown in any window.
199 " Otherwise, display the alternate buffer before deleting
200 " it so the window is not closed.
201 let l:bwnr = bufwinnr(bnr)
202 if l:bwnr < 0 || getbufvar(bnr, 'lawrencium_quit_on_delete') == 1
203 if bufloaded(l:bnr)
204 call lawrencium#trace("Deleting dependency buffer " . bnr)
205 execute "bdelete! " . bnr
206 else
207 call lawrencium#trace("Dependency buffer " . bnr . " is already unladed.")
208 endif
209 else
210 execute l:bwnr . "wincmd w"
211 " TODO: better handle case where there's no previous/alternate buffer?
212 let l:prev_bnr = bufnr('#')
213 if l:prev_bnr > 0 && bufloaded(l:prev_bnr)
214 execute "buffer " . l:prev_bnr
215 if bufloaded(l:bnr)
216 call lawrencium#trace("Deleting dependency buffer " . bnr . " after switching to " . l:prev_bnr . " in window " . l:bwnr)
217 execute "bdelete! " . bnr
218 else
219 call lawrencium#trace("Dependency buffer " . bnr . " is unladed after switching to " . l:prev_bnr)
220 endif
221 else
222 call lawrencium#trace("Deleting dependency buffer " . bnr . " and window.")
223 bdelete!
224 endif
225 endif
226 endif
227 endfor
228 if l:cur_winnr != winnr()
229 call lawrencium#trace("Returning to window " . l:cur_winnr)
230 execute l:cur_winnr . "wincmd w"
231 endif
232 endfunction
233
234 " }}}
235
236 " Mercurial Repository Object {{{
237
238 " Let's define a Mercurial repo 'class' using prototype-based object-oriented
239 " programming.
240 "
241 " The prototype dictionary.
242 let s:HgRepo = {}
243
244 " Constructor.
245 function! s:HgRepo.New(path) abort
246 let l:newRepo = copy(self)
247 let l:newRepo.root_dir = lawrencium#find_repo_root(a:path)
248 call lawrencium#trace("Built new Mercurial repository object at : " . l:newRepo.root_dir)
249 return l:newRepo
250 endfunction
251
252 " Gets a full path given a repo-relative path.
253 function! s:HgRepo.GetFullPath(path) abort
254 let l:root_dir = self.root_dir
255 if lawrencium#isabspath(a:path)
256 call lawrencium#throw("Expected relative path, got absolute path: " . a:path)
257 endif
258 return lawrencium#normalizepath(l:root_dir . a:path)
259 endfunction
260
261 " Gets a repo-relative path given any path.
262 function! s:HgRepo.GetRelativePath(path) abort
263 execute 'lcd! ' . fnameescape(self.root_dir)
264 let l:relative_path = fnamemodify(a:path, ':.')
265 execute 'lcd! -'
266 return l:relative_path
267 endfunction
268
269 " Gets, and optionally creates, a temp folder for some operation in the `.hg`
270 " directory.
271 function! s:HgRepo.GetTempDir(path, ...) abort
272 let l:tmp_dir = self.GetFullPath('.hg/lawrencium/' . a:path)
273 if !isdirectory(l:tmp_dir)
274 if a:0 > 0 && !a:1
275 return ''
276 endif
277 call mkdir(l:tmp_dir, 'p')
278 endif
279 return l:tmp_dir
280 endfunction
281
282 " Gets a list of files matching a root-relative pattern.
283 " If a flag is passed and is TRUE, a slash will be appended to all
284 " directories.
285 function! s:HgRepo.Glob(pattern, ...) abort
286 let l:root_dir = self.root_dir
287 if (a:pattern =~# '\v^[/\\]')
288 let l:root_dir = lawrencium#stripslash(l:root_dir)
289 endif
290 let l:matches = split(glob(l:root_dir . a:pattern), '\n')
291 if a:0 && a:1
292 for l:idx in range(len(l:matches))
293 if !filereadable(l:matches[l:idx])
294 let l:matches[l:idx] = l:matches[l:idx] . '/'
295 endif
296 endfor
297 endif
298 let l:strip_len = len(l:root_dir)
299 call map(l:matches, 'v:val[l:strip_len : -1]')
300 return l:matches
301 endfunction
302
303 " Gets a full Mercurial command.
304 function! s:HgRepo.GetCommand(command, ...) abort
305 " If there's only one argument, and it's a list, then use that as the
306 " argument list.
307 let l:arg_list = a:000
308 if a:0 == 1 && type(a:1) == type([])
309 let l:arg_list = a:1
310 endif
311 let l:prev_shellslash = &shellslash
312 setlocal noshellslash
313 let l:hg_command = g:lawrencium_hg_executable . ' --repository ' . shellescape(lawrencium#stripslash(self.root_dir))
314 let l:hg_command = l:hg_command . ' ' . a:command
315 for l:arg in l:arg_list
316 let l:hg_command = l:hg_command . ' ' . shellescape(l:arg)
317 endfor
318 if l:prev_shellslash
319 setlocal shellslash
320 endif
321 return l:hg_command
322 endfunction
323
324 " Runs a Mercurial command in the repo.
325 function! s:HgRepo.RunCommand(command, ...) abort
326 let l:all_args = [1, a:command] + a:000
327 return call(self['RunCommandEx'], l:all_args, self)
328 endfunction
329
330 function! s:HgRepo.RunCommandEx(plain_mode, command, ...) abort
331 let l:prev_hgplain = $HGPLAIN
332 if a:plain_mode
333 let $HGPLAIN = 'true'
334 endif
335 let l:all_args = [a:command] + a:000
336 let l:hg_command = call(self['GetCommand'], l:all_args, self)
337 call lawrencium#trace("Running Mercurial command: " . l:hg_command)
338 let l:cmd_out = system(l:hg_command)
339 if a:plain_mode
340 let $HGPLAIN = l:prev_hgplain
341 endif
342 return l:cmd_out
343 endfunction
344
345 " Runs a Mercurial command in the repo and reads its output into the current
346 " buffer.
347 function! s:HgRepo.ReadCommandOutput(command, ...) abort
348 function! s:PutOutputIntoBuffer(command_line)
349 let l:was_buffer_empty = (line('$') == 1 && getline(1) == '')
350 execute '0read!' . escape(a:command_line, '%#\')
351 if l:was_buffer_empty " (Always true?)
352 " '0read' inserts before the cursor, leaving a blank line which
353 " needs to be deleted... but if there are folds in this thing, we
354 " must open them all first otherwise we could delete the whole
355 " contents of the last fold (since Vim may close them all by
356 " default).
357 normal! zRG"_dd
358 endif
359 endfunction
360
361 let l:all_args = [a:command] + a:000
362 let l:hg_command = call(self['GetCommand'], l:all_args, self)
363 call lawrencium#trace("Running Mercurial command: " . l:hg_command)
364 call s:PutOutputIntoBuffer(l:hg_command)
365 endfunction
366
367 " Build a Lawrencium path for the given file and action.
368 " By default, the given path will be made relative to the repository root,
369 " unless '0' is passed as the 4th argument.
370 function! s:HgRepo.GetLawrenciumPath(path, action, value, ...) abort
371 let l:path = a:path
372 if a:0 == 0 || !a:1
373 let l:path = self.GetRelativePath(a:path)
374 endif
375 let l:path = fnameescape(l:path)
376 let l:result = 'lawrencium://' . lawrencium#stripslash(self.root_dir) . '//' . l:path
377 if a:action !=? ''
378 let l:result = l:result . '//' . a:action
379 if a:value !=? ''
380 let l:result = l:result . '=' . a:value
381 endif
382 endif
383 return l:result
384 endfunction
385
386 " Repo cache map.
387 let s:buffer_repos = {}
388
389 " Get a cached repo.
390 function! lawrencium#hg_repo(...) abort
391 " Use the given path, or the mercurial directory of the current buffer.
392 if a:0 == 0
393 if exists('b:mercurial_dir')
394 let l:path = b:mercurial_dir
395 else
396 let l:path = lawrencium#find_repo_root(expand('%:p'))
397 endif
398 else
399 let l:path = a:1
400 endif
401 " Find a cache repo instance, or make a new one.
402 if has_key(s:buffer_repos, l:path)
403 return get(s:buffer_repos, l:path)
404 else
405 let l:repo = s:HgRepo.New(l:path)
406 let s:buffer_repos[l:path] = l:repo
407 return l:repo
408 endif
409 endfunction
410
411 " }}}
412
413 " Buffer Object {{{
414
415 " The prototype dictionary.
416 let s:Buffer = {}
417
418 " Constructor.
419 function! s:Buffer.New(number) dict abort
420 let l:newBuffer = copy(self)
421 let l:newBuffer.nr = a:number
422 let l:newBuffer.var_backup = {}
423 let l:newBuffer.cmd_names = {}
424 let l:newBuffer.on_delete = []
425 let l:newBuffer.on_winleave = []
426 let l:newBuffer.on_unload = []
427 execute 'augroup lawrencium_buffer_' . a:number
428 execute ' autocmd!'
429 execute ' autocmd BufDelete <buffer=' . a:number . '> call s:buffer_on_delete(' . a:number . ')'
430 execute 'augroup end'
431 call lawrencium#trace("Built new buffer object for buffer: " . a:number)
432 return l:newBuffer
433 endfunction
434
435 function! s:Buffer.GetName(...) dict abort
436 let l:name = bufname(self.nr)
437 if a:0 > 0
438 let l:name = fnamemodify(l:name, a:1)
439 endif
440 return l:name
441 endfunction
442
443 function! s:Buffer.GetVar(var) dict abort
444 return getbufvar(self.nr, a:var)
445 endfunction
446
447 function! s:Buffer.SetVar(var, value) dict abort
448 if !has_key(self.var_backup, a:var)
449 let self.var_backup[a:var] = getbufvar(self.nr, a:var)
450 endif
451 return setbufvar(self.nr, a:var, a:value)
452 endfunction
453
454 function! s:Buffer.RestoreVars() dict abort
455 for key in keys(self.var_backup)
456 setbufvar(self.nr, key, self.var_backup[key])
457 endfor
458 endfunction
459
460 function! s:Buffer.DefineCommand(name, ...) dict abort
461 if a:0 == 0
462 call lawrencium#throw("Not enough parameters for s:Buffer.DefineCommands()")
463 endif
464 if a:0 == 1
465 let l:flags = ''
466 let l:cmd = a:1
467 else
468 let l:flags = a:1
469 let l:cmd = a:2
470 endif
471 if has_key(self.cmd_names, a:name)
472 call lawrencium#throw("Command '".a:name."' is already defined in buffer ".self.nr)
473 endif
474 if bufnr('%') != self.nr
475 call lawrencium#throw("You must move to buffer ".self.nr."first before defining local commands")
476 endif
477 let self.cmd_names[a:name] = 1
478 let l:real_flags = ''
479 if type(l:flags) == type('')
480 let l:real_flags = l:flags
481 endif
482 execute 'command -buffer '.l:real_flags.' '.a:name.' '.l:cmd
483 endfunction
484
485 function! s:Buffer.DeleteCommand(name) dict abort
486 if !has_key(self.cmd_names, a:name)
487 call lawrencium#throw("Command '".a:name."' has not been defined in buffer ".self.nr)
488 endif
489 if bufnr('%') != self.nr
490 call lawrencium#throw("You must move to buffer ".self.nr."first before deleting local commands")
491 endif
492 execute 'delcommand '.a:name
493 call remove(self.cmd_names, a:name)
494 endfunction
495
496 function! s:Buffer.DeleteCommands() dict abort
497 if bufnr('%') != self.nr
498 call lawrencium#throw("You must move to buffer ".self.nr."first before deleting local commands")
499 endif
500 for name in keys(self.cmd_names)
501 execute 'delcommand '.name
502 endfor
503 let self.cmd_names = {}
504 endfunction
505
506 function! s:Buffer.MoveToFirstWindow() dict abort
507 let l:win_nr = bufwinnr(self.nr)
508 if l:win_nr < 0
509 if a:0 > 0 && a:1 == 0
510 return 0
511 endif
512 call lawrencium#throw("No windows currently showing buffer ".self.nr)
513 endif
514 execute l:win_nr.'wincmd w'
515 return 1
516 endfunction
517
518 function! s:Buffer.OnDelete(cmd) dict abort
519 call lawrencium#trace("Adding BufDelete callback for buffer " . self.nr . ": " . a:cmd)
520 call add(self.on_delete, a:cmd)
521 endfunction
522
523 function! s:Buffer.OnWinLeave(cmd) dict abort
524 if len(self.on_winleave) == 0
525 call lawrencium#trace("Adding BufWinLeave auto-command on buffer " . self.nr)
526 execute 'augroup lawrencium_buffer_' . self.nr . '_winleave'
527 execute ' autocmd!'
528 execute ' autocmd BufWinLeave <buffer=' . self.nr . '> call s:buffer_on_winleave(' . self.nr .')'
529 execute 'augroup end'
530 endif
531 call lawrencium#trace("Adding BufWinLeave callback for buffer " . self.nr . ": " . a:cmd)
532 call add(self.on_winleave, a:cmd)
533 endfunction
534
535 function! s:Buffer.OnUnload(cmd) dict abort
536 if len(self.on_unload) == 0
537 call lawrencium#trace("Adding BufUnload auto-command on buffer " . self.nr)
538 execute 'augroup lawrencium_buffer_' . self.nr . '_unload'
539 execute ' autocmd!'
540 execute ' autocmd BufUnload <buffer=' . self.nr . '> call s:buffer_on_unload(' . self.nr . ')'
541 execute 'augroup end'
542 endif
543 call lawrencium#trace("Adding BufUnload callback for buffer " . self.nr . ": " . a:cmd)
544 call add(self.on_unload, a:cmd)
545 endfunction
546
547 let s:buffer_objects = {}
548
549 " Get a buffer instance for the specified buffer number, or the
550 " current buffer if nothing is specified.
551 function! lawrencium#buffer_obj(...) abort
552 let l:bufnr = a:0 ? a:1 : bufnr('%')
553 if !has_key(s:buffer_objects, l:bufnr)
554 let s:buffer_objects[l:bufnr] = s:Buffer.New(l:bufnr)
555 endif
556 return s:buffer_objects[l:bufnr]
557 endfunction
558
559 " Execute all the "on delete" callbacks.
560 function! s:buffer_on_delete(number) abort
561 let l:bufobj = s:buffer_objects[a:number]
562 call lawrencium#trace("Calling BufDelete callbacks on buffer " . l:bufobj.nr)
563 for cmd in l:bufobj.on_delete
564 call lawrencium#trace(" [" . cmd . "]")
565 execute cmd
566 endfor
567 call lawrencium#trace("Deleted buffer object " . l:bufobj.nr)
568 call remove(s:buffer_objects, l:bufobj.nr)
569 execute 'augroup lawrencium_buffer_' . l:bufobj.nr
570 execute ' autocmd!'
571 execute 'augroup end'
572 endfunction
573
574 " Execute all the "on winleave" callbacks.
575 function! s:buffer_on_winleave(number) abort
576 let l:bufobj = s:buffer_objects[a:number]
577 call lawrencium#trace("Calling BufWinLeave callbacks on buffer " . l:bufobj.nr)
578 for cmd in l:bufobj.on_winleave
579 call lawrencium#trace(" [" . cmd . "]")
580 execute cmd
581 endfor
582 execute 'augroup lawrencium_buffer_' . l:bufobj.nr . '_winleave'
583 execute ' autocmd!'
584 execute 'augroup end'
585 endfunction
586
587 " Execute all the "on unload" callbacks.
588 function! s:buffer_on_unload(number) abort
589 let l:bufobj = s:buffer_objects[a:number]
590 call lawrencium#trace("Calling BufUnload callbacks on buffer " . l:bufobj.nr)
591 for cmd in l:bufobj.on_unload
592 call lawrencium#trace(" [" . cmd . "]")
593 execute cmd
594 endfor
595 execute 'augroup lawrencium_buffer_' . l:bufobj.nr . '_unload'
596 execute ' autocmd!'
597 execute 'augroup end'
598 endfunction
599
600 " }}}
601
602 " Buffer Commands Management {{{
603
604 " Store the commands for Lawrencium-enabled buffers so that we can add them in
605 " batch when we need to.
606 let s:main_commands = []
607
608 function! lawrencium#add_command(command) abort
609 let s:main_commands += [a:command]
610 endfunction
611
612 function! lawrencium#define_commands()
613 for l:command in s:main_commands
614 execute 'command! -buffer ' . l:command
615 endfor
616 endfunction
617
618 augroup lawrencium_main
619 autocmd!
620 autocmd User Lawrencium call lawrencium#define_commands()
621 augroup end
622
623 " Sets up the current buffer with Lawrencium commands if it contains a file from a Mercurial repo.
624 " If the file is not in a Mercurial repo, just exit silently.
625 function! lawrencium#setup_buffer_commands() abort
626 call lawrencium#trace("Scanning buffer '" . bufname('%') . "' for Lawrencium setup...")
627 let l:do_setup = 1
628 if exists('b:mercurial_dir')
629 if b:mercurial_dir =~# '\v^\s*$'
630 unlet b:mercurial_dir
631 else
632 let l:do_setup = 0
633 endif
634 endif
635 try
636 let l:repo = lawrencium#hg_repo()
637 catch /^lawrencium\:/
638 return
639 endtry
640 let b:mercurial_dir = l:repo.root_dir
641 if exists('b:mercurial_dir') && l:do_setup
642 call lawrencium#trace("Setting Mercurial commands for buffer '" . bufname('%'))
643 call lawrencium#trace(" with repo : " . expand(b:mercurial_dir))
644 silent doautocmd User Lawrencium
645 endif
646 endfunction
647
648 " }}}
649
650 " Commands Auto-Complete {{{
651
652 " Auto-complete function for commands that take repo-relative file paths.
653 function! lawrencium#list_repo_files(ArgLead, CmdLine, CursorPos) abort
654 let l:matches = lawrencium#hg_repo().Glob(a:ArgLead . '*', 1)
655 call map(l:matches, 'lawrencium#normalizepath(v:val)')
656 return l:matches
657 endfunction
658
659 " Auto-complete function for commands that take repo-relative directory paths.
660 function! lawrencium#list_repo_dirs(ArgLead, CmdLine, CursorPos) abort
661 let l:matches = lawrencium#hg_repo().Glob(a:ArgLead . '*/')
662 call map(l:matches, 'lawrencium#normalizepath(v:val)')
663 return l:matches
664 endfunction
665
666 " }}}
667
668 " Lawrencium Files {{{
669
670 " Generic read
671 let s:lawrencium_file_readers = {}
672 let s:lawrencium_file_customoptions = {}
673
674 function! lawrencium#add_reader(action, callback, ...) abort
675 if has_key(s:lawrencium_file_readers, a:action)
676 call lawrencium#throw("Lawrencium file '".a:action."' has alredy been registered.")
677 endif
678 let s:lawrencium_file_readers[a:action] = function(a:callback)
679 if a:0 && a:1
680 let s:lawrencium_file_customoptions[a:action] = 1
681 endif
682 endfunction
683
684 function! lawrencium#read_lawrencium_file(path) abort
685 call lawrencium#trace("Reading Lawrencium file: " . a:path)
686 let l:path_parts = lawrencium#parse_lawrencium_path(a:path)
687 if l:path_parts['root'] == ''
688 call lawrencium#throw("Can't get repository root from: " . a:path)
689 endif
690 if !has_key(s:lawrencium_file_readers, l:path_parts['action'])
691 call lawrencium#throw("No registered reader for action: " . l:path_parts['action'])
692 endif
693
694 " Call the registered reader.
695 let l:repo = lawrencium#hg_repo(l:path_parts['root'])
696 let l:full_path = l:repo.root_dir . l:path_parts['path']
697 let LawrenciumFileReader = s:lawrencium_file_readers[l:path_parts['action']]
698 call LawrenciumFileReader(l:repo, l:path_parts, l:full_path)
699
700 " Setup the new buffer.
701 if !has_key(s:lawrencium_file_customoptions, l:path_parts['action'])
702 setlocal readonly
703 setlocal nomodified
704 setlocal bufhidden=delete
705 setlocal buftype=nofile
706 endif
707 goto
708
709 " Remember the real Lawrencium path, because Vim can fuck up the slashes
710 " on Windows.
711 let b:lawrencium_path = a:path
712
713 " Remember the repo it belongs to and make
714 " the Lawrencium commands available.
715 let b:mercurial_dir = l:repo.root_dir
716 call lawrencium#define_commands()
717
718 return ''
719 endfunction
720
721 function! lawrencium#write_lawrencium_file(path) abort
722 call lawrencium#trace("Writing Lawrencium file: " . a:path)
723 endfunction
724
725 " }}}
726
727 " Statusline {{{
728
729 " Prints a summary of the current repo (if any) that's appropriate for
730 " displaying on the status line.
731 function! lawrencium#statusline(...)
732 if !exists('b:mercurial_dir')
733 return ''
734 endif
735 let l:repo = lawrencium#hg_repo()
736 let l:prefix = (a:0 > 0 ? a:1 : '')
737 let l:suffix = (a:0 > 1 ? a:2 : '')
738 let l:branch = 'default'
739 let l:branch_file = l:repo.GetFullPath('.hg/branch')
740 if filereadable(l:branch_file)
741 let l:branch = readfile(l:branch_file)[0]
742 endif
743 let l:bookmarks = ''
744 let l:bookmarks_file = l:repo.GetFullPath('.hg/bookmarks.current')
745 if filereadable(l:bookmarks_file)
746 let l:bookmarks = join(readfile(l:bookmarks_file), ', ')
747 endif
748 let l:line = l:prefix . l:branch
749 if strlen(l:bookmarks) > 0
750 let l:line = l:line . ' - ' . l:bookmarks
751 endif
752 let l:line = l:line . l:suffix
753 return l:line
754 endfunction
755
756 " }}}
757
758 " Miscellaneous User Functions {{{
759
760 " Rescans the current buffer for setting up Mercurial commands.
761 " Passing '1' as the parameter enables debug traces temporarily.
762 function! lawrencium#rescan(...)
763 if exists('b:mercurial_dir')
764 unlet b:mercurial_dir
765 endif
766 if a:0 && a:1
767 let l:trace_backup = g:lawrencium_trace
768 let g:lawrencium_trace = 1
769 endif
770 call lawrencium#setup_buffer_commands()
771 if a:0 && a:1
772 let g:lawrencium_trace = l:trace_backup
773 endif
774 endfunction
775
776 " Enables/disables the debug trace.
777 function! lawrencium#debugtrace(...)
778 let g:lawrencium_trace = (a:0 == 0 || (a:0 && a:1))
779 echom "Lawrencium debug trace is now " . (g:lawrencium_trace ? "enabled." : "disabled.")
780 endfunction
781
782 " }}}
783
784 " Setup {{{
785
786 function! lawrencium#init() abort
787 let s:builtin_exts = [
788 \'lawrencium#addremove',
789 \'lawrencium#annotate',
790 \'lawrencium#cat',
791 \'lawrencium#commit',
792 \'lawrencium#diff',
793 \'lawrencium#hg',
794 \'lawrencium#log',
795 \'lawrencium#mq',
796 \'lawrencium#record',
797 \'lawrencium#revert',
798 \'lawrencium#status',
799 \'lawrencium#vimutils'
800 \]
801 let s:user_exts = copy(g:lawrencium_extensions)
802 let s:exts = s:builtin_exts + s:user_exts
803 for ext in s:exts
804 call lawrencium#trace("Initializing Lawrencium extension " . ext)
805 execute ('call ' . ext . '#init()')
806 endfor
807 endfunction
808
809 " }}}
810