1 "=============================================================================
3 " Author: Mikolaj Machowski
4 " Created: Tue Apr 23 06:00 PM 2002 PST
5 " CVS: $Id: packages.vim 997 2006-03-20 09:45:45Z srinathava $
7 " Description: handling packages from within vim
8 "=============================================================================
11 if !g:Tex_PackagesMenu || exists('s:doneOnce')
16 let s:path = expand("<sfile>:p:h")
20 com! -nargs=0 TPackageUpdate :silent! call Tex_pack_updateall(1)
21 com! -nargs=0 TPackageUpdateAll :silent! call Tex_pack_updateall(1)
23 " Custom command-line completion of Tcommands is very useful but this feature
24 " is available only in Vim 6.2 and above. Check number of version and choose
25 " proper command and function.
27 com! -complete=custom,Tex_CompletePackageName -nargs=* TPackage let s:retVal = Tex_pack_one(<f-args>) <bar> normal! i<C-r>=s:retVal<CR>
29 " Tex_CompletePackageName: for completing names in TPackage command {{{
30 " Description: get list of package names with globpath(), remove full path
31 " and return list of names separated with newlines.
33 function! Tex_CompletePackageName(A,P,L)
34 " Get name of packages from all runtimepath directories
35 let packnames = Tex_FindInRtp('', 'packages')
36 let packnames = substitute(packnames, '^,', '', 'e')
37 " Separate names with \n not ,
38 let packnames = substitute(packnames,',','\n','g')
44 com! -nargs=* TPackage let s:retVal = Tex_pack_one(<f-args>) <bar> normal! i<C-r>=s:retVal<CR>
48 imap <silent> <plug> <Nop>
49 nmap <silent> <plug> i
51 let g:Tex_package_supported = ''
52 let g:Tex_package_detected = ''
53 " Remember the defaults because we want g:Tex_PromptedEnvironments to contain
54 " in addition to the default, \newenvironments, and the \newenvironments might
56 let g:Tex_PromptedEnvironmentsDefault = g:Tex_PromptedEnvironments
57 let g:Tex_PromptedCommandsDefault = g:Tex_PromptedCommands
60 " Tex_pack_check: creates the package menu and adds to 'dict' setting. {{{
62 function! Tex_pack_check(package)
63 " Use Tex_FindInRtp() function to get first name from packages list in all
64 " rtp directories conforming with latex-suite directories hierarchy
65 " Store names in variables to process functions only once.
66 let packname = Tex_FindInRtp(a:package, 'packages')
68 exe 'runtime! ftplugin/latex-suite/packages/' . a:package
70 call Tex_pack(a:package)
72 if g:Tex_package_supported !~ a:package
73 let g:Tex_package_supported = g:Tex_package_supported.','.a:package
76 " Return full list of dictionaries (separated with ,) for package in &rtp
77 call Tex_Debug("Tex_pack_check: searching for ".a:package." in dictionaries/ in &rtp", "pack")
78 let dictname = Tex_FindInRtp(a:package, 'dictionaries', ':p')
80 exe 'setlocal dict^=' . dictname
81 call Tex_Debug('Tex_pack_check: setlocal dict^=' . dictname, 'pack')
82 if g:Tex_package_supported !~ a:package
83 let g:Tex_package_supported = g:Tex_package_supported.','.a:package
86 if g:Tex_package_detected !~ '\<'.a:package.'\>'
87 let g:Tex_package_detected = g:Tex_package_detected.','.a:package
89 let g:Tex_package_detected = substitute(g:Tex_package_detected, '^,', '', '')
90 let g:Tex_package_supported = substitute(g:Tex_package_supported, '^,', '', '')
94 " Tex_pack_uncheck: removes package from menu and 'dict' settings. {{{
95 function! Tex_pack_uncheck(package)
96 if has("gui_running") && Tex_FindInRtp(a:package, 'packages') != ''
97 exe 'silent! aunmenu '.g:Tex_PackagesMenuLocation.'-sep'.a:package.'-'
98 exe 'silent! aunmenu '.g:Tex_PackagesMenuLocation.a:package.'\ Options'
99 exe 'silent! aunmenu '.g:Tex_PackagesMenuLocation.a:package.'\ Commands'
101 if Tex_FindInRtp(a:package, 'dictionaries') != ''
102 exe 'setlocal dict-='.Tex_FindInRtp(a:package, 'dictionaries')
107 " Tex_pack_updateall: updates the TeX-Packages menu {{{
109 " This function first calls Tex_pack_all to scan for \usepackage's etc if
110 " necessary. After that, it 'supports' and 'unsupports' packages as needed
111 " in such a way as to not repeat work.
112 function! Tex_pack_updateall(force)
113 call Tex_Debug('+Tex_pack_updateall', 'pack')
115 " Find out which file we need to scan.
116 let fname = Tex_GetMainFileName(':p')
118 " If this is the same as last time, don't repeat.
119 if !a:force && exists('s:lastScannedFile') &&
120 \ s:lastScannedFile == fname
123 " Remember which file we scanned for next time.
124 let s:lastScannedFile = fname
126 " Remember which packages we detected last time.
127 if exists('g:Tex_package_detected')
128 let oldpackages = g:Tex_package_detected
133 " This sets up a global variable of all detected packages.
134 let g:Tex_package_detected = ''
135 " reset the environments and commands.
136 let g:Tex_PromptedEnvironments = g:Tex_PromptedEnvironmentsDefault
137 let g:Tex_PromptedCommands = g:Tex_PromptedCommandsDefault
139 if expand('%:p') != fname
140 call Tex_Debug(':Tex_pack_updateall: sview '.Tex_EscapeSpaces(fname), 'pack')
141 exe 'sview '.Tex_EscapeSpaces(fname)
143 call Tex_Debug(':Tex_pack_updateall: split', 'pack')
147 call Tex_ScanForPackages()
150 call Tex_Debug(':Tex_pack_updateall: detected ['.g:Tex_package_detected.'] in first run', 'pack')
152 " Now for each package find out if this is a custom package and if so,
153 " scan that as well. We will use the ':find' command in vim to let vim
154 " search through the file paths for us.
156 " NOTE: This while loop will also take into account packages included
157 " within packages to any level of recursion as long as
158 " g:Tex_package_detected is always padded with new package names
161 " First set the &path setting to the user's TEXINPUTS setting.
163 let _suffixesadd = &suffixesadd
165 let &path = '.,'.g:Tex_TEXINPUTS
166 let &suffixesadd = '.sty,.tex'
168 let scannedPackages = ''
171 let packname = Tex_Strntok(g:Tex_package_detected, ',', i)
174 call Tex_Debug(':Tex_pack_updateall: scanning package '.packname, 'pack')
176 " Scan this package only if we have not scanned it before in this
178 if scannedPackages =~ '\<'.packname.'\>'
181 call Tex_Debug(':Tex_pack_updateall: '.packname.' already scanned', 'pack')
182 let packname = Tex_Strntok(g:Tex_package_detected, ',', i)
186 " Split this window in two. The packages/files being found will open
187 " in this new window and we also need not bother with files being
191 call Tex_Debug(':Tex_pack_updateall: silent! find '.Tex_EscapeSpaces(packname).'.sty', 'pack')
192 let thisbufnum = bufnr('%')
193 exec 'silent! find '.Tex_EscapeSpaces(packname).'.sty'
194 call Tex_Debug(':Tex_pack_updateall: present file = '.bufname('%'), 'pack')
196 " If this file was not found, assume that it means its not a
197 " custom package and mark it "scanned".
198 " A package is not found if we stay in the same buffer as before and
199 " its not the one where we want to go.
200 if bufnr('%') == thisbufnum && bufnr('%') != bufnr(packname.'.sty')
201 let scannedPackages = scannedPackages.','.packname
204 call Tex_Debug(':Tex_pack_updateall: '.packname.' not found anywhere', 'pack')
206 let packname = Tex_Strntok(g:Tex_package_detected, ',', i)
210 " otherwise we are presently editing a custom package, scan it for
211 " more \usepackage lines from the first line to the last.
212 let packpath = expand('%:p')
213 let &complete = &complete.'s'.packpath
215 call Tex_Debug(':Tex_pack_updateall: found custom package '.packpath, 'pack')
216 call Tex_ScanForPackages(line('$'), line('$'))
217 call Tex_Debug(':Tex_pack_updateall: After scanning, g:Tex_package_detected = '.g:Tex_package_detected, 'pack')
219 let scannedPackages = scannedPackages.','.packname
220 " Do not use bwipe, but that leads to excessive buffer number
221 " consumption. Besides, its intuitive for a custom package to remain
222 " on the buffer list.
226 let packname = Tex_Strntok(g:Tex_package_detected, ',', i)
230 let &suffixesadd = _suffixesadd
232 " Now only support packages we didn't last time.
233 " First remove packages which were used last time but are no longer used.
235 let oldPackName = Tex_Strntok(oldpackages, ',', i)
236 while oldPackName != ''
237 if g:Tex_package_detected !~ oldPackName
238 call Tex_pack_uncheck(oldPackName)
241 let oldPackName = Tex_Strntok(oldpackages, ',', i)
244 " Then support packages which are used this time but weren't used last
247 let newPackName = Tex_Strntok(g:Tex_package_detected, ',', i)
248 while newPackName != ''
249 if oldpackages !~ newPackName
250 call Tex_pack_one(newPackName)
253 let newPackName = Tex_Strntok(g:Tex_package_detected, ',', i)
256 " Throw an event that we are done scanning packages. Some packages might
257 " use this to change behavior based on which options have been used etc.
258 call Tex_Debug(":Tex_pack_updateall: throwing LatexSuiteScannedPackages event", "pack")
259 silent! do LatexSuite User LatexSuiteScannedPackages
261 call Tex_Debug("-Tex_pack_updateall", "pack")
265 " Tex_pack_one: supports each package in the argument list.{{{
267 " If no arguments are supplied, then the user is asked to choose from the
268 " packages found in the packages/ directory
269 function! Tex_pack_one(...)
270 if a:0 == 0 || (a:0 > 0 && a:1 == '')
271 let packlist = Tex_FindInRtp('', 'packages')
272 let packname = Tex_ChooseFromPrompt(
273 \ "Choose a package: \n" .
274 \ Tex_CreatePrompt(packlist, '3', ',') .
275 \ "\nEnter number or filename :",
278 return Tex_pack_one(packname)
283 " Support the packages supplied. This function can be called with
284 " multiple arguments in which case, support each of them in turn.
288 let packname = a:{omega}
289 if Tex_FindInRtp(packname, 'packages') != ''
290 call Tex_pack_check(packname)
291 if exists('g:TeX_package_option_'.packname)
292 \ && g:TeX_package_option_{packname} != ''
293 let retVal = retVal.'\usepackage[<++>]{'.packname.'}<++>'
295 let retVal = retVal.'\usepackage{'.packname.'}'."\<CR>"
298 let retVal = retVal.'\usepackage{'.packname.'}'."\<CR>"
300 let omega = omega + 1
302 return IMAP_PutTextWithMovement(substitute(retVal, "\<CR>$", '', ''), '<+', '+>')
306 " Tex_ScanForPackages: scans the current file for \usepackage{} lines {{{
307 " and if supported, loads the options and commands found in the
308 " corresponding package file. Also scans for \newenvironment and
309 " \newcommand lines and adds names to g:Tex_Prompted variables, they can be
310 " easy available through <F5> and <F7> shortcuts
311 function! Tex_ScanForPackages(...)
312 call Tex_Debug("+Tex_ScanForPackages", "pack")
314 let pos = line('.').' | normal! '.virtcol('.').'|'
316 " For package files without \begin and \end{document}, we might be told to
317 " search from beginning to end.
320 let beginline = search('\\begin{document}', 'W')
321 let endline = search('\\end{document}', 'W')
328 call Tex_Debug(":Tex_ScanForPackages: Begining scans in [".bufname('%')."], beginline = ".beginline, "pack")
331 " Scan the file. First open up all the folds, because the command
333 " issued in a closed fold _always_ goes to the first match.
335 silent! normal! ggVGzO
338 call Tex_Debug(":Tex_ScanForPackages: beginning scan for \\usepackage lines", "pack")
339 " The wrap trick enables us to match \usepackage on the first line as
342 while search('^\s*\\usepackage\_.\{-}{\_.\+}', wrap)
345 if line('.') > beginline
351 " If there are options, then find those.
352 if getline('.') =~ '\\usepackage\[.\{-}\]'
353 let options = matchstr(getline('.'), '\\usepackage\[\zs.\{-}\ze\]')
354 elseif getline('.') =~ '\\usepackage\['
355 " Entering here means that the user has split the \usepackage
356 " across newlines. Therefore, use yank.
357 exec "normal! /{\<CR>\"ayi}"
363 " The following statement puts the stuff between the { }'s of a
364 " \usepackage{stuff,foo} into @a. Do not use matchstr() and the like
365 " because we can have things split across lines and such.
366 exec "normal! /{\<CR>\"ay/}\<CR>"
368 " now remove all whitespace from @a. We need to remove \n and \r
369 " because we can encounter stuff like
371 " newpackonanotherline}
372 let @a = substitute(@a, "[ \t\n\r]", '', 'g')
374 " Now we have something like pack1,pack2,pack3 with possibly commas
375 " and stuff before the first package and after the last package name.
377 let @a = substitute(@a, '\(^\W*\|\W*$\)', '', 'g')
379 " This gets us a string like 'pack1,pack2,pack3'
380 " TODO: This will contain duplicates if the user has duplicates.
381 " Should we bother taking care of this?
382 let g:Tex_package_detected = g:Tex_package_detected.','.@a
384 " For each package found, form a global variable of the form
385 " g:Tex_{packagename}_options
386 " which contains a list of the options.
388 while Tex_Strntok(@a, ',', j) != ''
389 let g:Tex_{Tex_Strntok(@a, ',', j)}_options = options
393 " Finally convert @a into something like '"pack1","pack2"'
394 let @a = substitute(@a, '^\|$', '"', 'g')
395 let @a = substitute(@a, ',', '","', 'g')
397 call Tex_Debug(":Tex_ScanForPackages: found package(s) [".@a."] on line ".line('.'), "pack")
402 call Tex_Debug(":Tex_ScanForPackages: End scan \\usepackage, detected packages = ".g:Tex_package_detected, "pack")
404 " TODO: This needs to be changed. In the future, we might have
405 " functionality to remember the fold-state before opening up all the folds
406 " and then re-creating them. Use mkview.vim.
408 silent! normal! ggVGzC
411 " Because creating list of detected packages gives string
412 " ',pack1,pack2,pack3' remove leading ,
413 let g:Tex_package_detected = substitute(g:Tex_package_detected, '^,', '', '')
415 call Tex_Debug(":Tex_ScanForPackages: Beginning scan for \\newcommand's", "pack")
416 " Scans whole file (up to \end{document}) for \newcommand and adds this
417 " commands to g:Tex_PromptedCommands variable, it is easily available
420 while search('^\s*\\newcommand\*\?{.\{-}}', 'W')
422 if line('.') > endline
426 let newcommand = matchstr(getline('.'), '\\newcommand\*\?{\\\zs.\{-}\ze}')
427 let g:Tex_PromptedCommands = g:Tex_PromptedCommands . ',' . newcommand
431 " Scans whole file (up to \end{document}) for \newenvironment and adds this
432 " environments to g:Tex_PromptedEnvironments variable, it is easily available
435 call Tex_Debug(":Tex_ScanForPackages: Beginning scan for \\newenvironment's", 'pack')
437 while search('^\s*\\newenvironment\*\?{.\{-}}', 'W')
438 call Tex_Debug('found newenvironment on '.line('.'), 'pack')
440 if line('.') > endline
444 let newenvironment = matchstr(getline('.'), '\\newenvironment\*\?{\zs.\{-}\ze}')
445 let g:Tex_PromptedEnvironments = g:Tex_PromptedEnvironments . ',' . newenvironment
450 " first make a random search so that we push at least one item onto the
451 " search history. Since vim puts only one item in the history per function
452 " call, this way we make sure that one and only item is put into the
456 call histdel('/', -1)
458 call Tex_Debug("-Tex_ScanForPackages", "pack")
462 " Tex_pack_supp_menu: sets up a menu for package files {{{
463 " found in the packages directory groups the packages thus found into groups
465 function! Tex_pack_supp_menu()
466 let suplist = Tex_FindInRtp('', 'packages')
468 call Tex_MakeSubmenu(suplist, g:Tex_PackagesMenuLocation.'Supported.',
469 \ '<plug><C-r>=Tex_pack_one("', '")<CR>')
473 " Tex_pack: loads the options (and commands) for the given package {{{
474 function! Tex_pack(pack)
475 if exists('g:TeX_package_'.a:pack)
477 let optionList = g:TeX_package_option_{a:pack}.','
478 let commandList = g:TeX_package_{a:pack}.','
480 " Don't create separator if in package file are only Vim commands.
482 if !(commandList == ',' && optionList == ',')
483 exec 'amenu '.g:Tex_PackagesMenuLocation.'-sep'.a:pack.'- <Nop>'
488 let mainMenuName = g:Tex_PackagesMenuLocation.a:pack.'\ Options.'
489 call s:GroupPackageMenuItems(optionList, mainMenuName,
490 \ '<plug><C-r>=IMAP_PutTextWithMovement("', ',")<CR>')
496 let mainMenuName = g:Tex_PackagesMenuLocation.a:pack.'\ Commands.'
497 call s:GroupPackageMenuItems(commandList, mainMenuName,
498 \ '<plug><C-r>=Tex_ProcessPackageCommand("', '")<CR>',
499 \ '<SID>FilterPackageMenuLHS')
506 " ==============================================================================
508 " Creating menu items for the all the package files found in the packages/
509 " directory as well as creating menus for each supported package found in the
511 " ==============================================================================
512 " Tex_MakeSubmenu: makes a submenu given a list of items {{{
514 " This function takes a comma seperated list of menu items and creates a
515 " 'grouped' menu. i.e, it groups the items into s:menu_div items each and
516 " puts them in submenus of the given mainMenu.
517 " Each menu item is linked to the HandlerFunc.
518 " If an additional argument is supplied, then it is used to filter each of
519 " the menu items to generate better names for the menu display.
521 function! Tex_MakeSubmenu(menuList, mainMenuName,
522 \ handlerFuncLHS, handlerFuncRHS, ...)
524 let extractFunction = (a:0 > 0 ? a:1 : '' )
525 let menuList = substitute(a:menuList, '[^,]$', ',', '')
527 let doneMenuSubmenu = 0
531 " Extract upto s:menu_div menus at once.
532 let menuBunch = matchstr(menuList, '\v(.{-},){,'.s:menu_div.'}')
534 " The remaining menus go into the list.
535 let menuList = strpart(menuList, strlen(menuBunch))
538 " If there is something remaining, then we got s:menu_div items.
539 " therefore put these menu items into a submenu.
540 if strlen(menuList) || doneMenuSubmenu
541 exec 'let firstMenu = '.extractFunction."(matchstr(menuBunch, '\\v^.{-}\\ze,'))"
542 exec 'let lastMenu = '.extractFunction."(matchstr(menuBunch, '\\v[^,]{-}\\ze,$'))"
544 let submenu = firstMenu.'\ \-\ '.lastMenu.'.'
546 let doneMenuSubmenu = 1
549 " Now for each menu create a menu under the submenu
551 let menuName = Tex_Strntok(menuBunch, ',', i)
553 exec 'let menuItem = '.extractFunction.'(menuName)'
554 execute 'amenu '.a:mainMenuName.submenu.menuItem
555 \ ' '.a:handlerFuncLHS.menuName.a:handlerFuncRHS
558 let menuName = Tex_Strntok(menuBunch, ',', i)
564 " GroupPackageMenuItems: uses the sbr: to split menus into groups {{{
566 " This function first splits up the menuList into groups based on the
567 " special sbr: tag and then calls Tex_MakeSubmenu
569 function! <SID>GroupPackageMenuItems(menuList, menuName,
570 \ handlerFuncLHS, handlerFuncRHS,...)
573 let extractFunction = a:1
575 let extractFunction = ''
577 let menuList = a:menuList
579 while matchstr(menuList, 'sbr:') != ''
580 let groupName = matchstr(menuList, '\v^sbr:\zs.{-}\ze,')
581 let menuList = strpart(menuList, strlen('sbr:'.groupName.','))
582 if matchstr(menuList, 'sbr:') != ''
583 let menuGroup = matchstr(menuList, '\v^.{-},\zesbr:')
585 let menuGroup = menuList
588 call Tex_MakeSubmenu(menuGroup, a:menuName.groupName.'.',
589 \ a:handlerFuncLHS, a:handlerFuncRHS, extractFunction)
591 let menuList = strpart(menuList, strlen(menuGroup))
594 call Tex_MakeSubmenu(menuList, a:menuName,
595 \ a:handlerFuncLHS, a:handlerFuncRHS, extractFunction)
598 " Definition of what to do for various package commands {{{
599 let s:CommandSpec_bra = '\<+replace+>{<++>}<++>'
600 let s:CommandSpec_brs = '\<+replace+><++>'
601 let s:CommandSpec_brd = '\<+replace+>{<++>}{<++>}<++>'
602 let s:CommandSpec_env = '\begin{<+replace+>}'."\<CR><++>\<CR>".'\end{<+replace+>}<++>'
603 let s:CommandSpec_ens = '\begin{<+replace+>}<+extra+>'."\<CR><++>\<CR>".'\end{<+replace+>}<++>'
604 let s:CommandSpec_eno = '\begin[<++>]{<+replace+>}'."\<CR><++>\<CR>".'\end{<+replace+>}'
605 let s:CommandSpec_nor = '\<+replace+>'
606 let s:CommandSpec_noo = '\<+replace+>[<++>]'
607 let s:CommandSpec_nob = '\<+replace+>[<++>]{<++>}{<++>}<++>'
608 let s:CommandSpec_spe = '<+replace+>'
609 let s:CommandSpec_ = '\<+replace+>'
611 let s:MenuLHS_bra = '\\&<+replace+>{}'
612 let s:MenuLHS_brs = '\\&<+replace+>{}'
613 let s:MenuLHS_brd = '\\&<+replace+>{}{}'
614 let s:MenuLHS_env = '&<+replace+>\ (E)'
615 let s:MenuLHS_ens = '&<+replace+>\ (E)'
616 let s:MenuLHS_eno = '&<+replace+>\ (E)'
617 let s:MenuLHS_nor = '\\&<+replace+>'
618 let s:MenuLHS_noo = '\\&<+replace+>[]'
619 let s:MenuLHS_nob = '\\&<+replace+>[]{}{}'
620 let s:MenuLHS_spe = '&<+replace+>'
621 let s:MenuLHS_sep = '-sep<+replace+>-'
622 let s:MenuLHS_ = '\\&<+replace+>'
624 " Tex_ProcessPackageCommand: processes a command from the package menu {{{
626 function! Tex_ProcessPackageCommand(command)
628 let commandType = matchstr(a:command, '^\w\+\ze:')
629 let commandName = matchstr(a:command, '^\w\+:\zs[^:]\+\ze:\?')
630 let extrapart = strpart(a:command, strlen(commandType.':'.commandName.':'))
633 let commandName = a:command
637 let command = s:CommandSpec_{commandType}
638 let command = substitute(command, '<+replace+>', commandName, 'g')
639 let command = substitute(command, '<+extra+>', extrapart, 'g')
640 return IMAP_PutTextWithMovement(command)
643 " FilterPackageMenuLHS: filters the command description to provide a better menu item {{{
645 function! <SID>FilterPackageMenuLHS(command)
646 let commandType = matchstr(a:command, '^\w\+\ze:')
648 let commandName = strpart(a:command, strlen(commandType.':'))
650 let commandName = a:command
653 return substitute(s:MenuLHS_{commandType}, '<+replace+>', commandName, 'g')
657 exe 'amenu '.g:Tex_PackagesMenuLocation.'&UpdatePackage :call Tex_pack(expand("<cword>"))<cr>'
658 exe 'amenu '.g:Tex_PackagesMenuLocation.'&UpdateAll :call Tex_pack_updateall(1)<cr>'
660 call Tex_pack_supp_menu()
664 au LatexSuite User LatexSuiteFileType
665 \ call Tex_Debug('packages.vim: Catching LatexSuiteFileType event', 'pack') |
666 \ call Tex_pack_updateall(0)
669 " vim:fdm=marker:ts=4:sw=4:noet:ff=unix