"============================================================================= " File: packages.vim " Author: Mikolaj Machowski " Created: Tue Apr 23 06:00 PM 2002 PST " " Description: handling packages from within vim "============================================================================= " avoid reinclusion. if !g:Tex_PackagesMenu || exists('s:doneOnce') finish endif let s:doneOnce = 1 let s:path = fnameescape(expand(":p:h")) let s:menu_div = 20 com! -nargs=0 TPackageUpdate :silent! call Tex_pack_updateall(1) com! -nargs=0 TPackageUpdateAll :silent! call Tex_pack_updateall(1) " Custom command-line completion of Tcommands is very useful but this feature " is available only in Vim 6.2 and above. Check number of version and choose " proper command and function. if v:version >= 602 com! -complete=custom,Tex_CompletePackageName -nargs=* TPackage let s:retVal = Tex_pack_one() normal! i=s:retVal " Tex_CompletePackageName: for completing names in TPackage command {{{ " Description: get list of package names with globpath(), remove full path " and return list of names separated with newlines. " function! Tex_CompletePackageName(A,P,L) " Get name of packages from all runtimepath directories let packnames = Tex_FindInRtp('', 'packages') let packnames = substitute(packnames, '^,', '', 'e') " Separate names with \n not , let packnames = substitute(packnames,',','\n','g') return packnames endfunction " }}} else com! -nargs=* TPackage let s:retVal = Tex_pack_one() normal! i=s:retVal endif imap nmap i let g:Tex_package_supported = '' let g:Tex_package_detected = '' " Remember the defaults because we want g:Tex_PromptedEnvironments to contain " in addition to the default, \newenvironments, and the \newenvironments might " change... let g:Tex_PromptedEnvironmentsDefault = g:Tex_PromptedEnvironments let g:Tex_PromptedCommandsDefault = g:Tex_PromptedCommands " Tex_pack_check: creates the package menu and adds to 'dict' setting. {{{ " function! Tex_pack_check(package) " Use Tex_FindInRtp() function to get first name from packages list in all " rtp directories conforming with latex-suite directories hierarchy " Store names in variables to process functions only once. let packname = Tex_FindInRtp(a:package, 'packages') if packname != '' exe 'runtime! ftplugin/latex-suite/packages/' . a:package if has("gui_running") call Tex_pack(a:package) endif if g:Tex_package_supported !~ a:package let g:Tex_package_supported = g:Tex_package_supported.','.a:package endif endif " Return full list of dictionaries (separated with ,) for package in &rtp call Tex_Debug("Tex_pack_check: searching for ".a:package." in dictionaries/ in &rtp", "pack") let dictname = Tex_FindInRtp(a:package, 'dictionaries', ':p') if dictname != '' exe 'setlocal dict^=' . dictname call Tex_Debug('Tex_pack_check: setlocal dict^=' . dictname, 'pack') if g:Tex_package_supported !~ a:package let g:Tex_package_supported = g:Tex_package_supported.','.a:package endif endif if g:Tex_package_detected !~ '\<'.a:package.'\>' let g:Tex_package_detected = g:Tex_package_detected.','.a:package endif let g:Tex_package_detected = substitute(g:Tex_package_detected, '^,', '', '') let g:Tex_package_supported = substitute(g:Tex_package_supported, '^,', '', '') endfunction " }}} " Tex_pack_uncheck: removes package from menu and 'dict' settings. {{{ function! Tex_pack_uncheck(package) if has("gui_running") && Tex_FindInRtp(a:package, 'packages') != '' exe 'silent! aunmenu '.g:Tex_PackagesMenuLocation.'-sep'.a:package.'-' exe 'silent! aunmenu '.g:Tex_PackagesMenuLocation.a:package.'\ Options' exe 'silent! aunmenu '.g:Tex_PackagesMenuLocation.a:package.'\ Commands' endif if Tex_FindInRtp(a:package, 'dictionaries') != '' exe 'setlocal dict-='.Tex_FindInRtp(a:package, 'dictionaries') endif endfunction " }}} " Tex_pack_updateall: updates the TeX-Packages menu {{{ " Description: " This function first calls Tex_pack_all to scan for \usepackage's etc if " necessary. After that, it 'supports' and 'unsupports' packages as needed " in such a way as to not repeat work. function! Tex_pack_updateall(force) call Tex_Debug('+Tex_pack_updateall', 'pack') " Find out which file we need to scan. let fname = Tex_GetMainFileName(':p') " If this is the same as last time, don't repeat. if !a:force && exists('s:lastScannedFile') && \ s:lastScannedFile == fname return endif " Remember which file we scanned for next time. let s:lastScannedFile = fname " Remember which packages we detected last time. if exists('g:Tex_package_detected') let oldpackages = g:Tex_package_detected else let oldpackages = '' endif " This sets up a global variable of all detected packages. let g:Tex_package_detected = '' " reset the environments and commands. let g:Tex_PromptedEnvironments = g:Tex_PromptedEnvironmentsDefault let g:Tex_PromptedCommands = g:Tex_PromptedCommandsDefault if expand('%:p') != fname call Tex_Debug(':Tex_pack_updateall: sview '.Tex_EscapeSpaces(fname), 'pack') exe 'sview '.Tex_EscapeSpaces(fname) else call Tex_Debug(':Tex_pack_updateall: split', 'pack') split endif call Tex_ScanForPackages() q call Tex_Debug(':Tex_pack_updateall: detected ['.g:Tex_package_detected.'] in first run', 'pack') " Now for each package find out if this is a custom package and if so, " scan that as well. We will use the ':find' command in vim to let vim " search through the file paths for us. " " NOTE: This while loop will also take into account packages included " within packages to any level of recursion as long as " g:Tex_package_detected is always padded with new package names " from the end. " " First set the &path setting to the user's TEXINPUTS setting. let _path = &path let _suffixesadd = &suffixesadd let &path = '.,'.g:Tex_TEXINPUTS let &suffixesadd = '.sty,.tex' let scannedPackages = '' let i = 1 let packname = Tex_Strntok(g:Tex_package_detected, ',', i) while packname != '' call Tex_Debug(':Tex_pack_updateall: scanning package '.packname, 'pack') " Scan this package only if we have not scanned it before in this " run. if scannedPackages =~ '\<'.packname.'\>' let i = i + 1 call Tex_Debug(':Tex_pack_updateall: '.packname.' already scanned', 'pack') let packname = Tex_Strntok(g:Tex_package_detected, ',', i) continue endif " Split this window in two. The packages/files being found will open " in this new window and we also need not bother with files being " modified etc. split call Tex_Debug(':Tex_pack_updateall: silent! find '.Tex_EscapeSpaces(packname).'.sty', 'pack') let thisbufnum = bufnr('%') exec 'silent! find '.Tex_EscapeSpaces(packname).'.sty' call Tex_Debug(':Tex_pack_updateall: present file = '.bufname('%'), 'pack') " If this file was not found, assume that it means its not a " custom package and mark it "scanned". " A package is not found if we stay in the same buffer as before and " its not the one where we want to go. if bufnr('%') == thisbufnum && bufnr('%') != bufnr(packname.'.sty') let scannedPackages = scannedPackages.','.packname q call Tex_Debug(':Tex_pack_updateall: '.packname.' not found anywhere', 'pack') let i = i + 1 let packname = Tex_Strntok(g:Tex_package_detected, ',', i) continue endif " otherwise we are presently editing a custom package, scan it for " more \usepackage lines from the first line to the last. let packpath = expand('%:p') let &complete = &complete.'s'.packpath call Tex_Debug(':Tex_pack_updateall: found custom package '.packpath, 'pack') call Tex_ScanForPackages(line('$'), line('$')) call Tex_Debug(':Tex_pack_updateall: After scanning, g:Tex_package_detected = '.g:Tex_package_detected, 'pack') let scannedPackages = scannedPackages.','.packname " Do not use bwipe, but that leads to excessive buffer number " consumption. Besides, its intuitive for a custom package to remain " on the buffer list. q let i = i + 1 let packname = Tex_Strntok(g:Tex_package_detected, ',', i) endwhile let &path = _path let &suffixesadd = _suffixesadd " Now only support packages we didn't last time. " First remove packages which were used last time but are no longer used. let i = 1 let oldPackName = Tex_Strntok(oldpackages, ',', i) while oldPackName != '' if g:Tex_package_detected !~ oldPackName call Tex_pack_uncheck(oldPackName) endif let i = i + 1 let oldPackName = Tex_Strntok(oldpackages, ',', i) endwhile " Then support packages which are used this time but weren't used last " time. let i = 1 let newPackName = Tex_Strntok(g:Tex_package_detected, ',', i) while newPackName != '' if oldpackages !~ newPackName call Tex_pack_one(newPackName) endif let i = i + 1 let newPackName = Tex_Strntok(g:Tex_package_detected, ',', i) endwhile " Throw an event that we are done scanning packages. Some packages might " use this to change behavior based on which options have been used etc. call Tex_Debug(":Tex_pack_updateall: throwing LatexSuiteScannedPackages event", "pack") silent! do LatexSuite User LatexSuiteScannedPackages call Tex_Debug("-Tex_pack_updateall", "pack") endfunction " }}} " Tex_pack_one: supports each package in the argument list.{{{ " Description: " If no arguments are supplied, then the user is asked to choose from the " packages found in the packages/ directory function! Tex_pack_one(...) if a:0 == 0 || (a:0 > 0 && a:1 == '') let packlist = Tex_FindInRtp('', 'packages') let packname = Tex_ChooseFromPrompt( \ "Choose a package: \n" . \ Tex_CreatePrompt(packlist, '3', ',') . \ "\nEnter number or filename :", \ packlist, ',') if packname != '' return Tex_pack_one(packname) else return '' endif else " Support the packages supplied. This function can be called with " multiple arguments in which case, support each of them in turn. let retVal = '' let omega = 1 while omega <= a:0 let packname = a:{omega} if Tex_FindInRtp(packname, 'packages') != '' call Tex_pack_check(packname) if exists('g:TeX_package_option_'.packname) \ && g:TeX_package_option_{packname} != '' let retVal = retVal.'\usepackage[<++>]{'.packname.'}<++>' else let retVal = retVal.'\usepackage{'.packname.'}'."\" endif else let retVal = retVal.'\usepackage{'.packname.'}'."\" endif let omega = omega + 1 endwhile return IMAP_PutTextWithMovement(substitute(retVal, "\$", '', ''), '<+', '+>') endif endfunction " }}} " Tex_ScanForPackages: scans the current file for \usepackage{} lines {{{ " and if supported, loads the options and commands found in the " corresponding package file. Also scans for \newenvironment and " \newcommand lines and adds names to g:Tex_Prompted variables, they can be " easy available through and shortcuts function! Tex_ScanForPackages(...) call Tex_Debug("+Tex_ScanForPackages", "pack") let pos = Tex_GetPos() " For package files without \begin and \end{document}, we might be told to " search from beginning to end. if a:0 < 2 0 let beginline = search('\\begin{document}', 'W') let endline = search('\\end{document}', 'W') 0 else let beginline = a:1 let endline = a:2 endif call Tex_Debug(":Tex_ScanForPackages: Begining scans in [".bufname('%')."], beginline = ".beginline, "pack") " Scan the file. First open up all the folds, because the command " /somepattern " issued in a closed fold _always_ goes to the first match. let erm = v:errmsg silent! normal! ggVGzO let v:errmsg = erm call Tex_Debug(":Tex_ScanForPackages: beginning scan for \\usepackage lines", "pack") " The wrap trick enables us to match \usepackage on the first line as " well. let wrap = 'w' while search('^\s*\\usepackage\_.\{-}{\_.\+}', wrap) let wrap = 'W' if line('.') > beginline break endif let saveUnnamed = @" let saveA = @a " If there are options, then find those. if getline('.') =~ '\\usepackage\[.\{-}\]' let options = matchstr(getline('.'), '\\usepackage\[\zs.\{-}\ze\]') elseif getline('.') =~ '\\usepackage\[' " Entering here means that the user has split the \usepackage " across newlines. Therefore, use yank. exec "normal! /{\\"ayi}" let options = @a else let options = '' endif " The following statement puts the stuff between the { }'s of a " \usepackage{stuff,foo} into @a. Do not use matchstr() and the like " because we can have things split across lines and such. exec "normal! /{\\"ay/}\" " now remove all whitespace from @a. We need to remove \n and \r " because we can encounter stuff like " \usepackage{pack1, " newpackonanotherline} let @a = substitute(@a, "[ \t\n\r]", '', 'g') " Now we have something like pack1,pack2,pack3 with possibly commas " and stuff before the first package and after the last package name. " Remove those. let @a = substitute(@a, '\(^\W*\|\W*$\)', '', 'g') " This gets us a string like 'pack1,pack2,pack3' " TODO: This will contain duplicates if the user has duplicates. " Should we bother taking care of this? let g:Tex_package_detected = g:Tex_package_detected.','.@a " For each package found, form a global variable of the form " g:Tex_{packagename}_options " which contains a list of the options. let j = 1 while Tex_Strntok(@a, ',', j) != '' let g:Tex_{Tex_Strntok(@a, ',', j)}_options = options let j = j + 1 endwhile " Finally convert @a into something like '"pack1","pack2"' let @a = substitute(@a, '^\|$', '"', 'g') let @a = substitute(@a, ',', '","', 'g') call Tex_Debug(":Tex_ScanForPackages: found package(s) [".@a."] on line ".line('.'), "pack") " restore @a let @a = saveA let @" = saveUnnamed endwhile call Tex_Debug(":Tex_ScanForPackages: End scan \\usepackage, detected packages = ".g:Tex_package_detected, "pack") " TODO: This needs to be changed. In the future, we might have " functionality to remember the fold-state before opening up all the folds " and then re-creating them. Use mkview.vim. let erm = v:errmsg silent! normal! ggVGzC let v:errmsg = erm " Because creating list of detected packages gives string " ',pack1,pack2,pack3' remove leading , let g:Tex_package_detected = substitute(g:Tex_package_detected, '^,', '', '') call Tex_Debug(":Tex_ScanForPackages: Beginning scan for \\newcommand's", "pack") " Scans whole file (up to \end{document}) for \newcommand and adds this " commands to g:Tex_PromptedCommands variable, it is easily available " through 0 while search('^\s*\\newcommand\*\?{.\{-}}', 'W') if line('.') > endline break endif let newcommand = matchstr(getline('.'), '\\newcommand\*\?{\\\zs.\{-}\ze}') let g:Tex_PromptedCommands = g:Tex_PromptedCommands . ',' . newcommand endwhile " Scans whole file (up to \end{document}) for \newenvironment and adds this " environments to g:Tex_PromptedEnvironments variable, it is easily available " through 0 call Tex_Debug(":Tex_ScanForPackages: Beginning scan for \\newenvironment's", 'pack') while search('^\s*\\newenvironment\*\?{.\{-}}', 'W') call Tex_Debug('found newenvironment on '.line('.'), 'pack') if line('.') > endline break endif let newenvironment = matchstr(getline('.'), '\\newenvironment\*\?{\zs.\{-}\ze}') let g:Tex_PromptedEnvironments = g:Tex_PromptedEnvironments . ',' . newenvironment endwhile call Tex_SetPos(pos) " first make a random search so that we push at least one item onto the " search history. Since vim puts only one item in the history per function " call, this way we make sure that one and only item is put into the " search history. normal! /^ " now delete it... call histdel('/', -1) call Tex_Debug("-Tex_ScanForPackages", "pack") endfunction " }}} " Tex_pack_supp_menu: sets up a menu for package files {{{ " found in the packages directory groups the packages thus found into groups " of 20... function! Tex_pack_supp_menu() let suplist = Tex_FindInRtp('', 'packages') call Tex_MakeSubmenu(suplist, g:Tex_PackagesMenuLocation.'Supported.', \ '=Tex_pack_one("', '")') endfunction " }}} " Tex_pack: loads the options (and commands) for the given package {{{ function! Tex_pack(pack) if exists('g:TeX_package_'.a:pack) let optionList = g:TeX_package_option_{a:pack}.',' let commandList = g:TeX_package_{a:pack}.',' " Don't create separator if in package file are only Vim commands. " Rare but possible. if !(commandList == ',' && optionList == ',') exec 'amenu '.g:Tex_PackagesMenuLocation.'-sep'.a:pack.'- ' endif if optionList != '' let mainMenuName = g:Tex_PackagesMenuLocation.a:pack.'\ Options.' call s:GroupPackageMenuItems(optionList, mainMenuName, \ '=IMAP_PutTextWithMovement("', ',")') endif if commandList != '' let mainMenuName = g:Tex_PackagesMenuLocation.a:pack.'\ Commands.' call s:GroupPackageMenuItems(commandList, mainMenuName, \ '=Tex_ProcessPackageCommand("', '")', \ 'FilterPackageMenuLHS') endif endif endfunction " }}} " ============================================================================== " Menu Functions " Creating menu items for the all the package files found in the packages/ " directory as well as creating menus for each supported package found in the " preamble. " ============================================================================== " Tex_MakeSubmenu: makes a submenu given a list of items {{{ " Description: " This function takes a comma seperated list of menu items and creates a " 'grouped' menu. i.e, it groups the items into s:menu_div items each and " puts them in submenus of the given mainMenu. " Each menu item is linked to the HandlerFunc. " If an additional argument is supplied, then it is used to filter each of " the menu items to generate better names for the menu display. " function! Tex_MakeSubmenu(menuList, mainMenuName, \ handlerFuncLHS, handlerFuncRHS, ...) let extractFunction = (a:0 > 0 ? a:1 : '' ) let menuList = substitute(a:menuList, '[^,]$', ',', '') let doneMenuSubmenu = 0 while menuList != '' " Extract upto s:menu_div menus at once. let menuBunch = matchstr(menuList, '\v(.{-},){,'.s:menu_div.'}') " The remaining menus go into the list. let menuList = strpart(menuList, strlen(menuBunch)) let submenu = '' " If there is something remaining, then we got s:menu_div items. " therefore put these menu items into a submenu. if strlen(menuList) || doneMenuSubmenu exec 'let firstMenu = '.extractFunction."(matchstr(menuBunch, '\\v^.{-}\\ze,'))" exec 'let lastMenu = '.extractFunction."(matchstr(menuBunch, '\\v[^,]{-}\\ze,$'))" let submenu = firstMenu.'\ \-\ '.lastMenu.'.' let doneMenuSubmenu = 1 endif " Now for each menu create a menu under the submenu let i = 1 let menuName = Tex_Strntok(menuBunch, ',', i) while menuName != '' exec 'let menuItem = '.extractFunction.'(menuName)' execute 'amenu '.a:mainMenuName.submenu.menuItem \ ' '.a:handlerFuncLHS.menuName.a:handlerFuncRHS let i = i + 1 let menuName = Tex_Strntok(menuBunch, ',', i) endwhile endwhile endfunction " }}} " GroupPackageMenuItems: uses the sbr: to split menus into groups {{{ " Description: " This function first splits up the menuList into groups based on the " special sbr: tag and then calls Tex_MakeSubmenu " function! GroupPackageMenuItems(menuList, menuName, \ handlerFuncLHS, handlerFuncRHS,...) if a:0 > 0 let extractFunction = a:1 else let extractFunction = '' endif let menuList = a:menuList while matchstr(menuList, 'sbr:') != '' let groupName = matchstr(menuList, '\v^sbr:\zs.{-}\ze,') let menuList = strpart(menuList, strlen('sbr:'.groupName.',')) if matchstr(menuList, 'sbr:') != '' let menuGroup = matchstr(menuList, '\v^.{-},\zesbr:') else let menuGroup = menuList endif call Tex_MakeSubmenu(menuGroup, a:menuName.groupName.'.', \ a:handlerFuncLHS, a:handlerFuncRHS, extractFunction) let menuList = strpart(menuList, strlen(menuGroup)) endwhile call Tex_MakeSubmenu(menuList, a:menuName, \ a:handlerFuncLHS, a:handlerFuncRHS, extractFunction) endfunction " }}} " Definition of what to do for various package commands {{{ let s:CommandSpec_brs = '\<+replace+><++>' let s:CommandSpec_bra = '\<+replace+>{<++>}<++>' let s:CommandSpec_brd = '\<+replace+>{<++>}{<++>}<++>' let s:CommandSpec_nor = '\<+replace+>' let s:CommandSpec_noo = '\<+replace+>[<++>]' let s:CommandSpec_nob = '\<+replace+>[<++>]{<++>}{<++>}<++>' let s:CommandSpec_env = '\begin{<+replace+>}'."\<++>\".'\end{<+replace+>}<++>' let s:CommandSpec_ens = '\begin{<+replace+>}<+extra+>'."\<++>\".'\end{<+replace+>}<++>' let s:CommandSpec_eno = '\begin[<++>]{<+replace+>}'."\<++>\".'\end{<+replace+>}' let s:CommandSpec_spe = '<+replace+>' let s:CommandSpec_ = '\<+replace+>' let s:MenuLHS_bra = '\\&<+replace+>{}' let s:MenuLHS_brs = '\\&<+replace+>{}' let s:MenuLHS_brd = '\\&<+replace+>{}{}' let s:MenuLHS_env = '&<+replace+>\ (E)' let s:MenuLHS_ens = '&<+replace+>\ (E)' let s:MenuLHS_eno = '&<+replace+>\ (E)' let s:MenuLHS_nor = '\\&<+replace+>' let s:MenuLHS_noo = '\\&<+replace+>[]' let s:MenuLHS_nob = '\\&<+replace+>[]{}{}' let s:MenuLHS_spe = '&<+replace+>' let s:MenuLHS_sep = '-sep<+replace+>-' let s:MenuLHS_ = '\\&<+replace+>' " }}} " Tex_ProcessPackageCommand: processes a command from the package menu {{{ " Description: function! Tex_ProcessPackageCommand(command) if a:command =~ ':' let commandType = matchstr(a:command, '^\w\+\ze:') let commandName = matchstr(a:command, '^\w\+:\zs[^:]\+\ze:\?') let extrapart = strpart(a:command, strlen(commandType.':'.commandName.':')) else let commandType = '' let commandName = a:command let extrapart = '' endif let command = s:CommandSpec_{commandType} let command = substitute(command, '<+replace+>', commandName, 'g') let command = substitute(command, '<+extra+>', extrapart, 'g') return IMAP_PutTextWithMovement(command) endfunction " }}} " FilterPackageMenuLHS: filters the command description to provide a better menu item {{{ " Description: function! FilterPackageMenuLHS(command) let commandType = matchstr(a:command, '^\w\+\ze:') if commandType != '' let commandName = strpart(a:command, strlen(commandType.':')) else let commandName = a:command endif return substitute(s:MenuLHS_{commandType}, '<+replace+>', commandName, 'g') endfunction " }}} if g:Tex_Menus exe 'amenu '.g:Tex_PackagesMenuLocation.'&UpdatePackage :call Tex_pack(expand(""))' exe 'amenu '.g:Tex_PackagesMenuLocation.'&UpdateAll :call Tex_pack_updateall(1)' call Tex_pack_supp_menu() endif augroup LatexSuite au LatexSuite User LatexSuiteFileType \ call Tex_Debug('packages.vim: Catching LatexSuiteFileType event', 'pack') | \ let s:save_clipboard = &clipboard | \ set clipboard= | \ call Tex_pack_updateall(0) | \ let &clipboard=s:save_clipboard augroup END " vim:fdm=marker:ts=4:sw=4:noet:ff=unix