diff options
author | René 'Necoro' Neumann <necoro@necoro.net> | 2009-10-07 17:05:19 +0200 |
---|---|---|
committer | René 'Necoro' Neumann <necoro@necoro.net> | 2009-10-07 17:05:19 +0200 |
commit | dd5427baaf49f8de4355abeb6bc8c6dd14f74e25 (patch) | |
tree | 46fcfc70bd792e80ceebaab89a7f8fc06bc29101 /.vim/plugin | |
download | dotfiles-dd5427baaf49f8de4355abeb6bc8c6dd14f74e25.tar.gz dotfiles-dd5427baaf49f8de4355abeb6bc8c6dd14f74e25.tar.bz2 dotfiles-dd5427baaf49f8de4355abeb6bc8c6dd14f74e25.zip |
Initial check-in of files
Diffstat (limited to '')
-rw-r--r-- | .vim/plugin/NERD_tree.vim | 3917 | ||||
-rw-r--r-- | .vim/plugin/a.vim | 840 | ||||
-rw-r--r-- | .vim/plugin/autotag.vim | 150 | ||||
-rw-r--r-- | .vim/plugin/scmCloseParens.vim | 58 | ||||
-rw-r--r-- | .vim/plugin/surround.vim | 727 | ||||
-rw-r--r-- | .vim/plugin/tEchoPair.vim | 369 | ||||
-rw-r--r-- | .vim/plugin/taglist.vim | 4546 | ||||
-rw-r--r-- | .vim/plugin/toggle_words.vim | 67 |
8 files changed, 10674 insertions, 0 deletions
diff --git a/.vim/plugin/NERD_tree.vim b/.vim/plugin/NERD_tree.vim new file mode 100644 index 0000000..cb2d422 --- /dev/null +++ b/.vim/plugin/NERD_tree.vim @@ -0,0 +1,3917 @@ +" vim global plugin that provides a nice tree explorer +" Last Change: 26 august 2007 +" Maintainer: Martin Grenfell <martin_grenfell at msn dot com> +let s:NERD_tree_version = '2.6.2' + +"A help file is installed when the script is run for the first time. +"Go :help NERD_tree.txt to see it. + +" SECTION: Script init stuff {{{1 +"============================================================ +if exists("loaded_nerd_tree") + finish +endif +if v:version < 700 + echoerr "NERDTree: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!" + finish +endif +let loaded_nerd_tree = 1 +"Function: s:InitVariable() function {{{2 +"This function is used to initialise a given variable to a given value. The +"variable is only initialised if it does not exist prior +" +"Args: +"var: the name of the var to be initialised +"value: the value to initialise var to +" +"Returns: +"1 if the var is set, 0 otherwise +function! s:InitVariable(var, value) + if !exists(a:var) + exec 'let ' . a:var . ' = ' . "'" . a:value . "'" + return 1 + endif + return 0 +endfunction + +"SECTION: Init variable calls and other random constants {{{2 +call s:InitVariable("g:NERDChristmasTree", 1) +call s:InitVariable("g:NERDTreeAutoCenter", 1) +call s:InitVariable("g:NERDTreeAutoCenterThreshold", 3) +call s:InitVariable("g:NERDTreeCaseSensitiveSort", 0) +call s:InitVariable("g:NERDTreeChDirMode", 1) +if !exists("g:NERDTreeIgnore") + let g:NERDTreeIgnore = ['\~$'] +endif +call s:InitVariable("g:NERDTreeHighlightCursorline", 1) +call s:InitVariable("g:NERDTreeMouseMode", 1) +call s:InitVariable("g:NERDTreeNotificationThreshold", 100) +call s:InitVariable("g:NERDTreeShowHidden", 0) +call s:InitVariable("g:NERDTreeShowFiles", 1) +call s:InitVariable("g:NERDTreeSortDirs", 1) + +if !exists("g:NERDTreeSortOrder") + let g:NERDTreeSortOrder = ['\/$', '*', '\.swp$', '\.bak$', '\~$'] +else + "if there isnt a * in the sort sequence then add one + if count(g:NERDTreeSortOrder, '*') < 1 + call add(g:NERDTreeSortOrder, '*') + endif +endif + +"we need to use this number many times for sorting... so we calculate it only +"once here +let g:NERDTreeSortStarIndex = index(g:NERDTreeSortOrder, '*') + +call s:InitVariable("g:NERDTreeSplitVertical", 1) +call s:InitVariable("g:NERDTreeWinPos", 1) +call s:InitVariable("g:NERDTreeWinSize", 31) + +let s:running_windows = has("win16") || has("win32") || has("win64") + +"init the shell command that will be used to remove dir trees +" +"Note: the space after the command is important +if s:running_windows + call s:InitVariable("g:NERDTreeRemoveDirCmd", 'rmdir /s /q ') +else + call s:InitVariable("g:NERDTreeRemoveDirCmd", 'rm -rf ') +endif + + +"SECTION: Init variable calls for key mappings {{{2 +call s:InitVariable("g:NERDTreeMapActivateNode", "o") +call s:InitVariable("g:NERDTreeMapChangeRoot", "C") +call s:InitVariable("g:NERDTreeMapChdir", "cd") +call s:InitVariable("g:NERDTreeMapCloseChildren", "X") +call s:InitVariable("g:NERDTreeMapCloseDir", "x") +call s:InitVariable("g:NERDTreeMapExecute", "!") +call s:InitVariable("g:NERDTreeMapFilesystemMenu", "m") +call s:InitVariable("g:NERDTreeMapHelp", "?") +call s:InitVariable("g:NERDTreeMapJumpFirstChild", "K") +call s:InitVariable("g:NERDTreeMapJumpLastChild", "J") +call s:InitVariable("g:NERDTreeMapJumpNextSibling", "<C-j>") +call s:InitVariable("g:NERDTreeMapJumpParent", "p") +call s:InitVariable("g:NERDTreeMapJumpPrevSibling", "<C-k>") +call s:InitVariable("g:NERDTreeMapJumpRoot", "P") +call s:InitVariable("g:NERDTreeMapOpenExpl", "e") +call s:InitVariable("g:NERDTreeMapOpenInTab", "t") +call s:InitVariable("g:NERDTreeMapOpenInTabSilent", "T") +call s:InitVariable("g:NERDTreeMapOpenRecursively", "O") +call s:InitVariable("g:NERDTreeMapOpenSplit", "<tab>") +call s:InitVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode) +call s:InitVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit) +call s:InitVariable("g:NERDTreeMapQuit", "q") +call s:InitVariable("g:NERDTreeMapRefresh", "r") +call s:InitVariable("g:NERDTreeMapRefreshRoot", "R") +call s:InitVariable("g:NERDTreeMapToggleFiles", "F") +call s:InitVariable("g:NERDTreeMapToggleFilters", "f") +call s:InitVariable("g:NERDTreeMapToggleHidden", "H") +call s:InitVariable("g:NERDTreeMapUpdir", "u") +call s:InitVariable("g:NERDTreeMapUpdirKeepOpen", "U") + +"SECTION: Script level variable declaration{{{2 +let s:escape_chars = " `|\"~'#" +let s:NERDTreeWinName = '_NERD_tree_' + +"init all the nerd tree markup +let s:tree_vert = '|' +let s:tree_vert_last = '`' +let s:tree_wid = 2 +let s:tree_wid_str = ' ' +let s:tree_wid_strM1 = ' ' +let s:tree_dir_open = '~' +let s:tree_dir_closed = '+' +let s:tree_file = '-' +let s:tree_markup_reg = '[ \-+~`|]' +let s:tree_markup_reg_neg = '[^ \-+~`|]' +let s:tree_up_dir_line = '.. (up a dir)' +let s:tree_RO_str = ' [RO]' +let s:tree_RO_str_reg = ' \[RO\]' + +let s:os_slash = '/' +if s:running_windows + let s:os_slash = '\' +endif + + +" SECTION: Commands {{{1 +"============================================================ +"init the command that users start the nerd tree with +command! -n=? -complete=dir NERDTree :call s:InitNerdTree('<args>') +command! -n=? -complete=dir NERDTreeToggle :call s:Toggle('<args>') +" SECTION: Auto commands {{{1 +"============================================================ +"Save the cursor position whenever we close the nerd tree +exec "autocmd BufWinLeave *". s:NERDTreeWinName ."* :call <SID>SaveScreenState()" + +"SECTION: Classes {{{1 +"============================================================ +"CLASS: oTreeFileNode {{{2 +"This class is the parent of the oTreeDirNode class and constitures the +"'Component' part of the composite design pattern between the treenode +"classes. +"============================================================ +let s:oTreeFileNode = {} +"FUNCTION: oTreeFileNode.CompareNodes {{{3 +"This is supposed to be a class level method but i cant figure out how to +"get func refs to work from a dict.. +" +"A class level method that compares two nodes +" +"Args: +"n1, n2: the 2 nodes to compare +function! s:CompareNodes(n1, n2) + return a:n1.path.CompareTo(a:n2.path) +endfunction + +"FUNCTION: oTreeFileNode.Delete {{{3 +"Removes this node from the tree and calls the Delete method for its path obj +function! s:oTreeFileNode.Delete() dict + call self.path.Delete() + call self.parent.RemoveChild(self) +endfunction + +"FUNCTION: oTreeFileNode.Equals(treenode) {{{3 +" +"Compares this treenode to the input treenode and returns 1 if they are the +"same node. +" +"Use this method instead of == because sometimes when the treenodes contain +"many children, vim seg faults when doing == +" +"Args: +"treenode: the other treenode to compare to +function! s:oTreeFileNode.Equals(treenode) dict + return self.path.Str(1) == a:treenode.path.Str(1) +endfunction + +"FUNCTION: oTreeFileNode.FindNode(path) {{{3 +"Returns self if this node.path.Equals the given path. +"Returns {} if not equal. +" +"Args: +"path: the path object to compare against +function! s:oTreeFileNode.FindNode(path) dict + if a:path.Equals(self.path) + return self + endif + return {} +endfunction +"FUNCTION: oTreeFileNode.FindOpenDirSiblingWithChildren(direction) {{{3 +" +"Finds the next sibling for this node in the indicated direction. This sibling +"must be a directory and may/may not have children as specified. +" +"Args: +"direction: 0 if you want to find the previous sibling, 1 for the next sibling +" +"Return: +"a treenode object or {} if no appropriate sibling could be found +function! s:oTreeFileNode.FindOpenDirSiblingWithChildren(direction) dict + "if we have no parent then we can have no siblings + if self.parent != {} + let nextSibling = self.FindSibling(a:direction) + + while nextSibling != {} + if nextSibling.path.isDirectory && nextSibling.HasVisibleChildren() && nextSibling.isOpen + return nextSibling + endif + let nextSibling = nextSibling.FindSibling(a:direction) + endwhile + endif + + return {} +endfunction +"FUNCTION: oTreeFileNode.FindSibling(direction) {{{3 +" +"Finds the next sibling for this node in the indicated direction +" +"Args: +"direction: 0 if you want to find the previous sibling, 1 for the next sibling +" +"Return: +"a treenode object or {} if no sibling could be found +function! s:oTreeFileNode.FindSibling(direction) dict + "if we have no parent then we can have no siblings + if self.parent != {} + + "get the index of this node in its parents children + let siblingIndx = self.parent.GetChildIndex(self.path) + + if siblingIndx != -1 + "move a long to the next potential sibling node + let siblingIndx = a:direction == 1 ? siblingIndx+1 : siblingIndx-1 + + "keep moving along to the next sibling till we find one that is valid + let numSiblings = self.parent.GetChildCount() + while siblingIndx >= 0 && siblingIndx < numSiblings + + "if the next node is not an ignored node (i.e. wont show up in the + "view) then return it + if self.parent.children[siblingIndx].path.Ignore() == 0 + return self.parent.children[siblingIndx] + endif + + "go to next node + let siblingIndx = a:direction == 1 ? siblingIndx+1 : siblingIndx-1 + endwhile + endif + endif + + return {} +endfunction + +"FUNCTION: oTreeFileNode.IsVisible() {{{3 +"returns 1 if this node should be visible according to the tree filters and +"hidden file filters (and their on/off status) +function! s:oTreeFileNode.IsVisible() dict + return !self.path.Ignore() +endfunction + + +"FUNCTION: oTreeFileNode.IsRoot() {{{3 +"returns 1 if this node is t:NERDTreeRoot +function! s:oTreeFileNode.IsRoot() dict + if !s:TreeExistsForTab() + throw "NERDTree.TreeFileNode.IsRoot exception: No tree exists for the current tab" + endif + return self.Equals(t:NERDTreeRoot) +endfunction + +"FUNCTION: oTreeFileNode.New(path) {{{3 +"Returns a new TreeNode object with the given path and parent +" +"Args: +"path: a path object representing the full filesystem path to the file/dir that the node represents +function! s:oTreeFileNode.New(path) dict + if a:path.isDirectory + return s:oTreeDirNode.New(a:path) + else + let newTreeNode = {} + let newTreeNode = copy(self) + let newTreeNode.path = a:path + let newTreeNode.parent = {} + return newTreeNode + endif +endfunction + +"FUNCTION: oTreeFileNode.Rename {{{3 +"Calls the rename method for this nodes path obj +function! s:oTreeFileNode.Rename(newName) dict + call self.path.Rename(a:newName) + call self.parent.RemoveChild(self) + + let parentPath = self.path.GetPathTrunk() + let newParent = t:NERDTreeRoot.FindNode(parentPath) + + if newParent != {} + call newParent.CreateChild(self.path, 1) + endif +endfunction + +"FUNCTION: oTreeFileNode.StrDisplay() {{{3 +" +"Returns a string that specifies how the node should be represented as a +"string +" +"Return: +"a string that can be used in the view to represent this node +function! s:oTreeFileNode.StrDisplay() dict + return self.path.StrDisplay() +endfunction + +"CLASS: oTreeDirNode {{{2 +"This class is a child of the oTreeFileNode class and constitutes the +"'Composite' part of the composite design pattern between the treenode +"classes. +"============================================================ +let s:oTreeDirNode = copy(s:oTreeFileNode) +"FUNCTION: oTreeDirNode.AddChild(treenode, inOrder) {{{3 +"Adds the given treenode to the list of children for this node +" +"Args: +"-treenode: the node to add +"-inOrder: 1 if the new node should be inserted in sorted order +function! s:oTreeDirNode.AddChild(treenode, inOrder) dict + call add(self.children, a:treenode) + let a:treenode.parent = self + + if a:inOrder + call self.SortChildren() + endif +endfunction + +"FUNCTION: oTreeDirNode.Close {{{3 +"Closes this directory +function! s:oTreeDirNode.Close() dict + let self.isOpen = 0 +endfunction + +"FUNCTION: oTreeDirNode.CloseChildren {{{3 +"Closes all the child dir nodes of this node +function! s:oTreeDirNode.CloseChildren() dict + for i in self.children + if i.path.isDirectory + call i.Close() + call i.CloseChildren() + endif + endfor +endfunction + +"FUNCTION: oTreeDirNode.CreateChild(path, inOrder) {{{3 +"Instantiates a new child node for this node with the given path. The new +"nodes parent is set to this node. +" +"Args: +"path: a Path object that this node will represent/contain +"inOrder: 1 if the new node should be inserted in sorted order +" +"Returns: +"the newly created node +function! s:oTreeDirNode.CreateChild(path, inOrder) dict + let newTreeNode = s:oTreeFileNode.New(a:path) + call self.AddChild(newTreeNode, a:inOrder) + return newTreeNode +endfunction + +"FUNCTION: oTreeDirNode.FindNode(path) {{{3 +"Will find one of the children (recursively) that has the given path +" +"Args: +"path: a path object +unlet s:oTreeDirNode.FindNode +function! s:oTreeDirNode.FindNode(path) dict + if a:path.Equals(self.path) + return self + endif + if stridx(a:path.Str(1), self.path.Str(1), 0) == -1 + return {} + endif + + if self.path.isDirectory + for i in self.children + let retVal = i.FindNode(a:path) + if retVal != {} + return retVal + endif + endfor + endif + return {} +endfunction + +"FUNCTION: oTreeDirNode.GetChildDirs() {{{3 +"Returns the number of children this node has +function! s:oTreeDirNode.GetChildCount() dict + return len(self.children) +endfunction + +"FUNCTION: oTreeDirNode.GetChildDirs() {{{3 +"Returns an array of all children of this node that are directories +" +"Return: +"an array of directory treenodes +function! s:oTreeDirNode.GetChildDirs() dict + let toReturn = [] + for i in self.children + if i.path.isDirectory + call add(toReturn, i) + endif + endfor + return toReturn +endfunction + +"FUNCTION: oTreeDirNode.GetChildFiles() {{{3 +"Returns an array of all children of this node that are files +" +"Return: +"an array of file treenodes +function! s:oTreeDirNode.GetChildFiles() dict + let toReturn = [] + for i in self.children + if i.path.isDirectory == 0 + call add(toReturn, i) + endif + endfor + return toReturn +endfunction + +"FUNCTION: oTreeDirNode.GetChild(path) {{{3 +"Returns child node of this node that has the given path or {} if no such node +"exists. +" +"This function doesnt not recurse into child dir nodes +" +"Args: +"path: a path object +function! s:oTreeDirNode.GetChild(path) dict + if stridx(a:path.Str(1), self.path.Str(1), 0) == -1 + return {} + endif + + let index = self.GetChildIndex(a:path) + if index == -1 + return {} + else + return self.children[index] + endif + +endfunction + +"FUNCTION: oTreeDirNode.GetChildByIndex(indx, visible) {{{3 +"returns the child at the given index +"Args: +"indx: the index to get the child from +"visible: 1 if only the visible children array should be used, 0 if all the +"children should be searched. +function! s:oTreeDirNode.GetChildByIndex(indx, visible) dict + let array_to_search = a:visible? self.GetVisibleChildren() : self.children + if a:indx > len(array_to_search) + throw "NERDTree.TreeDirNode.InvalidArguments exception. Index is out of bounds." + endif + return array_to_search[a:indx] +endfunction + +"FUNCTION: oTreeDirNode.GetChildIndex(path) {{{3 +"Returns the index of the child node of this node that has the given path or +"-1 if no such node exists. +" +"This function doesnt not recurse into child dir nodes +" +"Args: +"path: a path object +function! s:oTreeDirNode.GetChildIndex(path) dict + if stridx(a:path.Str(1), self.path.Str(1), 0) == -1 + return -1 + endif + + "do a binary search for the child + let a = 0 + let z = self.GetChildCount() + while a < z + let mid = (a+z)/2 + let diff = a:path.CompareTo(self.children[mid].path) + + if diff == -1 + let z = mid + elseif diff == 1 + let a = mid+1 + else + return mid + endif + endwhile + return -1 +endfunction + +"FUNCTION: oTreeDirNode.GetVisibleChildCount() {{{3 +"Returns the number of visible children this node has +function! s:oTreeDirNode.GetVisibleChildCount() dict + return len(self.GetVisibleChildren()) +endfunction + +"FUNCTION: oTreeDirNode.GetVisibleChildren() {{{3 +"Returns a list of children to display for this node, in the correct order +" +"Return: +"an array of treenodes +function! s:oTreeDirNode.GetVisibleChildren() dict + let toReturn = [] + for i in self.children + if i.path.Ignore() == 0 + call add(toReturn, i) + endif + endfor + return toReturn +endfunction + +"FUNCTION: oTreeDirNode.HasVisibleChildren {{{3 +"returns 1 if this node has any childre, 0 otherwise.. +function! s:oTreeDirNode.HasVisibleChildren() + return self.GetChildCount() != 0 +endfunction + +"FUNCTION: oTreeDirNode.InitChildren {{{3 +"Removes all childen from this node and re-reads them +" +"Args: +"silent: 1 if the function should not echo any "please wait" messages for +"large directories +" +"Return: the number of child nodes read +function! s:oTreeDirNode.InitChildren(silent) dict + "remove all the current child nodes + let self.children = [] + + "get an array of all the files in the nodes dir + let filesStr = globpath(self.path.GetDir(0), '*') . "\n" . globpath(self.path.GetDir(0), '.*') + let files = split(filesStr, "\n") + + if !a:silent && len(files) > g:NERDTreeNotificationThreshold + call s:Echo("Please wait, caching a large dir ...") + endif + + let invalidFilesFound = 0 + for i in files + + "filter out the .. and . directories + if i !~ '\.\.$' && i !~ '\.$' + + "put the next file in a new node and attach it + try + let path = s:oPath.New(i) + call self.CreateChild(path, 0) + catch /^NERDTree.Path.InvalidArguments/ + let invalidFilesFound = 1 + endtry + endif + endfor + + call self.SortChildren() + + if !a:silent && len(files) > g:NERDTreeNotificationThreshold + call s:Echo("Please wait, caching a large dir ...") + call s:Echo("Please wait, caching a large dir ... DONE (". self.GetChildCount() ." nodes cached).") + endif + + if invalidFilesFound + call s:EchoWarning("some files could not be loaded into the NERD tree") + endif + return self.GetChildCount() +endfunction +"FUNCTION: oTreeDirNode.New(path) {{{3 +"Returns a new TreeNode object with the given path and parent +" +"Args: +"path: a path object representing the full filesystem path to the file/dir that the node represents +unlet s:oTreeDirNode.New +function! s:oTreeDirNode.New(path) dict + if a:path.isDirectory != 1 + throw "NERDTree.TreeDirNode.InvalidArguments exception. A TreeDirNode object must be instantiated with a directory Path object." + endif + + let newTreeNode = copy(self) + let newTreeNode.path = a:path + + let newTreeNode.isOpen = 0 + let newTreeNode.children = [] + + let newTreeNode.parent = {} + + return newTreeNode +endfunction +"FUNCTION: oTreeDirNode.Open {{{3 +"Reads in all this nodes children +" +"Return: the number of child nodes read +function! s:oTreeDirNode.Open() dict + let self.isOpen = 1 + if self.children == [] + return self.InitChildren(0) + else + return 0 + endif +endfunction + +"FUNCTION: oTreeDirNode.OpenRecursively {{{3 +"Opens this treenode and all of its children whose paths arent 'ignored' +"because of the file filters. +" +"This method is actually a wrapper for the OpenRecursively2 method which does +"the work. +function! s:oTreeDirNode.OpenRecursively() dict + call self.OpenRecursively2(1) +endfunction + +"FUNCTION: oTreeDirNode.OpenRecursively2 {{{3 +"Dont call this method from outside this object. +" +"Opens this all children of this treenode recursively if either: +" *they arent filtered by file filters +" *a:forceOpen is 1 +" +"Args: +"forceOpen: 1 if this node should be opened regardless of file filters +function! s:oTreeDirNode.OpenRecursively2(forceOpen) dict + if self.path.Ignore() == 0 || a:forceOpen + let self.isOpen = 1 + if self.children == [] + call self.InitChildren(1) + endif + + for i in self.children + if i.path.isDirectory == 1 + call i.OpenRecursively2(0) + endif + endfor + endif +endfunction + +"FUNCTION: oTreeDirNode.Refresh {{{3 +function! s:oTreeDirNode.Refresh() dict + let newChildNodes = [] + let invalidFilesFound = 0 + + "go thru all the files/dirs under this node + let filesStr = globpath(self.path.GetDir(0), '*') . "\n" . globpath(self.path.GetDir(0), '.*') + let files = split(filesStr, "\n") + for i in files + if i !~ '\.\.$' && i !~ '\.$' + + try + "create a new path and see if it exists in this nodes children + let path = s:oPath.New(i) + let newNode = self.GetChild(path) + if newNode != {} + + "if the existing node is a dir can be refreshed then + "refresh it + if newNode.path.isDirectory && (!empty(newNode.children) || newNode.isOpen == 1) + call newNode.Refresh() + + "if we have a filenode then refresh the path + elseif newNode.path.isDirectory == 0 + call newNode.path.Refresh() + endif + + call add(newChildNodes, newNode) + + "the node doesnt exist so create it + else + let newNode = s:oTreeFileNode.New(path) + let newNode.parent = self + call add(newChildNodes, newNode) + endif + + + catch /^NERDTree.InvalidArguments/ + let invalidFilesFound = 1 + endtry + endif + endfor + + "swap this nodes children out for the children we just read/refreshed + let self.children = newChildNodes + call self.SortChildren() + + if invalidFilesFound + call s:EchoWarning("some files could not be loaded into the NERD tree") + endif +endfunction + +"FUNCTION: oTreeDirNode.RemoveChild {{{3 +" +"Removes the given treenode from this nodes set of children +" +"Args: +"treenode: the node to remove +" +"Throws a NERDTree.TreeDirNode exception if the given treenode is not found +function! s:oTreeDirNode.RemoveChild(treenode) dict + for i in range(0, self.GetChildCount()-1) + if self.children[i].Equals(a:treenode) + call remove(self.children, i) + return + endif + endfor + + throw "NERDTree.TreeDirNode exception: child node was not found" +endfunction + +"FUNCTION: oTreeDirNode.SortChildren {{{3 +" +"Sorts the children of this node according to alphabetical order and the +"directory priority. +" +function! s:oTreeDirNode.SortChildren() dict + let CompareFunc = function("s:CompareNodes") + call sort(self.children, CompareFunc) +endfunction + +"FUNCTION: oTreeDirNode.ToggleOpen {{{3 +"Opens this directory if it is closed and vice versa +function! s:oTreeDirNode.ToggleOpen() dict + if self.isOpen == 1 + call self.Close() + else + call self.Open() + endif +endfunction + +"FUNCTION: oTreeDirNode.TransplantChild(newNode) {{{3 +"Replaces the child of this with the given node (where the child node's full +"path matches a:newNode's fullpath). The search for the matching node is +"non-recursive +" +"Arg: +"newNode: the node to graft into the tree +function! s:oTreeDirNode.TransplantChild(newNode) dict + for i in range(0, self.GetChildCount()-1) + if self.children[i].Equals(a:newNode) + let self.children[i] = a:newNode + let a:newNode.parent = self + break + endif + endfor +endfunction +"============================================================ +"CLASS: oPath {{{2 +"============================================================ +let s:oPath = {} +"FUNCTION: oPath.ChangeToDir() {{{3 +function! s:oPath.ChangeToDir() dict + let dir = self.Str(1) + if self.isDirectory == 0 + let dir = self.GetPathTrunk().Str(1) + endif + + try + execute "cd " . dir + call s:Echo("CWD is now: " . getcwd()) + catch + throw "NERDTree.Path.Change exception: cannot change to " . dir + endtry +endfunction + +"FUNCTION: oPath.ChopTrailingSlash(str) {{{3 +function! s:oPath.ChopTrailingSlash(str) dict + if a:str =~ '\/$' + return substitute(a:str, "\/$", "", "") + else + return substitute(a:str, "\\$", "", "") + endif +endfunction + +"FUNCTION: oPath.CompareTo() {{{3 +" +"Compares this oPath to the given path and returns 0 if they are equal, -1 if +"this oPath is "less than" the given path, or 1 if it is "greater". +" +"Args: +"path: the path object to compare this to +" +"Return: +"1, -1 or 0 +function! s:oPath.CompareTo(path) dict + let thisPath = self.GetLastPathComponent(1) + let thatPath = a:path.GetLastPathComponent(1) + + "if the paths are the same then clearly we return 0 + if thisPath == thatPath + return 0 + endif + + let thisSS = self.GetSortOrderIndex() + let thatSS = a:path.GetSortOrderIndex() + + "compare the sort sequences, if they are different then the return + "value is easy + if thisSS < thatSS + return -1 + elseif thisSS > thatSS + return 1 + else + "if the sort sequences are the same then compare the paths + "alphabetically + let pathCompare = g:NERDTreeCaseSensitiveSort ? thisPath <# thatPath : thisPath <? thatPath + if pathCompare + return -1 + else + return 1 + endif + endif +endfunction + +"FUNCTION: oPath.Create() {{{3 +" +"Factory method. +" +"Creates a path object with the given path. The path is also created on the +"filesystem. If the path already exists, a NERDTree.Path.Exists exception is +"thrown. If any other errors occur, a NERDTree.Path exception is thrown. +" +"Args: +"fullpath: the full filesystem path to the file/dir to create +function! s:oPath.Create(fullpath) dict + "bail if the a:fullpath already exists + if isdirectory(a:fullpath) || filereadable(a:fullpath) + throw "NERDTree.Path.Exists Exception: Directory Exists: '" . a:fullpath . "'" + endif + + "get the unix version of the input path + let fullpath = a:fullpath + if s:running_windows + let fullpath = s:oPath.WinToUnixPath(fullpath) + endif + + try + + "if it ends with a slash, assume its a dir create it + if fullpath =~ '\/$' + "whack the trailing slash off the end if it exists + let fullpath = substitute(fullpath, '\/$', '', '') + + call mkdir(fullpath, 'p') + + "assume its a file and create + else + call writefile([], fullpath) + endif + catch /.*/ + throw "NERDTree.Path Exception: Could not create path: '" . a:fullpath . "'" + endtry + + return s:oPath.New(fullpath) +endfunction + +"FUNCTION: oPath.Delete() {{{3 +" +"Deletes the file represented by this path. +"Deletion of directories is not supported +" +"Throws NERDTree.Path.Deletion exceptions +function! s:oPath.Delete() dict + if self.isDirectory + + let cmd = "" + if s:running_windows + "if we are runnnig windows then put quotes around the pathstring + let cmd = g:NERDTreeRemoveDirCmd . self.StrForOS(1) + else + let cmd = g:NERDTreeRemoveDirCmd . self.StrForOS(0) + endif + let success = system(cmd) + + if v:shell_error != 0 + throw "NERDTree.Path.Deletion Exception: Could not delete directory: '" . self.StrForOS(0) . "'" + endif + else + let success = delete(self.Str(0)) + if success != 0 + throw "NERDTree.Path.Deletion Exception: Could not delete file: '" . self.Str(0) . "'" + endif + endif +endfunction + +"FUNCTION: oPath.GetDir() {{{3 +" +"Gets the directory part of this path. If this path IS a directory then the +"whole thing is returned +" +"Args: +"trailingSlash: 1 if a trailing slash is to be stuck on the end of the +"returned dir +" +"Return: +"string +function! s:oPath.GetDir(trailingSlash) dict + let toReturn = '' + if self.isDirectory + let toReturn = '/'. join(self.pathSegments, '/') + else + let toReturn = '/'. join(self.pathSegments[0:-2], '/') + endif + + if a:trailingSlash && toReturn !~ '\/$' + let toReturn = toReturn . '/' + endif + + return toReturn +endfunction + +"FUNCTION: oPath.GetFile() {{{3 +" +"Returns the file component of this path. +" +"Throws NERDTree.IllegalOperation exception if the node is a directory node +function! s:oPath.GetFile() dict + if self.isDirectory == 0 + return self.GetLastPathComponent(0) + else + throw "NERDTree.Path.IllegalOperation Exception: cannot get file component of a directory path" + endif +endfunction + +"FUNCTION: oPath.GetLastPathComponent(dirSlash) {{{3 +" +"Gets the last part of this path. +" +"Args: +"dirSlash: if 1 then a trailing slash will be added to the returned value for +"directory nodes. +function! s:oPath.GetLastPathComponent(dirSlash) dict + if empty(self.pathSegments) + return '' + endif + let toReturn = self.pathSegments[-1] + if a:dirSlash && self.isDirectory + let toReturn = toReturn . '/' + endif + return toReturn +endfunction + +"FUNCTION: oPath.GetPathTrunk() {{{3 +"Gets the path without the last segment on the end. +function! s:oPath.GetPathTrunk() dict + return s:oPath.New('/' . join(self.pathSegments[0:-2], '/')) +endfunction + +"FUNCTION: oPath.GetSortOrderIndex() {{{3 +"returns the index of the pattern in g:NERDTreeSortOrder that this path matches +function! s:oPath.GetSortOrderIndex() dict + let i = 0 + while i < len(g:NERDTreeSortOrder) + if self.GetLastPathComponent(1) =~ g:NERDTreeSortOrder[i] + return i + endif + let i = i + 1 + endwhile + return g:NERDTreeSortStarIndex +endfunction + +"FUNCTION: oPath.Ignore() {{{3 +"returns true if this path should be ignored +function! s:oPath.Ignore() dict + let lastPathComponent = self.GetLastPathComponent(0) + + "filter out the user specified paths to ignore + if t:NERDTreeIgnoreEnabled + for i in g:NERDTreeIgnore + if lastPathComponent =~ i + return 1 + endif + endfor + endif + + "dont show hidden files unless instructed to + if g:NERDTreeShowHidden == 0 && lastPathComponent =~ '^\.' + return 1 + endif + + if g:NERDTreeShowFiles == 0 && self.isDirectory == 0 + return 1 + endif + + return 0 +endfunction + +"FUNCTION: oPath.Equals() {{{3 +" +"Determines whether 2 path objecs are "equal". +"They are equal if the paths they represent are the same +" +"Args: +"path: the other path obj to compare this with +function! s:oPath.Equals(path) dict + let this = self.ChopTrailingSlash(self.Str(1)) + let that = self.ChopTrailingSlash(a:path.Str(1)) + return this == that +endfunction + +"FUNCTION: oPath.New() {{{3 +" +"The Constructor for the Path object +"Throws NERDTree.Path.InvalidArguments exception. +function! s:oPath.New(fullpath) dict + let newPath = copy(self) + + call newPath.ReadInfoFromDisk(a:fullpath) + + return newPath +endfunction + +"FUNCTION: oPath.NewMinimal() {{{3 +function! s:oPath.NewMinimal(fullpath) dict + let newPath = copy(self) + + let fullpath = a:fullpath + + if s:running_windows + let fullpath = s:oPath.WinToUnixPath(fullpath) + endif + + let newPath.pathSegments = split(fullpath, '/') + + let newPath.isDirectory = isdirectory(fullpath) + + return newPath +endfunction + +"FUNCTION: oPath.ReadInfoFromDisk(fullpath) {{{3 +" +" +"Throws NERDTree.Path.InvalidArguments exception. +function! s:oPath.ReadInfoFromDisk(fullpath) dict + let fullpath = a:fullpath + + if s:running_windows + let fullpath = s:oPath.WinToUnixPath(fullpath) + endif + + let self.pathSegments = split(fullpath, '/') + + let self.isReadOnly = 0 + if isdirectory(fullpath) + let self.isDirectory = 1 + elseif filereadable(fullpath) + let self.isDirectory = 0 + let self.isReadOnly = filewritable(fullpath) == 0 + else + throw "NERDTree.Path.InvalidArguments Exception: Invalid path = " . fullpath + endif + + "grab the last part of the path (minus the trailing slash) + let lastPathComponent = self.GetLastPathComponent(0) + + "get the path to the new node with the parent dir fully resolved + let hardPath = resolve(self.StrTrunk()) . '/' . lastPathComponent + + "if the last part of the path is a symlink then flag it as such + let self.isSymLink = (resolve(hardPath) != hardPath) + if self.isSymLink + let self.symLinkDest = resolve(fullpath) + + "if the link is a dir then slap a / on the end of its dest + if isdirectory(self.symLinkDest) + + "we always wanna treat MS windows shortcuts as files for + "simplicity + if hardPath !~ '\.lnk$' + + let self.symLinkDest = self.symLinkDest . '/' + endif + endif + endif +endfunction + +"FUNCTION: oPath.Refresh() {{{3 +function! s:oPath.Refresh() dict + call self.ReadInfoFromDisk(self.Str(0)) +endfunction + +"FUNCTION: oPath.Rename() {{{3 +" +"Renames this node on the filesystem +function! s:oPath.Rename(newPath) dict + if a:newPath == '' + throw "NERDTree.Path.InvalidArguments exception. Invalid newPath for renaming = ". a:newPath + endif + + let success = rename(self.Str(0), a:newPath) + if success != 0 + throw "NERDTree.Path.Rename Exception: Could not rename: '" . self.Str(0) . "'" . 'to:' . a:newPath + endif + let self.pathSegments = split(a:newPath, '/') +endfunction + +"FUNCTION: oPath.Str(esc) {{{3 +" +"Gets the actual string path that this obj represents. +" +"Args: +"esc: if 1 then all the tricky chars in the returned string will be escaped +function! s:oPath.Str(esc) dict + let toReturn = '/' . join(self.pathSegments, '/') + if self.isDirectory && toReturn != '/' + let toReturn = toReturn . '/' + endif + + if a:esc + let toReturn = escape(toReturn, s:escape_chars) + endif + return toReturn +endfunction + +"FUNCTION: oPath.StrAbs() {{{3 +" +"Returns a string representing this path with all the symlinks resolved +" +"Return: +"string +function! s:oPath.StrAbs() dict + return resolve(self.Str(1)) +endfunction + +"FUNCTION: oPath.StrDisplay() {{{3 +" +"Returns a string that specifies how the path should be represented as a +"string +" +"Return: +"a string that can be used in the view to represent this path +function! s:oPath.StrDisplay() dict + let toReturn = self.GetLastPathComponent(1) + + if self.isSymLink + let toReturn .= ' -> ' . self.symLinkDest + endif + + if self.isReadOnly + let toReturn .= s:tree_RO_str + endif + + return toReturn +endfunction + +"FUNCTION: oPath.StrForEditCmd() {{{3 +" +"Return: the string for this path that is suitable to be used with the :edit +"command +function! s:oPath.StrForEditCmd() dict + if s:running_windows + return self.StrForOS(0) + else + return self.Str(1) + endif + +endfunction +"FUNCTION: oPath.StrForOS(esc) {{{3 +" +"Gets the string path for this path object that is appropriate for the OS. +"EG, in windows c:\foo\bar +" in *nix /foo/bar +" +"Args: +"esc: if 1 then all the tricky chars in the returned string will be +" escaped. If we are running windows then the str is double quoted instead. +function! s:oPath.StrForOS(esc) dict + let lead = s:os_slash + + "if we are running windows then slap a drive letter on the front + if s:running_windows + let lead = strpart(getcwd(), 0, 2) . s:os_slash + endif + + let toReturn = lead . join(self.pathSegments, s:os_slash) + + if a:esc + if s:running_windows + let toReturn = '"' . toReturn . '"' + else + let toReturn = escape(toReturn, s:escape_chars) + endif + endif + return toReturn +endfunction + +"FUNCTION: oPath.StrTrunk() {{{3 +"Gets the path without the last segment on the end. +function! s:oPath.StrTrunk() dict + return '/' . join(self.pathSegments[0:-2], '/') +endfunction + +"FUNCTION: oPath.WinToUnixPath(pathstr){{{3 +"Takes in a windows path and returns the unix equiv +" +"A class level method +" +"Args: +"pathstr: the windows path to convert +function! s:oPath.WinToUnixPath(pathstr) dict + let toReturn = a:pathstr + + "remove the x:\ of the front + let toReturn = substitute(toReturn, '^.*:\(\\\|/\)\?', '/', "") + + "convert all \ chars to / + let toReturn = substitute(toReturn, '\', '/', "g") + + return toReturn +endfunction + +" SECTION: General Functions {{{1 +"============================================================ +"FUNCTION: s:Abs(num){{{2 +"returns the absolute value of the input +function! s:Abs(num) + if a:num > 0 + return a:num + else + return 0 - a:num + end +endfunction + +"FUNCTION: s:BufInWindows(bnum){{{2 +"[[STOLEN FROM VTREEEXPLORER.VIM]] +"Determine the number of windows open to this buffer number. +"Care of Yegappan Lakshman. Thanks! +" +"Args: +"bnum: the subject buffers buffer number +function! s:BufInWindows(bnum) + let cnt = 0 + let winnum = 1 + while 1 + let bufnum = winbufnr(winnum) + if bufnum < 0 + break + endif + if bufnum == a:bnum + let cnt = cnt + 1 + endif + let winnum = winnum + 1 + endwhile + + return cnt +endfunction " >>> + +"FUNCTION: s:InitNerdTree(dir) {{{2 +"Initialized the NERD tree, where the root will be initialized with the given +"directory +" +"Arg: +"dir: the dir to init the root with +function! s:InitNerdTree(dir) + let dir = a:dir == '' ? expand('%:p:h') : a:dir + let dir = resolve(dir) + + if !isdirectory(dir) + call s:EchoWarning("Error reading: " . dir) + return + endif + + "if instructed to, then change the vim CWD to the dir the NERDTree is + "inited in + if g:NERDTreeChDirMode != 0 + exec "cd " . dir + endif + + let t:treeShowHelp = 0 + let t:NERDTreeIgnoreEnabled = 1 + + if s:TreeExistsForTab() + if s:IsTreeOpen() + call s:CloseTree() + endif + unlet t:NERDTreeRoot + endif + + let path = s:oPath.New(dir) + let t:NERDTreeRoot = s:oTreeDirNode.New(path) + call t:NERDTreeRoot.Open() + + call s:CreateTreeWin() + + call s:RenderView() +endfunction + +" Function: s:InstallDocumentation(full_name, revision) {{{2 +" Install help documentation. +" Arguments: +" full_name: Full name of this vim plugin script, including path name. +" revision: Revision of the vim script. #version# mark in the document file +" will be replaced with this string with 'v' prefix. +" Return: +" 1 if new document installed, 0 otherwise. +" Note: Cleaned and generalized by guo-peng Wen. +" +" Note about authorship: this function was taken from the vimspell plugin +" which can be found at http://www.vim.org/scripts/script.php?script_id=465 +" +function! s:InstallDocumentation(full_name, revision) + " Name of the document path based on the system we use: + if has("vms") + " No chance that this script will work with + " VMS - to much pathname juggling here. + return 1 + elseif (has("unix")) + " On UNIX like system, using forward slash: + let l:slash_char = '/' + let l:mkdir_cmd = ':silent !mkdir -p ' + else + " On M$ system, use backslash. Also mkdir syntax is different. + " This should only work on W2K and up. + let l:slash_char = '\' + let l:mkdir_cmd = ':silent !mkdir ' + endif + + let l:doc_path = l:slash_char . 'doc' + let l:doc_home = l:slash_char . '.vim' . l:slash_char . 'doc' + + " Figure out document path based on full name of this script: + let l:vim_plugin_path = fnamemodify(a:full_name, ':h') + let l:vim_doc_path = fnamemodify(a:full_name, ':h:h') . l:doc_path + if (!(filewritable(l:vim_doc_path) == 2)) + "Doc path: " . l:vim_doc_path + call s:Echo("Doc path: " . l:vim_doc_path) + execute l:mkdir_cmd . '"' . l:vim_doc_path . '"' + if (!(filewritable(l:vim_doc_path) == 2)) + " Try a default configuration in user home: + let l:vim_doc_path = expand("~") . l:doc_home + if (!(filewritable(l:vim_doc_path) == 2)) + execute l:mkdir_cmd . '"' . l:vim_doc_path . '"' + if (!(filewritable(l:vim_doc_path) == 2)) + " Put a warning: + call s:Echo("Unable to open documentation directory") + call s:Echo("type :help add-local-help for more information.") + call s:Echo(l:vim_doc_path) + return 0 + endif + endif + endif + endif + + " Exit if we have problem to access the document directory: + if (!isdirectory(l:vim_plugin_path) || !isdirectory(l:vim_doc_path) || filewritable(l:vim_doc_path) != 2) + return 0 + endif + + " Full name of script and documentation file: + let l:script_name = fnamemodify(a:full_name, ':t') + let l:doc_name = fnamemodify(a:full_name, ':t:r') . '.txt' + let l:plugin_file = l:vim_plugin_path . l:slash_char . l:script_name + let l:doc_file = l:vim_doc_path . l:slash_char . l:doc_name + + " Bail out if document file is still up to date: + if (filereadable(l:doc_file) && getftime(l:plugin_file) < getftime(l:doc_file)) + return 0 + endif + + " Prepare window position restoring command: + if (strlen(@%)) + let l:go_back = 'b ' . bufnr("%") + else + let l:go_back = 'enew!' + endif + + " Create a new buffer & read in the plugin file (me): + setl nomodeline + exe 'enew!' + exe 'r ' . l:plugin_file + + setl modeline + let l:buf = bufnr("%") + setl noswapfile modifiable + + norm zR + norm gg + + " Delete from first line to a line starts with + " === START_DOC + 1,/^=\{3,}\s\+START_DOC\C/ d + + " Delete from a line starts with + " === END_DOC + " to the end of the documents: + /^=\{3,}\s\+END_DOC\C/,$ d + + " Remove fold marks: + :%s/{{{[1-9]/ / + + " Add modeline for help doc: the modeline string is mangled intentionally + " to avoid it be recognized by VIM: + call append(line('$'), '') + call append(line('$'), ' v' . 'im:tw=78:ts=8:ft=help:norl:') + + " Replace revision: + "exe "normal :1s/#version#/ v" . a:revision . "/\<CR>" + exe "normal! :%s/#version#/ v" . a:revision . "/\<CR>" + + " Save the help document: + exe 'w! ' . l:doc_file + exe l:go_back + exe 'bw ' . l:buf + + " Build help tags: + exe 'helptags ' . l:vim_doc_path + + return 1 +endfunction + +" Function: s:TreeExistsForTab() {{{2 +" Returns 1 if a nerd tree root exists in the current tab +function! s:TreeExistsForTab() + return exists("t:NERDTreeRoot") +endfunction + +" SECTION: Public Functions {{{1 +"============================================================ +"Returns the node that the cursor is currently on. +" +"If the cursor is not in the NERDTree window, it is temporarily put there. +" +"If no NERD tree window exists for the current tab, a NERDTree.NoTreeForTab +"exception is thrown. +" +"If the cursor is not on a node then an empty dictionary {} is returned. +function! NERDTreeGetCurrentNode() + if !s:TreeExistsForTab() || !s:IsTreeOpen() + throw "NERDTree.NoTreeForTab exception: there is no NERD tree open for the current tab" + endif + + let winnr = winnr() + if winnr != s:GetTreeWinNum() + call s:PutCursorInTreeWin() + endif + + let treenode = s:GetSelectedNode() + + if winnr != winnr() + wincmd w + endif + + return treenode +endfunction + +"Returns the path object for the current node. +" +"Subject to the same conditions as NERDTreeGetCurrentNode +function! NERDTreeGetCurrentPath() + let node = NERDTreeGetCurrentNode() + if node != {} + return node.path + else + return {} + endif +endfunction + +" SECTION: View Functions {{{1 +"============================================================ +"FUNCTION: s:CenterView() {{{2 +"centers the nerd tree window around the cursor (provided the nerd tree +"options permit) +function! s:CenterView() + if g:NERDTreeAutoCenter + let current_line = winline() + let lines_to_top = current_line + let lines_to_bottom = winheight(s:GetTreeWinNum()) - current_line + if lines_to_top < g:NERDTreeAutoCenterThreshold || lines_to_bottom < g:NERDTreeAutoCenterThreshold + normal! zz + endif + endif +endfunction + +"FUNCTION: s:CloseTree() {{{2 +"Closes the NERD tree window +function! s:CloseTree() + if !s:IsTreeOpen() + throw "NERDTree.view.CloseTree exception: no NERDTree is open" + endif + + if winnr("$") != 1 + execute s:GetTreeWinNum() . " wincmd w" + close + execute "wincmd p" + else + :q + endif +endfunction + +"FUNCTION: s:CreateTreeWin() {{{2 +"Inits the NERD tree window. ie. opens it, sizes it, sets all the local +"options etc +function! s:CreateTreeWin() + "create the nerd tree window + let splitLocation = g:NERDTreeWinPos ? "topleft " : "belowright " + let splitMode = g:NERDTreeSplitVertical ? "vertical " : "" + let splitSize = g:NERDTreeWinSize + let t:NERDTreeWinName = localtime() . s:NERDTreeWinName + let cmd = splitLocation . splitMode . splitSize . ' new ' . t:NERDTreeWinName + silent! execute cmd + + setl winfixwidth + + "throwaway buffer options + setlocal noswapfile + setlocal buftype=nofile + setlocal bufhidden=delete + setlocal nowrap + setlocal foldcolumn=0 + setlocal nobuflisted + setlocal nospell + iabc <buffer> + + if g:NERDTreeHighlightCursorline + setlocal cursorline + endif + + " syntax highlighting + if has("syntax") && exists("g:syntax_on") && !has("syntax_items") + call s:SetupSyntaxHighlighting() + endif + + " for line continuation + let cpo_save1 = &cpo + set cpo&vim + + call s:BindMappings() +endfunction + +"FUNCTION: s:DrawTree {{{2 +"Draws the given node recursively +" +"Args: +"curNode: the node that is being rendered with this call +"depth: the current depth in the tree for this call +"drawText: 1 if we should actually draw the line for this node (if 0 then the +"child nodes are rendered only) +"vertMap: a binary array that indicates whether a vertical bar should be draw +"for each depth in the tree +"isLastChild:true if this curNode is the last child of its parent +function! s:DrawTree(curNode, depth, drawText, vertMap, isLastChild) + if a:drawText == 1 + + let treeParts = '' + + "get all the leading spaces and vertical tree parts for this line + if a:depth > 1 + for j in a:vertMap[0:-2] + if j == 1 + let treeParts = treeParts . s:tree_vert . s:tree_wid_strM1 + else + let treeParts = treeParts . s:tree_wid_str + endif + endfor + endif + + "get the last vertical tree part for this line which will be different + "if this node is the last child of its parent + if a:isLastChild + let treeParts = treeParts . s:tree_vert_last + else + let treeParts = treeParts . s:tree_vert + endif + + + "smack the appropriate dir/file symbol on the line before the file/dir + "name itself + if a:curNode.path.isDirectory + if a:curNode.isOpen + let treeParts = treeParts . s:tree_dir_open + else + let treeParts = treeParts . s:tree_dir_closed + endif + else + let treeParts = treeParts . s:tree_file + endif + let line = treeParts . a:curNode.StrDisplay() + + call setline(line(".")+1, line) + call cursor(line(".")+1, col(".")) + endif + + "if the node is an open dir, draw its children + if a:curNode.path.isDirectory == 1 && a:curNode.isOpen == 1 + + let childNodesToDraw = a:curNode.GetVisibleChildren() + if len(childNodesToDraw) > 0 + + "draw all the nodes children except the last + let lastIndx = len(childNodesToDraw)-1 + if lastIndx > 0 + for i in childNodesToDraw[0:lastIndx-1] + call s:DrawTree(i, a:depth + 1, 1, add(copy(a:vertMap), 1), 0) + endfor + endif + + "draw the last child, indicating that it IS the last + call s:DrawTree(childNodesToDraw[lastIndx], a:depth + 1, 1, add(copy(a:vertMap), 0), 1) + endif + endif +endfunction + + +"FUNCTION: s:DumpHelp {{{2 +"prints out the quick help +function! s:DumpHelp() + let old_h = @h + if t:treeShowHelp == 1 + let @h= "\" NERD tree (" . s:NERD_tree_version . ") quickhelp~\n" + let @h=@h."\" ============================\n" + let @h=@h."\" File node mappings~\n" + let @h=@h."\" ". (g:NERDTreeMouseMode == 3 ? "single" : "double") ."-click,\n" + let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in prev window\n" + let @h=@h."\" ". g:NERDTreeMapPreview .": preview \n" + let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n" + let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n" + let @h=@h."\" middle-click,\n" + let @h=@h."\" ". g:NERDTreeMapOpenSplit .": open split\n" + let @h=@h."\" ". g:NERDTreeMapPreviewSplit .": preview split\n" + let @h=@h."\" ". g:NERDTreeMapExecute.": Execute file\n" + + let @h=@h."\" \n\" ----------------------------\n" + let @h=@h."\" Directory node mappings~\n" + let @h=@h."\" ". (g:NERDTreeMouseMode == 1 ? "double" : "single") ."-click,\n" + let @h=@h."\" ". g:NERDTreeMapActivateNode .": open/close node \n" + let @h=@h."\" ". g:NERDTreeMapOpenRecursively .": recursively open node\n" + let @h=@h."\" ". g:NERDTreeMapCloseDir .": close parent of node\n" + let @h=@h."\" ". g:NERDTreeMapCloseChildren .": close all child nodes of\n" + let @h=@h."\" current node recursively\n" + let @h=@h."\" middle-click,\n" + let @h=@h."\" ". g:NERDTreeMapOpenExpl.": Open netrw for selected\n" + let @h=@h."\" node \n" + + let @h=@h."\" \n\" ----------------------------\n" + let @h=@h."\" Tree navigation mappings~\n" + let @h=@h."\" ". g:NERDTreeMapJumpRoot .": go to root\n" + let @h=@h."\" ". g:NERDTreeMapJumpParent .": go to parent\n" + let @h=@h."\" ". g:NERDTreeMapJumpFirstChild .": go to first child\n" + let @h=@h."\" ". g:NERDTreeMapJumpLastChild .": go to last child\n" + let @h=@h."\" ". g:NERDTreeMapJumpNextSibling .": go to next sibling\n" + let @h=@h."\" ". g:NERDTreeMapJumpPrevSibling .": go to prev sibling\n" + + let @h=@h."\" \n\" ----------------------------\n" + let @h=@h."\" Filesystem mappings~\n" + let @h=@h."\" ". g:NERDTreeMapChangeRoot .": change tree root to the\n" + let @h=@h."\" selected dir\n" + let @h=@h."\" ". g:NERDTreeMapUpdir .": move tree root up a dir\n" + let @h=@h."\" ". g:NERDTreeMapUpdirKeepOpen .": move tree root up a dir\n" + let @h=@h."\" but leave old root open\n" + let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n" + let @h=@h."\" ". g:NERDTreeMapRefreshRoot .": refresh current root\n" + let @h=@h."\" ". g:NERDTreeMapFilesystemMenu .": Show filesystem menu\n" + let @h=@h."\" ". g:NERDTreeMapChdir .":change the CWD to the\n" + let @h=@h."\" selected dir\n" + + let @h=@h."\" \n\" ----------------------------\n" + let @h=@h."\" Tree filtering mappings~\n" + let @h=@h."\" ". g:NERDTreeMapToggleHidden .": hidden files (" . (g:NERDTreeShowHidden ? "on" : "off") . ")\n" + let @h=@h."\" ". g:NERDTreeMapToggleFilters .": file filters (" . (t:NERDTreeIgnoreEnabled ? "on" : "off") . ")\n" + let @h=@h."\" ". g:NERDTreeMapToggleFiles .": files (" . (g:NERDTreeShowFiles ? "on" : "off") . ")\n" + + let @h=@h."\" \n\" ----------------------------\n" + let @h=@h."\" Other mappings~\n" + let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n" + let @h=@h."\" ". g:NERDTreeMapHelp .": toggle help\n" + else + let @h="\" Press ". g:NERDTreeMapHelp ." for help\n" + endif + + silent! put h + + let @h = old_h +endfunction +"FUNCTION: s:Echo {{{2 +"A wrapper for :echo. Appends 'NERDTree:' on the front of all messages +" +"Args: +"msg: the message to echo +function! s:Echo(msg) + redraw + echo "NERDTree: " . a:msg +endfunction +"FUNCTION: s:EchoWarning {{{2 +"Wrapper for s:Echo, sets the message type to warningmsg for this message +"Args: +"msg: the message to echo +function! s:EchoWarning(msg) + echohl warningmsg + call s:Echo(a:msg) + echohl normal +endfunction +"FUNCTION: s:EchoError {{{2 +"Wrapper for s:Echo, sets the message type to errormsg for this message +"Args: +"msg: the message to echo +function! s:EchoError(msg) + echohl errormsg + call s:Echo(a:msg) + echohl normal +endfunction +"FUNCTION: s:FindNodeLineNumber(treenode){{{2 +"Finds the line number for the given tree node +" +"Args: +"treenode: the node to find the line no. for +function! s:FindNodeLineNumber(treenode) + "if the node is the root then return the root line no. + if a:treenode.IsRoot() + return s:FindRootNodeLineNumber() + endif + + let totalLines = line("$") + + "the path components we have matched so far + let pathcomponents = [substitute(t:NERDTreeRoot.path.Str(0), '/ *$', '', '')] + "the index of the component we are searching for + let curPathComponent = 1 + + let fullpath = a:treenode.path.Str(0) + + + let lnum = s:FindRootNodeLineNumber() + while lnum > 0 + let lnum = lnum + 1 + "have we reached the bottom of the tree? + if lnum == totalLines+1 + return -1 + endif + + let curLine = getline(lnum) + + let indent = match(curLine,s:tree_markup_reg_neg) / s:tree_wid + if indent == curPathComponent + let curLine = s:StripMarkupFromLine(curLine, 1) + + let curPath = join(pathcomponents, '/') . '/' . curLine + if stridx(fullpath, curPath, 0) == 0 + if fullpath == curPath || strpart(fullpath, len(curPath)-1,1) == '/' + let curLine = substitute(curLine, '/ *$', '', '') + call add(pathcomponents, curLine) + let curPathComponent = curPathComponent + 1 + + if fullpath == curPath + return lnum + endif + endif + endif + endif + endwhile + return -1 +endfunction + +"FUNCTION: s:FindRootNodeLineNumber(path){{{2 +"Finds the line number of the root node +function! s:FindRootNodeLineNumber() + let rootLine = 1 + while getline(rootLine) !~ '^/' + let rootLine = rootLine + 1 + endwhile + return rootLine +endfunction + +"FUNCTION: s:GetPath(ln) {{{2 +"Gets the full path to the node that is rendered on the given line number +" +"Args: +"ln: the line number to get the path for +" +"Return: +"A path if a node was selected, {} if nothing is selected. +"If the 'up a dir' line was selected then the path to the parent of the +"current root is returned +function! s:GetPath(ln) + let line = getline(a:ln) + + "check to see if we have the root node + if line =~ '^\/' + return t:NERDTreeRoot.path + endif + + " in case called from outside the tree + if line !~ '^ *[|`]' || line =~ '^$' + return {} + endif + + if line == s:tree_up_dir_line + return s:oPath.New( t:NERDTreeRoot.path.GetDir(0) ) + endif + + "get the indent level for the file (i.e. how deep in the tree it is) + "let indent = match(line,'[^-| `]') / s:tree_wid + let indent = match(line, s:tree_markup_reg_neg) / s:tree_wid + + + "remove the tree parts and the leading space + let curFile = s:StripMarkupFromLine(line, 0) + + let wasdir = 0 + if curFile =~ '/$' + let wasdir = 1 + endif + let curFile = substitute (curFile,' -> .*',"","") " remove link to + if wasdir == 1 + let curFile = substitute (curFile, '/\?$', '/', "") + endif + + + let dir = "" + let lnum = a:ln + while lnum > 0 + let lnum = lnum - 1 + let curLine = getline(lnum) + + "have we reached the top of the tree? + if curLine =~ '^/' + let sd = substitute (curLine, '[ ]*$', "", "") + let dir = sd . dir + break + endif + if curLine =~ '/$' + let lpindent = match(curLine,s:tree_markup_reg_neg) / s:tree_wid + if lpindent < indent + let indent = indent - 1 + let sd = substitute (curLine, '^' . s:tree_markup_reg . '*',"","") + let sd = substitute (sd, ' -> .*', '',"") + + " remove leading escape + let sd = substitute (sd,'^\\', "", "") + + let dir = sd . dir + continue + endif + endif + endwhile + let curFile = dir . curFile + return s:oPath.NewMinimal(curFile) +endfunction + +"FUNCTION: s:GetSelectedDir() {{{2 +"Returns the current node if it is a dir node, or else returns the current +"nodes parent +function! s:GetSelectedDir() + let currentDir = s:GetSelectedNode() + if currentDir != {} && !currentDir.IsRoot() + if currentDir.path.isDirectory == 0 + let currentDir = currentDir.parent + endif + endif + return currentDir +endfunction +"FUNCTION: s:GetSelectedNode() {{{2 +"gets the treenode that the cursor is currently over +function! s:GetSelectedNode() + try + let path = s:GetPath(line(".")) + if path == {} + return {} + endif + return t:NERDTreeRoot.FindNode(path) + catch /^NERDTree/ + return {} + endtry +endfunction + +"FUNCTION: s:GetTreeBufNum() {{{2 +"gets the nerd tree buffer number for this tab +function! s:GetTreeBufNum() + if exists("t:NERDTreeWinName") + return bufnr(t:NERDTreeWinName) + else + return -1 + endif +endfunction +"FUNCTION: s:GetTreeWinNum() {{{2 +"gets the nerd tree window number for this tab +function! s:GetTreeWinNum() + if exists("t:NERDTreeWinName") + return bufwinnr(t:NERDTreeWinName) + else + return -1 + endif +endfunction + +"FUNCTION: s:IsTreeOpen() {{{2 +function! s:IsTreeOpen() + return s:GetTreeWinNum() != -1 +endfunction + +" FUNCTION: s:JumpToChild(direction) {{{2 +" Args: +" direction: 0 if going to first child, 1 if going to last +function! s:JumpToChild(direction) + let currentNode = s:GetSelectedNode() + if currentNode == {} || currentNode.IsRoot() + call s:Echo("cannot jump to " . (a:direction ? "last" : "first") . " child") + return + end + let dirNode = currentNode.parent + let childNodes = dirNode.GetVisibleChildren() + + let targetNode = childNodes[0] + if a:direction + let targetNode = childNodes[len(childNodes) - 1] + endif + + if targetNode.Equals(currentNode) + let siblingDir = currentNode.parent.FindOpenDirSiblingWithChildren(a:direction) + if siblingDir != {} + let indx = a:direction ? siblingDir.GetVisibleChildCount()-1 : 0 + let targetNode = siblingDir.GetChildByIndex(indx, 1) + endif + endif + + call s:PutCursorOnNode(targetNode, 1) + + call s:CenterView() +endfunction + + +"FUNCTION: s:OpenDirNodeSplit(treenode) {{{2 +"Open the file represented by the given node in a new window. +"No action is taken for file nodes +" +"ARGS: +"treenode: file node to open +function! s:OpenDirNodeSplit(treenode) + if a:treenode.path.isDirectory == 1 + call s:OpenNodeSplit(a:treenode) + endif +endfunction + +"FUNCTION: s:OpenFileNode(treenode) {{{2 +"Open the file represented by the given node in the current window, splitting +"the window if needed +" +"ARGS: +"treenode: file node to open +function! s:OpenFileNode(treenode) + call s:PutCursorInTreeWin() + + if s:ShouldSplitToOpen(winnr("#")) + call s:OpenFileNodeSplit(a:treenode) + else + try + wincmd p + exec ("edit " . a:treenode.path.StrForEditCmd()) + catch /^Vim\%((\a\+)\)\=:E37/ + call s:PutCursorInTreeWin() + call s:Echo("Cannot open file, it is already open and modified") + catch /^Vim\%((\a\+)\)\=:/ + echo v:exception + endtry + endif +endfunction + +"FUNCTION: s:OpenFileNodeSplit(treenode) {{{2 +"Open the file represented by the given node in a new window. +"No action is taken for dir nodes +" +"ARGS: +"treenode: file node to open +function! s:OpenFileNodeSplit(treenode) + if a:treenode.path.isDirectory == 0 + try + call s:OpenNodeSplit(a:treenode) + catch /^NERDTree.view.FileOpen/ + call s:Echo("Cannot open file, it is already open and modified" ) + endtry + endif +endfunction + +"FUNCTION: s:OpenNodeSplit(treenode) {{{2 +"Open the file/dir represented by the given node in a new window +" +"ARGS: +"treenode: file node to open +function! s:OpenNodeSplit(treenode) + call s:PutCursorInTreeWin() + + " Save the user's settings for splitbelow and splitright + let savesplitbelow=&splitbelow + let savesplitright=&splitright + + " Figure out how to do the split based on the user's preferences. + " We want to split to the (left,right,top,bottom) of the explorer + " window, but we want to extract the screen real-estate from the + " window next to the explorer if possible. + " + " 'there' will be set to a command to move from the split window + " back to the explorer window + " + " 'back' will be set to a command to move from the explorer window + " back to the newly split window + " + " 'right' and 'below' will be set to the settings needed for + " splitbelow and splitright IF the explorer is the only window. + " + if g:NERDTreeSplitVertical == 1 + let there= g:NERDTreeWinPos ? "wincmd h" : "wincmd l" + let back= g:NERDTreeWinPos ? "wincmd l" : "wincmd h" + let right=g:NERDTreeWinPos ? 1 : 0 + let below=0 + else + let there= g:NERDTreeWinPos ? "wincmd k" : "wincmd j" + let back= g:NERDTreeWinPos ? "wincmd j" : "wincmd k" + let right=0 + let below=g:NERDTreeWinPos ? 1 : 0 + endif + + " Attempt to go to adjacent window + exec(back) + + let onlyOneWin = (winnr() == s:GetTreeWinNum()) + + " If no adjacent window, set splitright and splitbelow appropriately + if onlyOneWin + let &splitright=right + let &splitbelow=below + else + " found adjacent window - invert split direction + let &splitright=!right + let &splitbelow=!below + endif + + " Create a variable to use if splitting vertically + let splitMode = "" + if (onlyOneWin && g:NERDTreeSplitVertical) || (!onlyOneWin && !g:NERDTreeSplitVertical) + let splitMode = "vertical" + endif + + " Open the new window + try + exec("silent " . splitMode." sp " . a:treenode.path.StrForEditCmd()) + catch /^Vim\%((\a\+)\)\=:E37/ + call s:PutCursorInTreeWin() + throw "NERDTree.view.FileOpen exception: ". a:treenode.path.Str(0) ." is already open and modified." + catch /^Vim\%((\a\+)\)\=:/ + do nothing + endtry + + " resize the explorer window if it is larger than the requested size + exec(there) + + if g:NERDTreeWinSize =~ '[0-9]\+' && winheight("") > g:NERDTreeWinSize + exec("silent vertical resize ".g:NERDTreeWinSize) + endif + + wincmd p + + " Restore splitmode settings + let &splitbelow=savesplitbelow + let &splitright=savesplitright +endfunction + +"FUNCTION: s:PromptToDelBuffer(bufnum, msg){{{2 +"prints out the given msg and, if the user responds by pushing 'y' then the +"buffer with the given bufnum is deleted +" +"Args: +"bufnum: the buffer that may be deleted +"msg: a message that will be echoed to the user asking them if they wish to +" del the buffer +function! s:PromptToDelBuffer(bufnum, msg) + echo a:msg + if nr2char(getchar()) == 'y' + exec "silent bdelete! " . a:bufnum + endif +endfunction + +"FUNCTION: s:PutCursorOnNode(treenode, is_jump){{{2 +"Places the cursor on the line number representing the given node +" +"Args: +"treenode: the node to put the cursor on +"is_jump: 1 if this cursor movement should be counted as a jump by vim +function! s:PutCursorOnNode(treenode, is_jump) + let ln = s:FindNodeLineNumber(a:treenode) + if ln != -1 + if a:is_jump + mark ' + endif + call cursor(ln, col(".")) + endif +endfunction + +"FUNCTION: s:PutCursorInTreeWin(){{{2 +"Places the cursor in the nerd tree window +function! s:PutCursorInTreeWin() + if !s:IsTreeOpen() + throw "NERDTree.view.InvalidOperation Exception: No NERD tree window exists" + endif + + exec s:GetTreeWinNum() . "wincmd w" +endfunction + +"FUNCTION: s:RenderView {{{2 +"The entry function for rendering the tree. Renders the root then calls +"s:DrawTree to draw the children of the root +" +"Args: +function! s:RenderView() + execute s:GetTreeWinNum() . "wincmd w" + + setlocal modifiable + + "remember the top line of the buffer and the current line so we can + "restore the view exactly how it was + let curLine = line(".") + let curCol = col(".") + let topLine = line("w0") + + "delete all lines in the buffer (being careful not to clobber a register) + :silent 1,$delete _ + + call s:DumpHelp() + + "delete the blank line before the help and add one after it + call setline(line(".")+1, " ") + call cursor(line(".")+1, col(".")) + + "add the 'up a dir' line + call setline(line(".")+1, s:tree_up_dir_line) + call cursor(line(".")+1, col(".")) + + "draw the header line + call setline(line(".")+1, t:NERDTreeRoot.path.Str(0)) + call cursor(line(".")+1, col(".")) + + "draw the tree + call s:DrawTree(t:NERDTreeRoot, 0, 0, [], t:NERDTreeRoot.GetChildCount() == 1) + + "delete the blank line at the top of the buffer + :silent 1,1delete _ + + "restore the view + call cursor(topLine, 1) + normal! zt + call cursor(curLine, curCol) + + setlocal nomodifiable +endfunction + +"FUNCTION: s:RenderViewSavingPosition {{{2 +"Renders the tree and ensures the cursor stays on the current node or the +"current nodes parent if it is no longer available upon re-rendering +function! s:RenderViewSavingPosition() + let currentNode = s:GetSelectedNode() + + "go up the tree till we find a node that will be visible or till we run + "out of nodes + while currentNode != {} && !currentNode.IsVisible() && !currentNode.IsRoot() + let currentNode = currentNode.parent + endwhile + + call s:RenderView() + + if currentNode != {} + call s:PutCursorOnNode(currentNode, 0) + endif +endfunction +"FUNCTION: s:RestoreScreenState() {{{2 +" +"Sets the screen state back to what it was when s:SaveScreenState was last +"called. +" +"Assumes the cursor is in the NERDTree window +function! s:RestoreScreenState() + if !exists("t:NERDTreeOldTopLine") || !exists("t:NERDTreeOldPos") + return + endif + + call cursor(t:NERDTreeOldTopLine, 0) + normal! zt + call setpos(".", t:NERDTreeOldPos) +endfunction + +"FUNCTION: s:SaveScreenState() {{{2 +"Saves the current cursor position in the current buffer and the window +"scroll position +" +"Assumes the cursor is in the NERDTree window +function! s:SaveScreenState() + let t:NERDTreeOldPos = getpos(".") + let t:NERDTreeOldTopLine = line("w0") +endfunction + +"FUNCTION: s:SetupSyntaxHighlighting() {{{2 +function! s:SetupSyntaxHighlighting() + "treeFlags are syntax items that should be invisible, but give clues as to + "how things should be highlighted + syn match treeFlag #\~# + syn match treeFlag #\[RO\]# + + "highlighting for the .. (up dir) line at the top of the tree + execute "syn match treeUp #". s:tree_up_dir_line ."#" + + "highlighting for the ~/+ symbols for the directory nodes + syn match treeClosable #\~\<# + syn match treeClosable #\~\.# + syn match treeOpenable #+\<# + syn match treeOpenable #+\.#he=e-1 + + "highlighting for the tree structural parts + syn match treePart #|# + syn match treePart #`# + syn match treePartFile #[|`]-#hs=s+1 contains=treePart + + "quickhelp syntax elements + syn match treeHelpKey #" \{1,2\}[^ ]*:#hs=s+2,he=e-1 + syn match treeHelpKey #" \{1,2\}[^ ]*,#hs=s+2,he=e-1 + syn match treeHelpTitle #" .*\~#hs=s+2,he=e-1 contains=treeFlag + syn match treeToggleOn #".*(on)#hs=e-2,he=e-1 contains=treeHelpKey + syn match treeToggleOff #".*(off)#hs=e-3,he=e-1 contains=treeHelpKey + syn match treeHelp #^" .*# contains=treeHelpKey,treeHelpTitle,treeFlag,treeToggleOff,treeToggleOn + + "highlighting for sym links + syn match treeLink #[^-| `].* -> # + + "highlighting for readonly files + syn match treeRO #[0-9a-zA-Z]\+.*\[RO\]# contains=treeFlag + + "highlighing for directory nodes and file nodes + syn match treeDirSlash #/# + syn match treeDir #[^-| `].*/\([ {}]\{4\}\)*$# contains=treeLink,treeDirSlash,treeOpenable,treeClosable + syn match treeFile #|-.*# contains=treeLink,treePart,treeRO,treePartFile + syn match treeFile #`-.*# contains=treeLink,treePart,treeRO,treePartFile + syn match treeCWD #^/.*$# + + if g:NERDChristmasTree + hi def link treePart Special + hi def link treePartFile Type + hi def link treeFile Macro + hi def link treeDirSlash Identifier + hi def link treeClosable Type + else + hi def link treePart Normal + hi def link treePartFile Normal + hi def link treeFile Normal + hi def link treeClosable Title + endif + + hi def link treeHelp String + hi def link treeHelpKey Identifier + hi def link treeHelpTitle Macro + hi def link treeToggleOn Question + hi def link treeToggleOff WarningMsg + + hi def link treeDir Directory + hi def link treeUp Directory + hi def link treeCWD Statement + hi def link treeLink Title + hi def link treeOpenable Title + hi def link treeFlag ignore + hi def link treeRO WarningMsg + + hi def link NERDTreeCurrentNode Search +endfunction + +"FUNCTION: s:ShouldSplitToOpen() {{{2 +"Returns 1 if opening a file from the tree in the given window requires it to +"be split +" +"Args: +"winnumber: the number of the window in question +function! s:ShouldSplitToOpen(winnumber) + if &hidden + return 0 + endif + let oldwinnr = winnr() + + exec a:winnumber . "wincmd p" + let modified = &modified + exec oldwinnr . "wincmd p" + + return winnr("$") == 1 || (modified && s:BufInWindows(winbufnr(a:winnumber)) < 2) +endfunction + +"FUNCTION: s:StripMarkupFromLine(line){{{2 +"returns the given line with all the tree parts stripped off +" +"Args: +"line: the subject line +"removeLeadingSpaces: 1 if leading spaces are to be removed (leading spaces = +"any spaces before the actual text of the node) +function! s:StripMarkupFromLine(line, removeLeadingSpaces) + let line = a:line + "remove the tree parts and the leading space + let line = substitute (line,"^" . s:tree_markup_reg . "*","","") + + "strip off any read only flag + let line = substitute (line, s:tree_RO_str_reg, "","") + + let wasdir = 0 + if line =~ '/$' + let wasdir = 1 + endif + let line = substitute (line,' -> .*',"","") " remove link to + if wasdir == 1 + let line = substitute (line, '/\?$', '/', "") + endif + + if a:removeLeadingSpaces + let line = substitute (line, '^ *', '', '') + endif + + return line +endfunction + +"FUNCTION: s:Toggle(dir) {{{2 +"Toggles the NERD tree. I.e the NERD tree is open, it is closed, if it is +"closed it is restored or initialized (if it doesnt exist) +" +"Args: +"dir: the full path for the root node (is only used if the NERD tree is being +"initialized. +function! s:Toggle(dir) + if s:TreeExistsForTab() + if !s:IsTreeOpen() + call s:CreateTreeWin() + call s:RenderView() + + call s:RestoreScreenState() + else + call s:CloseTree() + endif + else + call s:InitNerdTree(a:dir) + endif +endfunction +"SECTION: Interface bindings {{{1 +"============================================================ +"FUNCTION: s:ActivateNode() {{{2 +"If the current node is a file, open it in the previous window (or a new one +"if the previous is modified). If it is a directory then it is opened. +function! s:ActivateNode() + if getline(".") == s:tree_up_dir_line + return s:UpDir(0) + endif + let treenode = s:GetSelectedNode() + if treenode == {} + call s:EchoWarning("cannot open selected entry") + return + endif + + if treenode.path.isDirectory + call treenode.ToggleOpen() + call s:RenderView() + call s:PutCursorOnNode(treenode, 0) + else + call s:OpenFileNode(treenode) + endif +endfunction + +"FUNCTION: s:BindMappings() {{{2 +function! s:BindMappings() + " set up mappings and commands for this buffer + nnoremap <silent> <buffer> <middlerelease> :call <SID>HandleMiddleMouse()<cr> + nnoremap <silent> <buffer> <leftrelease> <leftrelease>:call <SID>CheckForActivate()<cr> + nnoremap <silent> <buffer> <2-leftmouse> :call <SID>ActivateNode()<cr> + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapActivateNode . " :call <SID>ActivateNode()<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenSplit ." :call <SID>OpenEntrySplit()<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreview ." :call <SID>PreviewNode(0)<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewSplit ." :call <SID>PreviewNode(1)<cr>" + + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapExecute ." :call <SID>ExecuteNode()<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenRecursively ." :call <SID>OpenNodeRecursively()<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdirKeepOpen ." :call <SID>UpDir(1)<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdir ." :call <SID>UpDir(0)<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChangeRoot ." :call <SID>ChRoot()<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChdir ." :call <SID>ChCwd()<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapQuit ." :NERDTreeToggle<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefreshRoot ." :call <SID>RefreshRoot()<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefresh ." :call <SID>RefreshCurrent()<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapHelp ." :call <SID>DisplayHelp()<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleHidden ." :call <SID>ToggleShowHidden()<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFilters ." :call <SID>ToggleIgnoreFilter()<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFiles ." :call <SID>ToggleShowFiles()<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseDir ." :call <SID>CloseCurrentDir()<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseChildren ." :call <SID>CloseChildren()<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapFilesystemMenu ." :call <SID>ShowFileSystemMenu()<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpParent ." :call <SID>JumpToParent()<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpNextSibling ." :call <SID>JumpToSibling(1)<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpPrevSibling ." :call <SID>JumpToSibling(0)<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpFirstChild ." :call <SID>JumpToFirstChild()<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpLastChild ." :call <SID>JumpToLastChild()<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpRoot ." :call <SID>JumpToRoot()<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTab ." :call <SID>OpenNodeNewTab(0)<cr>" + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTabSilent ." :call <SID>OpenNodeNewTab(1)<cr>" + + exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenExpl ." :call <SID>OpenExplorer()<cr>" + + +endfunction + +"FUNCTION: s:CheckForActivate() {{{2 +"Checks if the click should open the current node, if so then activate() is +"called (directories are automatically opened if the symbol beside them is +"clicked) +function! s:CheckForActivate() + let currentNode = s:GetSelectedNode() + if currentNode != {} + let startToCur = strpart(getline(line(".")), 0, col(".")) + let char = strpart(startToCur, strlen(startToCur)-1, 1) + + "if they clicked a dir, check if they clicked on the + or ~ sign + "beside it + if currentNode.path.isDirectory + let reg = '^' . s:tree_markup_reg .'*[' . s:tree_dir_open . s:tree_dir_closed . ']$' + if startToCur =~ reg + call s:ActivateNode() + return + endif + endif + + if (g:NERDTreeMouseMode == 2 && currentNode.path.isDirectory) || g:NERDTreeMouseMode == 3 + if char !~ s:tree_markup_reg && startToCur !~ '\/$' + call s:ActivateNode() + return + endif + endif + endif +endfunction + +" FUNCTION: s:ChCwd() {{{2 +function! s:ChCwd() + let treenode = s:GetSelectedNode() + if treenode == {} + call s:Echo("Select a node first") + return + endif + + try + call treenode.path.ChangeToDir() + catch /^NERDTree.Path.Change/ + call s:EchoWarning("could not change cwd") + endtry +endfunction + +" FUNCTION: s:ChRoot() {{{2 +" changes the current root to the selected one +function! s:ChRoot() + let treenode = s:GetSelectedNode() + if treenode == {} || treenode.path.isDirectory == 0 + call s:Echo("Select a directory node first") + return + endif + + if treenode.isOpen == 0 + call treenode.Open() + endif + + let t:NERDTreeRoot = treenode + + "change dir to the dir of the new root if instructed to + if g:NERDTreeChDirMode == 2 + exec "cd " . treenode.path.StrForEditCmd() + endif + + + call s:RenderView() + call s:PutCursorOnNode(t:NERDTreeRoot, 0) +endfunction + +" FUNCTION: s:CloseChildren() {{{2 +" closes all childnodes of the current node +function! s:CloseChildren() + let currentNode = s:GetSelectedDir() + if currentNode == {} + call s:Echo("Select a node first") + return + endif + + call currentNode.CloseChildren() + call s:RenderView() + call s:PutCursorOnNode(currentNode, 0) +endfunction +" FUNCTION: s:CloseCurrentDir() {{{2 +" closes the parent dir of the current node +function! s:CloseCurrentDir() + let treenode = s:GetSelectedNode() + if treenode == {} + call s:Echo("Select a node first") + return + endif + + let parent = treenode.parent + if parent.IsRoot() + call s:Echo("cannot close tree root") + else + call treenode.parent.Close() + call s:RenderView() + call s:PutCursorOnNode(treenode.parent, 0) + endif +endfunction + +" FUNCTION: s:DeleteNode() {{{2 +" if the current node is a file, pops up a dialog giving the user the option +" to delete it +function! s:DeleteNode() + let currentNode = s:GetSelectedNode() + if currentNode == {} + call s:Echo("Put the cursor on a file node first") + return + endif + + let confirmed = 0 + + if currentNode.path.isDirectory + let choice =input("Delete the current node\n" . + \ "==========================================================\n" . + \ "STOP! To delete this entire directory, type 'yes'\n" . + \ "" . currentNode.path.StrForOS(0) . ": ") + let confirmed = choice == 'yes' + else + echo "Delete the current node\n" . + \ "==========================================================\n". + \ "Are you sure you wish to delete the node:\n" . + \ "" . currentNode.path.StrForOS(0) . " (yN):" + let choice = nr2char(getchar()) + let confirmed = choice == 'y' + endif + + + if confirmed + try + call currentNode.Delete() + call s:RenderView() + + "if the node is open in a buffer, ask the user if they want to + "close that buffer + let bufnum = bufnr(currentNode.path.Str(0)) + if buflisted(bufnum) + let prompt = "\nNode deleted.\n\nThe file is open in buffer ". bufnum . (bufwinnr(bufnum) == -1 ? " (hidden)" : "") .". Delete this buffer? (yN)" + call s:PromptToDelBuffer(bufnum, prompt) + endif + + redraw + catch /^NERDTree/ + call s:EchoWarning("Could not remove node") + endtry + else + call s:Echo("delete aborted" ) + endif + +endfunction + +" FUNCTION: s:DisplayHelp() {{{2 +" toggles the help display +function! s:DisplayHelp() + let t:treeShowHelp = t:treeShowHelp ? 0 : 1 + call s:RenderView() + call s:CenterView() +endfunction + +" FUNCTION: s:ExecuteNode() {{{2 +function! s:ExecuteNode() + let treenode = s:GetSelectedNode() + if treenode == {} || treenode.path.isDirectory + call s:Echo("Select an executable file node first" ) + else + echo "NERDTree executor\n" . + \ "==========================================================\n". + \ "Complete the command to execute (add arguments etc): \n\n" + let cmd = treenode.path.StrForOS(1) + let cmd = input(':!', cmd . ' ') + + if cmd != '' + exec ':!' . cmd + else + call s:Echo("command aborted") + endif + endif +endfunction + +" FUNCTION: s:HandleMiddleMouse() {{{2 +function! s:HandleMiddleMouse() + let curNode = s:GetSelectedNode() + if curNode == {} + call s:Echo("Put the cursor on a node first" ) + return + endif + + if curNode.path.isDirectory + call s:OpenExplorer() + else + call s:OpenEntrySplit() + endif +endfunction + + +" FUNCTION: s:InsertNewNode() {{{2 +" Adds a new node to the filesystem and then into the tree +function! s:InsertNewNode() + let curDirNode = s:GetSelectedDir() + if curDirNode == {} + call s:Echo("Put the cursor on a node first" ) + return + endif + + let newNodeName = input("Add a childnode\n". + \ "==========================================================\n". + \ "Enter the dir/file name to be created. Dirs end with a '/'\n" . + \ "", curDirNode.path.Str(0)) + + if newNodeName == '' + call s:Echo("Node Creation Aborted.") + return + endif + + try + let newPath = s:oPath.Create(newNodeName) + + let parentNode = t:NERDTreeRoot.FindNode(newPath.GetPathTrunk()) + + let newTreeNode = s:oTreeFileNode.New(newPath) + if parentNode.isOpen || !empty(parentNode.children) + call parentNode.AddChild(newTreeNode, 1) + call s:RenderView() + call s:PutCursorOnNode(newTreeNode, 1) + endif + catch /^NERDTree/ + call s:EchoWarning("Node Not Created.") + endtry +endfunction + +" FUNCTION: s:JumpToFirstChild() {{{2 +" wrapper for the jump to child method +function! s:JumpToFirstChild() + call s:JumpToChild(0) +endfunction + +" FUNCTION: s:JumpToLastChild() {{{2 +" wrapper for the jump to child method +function! s:JumpToLastChild() + call s:JumpToChild(1) +endfunction + +" FUNCTION: s:JumpToParent() {{{2 +" moves the cursor to the parent of the current node +function! s:JumpToParent() + let currentNode = s:GetSelectedNode() + if !empty(currentNode) + if !empty(currentNode.parent) + call s:PutCursorOnNode(currentNode.parent, 1) + call s:CenterView() + else + call s:Echo("cannot jump to parent") + endif + else + call s:Echo("put the cursor on a node first") + endif +endfunction + +" FUNCTION: s:JumpToRoot() {{{2 +" moves the cursor to the root node +function! s:JumpToRoot() + call s:PutCursorOnNode(t:NERDTreeRoot, 1) + call s:CenterView() +endfunction + +" FUNCTION: s:JumpToSibling() {{{2 +" moves the cursor to the sibling of the current node in the given direction +" +" Args: +" forward: 1 if the cursor should move to the next sibling, 0 if it should +" move back to the previous sibling +function! s:JumpToSibling(forward) + let currentNode = s:GetSelectedNode() + if !empty(currentNode) + + if !currentNode.path.isDirectory + + if a:forward + let sibling = currentNode.parent.FindSibling(1) + else + let sibling = currentNode.parent + endif + + else + let sibling = currentNode.FindSibling(a:forward) + endif + + if !empty(sibling) + call s:PutCursorOnNode(sibling, 1) + call s:CenterView() + endif + else + call s:Echo("put the cursor on a node first") + endif +endfunction + +" FUNCTION: s:OpenEntrySplit() {{{2 +" Opens the currently selected file from the explorer in a +" new window +function! s:OpenEntrySplit() + let treenode = s:GetSelectedNode() + if treenode != {} + call s:OpenFileNodeSplit(treenode) + else + call s:Echo("select a node first") + endif +endfunction + +" FUNCTION: s:OpenExplorer() {{{2 +function! s:OpenExplorer() + let treenode = s:GetSelectedDir() + if treenode != {} + let oldwin = winnr() + wincmd p + if oldwin == winnr() || (&modified && s:BufInWindows(winbufnr(winnr())) < 2) + wincmd p + call s:OpenDirNodeSplit(treenode) + else + exec ("silent edit " . treenode.path.StrForEditCmd()) + endif + else + call s:Echo("select a node first") + endif +endfunction + +" FUNCTION: s:OpenNodeNewTab(stayCurrentTab) {{{2 +" Opens the currently selected file from the explorer in a +" new tab +" +" Args: +" stayCurrentTab: if 1 then vim will stay in the current tab, if 0 then vim +" will go to the tab where the new file is opened +function! s:OpenNodeNewTab(stayCurrentTab) + let treenode = s:GetSelectedNode() + if treenode != {} + let curTabNr = tabpagenr() + exec "tabedit " . treenode.path.StrForEditCmd() + if a:stayCurrentTab + exec "tabnext " . curTabNr + endif + else + call s:Echo("select a node first") + endif +endfunction + + +" FUNCTION: s:OpenNodeRecursively() {{{2 +function! s:OpenNodeRecursively() + let treenode = s:GetSelectedNode() + if treenode == {} || treenode.path.isDirectory == 0 + call s:Echo("Select a directory node first" ) + else + call s:Echo("Recursively opening node. Please wait...") + call treenode.OpenRecursively() + call s:RenderView() + redraw + call s:Echo("Recursively opening node. Please wait... DONE") + endif + +endfunction + +"FUNCTION: s:PreviewNode() {{{2 +function! s:PreviewNode(openNewWin) + let treenode = s:GetSelectedNode() + if treenode == {} || treenode.path.isDirectory + call s:Echo("Select a file node first" ) + return + endif + + if a:openNewWin + call s:OpenEntrySplit() + else + call s:ActivateNode() + end + call s:PutCursorInTreeWin() +endfunction + +" FUNCTION: s:RefreshRoot() {{{2 +" Reloads the current root. All nodes below this will be lost and the root dir +" will be reloaded. +function! s:RefreshRoot() + call s:Echo("Refreshing the root node. This could take a while...") + call t:NERDTreeRoot.Refresh() + call s:RenderView() + redraw + call s:Echo("Refreshing the root node. This could take a while... DONE") +endfunction + +" FUNCTION: s:RefreshCurrent() {{{2 +" refreshes the root for the current node +function! s:RefreshCurrent() + let treenode = s:GetSelectedDir() + if treenode == {} + call s:Echo("Refresh failed. Select a node first") + return + endif + + call s:Echo("Refreshing node. This could take a while...") + call treenode.Refresh() + call s:RenderView() + redraw + call s:Echo("Refreshing node. This could take a while... DONE") +endfunction +" FUNCTION: s:RenameCurrent() {{{2 +" allows the user to rename the current node +function! s:RenameCurrent() + let curNode = s:GetSelectedNode() + if curNode == {} + call s:Echo("Put the cursor on a node first" ) + return + endif + + let newNodePath = input("Rename the current node\n" . + \ "==========================================================\n" . + \ "Enter the new path for the node: \n" . + \ "", curNode.path.Str(0)) + + if newNodePath == '' + call s:Echo("Node Renaming Aborted.") + return + endif + + let newNodePath = substitute(newNodePath, '\/$', '', '') + + try + let bufnum = bufnr(curNode.path.Str(0)) + + call curNode.Rename(newNodePath) + call s:RenderView() + + "if the node is open in a buffer, ask the user if they want to + "close that buffer + if bufnum != -1 + let prompt = "|\n|Node renamed.\n|\n|The old file is open in buffer ". bufnum . (bufwinnr(bufnum) == -1 ? " (hidden)" : "") .". Delete this buffer? (yN)" + call s:PromptToDelBuffer(bufnum, prompt) + endif + + call s:PutCursorOnNode(curNode, 1) + + redraw + catch /^NERDTree/ + call s:EchoWarning("Node Not Renamed.") + endtry +endfunction + +" FUNCTION: s:ShowFileSystemMenu() {{{2 +function! s:ShowFileSystemMenu() + let curNode = s:GetSelectedNode() + if curNode == {} + call s:Echo("Put the cursor on a node first" ) + return + endif + + + echo "NERDTree Filesystem Menu\n" . + \ "==========================================================\n". + \ "Select the desired operation: \n" . + \ " (1) - Add a childnode\n". + \ " (2) - Rename the current node\n". + \ " (3) - Delete the current node\n\n" + + let choice = nr2char(getchar()) + + if choice == 1 + call s:InsertNewNode() + elseif choice == 2 + call s:RenameCurrent() + elseif choice == 3 + call s:DeleteNode() + endif +endfunction + +" FUNCTION: s:ToggleIgnoreFilter() {{{2 +" toggles the use of the NERDTreeIgnore option +function! s:ToggleIgnoreFilter() + let t:NERDTreeIgnoreEnabled = !t:NERDTreeIgnoreEnabled + call s:RenderViewSavingPosition() + call s:CenterView() +endfunction + +" FUNCTION: s:ToggleShowFiles() {{{2 +" toggles the display of hidden files +function! s:ToggleShowFiles() + let g:NERDTreeShowFiles = !g:NERDTreeShowFiles + call s:RenderViewSavingPosition() + call s:CenterView() +endfunction + +" FUNCTION: s:ToggleShowHidden() {{{2 +" toggles the display of hidden files +function! s:ToggleShowHidden() + let g:NERDTreeShowHidden = !g:NERDTreeShowHidden + call s:RenderViewSavingPosition() + call s:CenterView() +endfunction + +"FUNCTION: s:UpDir(keepState) {{{2 +"moves the tree up a level +" +"Args: +"keepState: 1 if the current root should be left open when the tree is +"re-rendered +function! s:UpDir(keepState) + let cwd = t:NERDTreeRoot.path.Str(0) + if cwd == "/" || cwd =~ '^[^/]..$' + call s:Echo("already at top dir") + else + if !a:keepState + call t:NERDTreeRoot.Close() + endif + + let oldRoot = t:NERDTreeRoot + + if empty(t:NERDTreeRoot.parent) + let path = t:NERDTreeRoot.path.GetPathTrunk() + let newRoot = s:oTreeDirNode.New(path) + call newRoot.Open() + call newRoot.TransplantChild(t:NERDTreeRoot) + let t:NERDTreeRoot = newRoot + else + let t:NERDTreeRoot = t:NERDTreeRoot.parent + + endif + + call s:RenderView() + call s:PutCursorOnNode(oldRoot, 0) + endif +endfunction + + +" SECTION: Doc installation call {{{1 +silent call s:InstallDocumentation(expand('<sfile>:p'), s:NERD_tree_version) +"============================================================ +finish +" SECTION: The help file {{{1 +"============================================================================= +" Title {{{2 +" ============================================================================ +=== START_DOC +*NERD_tree.txt* A tree explorer plugin that owns your momma! #version# + + + + + + ________ ________ _ ____________ ____ __________ ____________~ + /_ __/ / / / ____/ / | / / ____/ __ \/ __ \ /_ __/ __ \/ ____/ ____/~ + / / / /_/ / __/ / |/ / __/ / /_/ / / / / / / / /_/ / __/ / __/ ~ + / / / __ / /___ / /| / /___/ _, _/ /_/ / / / / _, _/ /___/ /___ ~ + /_/ /_/ /_/_____/ /_/ |_/_____/_/ |_/_____/ /_/ /_/ |_/_____/_____/ ~ + + + Reference Manual~ + + + + +============================================================================== +CONTENTS {{{2 *NERDTree-contents* + + 1.Intro...................................|NERDTree| + 2.Functionality provided..................|NERDTreeFunctionality| + 2.1 Commands..........................|NERDTreeCommands| + 2.2 NERD tree mappings................|NERDTreeMappings| + 2.3 The filesystem menu...............|NERDTreeFilesysMenu| + 3.Options.................................|NERDTreeOptions| + 3.1 Option summary....................|NERDTreeOptionSummary| + 3.2 Option details....................|NERDTreeOptionDetails| + 4.Public functions........................|NERDTreePublicFunctions| + 5.TODO list...............................|NERDTreeTodo| + 6.The Author..............................|NERDTreeAuthor| + 7.Changelog...............................|NERDTreeChangelog| + 8.Credits.................................|NERDTreeCredits| + +============================================================================== +1. Intro {{{2 *NERDTree* + +What is this "NERD tree"?? + +The NERD tree allows you to explore your filesystem and to open files and +directories. It presents the filesystem to you in the form of a tree which you +manipulate with the keyboard and/or mouse. It also allows you to perform +simple filesystem operations so you can alter the tree dynamically. + +The following features and functionality are provided by the NERD tree: + * Files and directories are displayed in a hierarchical tree structure + * Different highlighting is provided for the following types of nodes: + * files + * directories + * sym-links + * windows .lnk files + * read-only files + * Many (customisable) mappings are provided to manipulate the tree: + * Mappings to open/close/explore directory nodes + * Mappings to open files in new/existing windows/tabs + * Mappings to change the current root of the tree + * Mappings to navigate around the tree + * ... + * Most NERD tree navigation can also be done with the mouse + * Dynamic customisation of tree content + * custom file filters to prevent e.g. vim backup files being displayed + * optional displaying of hidden files (. files) + * files can be "turned off" so that only directories are displayed + * A textual filesystem menu is provided which allows you to + create/delete/rename file and directory nodes + * The position and size of the NERD tree window can be customised + * The order in which the nodes in the tree are listed can be customised. + * A model of your filesystem is created/maintained as you explore it. This + has several advantages: + * All filesystem information is cached and is only re-read on demand + * If you revisit a part of the tree that you left earlier in your + session, the directory nodes will be opened/closed as you left them + * The script remembers the cursor position and window position in the NERD + tree so you can toggle it off (or just close the tree window) and then + reopen it (with NERDTreeToggle) the NERD tree window will appear EXACTLY + as you left it + * You can have a separate NERD tree for each tab + +============================================================================== +2. Functionality provided {{{2 *NERDTreeFunctionality* + +------------------------------------------------------------------------------ +2.1. Commands {{{3 *NERDTreeCommands* + +:NERDTree [start-directory] *:NERDTree* + Opens a fresh NERD tree in [start-directory] or the current + directory if [start-directory] isn't specified. + For example: > + :NERDTree /home/marty/vim7/src +< will open a NERD tree in /home/marty/vim7/src. + +:NERDTreeToggle [start-directory] *:NERDTreeToggle* + If a NERD tree already exists for this tab, it is reopened and + rendered again. If no NERD tree exists for this tab then this + command acts the same as the |:NERDTree| command. + +------------------------------------------------------------------------------ +2.2. NERD tree Mappings {{{3 *NERDTreeMappings* + +Default Description~ help-tag~ +Key~ + +o.......Open selected file, or expand selected dir...............|NERDTree-o| +go......Open selected file, but leave cursor in the NERDTree.....|NERDTree-go| +t.......Open selected node in a new tab..........................|NERDTree-t| +T.......Same as 't' but keep the focus on the current tab........|NERDTree-T| +<tab>...Open selected file in a split window.....................|NERDTree-tab| +g<tab>..Same as <tab>, but leave the cursor on the NERDTree......|NERDTree-gtab| +!.......Execute the current file.................................|NERDTree-!| +O.......Recursively open the selected directory..................|NERDTree-O| +x.......Close the current nodes parent...........................|NERDTree-x| +X.......Recursively close all children of the current node.......|NERDTree-X| +e.......Open a netrw for the current dir.........................|NERDTree-e| + +double-click.......same as the |NERDTree-o| map. +middle-click.......same as |NERDTree-tab| for files, same as + |NERDTree-e| for dirs. + +P.......Jump to the root node....................................|NERDTree-P| +p.......Jump to current nodes parent.............................|NERDTree-p| +K.......Jump up inside directories at the current tree depth.....|NERDTree-K| +J.......Jump down inside directories at the current tree depth...|NERDTree-J| +<C-j>...Jump down to the next sibling of the current directory...|NERDTree-c-j| +<C-k>...Jump up to the previous sibling of the current directory.|NERDTree-c-k| + +C.......Change the tree root to the selected dir.................|NERDTree-C| +u.......Move the tree root up one directory......................|NERDTree-u| +U.......Same as 'u' except the old root node is left open........|NERDTree-U| +r.......Recursively refresh the current directory................|NERDTree-r| +R.......Recursively refresh the current root.....................|NERDTree-R| +m.......Display the filesystem menu..............................|NERDTree-m| +cd......Change the CWD to the dir of the selected node...........|NERDTree-cd| + +H.......Toggle whether hidden files displayed....................|NERDTree-H| +f.......Toggle whether the file filters are used.................|NERDTree-f| +F.......Toggle whether files are displayed.......................|NERDTree-F| + +q.......Close the NERDTree window................................|NERDTree-q| +?.......Toggle the display of the quick help.....................|NERDTree-?| + +------------------------------------------------------------------------------ + *NERDTree-o* +Default key: o +Map option: NERDTreeMapActivateNode +Applies to: files and directories. + +If a file node is selected, it is opened in the previous window. If a +directory is selected it is opened or closed depending on its current state. + +------------------------------------------------------------------------------ + *NERDTree-go* +Default key: go +Map option: None +Applies to: files. + +If a file node is selected, it is opened in the previous window, but the +cursor does not move. + +The key combo for this mapping is always "g" + NERDTreeMapActivateNode (see +|NERDTree-o|). + +------------------------------------------------------------------------------ + *NERDTree-t* +Default key: t +Map option: NERDTreeMapOpenInTab +Applies to: files and directories. + +Opens the selected file in a new tab. If a directory is selected, a netrw is +opened in a new tab. + +------------------------------------------------------------------------------ + *NERDTree-T* +Default key: T +Map option: NERDTreeMapOpenInTabSilent +Applies to: files and directories. + +The same as |NERDTree-t| except that the focus is kept in the current tab. + +------------------------------------------------------------------------------ + *NERDTree-tab* +Default key: <tab> +Map option: NERDTreeMapOpenSplit +Applies to: files. + +Opens the selected file in a new split window and puts the cursor in the new +window. + +------------------------------------------------------------------------------ + *NERDTree-gtab* +Default key: g<tab> +Map option: None +Applies to: files. + +The same as |NERDTree-tab| except that the cursor is not moved. + +The key combo for this mapping is always "g" + NERDTreeMapOpenSplit (see +|NERDTree-tab|). + +------------------------------------------------------------------------------ + *NERDTree-!* +Default key: ! +Map option: NERDTreeMapExecute +Applies to: files. + +Executes the selected file, prompting for arguments first. + +------------------------------------------------------------------------------ + *NERDTree-O* +Default key: O +Map option: NERDTreeMapOpenRecursively +Applies to: directories. + +Recursively opens the selelected directory. + +All files and directories are cached, but if a directory would not be +displayed due to file filters (see |NERDTreeIgnore| |NERDTree-f|) or the +hidden file filter (see |NERDTreeShowHidden|) then it is not opened. This is +handy, especially if you have .svn directories. + + +------------------------------------------------------------------------------ + *NERDTree-x* +Default key: x +Map option: NERDTreeMapCloseDir +Applies to: files and directories. + +Closes the parent of the selected node. + +------------------------------------------------------------------------------ + *NERDTree-X* +Default key: X +Map option: NERDTreeMapCloseChildren +Applies to: directories. + +Recursively closes all children of the selected directory. + +Tip: To quickly "reset" the tree, use |NERDTree-P| with this mapping. + +------------------------------------------------------------------------------ + *NERDTree-e* +Default key: e +Map option: NERDTreeMapOpenExpl +Applies to: files and directories. + +Opens a netrw on the selected directory, or the selected file's directory. + +------------------------------------------------------------------------------ + *NERDTree-P* +Default key: P +Map option: NERDTreeMapJumpRoot +Applies to: no restrictions. + +Jump to the tree root. + +------------------------------------------------------------------------------ + *NERDTree-p* +Default key: p +Map option: NERDTreeMapJumpParent +Applies to: files and directories. + +Jump to the parent node of the selected node. + +------------------------------------------------------------------------------ + *NERDTree-K* +Default key: K +Map option: NERDTreeMapJumpFirstChild +Applies to: files and directories. + +Jump to the first child of the current nodes parent. + +If the cursor is already on the first node then do the following: + * loop back thru the siblings of the current nodes parent until we find an + open dir with children + * go to the first child of that node + +------------------------------------------------------------------------------ + *NERDTree-J* +Default key: J +Map option: NERDTreeMapJumpLastChild +Applies to: files and directories. + +Jump to the last child of the current nodes parent. + +If the cursor is already on the last node then do the following: + * loop forward thru the siblings of the current nodes parent until we find + an open dir with children + * go to the last child of that node + +------------------------------------------------------------------------------ + *NERDTree-c-j* +Default key: <C-j> +Map option: NERDTreeMapJumpNextSibling +Applies to: files and directories. + +If a dir node is selected, jump to the next sibling of that node. +If a file node is selected, jump to the next sibling of that nodes parent. + +------------------------------------------------------------------------------ + *NERDTree-c-k* +Default key: <C-k> +Map option: NERDTreeMapJumpPrevSibling +Applies to: files and directories. + +If a dir node is selected, jump to the previous sibling of that node. +If a file node is selected, jump to the previous sibling of that nodes parent. + +------------------------------------------------------------------------------ + *NERDTree-C* +Default key: C +Map option: NERDTreeMapChdir +Applies to: directories. + +Made the selected directory node the new tree root. + +------------------------------------------------------------------------------ + *NERDTree-u* +Default key: u +Map option: NERDTreeMapUpdir +Applies to: no restrictions. + +Move the tree root up a dir (like doing a "cd .."). + +------------------------------------------------------------------------------ + *NERDTree-U* +Default key: U +Map option: NERDTreeMapUpdirKeepOpen +Applies to: no restrictions. + +Like |NERDTree-u| except that the old tree root is kept open. + +------------------------------------------------------------------------------ + *NERDTree-r* +Default key: r +Map option: NERDTreeMapRefresh +Applies to: files and directories. + +If a dir is selected, recursively refresh that dir, i.e. scan the filesystem +for changes and represent them in the tree. + +If a file node is selected then the above is done on it's parent. + +------------------------------------------------------------------------------ + *NERDTree-R* +Default key: R +Map option: NERDTreeMapRefreshRoot +Applies to: no restrictions. + +Recursively refresh the tree root. + +------------------------------------------------------------------------------ + *NERDTree-m* +Default key: m +Map option: NERDTreeMapFilesystemMenu +Applies to: files and directories. + +Display the filesystem menu. See |NERDTreeFilesysMenu| for details. + +------------------------------------------------------------------------------ + *NERDTree-H* +Default key: H +Map option: NERDTreeMapToggleHidden +Applies to: no restrictions. + +Toggles whether hidden files are displayed. Hidden files are any +file/directory that starts with a "." + +------------------------------------------------------------------------------ + *NERDTree-f* +Default key: f +Map option: NERDTreeMapToggleFilters +Applies to: no restrictions. + +Toggles whether file filters are used. See |NERDTreeIgnore| for details. + +------------------------------------------------------------------------------ + *NERDTree-F* +Default key: F +Map option: NERDTreeMapToggleFiles +Applies to: no restrictions. + +Toggles whether file nodes are displayed. + +------------------------------------------------------------------------------ + *NERDTree-q* +Default key: q +Map option: NERDTreeMapQuit +Applies to: no restrictions. + +Closes the NERDtree window. + +------------------------------------------------------------------------------ + *NERDTree-?* +Default key: ? +Map option: NERDTreeMapHelp +Applies to: no restrictions. + +Toggles whether the quickhelp is displayed. + +------------------------------------------------------------------------------ +2.3. The filesystem menu {{{3 *NERDTreeFilesysMenu* + +The purpose of the filesystem menu is to allow you to perform basic filesystem +operations quickly from the NERD tree rather than the console. + +The filesystem menu can be accessed with 'm' mapping and has three supported +operations: > + 1. Adding nodes. + 2. Renaming nodes. + 3. Deleting nodes. +< +1. Adding nodes: +To add a node move the cursor onto (or anywhere inside) the directory you wish +to create the new node inside. Select the 'add node' option from the +filesystem menu and type a filename. If the filename you type ends with a '/' +character then a directory will be created. Once the operation is completed, +the cursor is placed on the new node. + +2. Renaming nodes: +To rename a node, put the cursor on it and select the 'rename' option from the +filesystem menu. Enter the new name for the node and it will be renamed. If +the old file is open in a buffer, you will be asked if you wish to delete that +buffer. Once the operation is complete the cursor will be placed on the +renamed node. + +3. Deleting nodes: +To delete a node put the cursor on it and select the 'delete' option from the +filesystem menu. After confirmation the node will be deleted. If a file is +deleted but still exists as a buffer you will be given the option to delete +that buffer. + +============================================================================== +3. Customisation {{{2 *NERDTreeOptions* + + +------------------------------------------------------------------------------ +3.1. Customisation summary {{{3 *NERDTreeOptionSummary* + +The script provides the following options that can customise the behaviour the +NERD tree. These options should be set in your vimrc. + +|loaded_nerd_tree| Turns off the script. + +|NERDChristmasTree| Tells the NERD tree to make itself colourful + and pretty. + +|NERDTreeAutoCenter| Controls whether the NERD tree window centers + when the cursor moves within a specified + distance to the top/bottom of the window. +|NERDTreeAutoCenterThreshold| Controls the sensitivity of autocentering. + +|NERDTreeCaseSensitiveSort| Tells the NERD tree whether to be case + sensitive or not when sorting nodes. + +|NERDTreeChDirMode| Tells the NERD tree if/when it should change + vim's current working directory. + +|NERDTreeHighlightCursorline| Tell the NERD tree whether to highlight the + current cursor line. + +|NERDTreeIgnore| Tells the NERD tree which files to ignore. + +|NERDTreeMouseMode| Tells the NERD tree how to handle mouse + clicks. + +|NERDTreeShowFiles| Tells the NERD tree whether to display files + in the tree on startup. + +|NERDTreeShowHidden| Tells the NERD tree whether to display hidden + files on startup. + +|NERDTreeSortOrder| Tell the NERD tree how to sort the nodes in + the tree. + +|NERDTreeSplitVertical| Tells the script whether the NERD tree should + be created by splitting the window vertically + or horizontally. + +|NERDTreeWinPos| Tells the script where to put the NERD tree + window. + + +|NERDTreeWinSize| Sets the window size when the NERD tree is + opened. + +------------------------------------------------------------------------------ +3.2. Customisation details {{{3 *NERDTreeOptionDetails* + +To enable any of the below options you should put the given line in your +~/.vimrc + + *loaded_nerd_tree* +If this plugin is making you feel homicidal, it may be a good idea to turn it +off with this line in your vimrc: > + let loaded_nerd_tree=1 +< +------------------------------------------------------------------------------ + *NERDChristmasTree* +Values: 0 or 1. +Default: 1. + +If this option is set to 1 then some extra syntax highlighting elements are +added to the nerd tree to make it more colourful. + +Set it to 0 for a more vanilla looking tree. + +------------------------------------------------------------------------------ + *NERDTreeAutoCenter* +Values: 0 or 1. +Default: 1 + +If set to 1, the NERD tree window will center around the cursor if it moves to +within |NERDTreeAutoCenterThreshold| lines of the top/bottom of the window. + +This is ONLY done in response to tree navigation mappings, +i.e. |NERDTree-J| |NERDTree-K| |NERDTree-C-J| |NERDTree-c-K| |NERDTree-p| +|NERDTree-P| + +The centering is done with a |zz| operation. + +------------------------------------------------------------------------------ + *NERDTreeAutoCenterThreshold* +Values: Any natural number. +Default: 3 + +This option controls the "sensitivity" of the NERD tree auto centering. See +|NERDTreeAutoCenter| for details. + +------------------------------------------------------------------------------ + *NERDTreeCaseSensitiveSort* +Values: 0 or 1. +Default: 0. + +By default the NERD tree does not sort nodes case sensitively, i.e. nodes +could appear like this: > + bar.c + Baz.c + blarg.c + boner.c + Foo.c +< +But, if you set this option to 1 then the case of the nodes will be taken into +account. The above nodes would then be sorted like this: > + Baz.c + Foo.c + bar.c + blarg.c + boner.c +< +------------------------------------------------------------------------------ + *NERDTreeChDirMode* + +Values: 0, 1 or 2. +Default: 1. + +Use this option to tell the script when (if at all) to change the current +working directory (CWD) for vim. + +If it is set to 0 then the CWD is never changed by the NERD tree. + +If set to 1 then the CWD is changed when the NERD tree is first loaded to the +directory it is initialized in. For example, if you start the NERD tree with > + :NERDTree /home/marty/foobar +< +then the CWD will be changed to /home/marty/foobar and will not be changed +again unless you init another NERD tree with a similar command. + +If the option is set to 2 then it behaves the same as if set to 1 except that +the CWD is changed whenever the tree root is changed. For example, if the CWD +is /home/marty/foobar and you make the node for /home/marty/foobar/baz the new +root then the CWD will become /home/marty/foobar/baz. + +Note to windows users: it is highly recommended that you have this option set +to either 1 or 2 or else the script wont function properly if you attempt to +open a NERD tree on a different drive to the one vim is currently in. + +Authors note: at work i have this option set to 1 because i have a giant ctags +file in the root dir of my project. This way i can initialise the NERD tree +with the root dir of my project and always have ctags available to me --- no +matter where i go with the NERD tree. + +------------------------------------------------------------------------------ + *NERDTreeHighlightCursorline* +Values: 0 or 1. +Default: 1. + +If set to 1, the current cursor line in the NERD tree buffer will be +highlighted. This is done using the |cursorline| option. + +------------------------------------------------------------------------------ + *NERDTreeIgnore* +Values: a list of regular expressions. +Default: ['\~$']. + +This option is used to specify which files the NERD tree should ignore. It +must be a list of regular expressions. When the NERD tree is rendered, any +files/dirs that match any of the regex's in NERDTreeIgnore wont be displayed. + +For example if you put the following line in your vimrc: > + let NERDTreeIgnore=['\.vim$', '\~$'] +< +then all files ending in .vim or ~ will be ignored. + +Note: to tell the NERD tree not to ignore any files you must use the following +line: > + let NERDTreeIgnore=[] +< + +The file filters can be turned on and off dynamically with the |NERDTree-f| +mapping. + +------------------------------------------------------------------------------ + *NERDTreeMouseMode* +Values: 1, 2 or 3. +Default: 1. + +If set to 1 then a double click on a node is required to open it. +If set to 2 then a single click will open directory nodes, while a double +click will still be required for file nodes. +If set to 3 then a single click will open any node. + +Note: a double click anywhere on a line that a tree node is on will +activate it, but all single-click activations must be done on name of the node +itself. For example, if you have the following node: > + | | |-application.rb +< +then (to single click activate it) you must click somewhere in +'application.rb'. + +------------------------------------------------------------------------------ + *NERDTreeShowFiles* +Values: 0 or 1. +Default: 1. + +If this option is set to 1 then files are displayed in the NERD tree. If it is +set to 0 then only directories are displayed. + +This option can be toggled dynamically with the |NERDTree-F| mapping and is +useful for drastically shrinking the tree when you are navigating to a +different part of the tree. + +------------------------------------------------------------------------------ + *NERDTreeShowHidden* +Values: 0 or 1. +Default: 0. + +This option tells vim whether to display hidden files by default. This option +can be dynamically toggled with the |NERDTree-H| mapping. +Use one of the follow lines to set this option: > + let NERDTreeShowHidden=0 + let NERDTreeShowHidden=1 +< + +------------------------------------------------------------------------------ + *NERDTreeSortOrder* +Values: a list of regular expressions. +Default: ['\/$', '*', '\.swp$', '\.bak$', '\~$'] + +This option is set to a list of regular expressions which are used to +specify the order of nodes under their parent. + +For example, if the option is set to: > + ['\.vim$', '\.c$', '\.h$', '*', 'foobar'] +< +then all .vim files will be placed at the top, followed by all .c files then +all .h files. All files containing the string 'foobar' will be placed at the +end. The star is a special flag: it tells the script that every node that +doesnt match any of the other regexps should be placed here. + +If no star is present in NERDTreeSortOrder then one is automatically appended +to the array. + +The regex '\/$' should be used to match directory nodes. + +After this sorting is done, the files in each group are sorted alphabetically. + +Other examples: > + (1) ['*', '\/$'] + (2) [] + (3) ['\/$', '\.rb$', '\.php$', '*', '\.swp$', '\.bak$', '\~$'] +< +1. Directories will appear last, everything else will appear above. +2. Every will simply appear in alphabetical order. +3. Dirs will appear first, then ruby and php. Swap files, bak files and vim + backup files will appear last with everything else preceding them. + +------------------------------------------------------------------------------ + *NERDTreeSplitVertical* +Values: 0 or 1. +Default: 1. + +This option, along with |NERDTreeWinPos|, is used to determine where the NERD +tree window appears. + +If it is set to 1 then the NERD tree window will appear on either the left or +right side of the screen (depending on the |NERDTreeWinPos| option). + +If it set to 0 then the NERD tree window will appear at the top of the screen. + +------------------------------------------------------------------------------ + *NERDTreeWinPos* +Values: 0 or 1. +Default: 1. + +This option works in conjunction with the |NERDTreeSplitVertical| option to +determine where NERD tree window is placed on the screen. + +If the option is set to 1 then the NERD tree will appear on the left or top of +the screen (depending on the value of |NERDTreeSplitVertical|). If set to 0, +the window will appear on the right or bottom of the screen. + +This option is makes it possible to use two different explorer type +plugins simultaneously. For example, you could have the taglist plugin on the +left of the window and the NERD tree on the right. + +------------------------------------------------------------------------------ + *NERDTreeWinSize* +Values: a positive integer. +Default: 31. + +This option is used to change the size of the NERD tree when it is loaded. + +============================================================================== + *NERDTreePublicFunctions* +5. Public functions {{{2 ~ + +The script provides 2 public functions for your hacking pleasure. Their +signatures are: > + function! NERDTreeGetCurrentNode() + function! NERDTreeGetCurrentPath() +< +The first returns the node object that the cursor is currently on, while the +second returns the corresponding path object. + +This is probably a good time to mention that the script implements prototype +style OO. To see the functions that each class provides you can read look at +the code. + +Use the node objects to manipulate the structure of the tree. Use the path +objects to access the data the tree represents and to make changes to the +filesystem. + +============================================================================== +5. TODO list {{{2 *NERDTreeTodo* + +Window manager integration? + +============================================================================== +6. The Author {{{2 *NERDTreeAuthor* + +The author of the NERD tree is a terrible terrible monster called Martyzilla +who gobbles up small children with milk and sugar for breakfast. He has an odd +love/hate relationship with computers (but monsters hate everything by nature +you know...) which can be awkward for him since he is a pro computer nerd for +a living. + +He can be reached at martin_grenfell at msn.com. He would love to hear from +you, so feel free to send him suggestions and/or comments about this plugin. +Don't be shy --- the worst he can do is slaughter you and stuff you in the +fridge for later ;) + +============================================================================== +7. Changelog {{{2 *NERDTreeChangelog* + +2.6.2 + - Now when you try to open a file node into a window that is modified, the + window is not split if the &hidden option is set. Thanks to Niels Aan + de Brugh for this suggestion. + +2.6.1 + - Fixed a major bug with the <tab> mapping. Thanks to Zhang Weiwu for + emailing me. + +2.6.0 + - Extended the behaviour of <c-j/k>. Now if the cursor is on a file node + and you use <c-j/k> the cursor will jump to its PARENTS next/previous + sibling. Go :help NERDTree-c-j and :help NERDTree-c-k for info. + - Extended the behaviour of the J/K mappings. Now if the cursor is on the + last child of a node and you push J/K it will jump down to the last child + of the next/prev of its parents siblings that is open and has children. + Go :help NERDTree-J and :help NERDTree-K for info. + - The goal of these changes is to make tree navigation faster. + - Reorganised the help page a bit. + - Removed the E mapping. + - bugfixes + +2.5.0 + - Added an option to enforce case sensitivity when sorting tree nodes. + Read :help NERDTreeCaseSensitiveSort for details. (thanks to Michael + Madsen for emailing me about this). Case sensitivity defaults to off. + - Made the script echo a "please wait" style message when opening large + directories. Thanks to AOYAMA Shotaro for this suggestion. + - Added 2 public functions that can be used to retrieve the treenode and + path that the cursor is on. Read :help NERDTreePublicFunctions for + details (thanks again to AOYAMA Shotaro for the idea :). + - added 2 new mappings for file nodes: "g<tab>" and "go". These are the + same as the "<tab>" and "o" maps except that the cursor stays in the + NERDTree. Note: these maps are slaved to the o and <tab> mappings, so if + eg you remap "<tab>" to "i" then the "g<tab>" map will also be changed + to "gi". + - Renamed many of the help tags to be simpler. + - Simplified the ascii "graphics" for the filesystem menu + - Fixed bugs. + - Probably created bugs. + - Refactoring. + +2.4.0 + - Added the P mapping to jump to the tree root. + - Added window centering functionality that can be triggered when doing + using any of the tree nav mappings. Essentially, if the cursor comes + within a certain distance of the top/bottom of the window then a zz is + done in the window. Two related options were added: NERDTreeAutoCenter + to turn this functionality on/off, and NERDTreeAutoCenterThreshold to + control how close the cursor has to be to the window edge to trigger the + centering. + +2.3.0 + - Tree navigation changes: + - Added J and K mappings to jump to last/first child of the current dir. + Options to customise these mappings have also been added. + - Remapped the jump to next/prev sibling commands to be <C-j> and <C-k> by + default. + These changes should hopefully make tree navigation mappings easier to + remember and use as the j and k keys are simply reused 3 times (twice + with modifier keys). + + - Made it so that, when any of the tree filters are toggled, the cursor + stays with the selected node (or goes to its parent/grandparent/... if + that node is no longer visible) + - Fixed an error in the doc for the mouse mode option. + - Made the quickhelp correctly display the current single/double click + mappings for opening nodes as specified by the NERDTreeMouseMode option. + - Fixed a bug where the script was spazzing after prompting you to delete + a modified buffer when using the filesystem menu. + - Refactoring +2.2.3 + - Refactored the :echo output from the script. + - Fixed some minor typos in the doc. + - Made some minor changes to the output of the 'Tree filtering mappings' + part of the quickhelp + +2.2.2 + - More bugfixes... doh. + +2.2.1 + - Bug fix that was causing an exception when closing the nerd tree. Thanks + to Tim carey-smith and Yu Jun for pointing this out. + +2.2.0 + - Now 'cursorline' is set in the NERD tree buffer by default. See :help + NERDTreeHighlightCursorline for how to disable it. + +2.1.2 + - Stopped the script from clobbering the 1,2,3 .. 9 registers. + - Made it "silent!"ly delete buffers when renaming/deleting file nodes. + - Minor correction to the doc + - Fixed a bug when refreshing that was occurring when the node you + refreshed had been deleted externally. + - Fixed a bug that was occurring when you open a file that is already open + and modified. + +2.1.1 + - Added a bit more info about the buffers you are prompted to delete when + renaming/deleting nodes from the filesystem menu that are already loaded + into buffers. + - Refactoring and bugfixes + +2.1.0 + - Finally removed the blank line that always appears at the top of the + NERDTree buffer + - Added NERDTreeMouseMode option. If set to 1, then a double click is + required to activate all nodes, if set to 2 then a single click will + activate directory nodes, if set to 3 then a single click will activate + all nodes. + - Now if you delete a file node and have it open in a buffer you are given + the option to delete that buffer as well. Similarly if you rename a file + you are given the option to delete any buffers containing the old file + (if any exist) + - When you rename or create a node, the cursor is now put on the new node, + this makes it easy immediately edit the new file. + - Fixed a bug with the ! mapping that was occurring on windows with paths + containing spaces. + - Made all the mappings customisable. See |NERD_tree-mappings| for + details. A side effect is that a lot of the "double mappings" have + disappeared. E.g 'o' is now the key that is used to activate a node, + <CR> is no longer mapped to the same. + - Made the script echo warnings in some places rather than standard echos + - Insane amounts of refactoring all over the place. + +2.0.0 + - Added two new NERDChristmasTree decorations. First person to spot them + and email me gets a free copy of the NERDTree. + - Made it so that when you jump around the tree (with the p, s and S + mappings) it is counted as a jump by vim. This means if you, eg, push + 'p' one too many times then you can go `` or ctrl-o. + - Added a new option called NERDTreeSortOrder which takes an array of + regexs and is used to determine the order that the treenodes are listed + in. Go :help NERDTreeSortOrder for details. + - Removed the NERDTreeSortDirs option because it is consumed by + NERDTreeSortOrder + - Added the 'i' mapping which is the same as <tab> but requires less + effort to reach. + - Added the ! mapping which is used to execute file in the tree (after it + prompts you for arguments etc) + + +============================================================================== +8. Credits {{{2 *NERDTreeCredits* + +Thanks to Tim Carey-Smith for testing/using the NERD tree from the first +pre-beta version, for his many suggestions and for his constant stream of bug +complaints. + +Thanks to Vigil for trying it out before the first release :) and suggesting +that mappings to open files in new tabs should be implemented. + +Thanks to Nick Brettell for testing, fixing my spelling and suggesting i put a + .. (up a directory) +line in the gui. + +Thanks to Thomas Scott Urban - the author of the vtreeexplorer plugin - whose +gui code i borrowed from. + +Thanks to Terrance Cohen for pointing out a bug where the script was changing +vims CWD all over the show. + +Thanks to Yegappan Lakshmanan (author of Taglist and other orgasmically +wonderful plugins) for telling me how to fix a bug that was causing vim to go +into visual mode everytime you double clicked a node :) + +Thanks to Jason Mills for sending me a fix that allows windows paths to use +forward slashes as well as backward. + +Thanks to Michael Geddes (frogonwheels on #vim at freenode) for giving me some +tips about syntax highlighting when i was doing highlighting for the +quickhelp. + +Thanks to Yu Jun for emailing me about a bug that was occurring when closing +the tree. + +Thanks to Michael Madsen for emailing me about making case sensitivity +optional when sorting nodes. + +Thanks to AOYAMA Shotaro for suggesting that i echo a "please wait" message +when opening large directories. + +Thanks to Michael Madsen for requesting the NERDTreeCaseSensitiveSort option. + +Thanks to AOYAMA Shotaro for suggesting that a "please wait" style message be +echoed when opening large directories. Also, thanks for the suggestion of +having public functions in the script to access the internal data :D + +Thanks to Zhang Weiwu for emailing me about a bug with the the <tab> mapping +in 2.6.0 + +Thanks to Niels Aan de Brugh for the suggestion that the script now split the +window if you try to open a file in a window containing a modified buffer when +the &hidden option is set. + +=== END_DOC +" vim: set ts=4 sw=4 foldmethod=marker foldmarker={{{,}}} foldlevel=2: diff --git a/.vim/plugin/a.vim b/.vim/plugin/a.vim new file mode 100644 index 0000000..e73817d --- /dev/null +++ b/.vim/plugin/a.vim @@ -0,0 +1,840 @@ +" Copyright (c) 1998-2006 +" Michael Sharpe <feline@irendi.com> +" +" We grant permission to use, copy modify, distribute, and sell this +" software for any purpose without fee, provided that the above copyright +" notice and this text are not removed. We make no guarantee about the +" suitability of this software for any purpose and we are not liable +" for any damages resulting from its use. Further, we are under no +" obligation to maintain or extend this software. It is provided on an +" "as is" basis without any expressed or implied warranty. + +" Directory & regex enhancements added by Bindu Wavell who is well known on +" vim.sf.net +" +" Patch for spaces in files/directories from Nathan Stien (also reported by +" Soeren Sonnenburg) + +" Do not load a.vim if is has already been loaded. +if exists("loaded_alternateFile") + finish +endif +if (v:progname == "ex") + finish +endif +let loaded_alternateFile = 1 + +let alternateExtensionsDict = {} + +" setup the default set of alternate extensions. The user can override in thier +" .vimrc if the defaults are not suitable. To override in a .vimrc simply set a +" g:alternateExtensions_<EXT> variable to a comma separated list of alternates, +" where <EXT> is the extension to map. +" E.g. let g:alternateExtensions_CPP = "inc,h,H,HPP,hpp" +" let g:alternateExtensions_{'aspx.cs'} = "aspx" + + +" This variable will be increased when an extension with greater number of dots +" is added by the AddAlternateExtensionMapping call. +let s:maxDotsInExtension = 1 + +" Function : AddAlternateExtensionMapping (PRIVATE) +" Purpose : simple helper function to add the default alternate extension +" mappings. +" Args : extension -- the extension to map +" alternates -- comma separated list of alternates extensions +" Returns : nothing +" Author : Michael Sharpe <feline@irendi.com> +function! <SID>AddAlternateExtensionMapping(extension, alternates) + " This code does not actually work for variables like foo{'a.b.c.d.e'} + "let varName = "g:alternateExtensions_" . a:extension + "if (!exists(varName)) + " let g:alternateExtensions_{a:extension} = a:alternates + "endif + + " This code handles extensions which contains a dot. exists() fails with + " such names. + "let v:errmsg = "" + " FIXME this line causes ex to return 1 instead of 0 for some reason?? + "silent! echo g:alternateExtensions_{a:extension} + "if (v:errmsg != "") + "let g:alternateExtensions_{a:extension} = a:alternates + "endif + + let g:alternateExtensionsDict[a:extension] = a:alternates + let dotsNumber = strlen(substitute(a:extension, "[^.]", "", "g")) + if s:maxDotsInExtension < dotsNumber + let s:maxDotsInExtension = dotsNumber + endif +endfunction + + +" Add all the default extensions +" Mappings for C and C++ +call <SID>AddAlternateExtensionMapping('h',"c,cpp,cxx,cc,CC") +call <SID>AddAlternateExtensionMapping('H',"C,CPP,CXX,CC") +call <SID>AddAlternateExtensionMapping('hpp',"cpp,c") +call <SID>AddAlternateExtensionMapping('HPP',"CPP,C") +call <SID>AddAlternateExtensionMapping('c',"h") +call <SID>AddAlternateExtensionMapping('C',"H") +call <SID>AddAlternateExtensionMapping('cpp',"h,hpp") +call <SID>AddAlternateExtensionMapping('CPP',"H,HPP") +call <SID>AddAlternateExtensionMapping('cc',"h") +call <SID>AddAlternateExtensionMapping('CC',"H,h") +call <SID>AddAlternateExtensionMapping('cxx',"h") +call <SID>AddAlternateExtensionMapping('CXX',"H") +" Mappings for PSL7 +call <SID>AddAlternateExtensionMapping('psl',"ph") +call <SID>AddAlternateExtensionMapping('ph',"psl") +" Mappings for ADA +call <SID>AddAlternateExtensionMapping('adb',"ads") +call <SID>AddAlternateExtensionMapping('ads',"adb") +" Mappings for lex and yacc files +call <SID>AddAlternateExtensionMapping('l',"y,yacc,ypp") +call <SID>AddAlternateExtensionMapping('lex',"yacc,y,ypp") +call <SID>AddAlternateExtensionMapping('lpp',"ypp,y,yacc") +call <SID>AddAlternateExtensionMapping('y',"l,lex,lpp") +call <SID>AddAlternateExtensionMapping('yacc',"lex,l,lpp") +call <SID>AddAlternateExtensionMapping('ypp',"lpp,l,lex") +" Mappings for OCaml +call <SID>AddAlternateExtensionMapping('ml',"mli") +call <SID>AddAlternateExtensionMapping('mli',"ml") +" ASP stuff +call <SID>AddAlternateExtensionMapping('aspx.cs', 'aspx') +call <SID>AddAlternateExtensionMapping('aspx.vb', 'aspx') +call <SID>AddAlternateExtensionMapping('aspx', 'aspx.cs,aspx.vb') + +" Setup default search path, unless the user has specified +" a path in their [._]vimrc. +if (!exists('g:alternateSearchPath')) + let g:alternateSearchPath = 'sfr:../source,sfr:../src,sfr:../include,sfr:../inc' +endif + +" If this variable is true then a.vim will not alternate to a file/buffer which +" does not exist. E.g while editing a.c and the :A will not swtich to a.h +" unless it exists. +if (!exists('g:alternateNoDefaultAlternate')) + " by default a.vim will alternate to a file which does not exist + let g:alternateNoDefaultAlternate = 0 +endif + +" If this variable is true then a.vim will convert the alternate filename to a +" filename relative to the current working directory. +" Feature by Nathan Huizinga +if (!exists('g:alternateRelativeFiles')) + " by default a.vim will not convert the filename to one relative to the + " current working directory + let g:alternateRelativeFiles = 0 +endif + + +" Function : GetNthItemFromList (PRIVATE) +" Purpose : Support reading items from a comma seperated list +" Used to iterate all the extensions in an extension spec +" Used to iterate all path prefixes +" Args : list -- the list (extension spec, file paths) to iterate +" n -- the extension to get +" Returns : the nth item (extension, path) from the list (extension +" spec), or "" for failure +" Author : Michael Sharpe <feline@irendi.com> +" History : Renamed from GetNthExtensionFromSpec to GetNthItemFromList +" to reflect a more generic use of this function. -- Bindu +function! <SID>GetNthItemFromList(list, n) + let itemStart = 0 + let itemEnd = -1 + let pos = 0 + let item = "" + let i = 0 + while (i != a:n) + let itemStart = itemEnd + 1 + let itemEnd = match(a:list, ",", itemStart) + let i = i + 1 + if (itemEnd == -1) + if (i == a:n) + let itemEnd = strlen(a:list) + endif + break + endif + endwhile + if (itemEnd != -1) + let item = strpart(a:list, itemStart, itemEnd - itemStart) + endif + return item +endfunction + +" Function : ExpandAlternatePath (PRIVATE) +" Purpose : Expand path info. A path with a prefix of "wdr:" will be +" treated as relative to the working directory (i.e. the +" directory where vim was started.) A path prefix of "abs:" will +" be treated as absolute. No prefix or "sfr:" will result in the +" path being treated as relative to the source file (see sfPath +" argument). +" +" A prefix of "reg:" will treat the pathSpec as a regular +" expression substitution that is applied to the source file +" path. The format is: +" +" reg:<sep><pattern><sep><subst><sep><flag><sep> +" +" <sep> seperator character, we often use one of [/|%#] +" <pattern> is what you are looking for +" <subst> is the output pattern +" <flag> can be g for global replace or empty +" +" EXAMPLE: 'reg:/inc/src/g/' will replace every instance +" of 'inc' with 'src' in the source file path. It is possible +" to use match variables so you could do something like: +" 'reg:|src/\([^/]*\)|inc/\1||' (see 'help :substitute', +" 'help pattern' and 'help sub-replace-special' for more details +" +" NOTE: a.vim uses ',' (comma) internally so DON'T use it +" in your regular expressions or other pathSpecs unless you update +" the rest of the a.vim code to use some other seperator. +" +" Args : pathSpec -- path component (or substitution patterns) +" sfPath -- source file path +" Returns : a path that can be used by AlternateFile() +" Author : Bindu Wavell <bindu@wavell.net> +function! <SID>ExpandAlternatePath(pathSpec, sfPath) + let prfx = strpart(a:pathSpec, 0, 4) + if (prfx == "wdr:" || prfx == "abs:") + let path = strpart(a:pathSpec, 4) + elseif (prfx == "reg:") + let re = strpart(a:pathSpec, 4) + let sep = strpart(re, 0, 1) + let patend = match(re, sep, 1) + let pat = strpart(re, 1, patend - 1) + let subend = match(re, sep, patend + 1) + let sub = strpart(re, patend+1, subend - patend - 1) + let flag = strpart(re, strlen(re) - 2) + if (flag == sep) + let flag = '' + endif + let path = substitute(a:sfPath, pat, sub, flag) + "call confirm('PAT: [' . pat . '] SUB: [' . sub . ']') + "call confirm(a:sfPath . ' => ' . path) + else + let path = a:pathSpec + if (prfx == "sfr:") + let path = strpart(path, 4) + endif + let path = a:sfPath . "/" . path + endif + return path +endfunction + +" Function : FindFileInSearchPath (PRIVATE) +" Purpose : Searches for a file in the search path list +" Args : filename -- name of the file to search for +" pathList -- the path list to search +" relPathBase -- the path which relative paths are expanded from +" Returns : An expanded filename if found, the empty string otherwise +" Author : Michael Sharpe (feline@irendi.com) +" History : inline code written by Bindu Wavell originally +function! <SID>FindFileInSearchPath(fileName, pathList, relPathBase) + let filepath = "" + let m = 1 + let pathListLen = strlen(a:pathList) + if (pathListLen > 0) + while (1) + let pathSpec = <SID>GetNthItemFromList(a:pathList, m) + if (pathSpec != "") + let path = <SID>ExpandAlternatePath(pathSpec, a:relPathBase) + let fullname = path . "/" . a:fileName + let foundMatch = <SID>BufferOrFileExists(fullname) + if (foundMatch) + let filepath = fullname + break + endif + else + break + endif + let m = m + 1 + endwhile + endif + return filepath +endfunction + +" Function : FindFileInSearchPathEx (PRIVATE) +" Purpose : Searches for a file in the search path list +" Args : filename -- name of the file to search for +" pathList -- the path list to search +" relPathBase -- the path which relative paths are expanded from +" count -- find the count'th occurence of the file on the path +" Returns : An expanded filename if found, the empty string otherwise +" Author : Michael Sharpe (feline@irendi.com) +" History : Based on <SID>FindFileInSearchPath() but with extensions +function! <SID>FindFileInSearchPathEx(fileName, pathList, relPathBase, count) + let filepath = "" + let m = 1 + let spath = "" + let pathListLen = strlen(a:pathList) + if (pathListLen > 0) + while (1) + let pathSpec = <SID>GetNthItemFromList(a:pathList, m) + if (pathSpec != "") + let path = <SID>ExpandAlternatePath(pathSpec, a:relPathBase) + if (spath != "") + let spath = spath . ',' + endif + let spath = spath . path + else + break + endif + let m = m + 1 + endwhile + endif + + if (&path != "") + if (spath != "") + let spath = spath . ',' + endif + let spath = spath . &path + endif + + let filepath = findfile(a:fileName, spath, a:count) + return filepath +endfunction + +" Function : EnumerateFilesByExtension (PRIVATE) +" Purpose : enumerates all files by a particular list of alternate extensions. +" Args : path -- path of a file (not including the file) +" baseName -- base name of the file to be expanded +" extension -- extension whose alternates are to be enumerated +" Returns : comma separated list of files with extensions +" Author : Michael Sharpe <feline@irendi.com> +function! EnumerateFilesByExtension(path, baseName, extension) + let enumeration = "" + let extSpec = "" + let v:errmsg = "" + silent! echo g:alternateExtensions_{a:extension} + if (v:errmsg == "") + let extSpec = g:alternateExtensions_{a:extension} + endif + if (extSpec == "") + if (has_key(g:alternateExtensionsDict, a:extension)) + let extSpec = g:alternateExtensionsDict[a:extension] + endif + endif + if (extSpec != "") + let n = 1 + let done = 0 + while (!done) + let ext = <SID>GetNthItemFromList(extSpec, n) + if (ext != "") + if (a:path != "") + let newFilename = a:path . "/" . a:baseName . "." . ext + else + let newFilename = a:baseName . "." . ext + endif + if (enumeration == "") + let enumeration = newFilename + else + let enumeration = enumeration . "," . newFilename + endif + else + let done = 1 + endif + let n = n + 1 + endwhile + endif + return enumeration +endfunction + +" Function : EnumerateFilesByExtensionInPath (PRIVATE) +" Purpose : enumerates all files by expanding the path list and the extension +" list. +" Args : baseName -- base name of the file +" extension -- extension whose alternates are to be enumerated +" pathList -- the list of paths to enumerate +" relPath -- the path of the current file for expansion of relative +" paths in the path list. +" Returns : A comma separated list of paths with extensions +" Author : Michael Sharpe <feline@irendi.com> +function! EnumerateFilesByExtensionInPath(baseName, extension, pathList, relPathBase) + let enumeration = "" + let filepath = "" + let m = 1 + let pathListLen = strlen(a:pathList) + if (pathListLen > 0) + while (1) + let pathSpec = <SID>GetNthItemFromList(a:pathList, m) + if (pathSpec != "") + let path = <SID>ExpandAlternatePath(pathSpec, a:relPathBase) + let pe = EnumerateFilesByExtension(path, a:baseName, a:extension) + if (enumeration == "") + let enumeration = pe + else + let enumeration = enumeration . "," . pe + endif + else + break + endif + let m = m + 1 + endwhile + endif + return enumeration +endfunction + +" Function : DetermineExtension (PRIVATE) +" Purpose : Determines the extension of a filename based on the register +" alternate extension. This allow extension which contain dots to +" be considered. E.g. foo.aspx.cs to foo.aspx where an alternate +" exists for the aspx.cs extension. Note that this will only accept +" extensions which contain less than 5 dots. This is only +" implemented in this manner for simplicity...it is doubtful that +" this will be a restriction in non-contrived situations. +" Args : The path to the file to find the extension in +" Returns : The matched extension if any +" Author : Michael Sharpe (feline@irendi.com) +" History : idea from Tom-Erik Duestad +" Notes : there is some magic occuring here. The exists() function does not +" work well when the curly brace variable has dots in it. And why +" should it, dots are not valid in variable names. But the exists +" function is wierd too. Lets say foo_c does exist. Then +" exists("foo_c.e.f") will be true...even though the variable does +" not exist. However the curly brace variables do work when the +" variable has dots in it. E.g foo_{'c'} is different from +" foo_{'c.d.e'}...and foo_{'c'} is identical to foo_c and +" foo_{'c.d.e'} is identical to foo_c.d.e right? Yes in the current +" implementation of vim. To trick vim to test for existence of such +" variables echo the curly brace variable and look for an error +" message. +function! DetermineExtension(path) + let mods = ":t" + let i = 0 + while i <= s:maxDotsInExtension + let mods = mods . ":e" + let extension = fnamemodify(a:path, mods) + if (has_key(g:alternateExtensionsDict, extension)) + return extension + endif + let v:errmsg = "" + silent! echo g:alternateExtensions_{extension} + if (v:errmsg == "") + return extension + endif + let i = i + 1 + endwhile + return "" +endfunction + +" Function : AlternateFile (PUBLIC) +" Purpose : Opens a new buffer by looking at the extension of the current +" buffer and finding the corresponding file. E.g. foo.c <--> foo.h +" Args : accepts one argument. If present it used the argument as the new +" extension. +" Returns : nothing +" Author : Michael Sharpe <feline@irendi.com> +" History : + When an alternate can't be found in the same directory as the +" source file, a search path will be traversed looking for the +" alternates. +" + Moved some code into a separate function, minor optimization +" + rework to favor files in memory based on complete enumeration of +" all files extensions and paths +function! AlternateFile(splitWindow, ...) + let extension = DetermineExtension(expand("%:p")) + let baseName = substitute(expand("%:t"), "\." . extension . '$', "", "") + let currentPath = expand("%:p:h") + + if (a:0 != 0) + let newFullname = currentPath . "/" . baseName . "." . a:1 + call <SID>FindOrCreateBuffer(newFullname, a:splitWindow, 0) + else + let allfiles = "" + if (extension != "") + let allfiles1 = EnumerateFilesByExtension(currentPath, baseName, extension) + let allfiles2 = EnumerateFilesByExtensionInPath(baseName, extension, g:alternateSearchPath, currentPath) + + if (allfiles1 != "") + if (allfiles2 != "") + let allfiles = allfiles1 . ',' . allfiles2 + else + let allfiles = allfiles1 + endif + else + let allfiles = allfiles2 + endif + endif + + if (allfiles != "") + let bestFile = "" + let bestScore = 0 + let score = 0 + let n = 1 + + let onefile = <SID>GetNthItemFromList(allfiles, n) + let bestFile = onefile + while (onefile != "" && score < 2) + let score = <SID>BufferOrFileExists(onefile) + if (score > bestScore) + let bestScore = score + let bestFile = onefile + endif + let n = n + 1 + let onefile = <SID>GetNthItemFromList(allfiles, n) + endwhile + + if (bestScore == 0 && g:alternateNoDefaultAlternate == 1) + echo "No existing alternate available" + else + call <SID>FindOrCreateBuffer(bestFile, a:splitWindow, 1) + let b:AlternateAllFiles = allfiles + endif + else + echo "No alternate file/buffer available" + endif + endif +endfunction + +" Function : AlternateOpenFileUnderCursor (PUBLIC) +" Purpose : Opens file under the cursor +" Args : splitWindow -- indicates how to open the file +" Returns : Nothing +" Author : Michael Sharpe (feline@irendi.com) www.irendi.com +function! AlternateOpenFileUnderCursor(splitWindow,...) + let cursorFile = (a:0 > 0) ? a:1 : expand("<cfile>") + let currentPath = expand("%:p:h") + let openCount = 1 + + let fileName = <SID>FindFileInSearchPathEx(cursorFile, g:alternateSearchPath, currentPath, openCount) + if (fileName != "") + call <SID>FindOrCreateBuffer(fileName, a:splitWindow, 1) + let b:openCount = openCount + let b:cursorFile = cursorFile + let b:currentPath = currentPath + else + echo "Can't find file" + endif +endfunction + +" Function : AlternateOpenNextFile (PUBLIC) +" Purpose : Opens the next file corresponding to the search which found the +" current file +" Args : bang -- indicates what to do if the current file has not been +" saved +" Returns : nothing +" Author : Michael Sharpe (feline@irendi.com) www.irendi.com +function! AlternateOpenNextFile(bang) + let cursorFile = "" + if (exists("b:cursorFile")) + let cursorFile = b:cursorFile + endif + + let currentPath = "" + if (exists("b:currentPath")) + let currentPath = b:currentPath + endif + + let openCount = 0 + if (exists("b:openCount")) + let openCount = b:openCount + 1 + endif + + if (cursorFile != "" && currentPath != "" && openCount != 0) + let fileName = <SID>FindFileInSearchPathEx(cursorFile, g:alternateSearchPath, currentPath, openCount) + if (fileName != "") + call <SID>FindOrCreateBuffer(fileName, "n".a:bang, 0) + let b:openCount = openCount + let b:cursorFile = cursorFile + let b:currentPath = currentPath + else + let fileName = <SID>FindFileInSearchPathEx(cursorFile, g:alternateSearchPath, currentPath, 1) + if (fileName != "") + call <SID>FindOrCreateBuffer(fileName, "n".a:bang, 0) + let b:openCount = 1 + let b:cursorFile = cursorFile + let b:currentPath = currentPath + else + echo "Can't find next file" + endif + endif + endif +endfunction + +comm! -nargs=? -bang IH call AlternateOpenFileUnderCursor("n<bang>", <f-args>) +comm! -nargs=? -bang IHS call AlternateOpenFileUnderCursor("h<bang>", <f-args>) +comm! -nargs=? -bang IHV call AlternateOpenFileUnderCursor("v<bang>", <f-args>) +comm! -nargs=? -bang IHT call AlternateOpenFileUnderCursor("t<bang>", <f-args>) +comm! -nargs=? -bang IHN call AlternateOpenNextFile("<bang>") +"imap <Leader>ih <ESC>:IHS<CR> +nmap <Leader>ih :IHS<CR> +"imap <Leader>is <ESC>:IHS<CR>:A<CR> +nmap <Leader>is :IHS<CR>:A<CR> +"imap <Leader>ihn <ESC>:IHN<CR> +nmap <Leader>ihn :IHN<CR> + +"function! <SID>PrintList(theList) +" let n = 1 +" let oneFile = <SID>GetNthItemFromList(a:theList, n) +" while (oneFile != "") +" let n = n + 1 +" let oneFile = <SID>GetNthItemFromList(a:theList, n) +" endwhile +"endfunction + +" Function : NextAlternate (PUBLIC) +" Purpose : Used to cycle through any other alternate file which existed on +" the search path. +" Args : bang (IN) - used to implement the AN vs AN! functionality +" Returns : nothing +" Author : Michael Sharpe <feline@irendi.com> +function! NextAlternate(bang) + if (exists('b:AlternateAllFiles')) + let currentFile = expand("%") + let n = 1 + let onefile = <SID>GetNthItemFromList(b:AlternateAllFiles, n) + while (onefile != "" && !<SID>EqualFilePaths(fnamemodify(onefile,":p"), fnamemodify(currentFile,":p"))) + let n = n + 1 + let onefile = <SID>GetNthItemFromList(b:AlternateAllFiles, n) + endwhile + + if (onefile != "") + let stop = n + let n = n + 1 + let foundAlternate = 0 + let nextAlternate = "" + while (n != stop) + let nextAlternate = <SID>GetNthItemFromList(b:AlternateAllFiles, n) + if (nextAlternate == "") + let n = 1 + continue + endif + let n = n + 1 + if (<SID>EqualFilePaths(fnamemodify(nextAlternate, ":p"), fnamemodify(currentFile, ":p"))) + continue + endif + if (filereadable(nextAlternate)) + " on cygwin filereadable("foo.H") returns true if "foo.h" exists + if (has("unix") && $WINDIR != "" && fnamemodify(nextAlternate, ":p") ==? fnamemodify(currentFile, ":p")) + continue + endif + let foundAlternate = 1 + break + endif + endwhile + if (foundAlternate == 1) + let s:AlternateAllFiles = b:AlternateAllFiles + "silent! execute ":e".a:bang." " . nextAlternate + call <SID>FindOrCreateBuffer(nextAlternate, "n".a:bang, 0) + let b:AlternateAllFiles = s:AlternateAllFiles + else + echo "Only this alternate file exists" + endif + else + echo "Could not find current file in alternates list" + endif + else + echo "No other alternate files exist" + endif +endfunction + +comm! -nargs=? -bang A call AlternateFile("n<bang>", <f-args>) +comm! -nargs=? -bang AS call AlternateFile("h<bang>", <f-args>) +comm! -nargs=? -bang AV call AlternateFile("v<bang>", <f-args>) +comm! -nargs=? -bang AT call AlternateFile("t<bang>", <f-args>) +comm! -nargs=? -bang AN call NextAlternate("<bang>") + +" Function : BufferOrFileExists (PRIVATE) +" Purpose : determines if a buffer or a readable file exists +" Args : fileName (IN) - name of the file to check +" Returns : 2 if it exists in memory, 1 if it exists, 0 otherwise +" Author : Michael Sharpe <feline@irendi.com> +" History : Updated code to handle buffernames using just the +" filename and not the path. +function! <SID>BufferOrFileExists(fileName) + let result = 0 + + let lastBuffer = bufnr("$") + let i = 1 + while i <= lastBuffer + if <SID>EqualFilePaths(expand("#".i.":p"), a:fileName) + let result = 2 + break + endif + let i = i + 1 + endwhile + + if (!result) + let bufName = fnamemodify(a:fileName,":t") + let memBufName = bufname(bufName) + if (memBufName != "") + let memBufBasename = fnamemodify(memBufName, ":t") + if (bufName == memBufBasename) + let result = 2 + endif + endif + + if (!result) + let result = bufexists(bufName) || bufexists(a:fileName) || filereadable(a:fileName) + endif + endif + + if (!result) + let result = filereadable(a:fileName) + endif + return result +endfunction + +" Function : FindOrCreateBuffer (PRIVATE) +" Purpose : searches the buffer list (:ls) for the specified filename. If +" found, checks the window list for the buffer. If the buffer is in +" an already open window, it switches to the window. If the buffer +" was not in a window, it switches to that buffer. If the buffer did +" not exist, it creates it. +" Args : filename (IN) -- the name of the file +" doSplit (IN) -- indicates whether the window should be split +" ("v", "h", "n", "v!", "h!", "n!", "t", "t!") +" findSimilar (IN) -- indicate weather existing buffers should be +" prefered +" Returns : nothing +" Author : Michael Sharpe <feline@irendi.com> +" History : + bufname() was not working very well with the possibly strange +" paths that can abound with the search path so updated this +" slightly. -- Bindu +" + updated window switching code to make it more efficient -- Bindu +" Allow ! to be applied to buffer/split/editing commands for more +" vim/vi like consistency +" + implemented fix from Matt Perry +function! <SID>FindOrCreateBuffer(fileName, doSplit, findSimilar) + " Check to see if the buffer is already open before re-opening it. + let FILENAME = escape(a:fileName, ' ') + let bufNr = -1 + let lastBuffer = bufnr("$") + let i = 1 + if (a:findSimilar) + while i <= lastBuffer + if <SID>EqualFilePaths(expand("#".i.":p"), a:fileName) + let bufNr = i + break + endif + let i = i + 1 + endwhile + + if (bufNr == -1) + let bufName = bufname(a:fileName) + let bufFilename = fnamemodify(a:fileName,":t") + + if (bufName == "") + let bufName = bufname(bufFilename) + endif + + if (bufName != "") + let tail = fnamemodify(bufName, ":t") + if (tail != bufFilename) + let bufName = "" + endif + endif + if (bufName != "") + let bufNr = bufnr(bufName) + let FILENAME = bufName + endif + endif + endif + + if (g:alternateRelativeFiles == 1) + let FILENAME = fnamemodify(FILENAME, ":p:.") + endif + + let splitType = a:doSplit[0] + let bang = a:doSplit[1] + if (bufNr == -1) + " Buffer did not exist....create it + let v:errmsg="" + if (splitType == "h") + silent! execute ":split".bang." " . FILENAME + elseif (splitType == "v") + silent! execute ":vsplit".bang." " . FILENAME + elseif (splitType == "t") + silent! execute ":tab split".bang." " . FILENAME + else + silent! execute ":e".bang." " . FILENAME + endif + if (v:errmsg != "") + echo v:errmsg + endif + else + + " Find the correct tab corresponding to the existing buffer + let tabNr = -1 + " iterate tab pages + for i in range(tabpagenr('$')) + " get the list of buffers in the tab + let tabList = tabpagebuflist(i + 1) + let idx = 0 + " iterate each buffer in the list + while idx < len(tabList) + " if it matches the buffer we are looking for... + if (tabList[idx] == bufNr) + " ... save the number + let tabNr = i + 1 + break + endif + let idx = idx + 1 + endwhile + if (tabNr != -1) + break + endif + endfor + " switch the the tab containing the buffer + if (tabNr != -1) + execute "tabn ".tabNr + endif + + " Buffer was already open......check to see if it is in a window + let bufWindow = bufwinnr(bufNr) + if (bufWindow == -1) + " Buffer was not in a window so open one + let v:errmsg="" + if (splitType == "h") + silent! execute ":sbuffer".bang." " . FILENAME + elseif (splitType == "v") + silent! execute ":vert sbuffer " . FILENAME + elseif (splitType == "t") + silent! execute ":tab sbuffer " . FILENAME + else + silent! execute ":buffer".bang." " . FILENAME + endif + if (v:errmsg != "") + echo v:errmsg + endif + else + " Buffer is already in a window so switch to the window + execute bufWindow."wincmd w" + if (bufWindow != winnr()) + " something wierd happened...open the buffer + let v:errmsg="" + if (splitType == "h") + silent! execute ":split".bang." " . FILENAME + elseif (splitType == "v") + silent! execute ":vsplit".bang." " . FILENAME + elseif (splitType == "t") + silent! execute ":tab split".bang." " . FILENAME + else + silent! execute ":e".bang." " . FILENAME + endif + if (v:errmsg != "") + echo v:errmsg + endif + endif + endif + endif +endfunction + +" Function : EqualFilePaths (PRIVATE) +" Purpose : Compares two paths. Do simple string comparison anywhere but on +" Windows. On Windows take into account that file paths could differ +" in usage of separators and the fact that case does not matter. +" "c:\WINDOWS" is the same path as "c:/windows". has("win32unix") Vim +" version does not count as one having Windows path rules. +" Args : path1 (IN) -- first path +" path2 (IN) -- second path +" Returns : 1 if path1 is equal to path2, 0 otherwise. +" Author : Ilya Bobir <ilya@po4ta.com> +function! <SID>EqualFilePaths(path1, path2) + if has("win16") || has("win32") || has("win64") || has("win95") + return substitute(a:path1, "\/", "\\", "g") ==? substitute(a:path2, "\/", "\\", "g") + else + return a:path1 == a:path2 + endif +endfunction diff --git a/.vim/plugin/autotag.vim b/.vim/plugin/autotag.vim new file mode 100644 index 0000000..8bb2d0e --- /dev/null +++ b/.vim/plugin/autotag.vim @@ -0,0 +1,150 @@ +" This file supplies automatic tag regeneration when saving files +" There's a problem with ctags when run with -a (append) +" ctags doesn't remove entries for the supplied source file that no longer exist +" so this script (implemented in python) finds a tags file for the file vim has +" just saved, removes all entries for that source file and *then* runs ctags -a + +if has("python") + +python << EEOOFF +import os +import string +import os.path +import fileinput +import sys +import vim + +def echo(str): + str=str.replace('\\', '\\\\') + str=str.replace('"', "'") + vim.command("redraw | echo \"%s\"" % str) + +class AutoTag: + def __init__(self, excludesuffix="", ctags_cmd="ctags", verbose=0): + self.tags = {} + self.excludesuffix = [ "." + s for s in excludesuffix.split(".") ] + verbose = long(verbose) + if verbose > 0: + self.verbose = verbose + else: + self.verbose = 0 + self.sep_used_by_ctags = '/' + self.cwd = os.getcwd() + self.ctags_cmd = ctags_cmd + self.count = 0 + + def findTagFile(self, source): + ( drive, file ) = os.path.splitdrive(source) + while file: + file = os.path.dirname(file) + tagsFile = os.path.join(drive, file, "tags") + self.diag(2, "does %s exist?", tagsFile) + if os.path.isfile(tagsFile): + self.diag(2, "Found tags file %s", tagsFile) + return tagsFile + elif not file or file == os.sep or file == "//" or file == "\\\\": + self.diag(2, "exhausted search for tag file for %s", source) + return None + self.diag(2, "Nope. :-| %s does NOT exist", tagsFile) + return None + + def addSource(self, source): + if not source: + return + if os.path.splitext(source)[1] in self.excludesuffix: + self.diag(1, "Ignoring excluded file " + source) + return + tagsFile = self.findTagFile(source) + if tagsFile: + self.diag(2, "if tagsFile:") + relativeSource = source[len(os.path.dirname(tagsFile)):] + self.diag(2, "relativeSource = source[len(os.path.dirname(tagsFile)):]") + if relativeSource[0] == os.sep: + self.diag(2, "if relativeSource[0] == os.sep:") + relativeSource = relativeSource[1:] + self.diag(2, "relativeSource = relativeSource[1:]") + if os.sep != self.sep_used_by_ctags: + self.diag(2, "if os.sep != self.sep_used_by_ctags:") + relativeSource = string.replace(relativeSource, os.sep, self.sep_used_by_ctags) + self.diag(2, "relativeSource = string.replace(relativeSource, os.sep, self.sep_used_by_ctags)") + if self.tags.has_key(tagsFile): + self.diag(2, "if self.tags.has_key(tagsFile):") + self.tags[tagsFile].append(relativeSource) + self.diag(2, "self.tags[tagsFile].append(relativeSource)") + else: + self.diag(2, "else:") + self.tags[tagsFile] = [ relativeSource ] + self.diag(2, "self.tags[tagsFile] = [ relativeSource ]") + + def stripTags(self, tagsFile, sources): + self.diag(1, "Removing tags for %s from tags file %s", (sources, tagsFile)) + backup = ".SAFE" + for line in fileinput.input(files=tagsFile, inplace=True, backup=backup): + if line[-1:] == '\n': + line = line[:-1] + if line[-1:] == '\r': + line = line[:-1] + if line[0] == "!": + print line + else: + fields = string.split(line, "\t") + if len(fields) > 3: + found = False + for source in sources: + if fields[1] == source: + found = True + break + if not found: + print line + else: + print line + os.unlink(tagsFile + backup) + + def rebuildTagFiles(self): + for tagsFile in self.tags.keys(): + tagsDir = os.path.dirname(tagsFile) + sources = self.tags[tagsFile] + os.chdir(tagsDir) + self.stripTags(tagsFile, sources) + cmd = "%s -a " % self.ctags_cmd + for source in sources: + if os.path.isfile(source): + cmd += " '%s'" % source + self.diag(1, "%s: %s", (tagsDir, cmd)) + (ch_in, ch_out) = os.popen2(cmd) + for line in ch_out: + pass + os.chdir(self.cwd) + + def diag(self, level, msg, args = None): + if msg and args: + msg = msg % args + if level <= self.verbose: + echo(msg) +EEOOFF + +function! AutoTag() +python << EEOOFF +at = AutoTag(vim.eval("g:autotagExcludeSuffixes"), vim.eval("g:autotagCtagsCmd"), long(vim.eval("g:autotagVerbosityLevel"))) +at.addSource(vim.eval("expand(\"%:p\")")) +at.rebuildTagFiles() +EEOOFF +endfunction + +if !exists("g:autotagVerbosityLevel") + let g:autotagVerbosityLevel=0 +endif +if !exists("g:autotagExcludeSuffixes") + let g:autotagExcludeSuffixes="tml.xml" +endif +if !exists("g:autotagCtagsCmd") + let g:autotagCtagsCmd="ctags" +endif +if !exists("g:autotag_autocmd_set") + let g:autotag_autocmd_set=1 + autocmd BufWritePost,FileWritePost * call AutoTag () +endif + +endif " has("python") + +" vim:sw=3:ts=3 diff --git a/.vim/plugin/scmCloseParens.vim b/.vim/plugin/scmCloseParens.vim new file mode 100644 index 0000000..8aca684 --- /dev/null +++ b/.vim/plugin/scmCloseParens.vim @@ -0,0 +1,58 @@ +" Description: Simple script (hack ?) that closes opened parens +" Author: Adrien Pierard <pierarda#iro.umontreal.ca> +" Modified: 04/05/07 +" Version: 0.1 +" +" Usage: I mapped it to <Leader>p +" So, just go to normal mode, and type the shortcut, or :call +" RunScmCloseParens() yourself + + +let b:msg = "" +let b:bcpt = 0 + +function! SetCursorWhereItIsGoodToPutItEh() + let line = substitute(getline("."), "\\s\\+$", "", "") + call setline(line("."),line) + norm $ + let charUnderCursor = strpart(line("."), col(".") - 1, 1) + norm a) + call CountAsMuchAsPossible() +endfunction + +function! CountAsMuchAsPossible() + let cpt = 0 + while (CanWeGoOn() > 0) + let cpt = cpt + 1 + call OhGetBackAndSetAnotherOne() + endwhile + let line = substitute(getline("."), ")$", "", "") + call setline(line("."),line) + let b:cpt = cpt +endfunction + +function! CanWeGoOn() + return (searchpair('(', '', ')' , 'b' )) +endfunction + +function! OhGetBackAndSetAnotherOne() + call searchpair('(', '', ')') + norm a) + +endfunction + +function! InitScmCloseParens() + if ! exists("g:ScmCloseParens") + let g:ScmCloseParens = "Scheme on you !" + execute 'nmap <Leader>p :call RunScmCloseParens()<Cr>' + endif +endfunction + +fun! RunScmCloseParens() + let b:bcpt = 0 + call SetCursorWhereItIsGoodToPutItEh() + norm :echo b:bcpt +endfunction + +call InitScmCloseParens() + diff --git a/.vim/plugin/surround.vim b/.vim/plugin/surround.vim new file mode 100644 index 0000000..ec00098 --- /dev/null +++ b/.vim/plugin/surround.vim @@ -0,0 +1,727 @@ +" surround.vim - Surroundings +" Maintainer: Tim Pope <vimNOSPAM@tpope.info> +" GetLatestVimScripts: 1697 1 :AutoInstall: surround.vim +" $Id: surround.vim,v 1.16 2006/11/06 05:53:09 tpope Exp $ +" Help is below; it may be read here. Alternatively, after the plugin is +" installed and running, :call SurroundHelp() to install a proper help file. + +" *surround.txt* Plugin for deleting, changing, and adding "surroundings" +" +" Author: Tim Pope <vimNOSPAM@tpope.info> *surround-author* +" License: Same terms as Vim itself (see |license|) +" +" This plugin is only available if 'compatible' is not set. +" +" Introduction: *surround* +" +" This plugin is a tool for dealing with pairs of "surroundings." Examples +" of surroundings include parentheses, quotes, and HTML tags. They are +" closely related to what Vim refers to as |text-objects|. Provided +" are mappings to allow for removing, changing, and adding surroundings. +" +" Details follow on the exact semantics, but first, consider the following +" examples. An asterisk (*) is used to denote the cursor position. +" +" Old text Command New text ~ +" "Hello *world!" ds" Hello world! +" [123+4*56]/2 cs]) (123+456)/2 +" "Look ma, I'm *HTML!" cs"<q> <q>Look ma, I'm HTML!</q> +" if *x>3 { ysW( if ( x>3 ) { +" my $str = *whee!; vlllls' my $str = 'whee!'; +" +" While a few features of this plugin will work in older versions of Vim, +" Vim 7 is recommended for full functionality. +" +" Mappings: *surround-mappings* +" +" Delete surroundings is *ds*. The next character given determines the target +" to delete. The exact nature of the target are explained in +" |surround-targets| but essentially it is the last character of a +" |text-object|. This mapping deletes the difference between the "inner" +" object and "an" object. This is easiest to understand with some examples: +" +" Old text Command New text ~ +" "Hello *world!" ds" Hello world! +" (123+4*56)/2 ds) 123+456/2 +" <div>Yo!*</div> dst Yo! +" +" Change surroundings is *cs*. It takes two arguments, a target like with +" |ds|, and a replacement. Details about the second argument can be found +" below in |surround-replacements|. Once again, examples are in order. +" +" Old text Command New text ~ +" "Hello *world!" cs"' 'Hello world!' +" "Hello *world!" cs"<q> <q>Hello world!</q> +" (123+4*56)/2 cs)] [123+456]/2 +" (123+4*56)/2 cs)[ [ 123+456 ]/2 +" <div>Yo!*</div> cst<p> <p>Yo!</p> +" +" *ys* takes an valid Vim motion or text object as the first object, and wraps +" it using the second argument as with |cs|. (Unfortunately there's no good +" mnemonic for "ys"). +" +" Old text Command New text ~ +" Hello w*orld! ysiw) Hello (world)! +" +" As a special case, *yss* operates on the current line, ignoring leading +" whitespace. +" +" Old text Command New text ~ +" Hello w*orld! yssB {Hello world!} +" +" There is also *yS* and *ySS* which indent the surrounded text and place it +" on a line of its own. +" +" In visual mode, a simple "s" with an argument wraps the selection. This is +" referred to as the *vs* mapping, although ordinarily there will be +" additional keystrokes between the v and s. In linewise visual mode, the +" surroundings are placed on separate lines. In blockwise visual mode, each +" line is surrounded. +" +" An "S" in visual mode (*vS*) behaves similarly but always places the +" surroundings on separate lines. Additionally, the surrounded text is +" indented. In blockwise visual mode, using "S" instead of "s" instead skips +" trailing whitespace. +" +" Note that "s" and "S" already have valid meaning in visual mode, but it is +" identical to "c". If you have muscle memory for "s" and would like to use a +" different key, add your own mapping and the existing one will be disabled. +" > +" vmap <Leader>s <Plug>Vsurround +" vmap <Leader>S <Plug>VSurround +" < +" Finally, there is an experimental insert mode mapping on <C-S>. Beware that +" this won't work on terminals with flow control (if you accidentally freeze +" your terminal, use <C-Q> to unfreeze it). The mapping inserts the specified +" surroundings and puts the cursor between them. If, immediately after <C-S> +" and before the replacement, a second <C-S> or carriage return is pressed, +" the prefix, cursor, and suffix will be placed on three separate lines. If +" this is a common use case you can add a mapping for it as well. +" > +" imap <C-Z> <Plug>Isurround<CR> +" < +" Targets: *surround-targets* +" +" The |ds| and |cs| commands both take a target as their first argument. The +" possible targets are based closely on the |text-objects| provided by Vim. +" In order for a target to work, the corresponding text object must be +" supported in the version of Vim used (Vim 7 adds several text objects, and +" thus is highly recommended). All targets are currently just one character. +" +" Eight punctuation marks, (, ), {, }, [, ], <, and >, represent themselves +" and their counterpart. If the opening mark is used, contained whitespace is +" also trimmed. The targets b, B, r, and a are aliases for ), }, ], and > +" (the first two mirror Vim; the second two are completely arbitrary and +" subject to change). +" +" Three quote marks, ', ", `, represent themselves, in pairs. They are only +" searched for on the current line. +" +" A t is a pair of HTML or XML tags. See |tag-blocks| for details. Remember +" that you can specify a numerical argument if you want to get to a tag other +" than the innermost one. +" +" The letters w, W, and s correspond to a |word|, a |WORD|, and a |sentence|, +" respectively. These are special in that they have nothing do delete, and +" used with |ds| they are a no-op. With |cs|, one could consider them a +" slight shortcut for ysi (cswb == ysiwb, more or less). +" +" A p represents a |paragraph|. This behaves similarly to w, W, and s above; +" however, newlines are sometimes added and/or removed. +" +" Replacements: *surround-replacements* +" +" A replacement argument is a single character, and is required by |cs|, |ys|, +" and |vs|. Undefined replacement characters (with the exception of +" alphabetic characters) default to placing themselves at the beginning and +" end of the destination, which can be useful for characters like / and |. +" +" If either ), }, ], or > is used, the text is wrapped in the appropriate +" pair of characters. Similar behavior can be found with (, {, and [ (but not +" <), which append an additional space to the inside. Like with the targets +" above, b, B, r, and a are aliases for ), }, ], and >. +" +" If t or < is used, Vim prompts for an HTML/XML tag to insert. You may +" specify attributes here and they will be stripped from the closing tag. +" End your input by pressing <CR> or >. As an experimental feature, if , or +" <C-T> is used, the tags will appear on lines by themselves. +" +" An experimental replacement of a LaTeX environment is provided on \ and l. +" The name of the environment and any arguments will be input from a prompt. +" The following shows the resulting environment from csp\tabular}{lc<CR> +" > +" \begin{tabular}{lc} +" \end{tabular} +" < +" Customizing: *surround-customizing* +" +" The following adds a potential replacement on "-" (ASCII 45) in PHP files. +" (To determine the ASCII code to use, :echo char2nr("-")). The carriage +" return will be replaced by the original text. +" > +" autocmd FileType php let b:surround_45 = "<?php \r ?>" +" < +" This can be used in a PHP file as in the following example. +" +" Old text Command New text ~ +" print "Hello *world!" yss- <?php print "Hello world!" ?> +" +" Additionally, one can use a global variable for globally available +" replacements. +" > +" let g:surround_45 = "<% \r %>" +" let g:surround_61 = "<%= \r %>" +" < +" Issues: *surround-issues* +" +" Vim could potentially get confused when deleting/changing occurs at the very +" end of the line. Please report any repeatable instances of this. +" +" Do we need to use |inputsave()|/|inputrestore()| with the tag replacement? +" +" Customization isn't very flexible. Need a system that allows for prompting, +" like with HTML tags and LaTeX environments. +" +" Indenting is handled haphazardly. Need to decide the most appropriate +" behavior and implement it. Right now one can do :let b:surround_indent = 1 +" (or the global equivalent) to enable automatic re-indenting by Vim via |=|; +" should this be the default? +" +" It would be nice if |.| would work to repeat an operation. + +" ============================================================================ + +" Exit quickly when: +" - this plugin was already loaded (or disabled) +" - when 'compatible' is set +if (exists("g:loaded_surround") && g:loaded_surround) || &cp + finish +endif +let g:loaded_surround = 1 + +let s:cpo_save = &cpo +set cpo&vim + +function! SurroundHelp() " {{{1 + if !isdirectory(s:dir."/doc/") && exists("*mkdir") + call mkdir(s:dir."/doc/") + endif + let old_hidden = &hidden + let old_cpo = &cpo + set hidden + set cpo&vim + exe "split ".fnamemodify(s:dir."/doc/surround.txt",":~") + setlocal noai modifiable noreadonly + %d_ + exe "0r ".fnamemodify(s:file,":~") + norm "_d}}"_dG + a + vim:tw=78:ts=8:ft=help:norl: +. + 1d_ + %s/^" \=// + silent! %s/^\(\u\l\+\):\(\s\+\*\)/\U\1 \2/ + setlocal noreadonly + write + bwipe! + let &hidden = old_hidden + let &cpo = old_cpo + exe "helptags ".fnamemodify(s:dir."/doc",":~") + help surround +endfunction +let s:file = expand("<sfile>:p") +let s:dir = expand("<sfile>:p:h:h") " }}}1 + +" Input functions {{{1 + +function! s:getchar() + let c = getchar() + if c =~ '^\d\+$' + let c = nr2char(c) + endif + return c +endfunction + +function! s:inputtarget() + let c = s:getchar() + while c =~ '^\d\+$' + let c = c . s:getchar() + endwhile + if c == " " + let c = c . s:getchar() + endif + if c =~ "\<Esc>\|\<C-C>\|\0" + return "" + else + return c + endif +endfunction + +function! s:inputreplacement() + "echo '-- SURROUND --' + let c = s:getchar() + if c == " " + let c = c . s:getchar() + endif + if c =~ "\<Esc>" || c =~ "\<C-C>" + return "" + else + return c + endif +endfunction + +function! s:beep() + exe "norm! \<Esc>" + return "" +endfunction + +function! s:redraw() + redraw + return "" +endfunction + +" }}}1 + +" Wrapping functions {{{1 + +function! s:extractbefore(str) + if a:str =~ '\r' + return matchstr(a:str,'.*\ze\r') + else + return matchstr(a:str,'.*\ze\n') + endif +endfunction + +function! s:extractafter(str) + if a:str =~ '\r' + return matchstr(a:str,'\r\zs.*') + else + return matchstr(a:str,'\n\zs.*') + endif +endfunction + +function! s:repeat(str,count) + let cnt = a:count + let str = "" + while cnt > 0 + let str = str . a:str + let cnt = cnt - 1 + endwhile + return str +endfunction + +function! s:fixindent(str,spc) + let str = substitute(a:str,'\t',s:repeat(' ',&sw),'g') + let spc = substitute(a:spc,'\t',s:repeat(' ',&sw),'g') + let str = substitute(str,'\(\n\|\%^\).\@=','\1'.spc,'g') + if ! &et + let str = substitute(str,'\s\{'.&ts.'\}',"\t",'g') + endif + return str +endfunction + +function! s:wrap(string,char,type,...) + let keeper = a:string + let newchar = a:char + let type = a:type + let linemode = type ==# 'V' ? 1 : 0 + let special = a:0 ? a:1 : 0 + let before = "" + let after = "" + if type == "V" + let initspaces = matchstr(keeper,'\%^\s*') + else + let initspaces = matchstr(getline('.'),'\%^\s*') + endif + " Duplicate b's are just placeholders (removed) + let pairs = "b()B{}r[]a<>" + let extraspace = "" + if newchar =~ '^ ' + let newchar = strpart(newchar,1) + let extraspace = ' ' + endif + let idx = stridx(pairs,newchar) + if exists("b:surround_".char2nr(newchar)) + let before = s:extractbefore(b:surround_{char2nr(newchar)}) + let after = s:extractafter(b:surround_{char2nr(newchar)}) + elseif exists("g:surround_".char2nr(newchar)) + let before = s:extractbefore(g:surround_{char2nr(newchar)}) + let after = s:extractafter(g:surround_{char2nr(newchar)}) + elseif newchar ==# "p" + let before = "\n" + let after = "\n\n" + elseif newchar =~# "[tT\<C-T><,]" + "let dounmapr = 0 + let dounmapb = 0 + "if !mapcheck("<CR>","c") + "let dounmapr = 1 + "cnoremap <CR> ><CR> + "endif + if !mapcheck(">","c") + let dounmapb= 1 + cnoremap > ><CR> + endif + let default = "" + if newchar ==# "T" + if !exists("s:lastdel") + let s:lastdel = "" + endif + let default = matchstr(s:lastdel,'<\zs.\{-\}\ze>') + endif + let tag = input("<",default) + echo "<".substitute(tag,'>*$','>','') + "if dounmapr + "silent! cunmap <CR> + "endif + if dounmapb + silent! cunmap > + endif + if tag != "" + let tag = substitute(tag,'>*$','','') + let before = "<".tag.">" + let after = "</".substitute(tag," .*",'','').">" + if newchar == "\<C-T>" || newchar == "," + if type ==# "v" || type ==# "V" + let before = before . "\n\t" + endif + if type ==# "v" + let after = "\n". after + endif + endif + endif + elseif newchar ==# 'l' || newchar == '\' + " LaTeX + let env = input('\begin{') + let env = '{' . env + let env = env . s:closematch(env) + echo '\begin'.env + if env != "" + let before = '\begin'.env + let after = '\end'.matchstr(env,'[^}]*').'}' + endif + "if type ==# 'v' || type ==# 'V' + "let before = before ."\n\t" + "endif + "if type ==# 'v' + "let after = "\n".initspaces.after + "endif + elseif newchar ==# 'f' || newchar ==# 'F' + let fnc = input('function: ') + if fnc != "" + let before = substitute(fnc,'($','','').'(' + let after = ')' + if newchar ==# 'F' + let before = before . ' ' + let after = ' ' . after + endif + endif + elseif idx >= 0 + let spc = (idx % 3) == 1 ? " " : "" + let idx = idx / 3 * 3 + let before = strpart(pairs,idx+1,1) . spc + let after = spc . strpart(pairs,idx+2,1) + elseif newchar !~ '\a' + let before = newchar + let after = newchar + else + let before = '' + let after = '' + endif + "let before = substitute(before,'\n','\n'.initspaces,'g') + let after = substitute(after ,'\n','\n'.initspaces,'g') + "let after = substitute(after,"\n\\s*\<C-U>\\s*",'\n','g') + if type ==# 'V' || (special && type ==# "v") + let before = substitute(before,' \+$','','') + let after = substitute(after ,'^ \+','','') + if after !~ '^\n' + let after = initspaces.after + endif + if keeper !~ '\n$' && after !~ '^\n' + let keeper = keeper . "\n" + endif + if before !~ '\n\s*$' + let before = before . "\n" + if special + let before = before . "\t" + endif + endif + endif + if type ==# 'V' + let before = initspaces.before + endif + if before =~ '\n\s*\%$' + if type ==# 'v' + let keeper = initspaces.keeper + endif + let padding = matchstr(before,'\n\zs\s\+\%$') + let before = substitute(before,'\n\s\+\%$','\n','') + let keeper = s:fixindent(keeper,padding) + endif + if type ==# 'V' + let keeper = before.keeper.after + elseif type =~ "^\<C-V>" + " Really we should be iterating over the buffer + let repl = substitute(before,'[\\~]','\\&','g').'\1'.substitute(after,'[\\~]','\\&','g') + let repl = substitute(repl,'\n',' ','g') + let keeper = substitute(keeper."\n",'\(.\{-\}\)\('.(special ? '\s\{-\}' : '').'\n\)',repl.'\n','g') + let keeper = substitute(keeper,'\n\%$','','') + else + let keeper = before.extraspace.keeper.extraspace.after + endif + return keeper +endfunction + +function! s:wrapreg(reg,char,...) + let orig = getreg(a:reg) + let type = substitute(getregtype(a:reg),'\d\+$','','') + let special = a:0 ? a:1 : 0 + let new = s:wrap(orig,a:char,type,special) + call setreg(a:reg,new,type) +endfunction +" }}}1 + +function! s:insert(...) " {{{1 + " Optional argument causes the result to appear on 3 lines, not 1 + "call inputsave() + let linemode = a:0 ? a:1 : 0 + let char = s:inputreplacement() + while char == "\<CR>" || char == "\<C-S>" + " TODO: use total count for additional blank lines + let linemode = linemode + 1 + let char = s:inputreplacement() + endwhile + "call inputrestore() + if char == "" + return "" + endif + "call inputsave() + let reg_save = @@ + call setreg('"',"\r",'v') + call s:wrapreg('"',char,linemode) + "if linemode + "call setreg('"',substitute(getreg('"'),'^\s\+','',''),'c') + "endif + if col('.') > col('$') + norm! p`] + else + norm! P`] + endif + call search('\r','bW') + let @@ = reg_save + return "\<Del>" +endfunction " }}}1 + +function! s:reindent() " {{{1 + if (exists("b:surround_indent") || exists("g:surround_indent")) + silent norm! '[='] + endif +endfunction " }}}1 + +function! s:dosurround(...) " {{{1 + let scount = v:count1 + let char = (a:0 ? a:1 : s:inputtarget()) + let spc = "" + if char =~ '^\d\+' + let scount = scount * matchstr(char,'^\d\+') + let char = substitute(char,'^\d\+','','') + endif + if char =~ '^ ' + let char = strpart(char,1) + let spc = 1 + endif + if char == 'a' + let char = '>' + endif + if char == 'r' + let char = ']' + endif + let newchar = "" + if a:0 > 1 + let newchar = a:2 + if newchar == "\<Esc>" || newchar == "\<C-C>" || newchar == "" + return s:beep() + endif + endif + let append = "" + let original = getreg('"') + let otype = getregtype('"') + call setreg('"',"") + exe "norm d".(scount==1 ? "": scount)."i".char + "exe "norm vi".char."d" + let keeper = getreg('"') + let okeeper = keeper " for reindent below + if keeper == "" + call setreg('"',original,otype) + return "" + endif + let oldline = getline('.') + let oldlnum = line('.') + if char ==# "p" + "let append = matchstr(keeper,'\n*\%$') + "let keeper = substitute(keeper,'\n*\%$','','') + call setreg('"','','V') + elseif char ==# "s" || char ==# "w" || char ==# "W" + " Do nothing + call setreg('"','') + elseif char =~ "[\"'`]" + exe "norm! i \<Esc>d2i".char + call setreg('"',substitute(getreg('"'),' ','','')) + else + exe "norm! da".char + endif + let removed = getreg('"') + let rem2 = substitute(removed,'\n.*','','') + let oldhead = strpart(oldline,0,strlen(oldline)-strlen(rem2)) + let oldtail = strpart(oldline, strlen(oldline)-strlen(rem2)) + let regtype = getregtype('"') + if char == 'p' + let regtype = "V" + endif + if char =~# '[\[({<T]' || spc + let keeper = substitute(keeper,'^\s\+','','') + let keeper = substitute(keeper,'\s\+$','','') + endif + if col("']") == col("$") && col('.') + 1 == col('$') + "let keeper = substitute(keeper,'^\n\s*','','') + "let keeper = substitute(keeper,'\n\s*$','','') + if oldhead =~# '^\s*$' && a:0 < 2 + "let keeper = substitute(keeper,oldhead.'\%$','','') + let keeper = substitute(keeper,'\%^\n'.oldhead.'\(\s*.\{-\}\)\n\s*\%$','\1','') + endif + let pcmd = "p" + else + if oldhead == "" && a:0 < 2 + "let keeper = substitute(keeper,'\%^\n\(.*\)\n\%$','\1','') + endif + let pcmd = "P" + endif + if line('.') < oldlnum && regtype ==# "V" + let pcmd = "p" + endif + call setreg('"',keeper,regtype) + if newchar != "" + call s:wrapreg('"',newchar) + endif + silent exe "norm! ".pcmd.'`[' + if removed =~ '\n' || okeeper =~ '\n' || getreg('"') =~ '\n' + call s:reindent() + else + endif + if getline('.') =~ '^\s\+$' && keeper =~ '^\s*\n' + silent norm! cc + endif + call setreg('"',removed,regtype) + let s:lastdel = removed +endfunction " }}}1 + +function! s:changesurround() " {{{1 + let a = s:inputtarget() + if a == "" + return s:beep() + endif + let b = s:inputreplacement() + if b == "" + return s:beep() + endif + call s:dosurround(a,b) +endfunction " }}}1 + +function! s:opfunc(type,...) " {{{1 + let char = s:inputreplacement() + if char == "" + return s:beep() + endif + let reg = '"' + let sel_save = &selection + let &selection = "inclusive" + let reg_save = getreg(reg) + let reg_type = getregtype(reg) + let type = a:type + if a:type == "char" + silent exe 'norm! `[v`]"'.reg."y" + let type = 'v' + elseif a:type == "line" + silent exe 'norm! `[V`]"'.reg."y" + let type = 'V' + elseif a:type ==# "v" || a:type ==# "V" || a:type ==# "\<C-V>" + silent exe 'norm! gv"'.reg."y" + elseif a:type =~ '^\d\+$' + silent exe 'norm! ^v'.a:type.'$h"'.reg.'y' + else + let &selection = sel_save + return s:beep() + endif + let keeper = getreg(reg) + if type == "v" + let append = matchstr(keeper,'\s*$') + let keeper = substitute(keeper,'\s*$','','') + endif + call setreg(reg,keeper,type) + call s:wrapreg(reg,char,a:0) + if type == "v" && append != "" + call setreg(reg,append,"ac") + endif + silent exe 'norm! gv'.(reg == '"' ? '' : '"' . reg).'p`[' + if type == 'V' || (getreg(reg) =~ '\n' && type == 'v') + call s:reindent() + endif + call setreg(reg,reg_save,reg_type) + let &selection = sel_save +endfunction + +function! s:opfunc2(arg) + call s:opfunc(a:arg,1) +endfunction " }}}1 + +function! s:closematch(str) " {{{1 + " Close an open (, {, [, or < on the command line. + let tail = matchstr(a:str,'.[^\[\](){}<>]*$') + if tail =~ '^\[.\+' + return "]" + elseif tail =~ '^(.\+' + return ")" + elseif tail =~ '^{.\+' + return "}" + elseif tail =~ '^<.+' + return ">" + else + return "" + endif +endfunction " }}}1 + +nnoremap <silent> <Plug>Dsurround :<C-U>call <SID>dosurround(<SID>inputtarget())<CR> +nnoremap <silent> <Plug>Csurround :<C-U>call <SID>changesurround()<CR> +nnoremap <silent> <Plug>Yssurround :<C-U>call <SID>opfunc(v:count1)<CR> +nnoremap <silent> <Plug>YSsurround :<C-U>call <SID>opfunc2(v:count1)<CR> +" <C-U> discards the numerical argument but there's not much we can do with it +nnoremap <silent> <Plug>Ysurround :<C-U>set opfunc=<SID>opfunc<CR>g@ +nnoremap <silent> <Plug>YSurround :<C-U>set opfunc=<SID>opfunc2<CR>g@ +vnoremap <silent> <Plug>Vsurround :<C-U>call <SID>opfunc(visualmode())<CR> +vnoremap <silent> <Plug>VSurround :<C-U>call <SID>opfunc2(visualmode())<CR> +inoremap <silent> <Plug>Isurround <C-R>=<SID>insert()<CR> +inoremap <silent> <Plug>ISurround <C-R>=<SID>insert(1)<CR> + +if !exists("g:surround_no_mappings") || ! g:surround_no_mappings + nmap ds <Plug>Dsurround + nmap cs <Plug>Csurround + nmap ys <Plug>Ysurround + nmap yS <Plug>YSurround + nmap yss <Plug>Yssurround + nmap ySs <Plug>YSsurround + nmap ySS <Plug>YSsurround + if !hasmapto("<Plug>Vsurround","v") + vmap s <Plug>Vsurround + endif + if !hasmapto("<Plug>VSurround","v") + vmap S <Plug>VSurround + endif + if !hasmapto("<Plug>Isurround","i") && !mapcheck("<C-S>","i") + imap <C-S> <Plug>Isurround + endif + "Implemented internally instead + "imap <C-S><C-S> <Plug>ISurround +endif + +let &cpo = s:cpo_save + +" vim:set ft=vim sw=4 sts=4 et: diff --git a/.vim/plugin/tEchoPair.vim b/.vim/plugin/tEchoPair.vim new file mode 100644 index 0000000..b1f3c19 --- /dev/null +++ b/.vim/plugin/tEchoPair.vim @@ -0,0 +1,369 @@ +" tEchoPair.vim +" @Author: Thomas Link (mailto:samul AT web de?subject=vim-tEchoPair) +" @Website: http://www.vim.org/account/profile.php?user_id=4037 +" @License: GPL (see http://www.gnu.org/licenses/gpl.txt) +" @Created: 2007-03-24. +" @Last Change: 2007-03-27. +" @Revision: 0.1.175 + +if &cp || exists("loaded_techopair") + finish +endif +let loaded_techopair = 1 + +" if !exists('g:tEchoPairStyle') | let g:tEchoPairStyle = 'inner' | endif +if !exists('g:tEchoPairStyle') | let g:tEchoPairStyle = 'indicate' | endif +if !exists('g:tEchoPairInstall') | let g:tEchoPairInstall = [] | endif + +if !exists('g:tEchoPairIndicateOpen') | let g:tEchoPairIndicateOpen = ' <<<&' | endif +if !exists('g:tEchoPairIndicateClose') | let g:tEchoPairIndicateClose = '&>>> ' | endif +if !exists('g:tEchoPairIndicateCursor') | let g:tEchoPairIndicateCursor = ' <<<&>>> ' | endif + +if !exists('g:tEchoPairs') + " \ 'viki': [ + " \ 'fold;-1', + " \ ], + let g:tEchoPairs = { + \ 'ruby': [ + \ ['(', ')'], + \ ['{', '}'], + \ ['[', ']'], + \ ['\<\(module\|class\|def\|begin\|do\|if\|unless\)\>', '', '\<end\>', 'TEchoSkipRuby()'], + \ ], + \ 'vim': [ + \ ['(', ')'], + \ ['{', '}'], + \ ['[', ']'], + \ ['\<for\>', '\<endfor\?\>'], + \ ['\<wh\(i\(l\(e\)\?\)\?\)\?\>', '\<endw\(h\(i\(l\(e\)\?\)\?\)\?\)\?\>'], + \ ['\<if\>', '\<end\(i\(f\)\?\)\?\>'], + \ ['\<try\>', '\<endt\(r\(y\)\?\)\?\>'], + \ ['\<fu\(n\(c\(tion\)\?\)\?\)\?\>', '\<endf\(u\(n\(c\(tion\)\?\)\?\)\?\)\?\>'], + \ ] + \ } +endif + +if !exists('g:tEchoPairStyle_inner') + let g:tEchoPairStyle_inner = ['lisp', 'scheme'] +endif + +if !exists('g:tEchoPairStyle_indicate') + let g:tEchoPairStyle_indicate = [] +endif + +if !exists('g:tEchoPairStyles') +endif + +let s:tEchoPairStyles = [] + +fun! TEchoCollectStyles() + redir => vars + silent let + redir END + let s:tEchoPairStyles = split(vars, '\n') + call filter(s:tEchoPairStyles, 'v:val =~ ''^tEchoPairStyle_''') + call map(s:tEchoPairStyles, 'matchstr(v:val, ''^tEchoPairStyle_\zs\S\+'')') +endf + +" fun! TEchoPair(backwards, type, ?what, ?[args]) +fun! TEchoPair(backwards, type, ...) + let lz = &lazyredraw + set lazyredraw + try + let w0 = line('w0') + let l0 = line('.') + let c0 = col('.') + " let c0 = col['.'] + let pos = getpos('.') + let what = a:0 >= 1 ? a:1 : '.' + if a:type == 'rx' + let args0 = a:0 >= 2 ? a:2 : [] + let args = [] + " call add(args, '\V'. escape(get(args0, 0, '('), '\')) + call add(args, '\V'. get(args0, 0, '(')) + " let m = escape(get(args0, 1, ''), '\') + let m = get(args0, 1, '') + call add(args, empty(m) ? m : '\V'.m) + " call add(args, '\V'. escape(get(args0, 2, ')'), '\')) + call add(args, '\V'. get(args0, 2, ')')) + call add(args, a:backwards ? 'bW' : 'W') + let args += args0[3:-1] + echom "DBG searchpairs: ". string(args) + " echom "DBG TEchoPair: ". string(args) + let what = a:backwards ? args[0] : args[2] + let this = a:backwards ? args[2] : args[0] + " echom 'DBG '. what .' '. a:backwards .' '. expand('<cword>') .' '. (expand('<cword>')=~ '\V\^'. what .'\$') + if a:backwards + if expand('<cword>') =~ '\V\^'. this .'\$' + call search('\V'. this, 'cb', l0) + endif + else + if expand('<cword>') =~ '\V\^'. this .'\$' + call search('\V'. this, 'c', l0) + endif + endif + call call('searchpair', args) + elseif a:type =~ '^fold' + let fold_args = split(a:type, ';') + if a:backwards + call s:Normal('[z', 'silent!') + else + call s:Normal(']z', 'silent!') + endif + let lineshift = get(fold_args, a:backwards ? 1 : 2, 0) + if lineshift > 0 + call s:Normal(lineshift .'j', 'silent!') + elseif lineshift < 0 + call s:Normal((-lineshift) .'k', 'silent!') + endif + endif + " else + " if a:backwards + " exec 'norm! ['. a:what + " else + " exec 'norm! ]'. a:what + " endif + " endif + let l1 = line('.') + let c1 = col('.') + if l1 != l0 + if a:backwards + let c0 = col('$') + else + " let c0 = matchend(getline(l1), '^\s*') - 1 + let c0 = matchend(getline(l1), '^\s*') + endif + endif + let text = getline(l1) + if empty(s:tEchoPairStyles) + call TEchoCollectStyles() + endif + if exists('b:tEchoPairStyle') + let style = b:tEchoPairStyle + else + let style = g:tEchoPairStyle + for s in s:tEchoPairStyles + if index(g:tEchoPairStyle_{s}, &filetype) != -1 + let style = s + endif + endfor + endif + " if l1 < l0 || c1 < c0 + if a:backwards + let text = TEchoPair_open_{style}(what, text, c0, l0, c1, l1) + else + let text = TEchoPair_close_{style}(what, text, c0, l0, c1, l1) + endif + if &debug != '' + echo text . ' '. a:backwards .':'. c0.'x'.l0.'-'.c1.'x'.l1 + else + echo text + endif + let b:tEchoPair = text + call s:Normal(w0 .'zt') + call setpos('.', pos) + " return a:what + finally + let &lz = lz + endtry +endf + +fun! s:Normal(cmd, ...) + let p = a:0 >= 1 ? a:1 : '' + let m = mode() + if m ==? 's' || m == '' + exec p .' norm! '. a:cmd + else + exec p .'norm! '. a:cmd + endif +endf + +fun! TEchoPair_open_inner(what, text, c0, l0, c1, l1) + return strpart(a:text, a:c1 - 1, a:c0 - a:c1) +endf + +fun! TEchoPair_close_inner(what, text, c0, l0, c1, l1) + return strpart(a:text, a:c0, a:c1 - a:c0) +endf + +fun! TEchoPair_open_indicate(what, text, c0, l0, c1, l1) + let text = s:IndicateCursor(a:text, a:c0, a:l0, a:c1, a:l1) + " let text = a:l1.': '. substitute(text, '\V\%'. a:c1 .'c'. a:what, ' <<<&<<< ', '') + let text = a:l1.': '. substitute(text, '\V\%'. a:c1 .'c'. a:what, g:tEchoPairIndicateOpen, '') + let cmdh = s:GetCmdHeight() + if cmdh > 1 + let acc = [text] + for i in range(a:l1 + 1, a:l1 + cmdh - 1) + if i > a:l0 + break + endif + " call add(acc, i.': '. s:IndicateCursor(getline(i), a:c0, a:l0, a:c1, i)) + call add(acc, i.': '. getline(i)) + endfor + let text = join(acc, "\<c-j>") + endif + return text +endf + +fun! TEchoPair_close_indicate(what, text, c0, l0, c1, l1) + " let text = substitute(a:text, '\V\%'. a:c1 .'c'. a:what, ' >>>&>>> ', '') + let text = substitute(a:text, '\V\%'. a:c1 .'c'. a:what, g:tEchoPairIndicateClose, '') + let text = a:l1.': '. s:IndicateCursor(text, a:c0, a:l0, a:c1, a:l1) + let cmdh = s:GetCmdHeight() + if cmdh > 1 + let acc = [text] + for i in range(a:l1 - 1, a:l1 - cmdh + 1, -1) + if i < a:l0 + break + endif + " call insert(acc, i.': '. s:IndicateCursor(getline(i), a:c0, a:l0, a:c1, i)) + call insert(acc, i.': '. getline(i)) + endfor + let text = join(acc, "\<c-j>") + endif + return text +endf + +fun! s:IndicateCursor(text, c0, l0, c1, l1) + if a:l0 == a:l1 + return substitute(a:text, '\%'. a:c0 .'c.', g:tEchoPairIndicateCursor, '') + else + return a:text + endif +endf + +fun! s:GetCmdHeight() + let ch = &cmdheight + if mode() == 'i' && &showmode + let ch -= 1 + endif + return ch +endf + +fun! TEchoSkipRuby() + let n = synIDattr(synID(line('.'), col('.'), 1), 'name') + return (n =~ '^ruby\(Comment\|String\)$') +endf + +fun! TEchoPairReset() + augroup TEchoPair + au! + augroup END +endf +" call TEchoPairReset() + +fun! TEchoPairInstall(pattern, ...) + augroup TEchoPair + if a:0 >= 1 + let list = a:1 + elseif has_key(g:tEchoPairs, &filetype) + let list = g:tEchoPairs[&filetype] + else + " [a b] + " [['(', ')'], ['{', '}']] + let list = split(&matchpairs, ',') + call map(list, 'split(v:val, ":")') + endif + for i in list + if type(i) == 1 + if i =~ '^fold' + exec 'au CursorMoved '. a:pattern .' if foldclosed(line(".")) == -1 '. + \ ' | keepjumps call TEchoPair(1, '. string(i) .') | endif' + endif + elseif type(i) == 3 + if len(i) == 2 + let [io, ie] = i + call insert(i, '', 1) + let im = '' + else + let [io, im, ie; rest] = i + endif + if len(io) == 1 + let condo = 'getline(".")[col(".") - 1] == '. string(io) + else + " let condo = 'strpart(getline("."), col(".") - 1) =~ ''\V\^'''. string(io) + let condo = 'expand("<cword>") =~ ''\V\^'. io .'\$''' + endif + if len(ie) == 1 + let conde = 'getline(".")[col(".") - 1] == '. string(ie) + else + " let conde = 'strpart(getline("."), col(".") - 1) =~ ''\V\^'''. string(io) + let conde = 'expand("<cword>") =~ ''\V\^'. ie .'\$''' + endif + exec 'au CursorMoved '. a:pattern .' if '. condo . + \ ' | keepjumps call TEchoPair(0, "rx", '. string(ie) .', '. string(i) .') | endif' + exec 'au CursorMoved '. a:pattern .' if '. conde . + \ ' | keepjumps call TEchoPair(1, "rx", '. string(io) .', '. string(i) .') | endif' + exec 'au CursorMovedI '. a:pattern .' if '. condo . + \ ' | keepjumps call TEchoPair(0, "rx", '. string(ie) .', '. string(i) .') | endif' + exec 'au CursorMovedI '. a:pattern .' if '. conde . + \ ' | keepjumps call TEchoPair(1, "rx", '. string(io) .', '. string(i) .') | endif' + endif + endfor + augroup END +endf + +" command! TEchoPairInstallGlobal call TEchoPairInstall('*') +command! TEchoPairInstallBuffer call TEchoPairInstall(expand('%:p')) + +for ft in g:tEchoPairInstall + exec 'au Filetype '. ft .' TEchoPairInstallBuffer' +endfor + + +finish +____________________________________________________________________ + +tEchoPair.vim -- Display matching parenthesis in the echo area + + +VIM is an excellent editor but in comparison too e.g. emacs it lacks a +minor feature that makes editing lisp code somewhat cumbersome. While +VIM can highlight the matching parenthesis, this doesn't help much with +long functions when the matching parenthesis is off the screen. +Emacs users are better off in such a situation, since emace displays the +matching line in the echo area. + +This plugin tries to mimic Emacs's behaviour. + + +In order to enable this plugin, you choose between the following +options: + TEchoPairInstallBuffer ... enable for the current buffer + + call TEchoPairInstall('*') ... enable globally + + let g:tEchoPairInstall = ['lisp', 'scheme'] ... enable for certain + filetypes + + +Currently, there are the following display modes: + indicate ... Display the whole line and highlight the matching + parenthesis. If 'cmdheight' is greater than 1, additional lines + are display. + + inner ... Display the inner text Emacs-style. + +In order to see the matching parenthesis when 'showmode' is on, set +'cmdheight' to something greater than 1. + +You can select the preferred display mode on a filetype basis, by +setting g:tEchoPairStyle_{STYLE}. + +Example: + let g:tEchoPairStyle_inner = ['lisp', 'scheme'] + let g:tEchoPairStyle_indicate = ['java'] + + +The pairs are usually deduced from the value of 'matchpairs' unless +there is an entry for the current buffer's filetype in g:tEchoPairs. For +the following filetypes custom pairs are pre-defined: + - ruby + - vim + + +CHANGES + +0.1: +Initial release + diff --git a/.vim/plugin/taglist.vim b/.vim/plugin/taglist.vim new file mode 100644 index 0000000..59901f6 --- /dev/null +++ b/.vim/plugin/taglist.vim @@ -0,0 +1,4546 @@ +" File: taglist.vim +" Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com) +" Version: 4.5 +" Last Modified: September 21, 2007 +" Copyright: Copyright (C) 2002-2007 Yegappan Lakshmanan +" Permission is hereby granted to use and distribute this code, +" with or without modifications, provided that this copyright +" notice is copied with it. Like anything else that's free, +" taglist.vim is provided *as is* and comes with no warranty of any +" kind, either expressed or implied. In no event will the copyright +" holder be liable for any damamges resulting from the use of this +" software. +" +" The "Tag List" plugin is a source code browser plugin for Vim and provides +" an overview of the structure of the programming language files and allows +" you to efficiently browse through source code files for different +" programming languages. You can visit the taglist plugin home page for more +" information: +" +" http://vim-taglist.sourceforge.net +" +" You can subscribe to the taglist mailing list to post your questions +" or suggestions for improvement or to report bugs. Visit the following +" page for subscribing to the mailing list: +" +" http://groups.yahoo.com/group/taglist/ +" +" For more information about using this plugin, after installing the +" taglist plugin, use the ":help taglist" command. +" +" Installation +" ------------ +" 1. Download the taglist.zip file and unzip the files to the $HOME/.vim +" or the $HOME/vimfiles or the $VIM/vimfiles directory. This should +" unzip the following two files (the directory structure should be +" preserved): +" +" plugin/taglist.vim - main taglist plugin file +" doc/taglist.txt - documentation (help) file +" +" Refer to the 'add-plugin', 'add-global-plugin' and 'runtimepath' +" Vim help pages for more details about installing Vim plugins. +" 2. Change to the $HOME/.vim/doc or $HOME/vimfiles/doc or +" $VIM/vimfiles/doc directory, start Vim and run the ":helptags ." +" command to process the taglist help file. +" 3. If the exuberant ctags utility is not present in your PATH, then set the +" Tlist_Ctags_Cmd variable to point to the location of the exuberant ctags +" utility (not to the directory) in the .vimrc file. +" 4. If you are running a terminal/console version of Vim and the +" terminal doesn't support changing the window width then set the +" 'Tlist_Inc_Winwidth' variable to 0 in the .vimrc file. +" 5. Restart Vim. +" 6. You can now use the ":TlistToggle" command to open/close the taglist +" window. You can use the ":help taglist" command to get more +" information about using the taglist plugin. +" +" ****************** Do not modify after this line ************************ + +" Line continuation used here +let s:cpo_save = &cpo +set cpo&vim + +if !exists('loaded_taglist') + " First time loading the taglist plugin + " + " To speed up the loading of Vim, the taglist plugin uses autoload + " mechanism to load the taglist functions. + " Only define the configuration variables, user commands and some + " auto-commands and finish sourcing the file + + " The taglist plugin requires the built-in Vim system() function. If this + " function is not available, then don't load the plugin. + if !exists('*system') + echomsg 'Taglist: Vim system() built-in function is not available. ' . + \ 'Plugin is not loaded.' + let loaded_taglist = 'no' + let &cpo = s:cpo_save + finish + endif + + " Location of the exuberant ctags tool + if !exists('Tlist_Ctags_Cmd') + if executable('exuberant-ctags') + " On Debian Linux, exuberant ctags is installed + " as exuberant-ctags + let Tlist_Ctags_Cmd = 'exuberant-ctags' + elseif executable('exctags') + " On Free-BSD, exuberant ctags is installed as exctags + let Tlist_Ctags_Cmd = 'exctags' + elseif executable('ctags') + let Tlist_Ctags_Cmd = 'ctags' + elseif executable('ctags.exe') + let Tlist_Ctags_Cmd = 'ctags.exe' + elseif executable('tags') + let Tlist_Ctags_Cmd = 'tags' + else + echomsg 'Taglist: Exuberant ctags (http://ctags.sf.net) ' . + \ 'not found in PATH. Plugin is not loaded.' + " Skip loading the plugin + let loaded_taglist = 'no' + let &cpo = s:cpo_save + finish + endif + endif + + + " Automatically open the taglist window on Vim startup + if !exists('Tlist_Auto_Open') + let Tlist_Auto_Open = 0 + endif + + " When the taglist window is toggle opened, move the cursor to the + " taglist window + if !exists('Tlist_GainFocus_On_ToggleOpen') + let Tlist_GainFocus_On_ToggleOpen = 0 + endif + + " Process files even when the taglist window is not open + if !exists('Tlist_Process_File_Always') + let Tlist_Process_File_Always = 0 + endif + + if !exists('Tlist_Show_Menu') + let Tlist_Show_Menu = 0 + endif + + " Tag listing sort type - 'name' or 'order' + if !exists('Tlist_Sort_Type') + let Tlist_Sort_Type = 'order' + endif + + " Tag listing window split (horizontal/vertical) control + if !exists('Tlist_Use_Horiz_Window') + let Tlist_Use_Horiz_Window = 0 + endif + + " Open the vertically split taglist window on the left or on the right + " side. This setting is relevant only if Tlist_Use_Horiz_Window is set to + " zero (i.e. only for vertically split windows) + if !exists('Tlist_Use_Right_Window') + let Tlist_Use_Right_Window = 0 + endif + + " Increase Vim window width to display vertically split taglist window. + " For MS-Windows version of Vim running in a MS-DOS window, this must be + " set to 0 otherwise the system may hang due to a Vim limitation. + if !exists('Tlist_Inc_Winwidth') + if (has('win16') || has('win95')) && !has('gui_running') + let Tlist_Inc_Winwidth = 0 + else + let Tlist_Inc_Winwidth = 1 + endif + endif + + " Vertically split taglist window width setting + if !exists('Tlist_WinWidth') + let Tlist_WinWidth = 30 + endif + + " Horizontally split taglist window height setting + if !exists('Tlist_WinHeight') + let Tlist_WinHeight = 10 + endif + + " Display tag prototypes or tag names in the taglist window + if !exists('Tlist_Display_Prototype') + let Tlist_Display_Prototype = 0 + endif + + " Display tag scopes in the taglist window + if !exists('Tlist_Display_Tag_Scope') + let Tlist_Display_Tag_Scope = 1 + endif + + " Use single left mouse click to jump to a tag. By default this is disabled. + " Only double click using the mouse will be processed. + if !exists('Tlist_Use_SingleClick') + let Tlist_Use_SingleClick = 0 + endif + + " Control whether additional help is displayed as part of the taglist or + " not. Also, controls whether empty lines are used to separate the tag + " tree. + if !exists('Tlist_Compact_Format') + let Tlist_Compact_Format = 0 + endif + + " Exit Vim if only the taglist window is currently open. By default, this is + " set to zero. + if !exists('Tlist_Exit_OnlyWindow') + let Tlist_Exit_OnlyWindow = 0 + endif + + " Automatically close the folds for the non-active files in the taglist + " window + if !exists('Tlist_File_Fold_Auto_Close') + let Tlist_File_Fold_Auto_Close = 0 + endif + + " Close the taglist window when a tag is selected + if !exists('Tlist_Close_On_Select') + let Tlist_Close_On_Select = 0 + endif + + " Automatically update the taglist window to display tags for newly + " edited files + if !exists('Tlist_Auto_Update') + let Tlist_Auto_Update = 1 + endif + + " Automatically highlight the current tag + if !exists('Tlist_Auto_Highlight_Tag') + let Tlist_Auto_Highlight_Tag = 1 + endif + + " Automatically highlight the current tag on entering a buffer + if !exists('Tlist_Highlight_Tag_On_BufEnter') + let Tlist_Highlight_Tag_On_BufEnter = 1 + endif + + " Enable fold column to display the folding for the tag tree + if !exists('Tlist_Enable_Fold_Column') + let Tlist_Enable_Fold_Column = 1 + endif + + " Display the tags for only one file in the taglist window + if !exists('Tlist_Show_One_File') + let Tlist_Show_One_File = 0 + endif + + if !exists('Tlist_Max_Submenu_Items') + let Tlist_Max_Submenu_Items = 20 + endif + + if !exists('Tlist_Max_Tag_Length') + let Tlist_Max_Tag_Length = 10 + endif + + " Do not change the name of the taglist title variable. The winmanager + " plugin relies on this name to determine the title for the taglist + " plugin. + let TagList_title = "__Tag_List__" + + " Taglist debug messages + let s:tlist_msg = '' + + " Define the taglist autocommand to automatically open the taglist window + " on Vim startup + if g:Tlist_Auto_Open + autocmd VimEnter * nested call s:Tlist_Window_Check_Auto_Open() + endif + + " Refresh the taglist + if g:Tlist_Process_File_Always + autocmd BufEnter * call s:Tlist_Refresh() + endif + + if g:Tlist_Show_Menu + autocmd GUIEnter * call s:Tlist_Menu_Init() + endif + + " When the taglist buffer is created when loading a Vim session file, + " the taglist buffer needs to be initialized. The BufFilePost event + " is used to handle this case. + autocmd BufFilePost __Tag_List__ call s:Tlist_Vim_Session_Load() + + " Define the user commands to manage the taglist window + command! -nargs=0 -bar TlistToggle call s:Tlist_Window_Toggle() + command! -nargs=0 -bar TlistOpen call s:Tlist_Window_Open() + " For backwards compatiblity define the Tlist command + command! -nargs=0 -bar Tlist TlistToggle + command! -nargs=+ -complete=file TlistAddFiles + \ call s:Tlist_Add_Files(<f-args>) + command! -nargs=+ -complete=dir TlistAddFilesRecursive + \ call s:Tlist_Add_Files_Recursive(<f-args>) + command! -nargs=0 -bar TlistClose call s:Tlist_Window_Close() + command! -nargs=0 -bar TlistUpdate call s:Tlist_Update_Current_File() + command! -nargs=0 -bar TlistHighlightTag call s:Tlist_Window_Highlight_Tag( + \ fnamemodify(bufname('%'), ':p'), line('.'), 2, 1) + " For backwards compatiblity define the TlistSync command + command! -nargs=0 -bar TlistSync TlistHighlightTag + command! -nargs=* -complete=buffer TlistShowPrototype + \ echo Tlist_Get_Tag_Prototype_By_Line(<f-args>) + command! -nargs=* -complete=buffer TlistShowTag + \ echo Tlist_Get_Tagname_By_Line(<f-args>) + command! -nargs=* -complete=file TlistSessionLoad + \ call s:Tlist_Session_Load(<q-args>) + command! -nargs=* -complete=file TlistSessionSave + \ call s:Tlist_Session_Save(<q-args>) + command! -bar TlistLock let Tlist_Auto_Update=0 + command! -bar TlistUnlock let Tlist_Auto_Update=1 + + " Commands for enabling/disabling debug and to display debug messages + command! -nargs=? -complete=file -bar TlistDebug + \ call s:Tlist_Debug_Enable(<q-args>) + command! -nargs=0 -bar TlistUndebug call s:Tlist_Debug_Disable() + command! -nargs=0 -bar TlistMessages call s:Tlist_Debug_Show() + + " Define autocommands to autoload the taglist plugin when needed. + + " Trick to get the current script ID + map <SID>xx <SID>xx + let s:tlist_sid = substitute(maparg('<SID>xx'), '<SNR>\(\d\+_\)xx$', + \ '\1', '') + unmap <SID>xx + + exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_* source ' . + \ escape(expand('<sfile>'), ' ') + exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_Window_* source ' . + \ escape(expand('<sfile>'), ' ') + exe 'autocmd FuncUndefined *' . s:tlist_sid . 'Tlist_Menu_* source ' . + \ escape(expand('<sfile>'), ' ') + exe 'autocmd FuncUndefined Tlist_* source ' . + \ escape(expand('<sfile>'), ' ') + exe 'autocmd FuncUndefined TagList_* source ' . + \ escape(expand('<sfile>'), ' ') + + let loaded_taglist = 'fast_load_done' + + if g:Tlist_Show_Menu && has('gui_running') + call s:Tlist_Menu_Init() + endif + + " restore 'cpo' + let &cpo = s:cpo_save + finish +endif + +if !exists('s:tlist_sid') + " Two or more versions of taglist plugin are installed. Don't + " load this version of the plugin. + finish +endif + +unlet! s:tlist_sid + +if loaded_taglist != 'fast_load_done' + " restore 'cpo' + let &cpo = s:cpo_save + finish +endif + +" Taglist plugin functionality is available +let loaded_taglist = 'available' + +"------------------- end of user configurable options -------------------- + +" Default language specific settings for supported file types and tag types +" +" Variable name format: +" +" s:tlist_def_{vim_ftype}_settings +" +" vim_ftype - Filetype detected by Vim +" +" Value format: +" +" <ctags_ftype>;<flag>:<name>;<flag>:<name>;... +" +" ctags_ftype - File type supported by exuberant ctags +" flag - Flag supported by exuberant ctags to generate a tag type +" name - Name of the tag type used in the taglist window to display the +" tags of this type +" + +" assembly language +let s:tlist_def_asm_settings = 'asm;d:define;l:label;m:macro;t:type' + +" aspperl language +let s:tlist_def_aspperl_settings = 'asp;f:function;s:sub;v:variable' + +" aspvbs language +let s:tlist_def_aspvbs_settings = 'asp;f:function;s:sub;v:variable' + +" awk language +let s:tlist_def_awk_settings = 'awk;f:function' + +" beta language +let s:tlist_def_beta_settings = 'beta;f:fragment;s:slot;v:pattern' + +" c language +let s:tlist_def_c_settings = 'c;d:macro;g:enum;s:struct;u:union;t:typedef;' . + \ 'v:variable;f:function' + +" c++ language +let s:tlist_def_cpp_settings = 'c++;n:namespace;v:variable;d:macro;t:typedef;' . + \ 'c:class;g:enum;s:struct;u:union;f:function' + +" c# language +let s:tlist_def_cs_settings = 'c#;d:macro;t:typedef;n:namespace;c:class;' . + \ 'E:event;g:enum;s:struct;i:interface;' . + \ 'p:properties;m:method' + +" cobol language +let s:tlist_def_cobol_settings = 'cobol;d:data;f:file;g:group;p:paragraph;' . + \ 'P:program;s:section' + +" eiffel language +let s:tlist_def_eiffel_settings = 'eiffel;c:class;f:feature' + +" erlang language +let s:tlist_def_erlang_settings = 'erlang;d:macro;r:record;m:module;f:function' + +" expect (same as tcl) language +let s:tlist_def_expect_settings = 'tcl;c:class;f:method;p:procedure' + +" fortran language +let s:tlist_def_fortran_settings = 'fortran;p:program;b:block data;' . + \ 'c:common;e:entry;i:interface;k:type;l:label;m:module;' . + \ 'n:namelist;t:derived;v:variable;f:function;s:subroutine' + +" HTML language +let s:tlist_def_html_settings = 'html;a:anchor;f:javascript function' + +" java language +let s:tlist_def_java_settings = 'java;p:package;c:class;i:interface;' . + \ 'f:field;m:method' + +" javascript language +let s:tlist_def_javascript_settings = 'javascript;f:function' + +" lisp language +let s:tlist_def_lisp_settings = 'lisp;f:function' + +" lua language +let s:tlist_def_lua_settings = 'lua;f:function' + +" makefiles +let s:tlist_def_make_settings = 'make;m:macro' + +" pascal language +let s:tlist_def_pascal_settings = 'pascal;f:function;p:procedure' + +" perl language +let s:tlist_def_perl_settings = 'perl;c:constant;l:label;p:package;s:subroutine' + +" php language +let s:tlist_def_php_settings = 'php;c:class;d:constant;v:variable;f:function' + +" python language +let s:tlist_def_python_settings = 'python;c:class;m:member;f:function' + +" rexx language +let s:tlist_def_rexx_settings = 'rexx;s:subroutine' + +" ruby language +let s:tlist_def_ruby_settings = 'ruby;c:class;f:method;F:function;' . + \ 'm:singleton method' + +" scheme language +let s:tlist_def_scheme_settings = 'scheme;s:set;f:function' + +" shell language +let s:tlist_def_sh_settings = 'sh;f:function' + +" C shell language +let s:tlist_def_csh_settings = 'sh;f:function' + +" Z shell language +let s:tlist_def_zsh_settings = 'sh;f:function' + +" slang language +let s:tlist_def_slang_settings = 'slang;n:namespace;f:function' + +" sml language +let s:tlist_def_sml_settings = 'sml;e:exception;c:functor;s:signature;' . + \ 'r:structure;t:type;v:value;f:function' + +" sql language +let s:tlist_def_sql_settings = 'sql;c:cursor;F:field;P:package;r:record;' . + \ 's:subtype;t:table;T:trigger;v:variable;f:function;p:procedure' + +" tcl language +let s:tlist_def_tcl_settings = 'tcl;c:class;f:method;m:method;p:procedure' + +" vera language +let s:tlist_def_vera_settings = 'vera;c:class;d:macro;e:enumerator;' . + \ 'f:function;g:enum;m:member;p:program;' . + \ 'P:prototype;t:task;T:typedef;v:variable;' . + \ 'x:externvar' + +"verilog language +let s:tlist_def_verilog_settings = 'verilog;m:module;c:constant;P:parameter;' . + \ 'e:event;r:register;t:task;w:write;p:port;v:variable;f:function' + +" vim language +let s:tlist_def_vim_settings = 'vim;a:autocmds;v:variable;f:function' + +" yacc language +let s:tlist_def_yacc_settings = 'yacc;l:label' + +"------------------- end of language specific options -------------------- + +" Vim window size is changed by the taglist plugin or not +let s:tlist_winsize_chgd = -1 +" Taglist window is maximized or not +let s:tlist_win_maximized = 0 +" Name of files in the taglist +let s:tlist_file_names='' +" Number of files in the taglist +let s:tlist_file_count = 0 +" Number of filetypes supported by taglist +let s:tlist_ftype_count = 0 +" Is taglist part of other plugins like winmanager or cream? +let s:tlist_app_name = "none" +" Are we displaying brief help text +let s:tlist_brief_help = 1 +" List of files removed on user request +let s:tlist_removed_flist = "" +" Index of current file displayed in the taglist window +let s:tlist_cur_file_idx = -1 +" Taglist menu is empty or not +let s:tlist_menu_empty = 1 + +" An autocommand is used to refresh the taglist window when entering any +" buffer. We don't want to refresh the taglist window if we are entering the +" file window from one of the taglist functions. The 'Tlist_Skip_Refresh' +" variable is used to skip the refresh of the taglist window and is set +" and cleared appropriately. +let s:Tlist_Skip_Refresh = 0 + +" Tlist_Window_Display_Help() +function! s:Tlist_Window_Display_Help() + if s:tlist_app_name == "winmanager" + " To handle a bug in the winmanager plugin, add a space at the + " last line + call setline('$', ' ') + endif + + if s:tlist_brief_help + " Add the brief help + call append(0, '" Press <F1> to display help text') + else + " Add the extensive help + call append(0, '" <enter> : Jump to tag definition') + call append(1, '" o : Jump to tag definition in new window') + call append(2, '" p : Preview the tag definition') + call append(3, '" <space> : Display tag prototype') + call append(4, '" u : Update tag list') + call append(5, '" s : Select sort field') + call append(6, '" d : Remove file from taglist') + call append(7, '" x : Zoom-out/Zoom-in taglist window') + call append(8, '" + : Open a fold') + call append(9, '" - : Close a fold') + call append(10, '" * : Open all folds') + call append(11, '" = : Close all folds') + call append(12, '" [[ : Move to the start of previous file') + call append(13, '" ]] : Move to the start of next file') + call append(14, '" q : Close the taglist window') + call append(15, '" <F1> : Remove help text') + endif +endfunction + +" Tlist_Window_Toggle_Help_Text() +" Toggle taglist plugin help text between the full version and the brief +" version +function! s:Tlist_Window_Toggle_Help_Text() + if g:Tlist_Compact_Format + " In compact display mode, do not display help + return + endif + + " Include the empty line displayed after the help text + let brief_help_size = 1 + let full_help_size = 16 + + setlocal modifiable + + " Set report option to a huge value to prevent informational messages + " while deleting the lines + let old_report = &report + set report=99999 + + " Remove the currently highlighted tag. Otherwise, the help text + " might be highlighted by mistake + match none + + " Toggle between brief and full help text + if s:tlist_brief_help + let s:tlist_brief_help = 0 + + " Remove the previous help + exe '1,' . brief_help_size . ' delete _' + + " Adjust the start/end line numbers for the files + call s:Tlist_Window_Update_Line_Offsets(0, 1, full_help_size - brief_help_size) + else + let s:tlist_brief_help = 1 + + " Remove the previous help + exe '1,' . full_help_size . ' delete _' + + " Adjust the start/end line numbers for the files + call s:Tlist_Window_Update_Line_Offsets(0, 0, full_help_size - brief_help_size) + endif + + call s:Tlist_Window_Display_Help() + + " Restore the report option + let &report = old_report + + setlocal nomodifiable +endfunction + +" Taglist debug support +let s:tlist_debug = 0 + +" File for storing the debug messages +let s:tlist_debug_file = '' + +" Tlist_Debug_Enable +" Enable logging of taglist debug messages. +function! s:Tlist_Debug_Enable(...) + let s:tlist_debug = 1 + + " Check whether a valid file name is supplied. + if a:1 != '' + let s:tlist_debug_file = fnamemodify(a:1, ':p') + + " Empty the log file + exe 'redir! > ' . s:tlist_debug_file + redir END + + " Check whether the log file is present/created + if !filewritable(s:tlist_debug_file) + call s:Tlist_Warning_Msg('Taglist: Unable to create log file ' + \ . s:tlist_debug_file) + let s:tlist_debug_file = '' + endif + endif +endfunction + +" Tlist_Debug_Disable +" Disable logging of taglist debug messages. +function! s:Tlist_Debug_Disable(...) + let s:tlist_debug = 0 + let s:tlist_debug_file = '' +endfunction + +" Tlist_Debug_Show +" Display the taglist debug messages in a new window +function! s:Tlist_Debug_Show() + if s:tlist_msg == '' + call s:Tlist_Warning_Msg('Taglist: No debug messages') + return + endif + + " Open a new window to display the taglist debug messages + new taglist_debug.txt + " Delete all the lines (if the buffer already exists) + silent! %delete _ + " Add the messages + silent! put =s:tlist_msg + " Move the cursor to the first line + normal! gg +endfunction + +" Tlist_Log_Msg +" Log the supplied debug message along with the time +function! s:Tlist_Log_Msg(msg) + if s:tlist_debug + if s:tlist_debug_file != '' + exe 'redir >> ' . s:tlist_debug_file + silent echon strftime('%H:%M:%S') . ': ' . a:msg . "\n" + redir END + else + " Log the message into a variable + " Retain only the last 3000 characters + let len = strlen(s:tlist_msg) + if len > 3000 + let s:tlist_msg = strpart(s:tlist_msg, len - 3000) + endif + let s:tlist_msg = s:tlist_msg . strftime('%H:%M:%S') . ': ' . + \ a:msg . "\n" + endif + endif +endfunction + +" Tlist_Warning_Msg() +" Display a message using WarningMsg highlight group +function! s:Tlist_Warning_Msg(msg) + echohl WarningMsg + echomsg a:msg + echohl None +endfunction + +" Last returned file index for file name lookup. +" Used to speed up file lookup +let s:tlist_file_name_idx_cache = -1 + +" Tlist_Get_File_Index() +" Return the index of the specified filename +function! s:Tlist_Get_File_Index(fname) + if s:tlist_file_count == 0 || a:fname == '' + return -1 + endif + + " If the new filename is same as the last accessed filename, then + " return that index + if s:tlist_file_name_idx_cache != -1 && + \ s:tlist_file_name_idx_cache < s:tlist_file_count + if s:tlist_{s:tlist_file_name_idx_cache}_filename == a:fname + " Same as the last accessed file + return s:tlist_file_name_idx_cache + endif + endif + + " First, check whether the filename is present + let s_fname = a:fname . "\n" + let i = stridx(s:tlist_file_names, s_fname) + if i == -1 + let s:tlist_file_name_idx_cache = -1 + return -1 + endif + + " Second, compute the file name index + let nl_txt = substitute(strpart(s:tlist_file_names, 0, i), "[^\n]", '', 'g') + let s:tlist_file_name_idx_cache = strlen(nl_txt) + return s:tlist_file_name_idx_cache +endfunction + +" Last returned file index for line number lookup. +" Used to speed up file lookup +let s:tlist_file_lnum_idx_cache = -1 + +" Tlist_Window_Get_File_Index_By_Linenum() +" Return the index of the filename present in the specified line number +" Line number refers to the line number in the taglist window +function! s:Tlist_Window_Get_File_Index_By_Linenum(lnum) + call s:Tlist_Log_Msg('Tlist_Window_Get_File_Index_By_Linenum (' . a:lnum . ')') + + " First try to see whether the new line number is within the range + " of the last returned file + if s:tlist_file_lnum_idx_cache != -1 && + \ s:tlist_file_lnum_idx_cache < s:tlist_file_count + if a:lnum >= s:tlist_{s:tlist_file_lnum_idx_cache}_start && + \ a:lnum <= s:tlist_{s:tlist_file_lnum_idx_cache}_end + return s:tlist_file_lnum_idx_cache + endif + endif + + let fidx = -1 + + if g:Tlist_Show_One_File + " Displaying only one file in the taglist window. Check whether + " the line is within the tags displayed for that file + if s:tlist_cur_file_idx != -1 + if a:lnum >= s:tlist_{s:tlist_cur_file_idx}_start + \ && a:lnum <= s:tlist_{s:tlist_cur_file_idx}_end + let fidx = s:tlist_cur_file_idx + endif + + endif + else + " Do a binary search in the taglist + let left = 0 + let right = s:tlist_file_count - 1 + + while left < right + let mid = (left + right) / 2 + + if a:lnum >= s:tlist_{mid}_start && a:lnum <= s:tlist_{mid}_end + let s:tlist_file_lnum_idx_cache = mid + return mid + endif + + if a:lnum < s:tlist_{mid}_start + let right = mid - 1 + else + let left = mid + 1 + endif + endwhile + + if left >= 0 && left < s:tlist_file_count + \ && a:lnum >= s:tlist_{left}_start + \ && a:lnum <= s:tlist_{left}_end + let fidx = left + endif + endif + + let s:tlist_file_lnum_idx_cache = fidx + + return fidx +endfunction + +" Tlist_Exe_Cmd_No_Acmds +" Execute the specified Ex command after disabling autocommands +function! s:Tlist_Exe_Cmd_No_Acmds(cmd) + let old_eventignore = &eventignore + set eventignore=all + exe a:cmd + let &eventignore = old_eventignore +endfunction + +" Tlist_Skip_File() +" Check whether tag listing is supported for the specified file +function! s:Tlist_Skip_File(filename, ftype) + " Skip buffers with no names and buffers with filetype not set + if a:filename == '' || a:ftype == '' + return 1 + endif + + " Skip files which are not supported by exuberant ctags + " First check whether default settings for this filetype are available. + " If it is not available, then check whether user specified settings are + " available. If both are not available, then don't list the tags for this + " filetype + let var = 's:tlist_def_' . a:ftype . '_settings' + if !exists(var) + let var = 'g:tlist_' . a:ftype . '_settings' + if !exists(var) + return 1 + endif + endif + + " Skip files which are not readable or files which are not yet stored + " to the disk + if !filereadable(a:filename) + return 1 + endif + + return 0 +endfunction + +" Tlist_User_Removed_File +" Returns 1 if a file is removed by a user from the taglist +function! s:Tlist_User_Removed_File(filename) + return stridx(s:tlist_removed_flist, a:filename . "\n") != -1 +endfunction + +" Tlist_Update_Remove_List +" Update the list of user removed files from the taglist +" add == 1, add the file to the removed list +" add == 0, delete the file from the removed list +function! s:Tlist_Update_Remove_List(filename, add) + if a:add + let s:tlist_removed_flist = s:tlist_removed_flist . a:filename . "\n" + else + let idx = stridx(s:tlist_removed_flist, a:filename . "\n") + let text_before = strpart(s:tlist_removed_flist, 0, idx) + let rem_text = strpart(s:tlist_removed_flist, idx) + let next_idx = stridx(rem_text, "\n") + let text_after = strpart(rem_text, next_idx + 1) + + let s:tlist_removed_flist = text_before . text_after + endif +endfunction + +" Tlist_FileType_Init +" Initialize the ctags arguments and tag variable for the specified +" file type +function! s:Tlist_FileType_Init(ftype) + call s:Tlist_Log_Msg('Tlist_FileType_Init (' . a:ftype . ')') + " If the user didn't specify any settings, then use the default + " ctags args. Otherwise, use the settings specified by the user + let var = 'g:tlist_' . a:ftype . '_settings' + if exists(var) + " User specified ctags arguments + let settings = {var} . ';' + else + " Default ctags arguments + let var = 's:tlist_def_' . a:ftype . '_settings' + if !exists(var) + " No default settings for this file type. This filetype is + " not supported + return 0 + endif + let settings = s:tlist_def_{a:ftype}_settings . ';' + endif + + let msg = 'Taglist: Invalid ctags option setting - ' . settings + + " Format of the option that specifies the filetype and ctags arugments: + " + " <language_name>;flag1:name1;flag2:name2;flag3:name3 + " + + " Extract the file type to pass to ctags. This may be different from the + " file type detected by Vim + let pos = stridx(settings, ';') + if pos == -1 + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let ctags_ftype = strpart(settings, 0, pos) + if ctags_ftype == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + " Make sure a valid filetype is supplied. If the user didn't specify a + " valid filetype, then the ctags option settings may be treated as the + " filetype + if ctags_ftype =~ ':' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + + " Remove the file type from settings + let settings = strpart(settings, pos + 1) + if settings == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + + " Process all the specified ctags flags. The format is + " flag1:name1;flag2:name2;flag3:name3 + let ctags_flags = '' + let cnt = 0 + while settings != '' + " Extract the flag + let pos = stridx(settings, ':') + if pos == -1 + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let flag = strpart(settings, 0, pos) + if flag == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + " Remove the flag from settings + let settings = strpart(settings, pos + 1) + + " Extract the tag type name + let pos = stridx(settings, ';') + if pos == -1 + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let name = strpart(settings, 0, pos) + if name == '' + call s:Tlist_Warning_Msg(msg) + return 0 + endif + let settings = strpart(settings, pos + 1) + + let cnt = cnt + 1 + + let s:tlist_{a:ftype}_{cnt}_name = flag + let s:tlist_{a:ftype}_{cnt}_fullname = name + let ctags_flags = ctags_flags . flag + endwhile + + let s:tlist_{a:ftype}_ctags_args = '--language-force=' . ctags_ftype . + \ ' --' . ctags_ftype . '-types=' . ctags_flags + let s:tlist_{a:ftype}_count = cnt + let s:tlist_{a:ftype}_ctags_flags = ctags_flags + + " Save the filetype name + let s:tlist_ftype_{s:tlist_ftype_count}_name = a:ftype + let s:tlist_ftype_count = s:tlist_ftype_count + 1 + + return 1 +endfunction + +" Tlist_Detect_Filetype +" Determine the filetype for the specified file using the filetypedetect +" autocmd. +function! s:Tlist_Detect_Filetype(fname) + " Ignore the filetype autocommands + let old_eventignore = &eventignore + set eventignore=FileType + + " Save the 'filetype', as this will be changed temporarily + let old_filetype = &filetype + + " Run the filetypedetect group of autocommands to determine + " the filetype + exe 'doautocmd filetypedetect BufRead ' . a:fname + + " Save the detected filetype + let ftype = &filetype + + " Restore the previous state + let &filetype = old_filetype + let &eventignore = old_eventignore + + return ftype +endfunction + +" Tlist_Get_Buffer_Filetype +" Get the filetype for the specified buffer +function! s:Tlist_Get_Buffer_Filetype(bnum) + let buf_ft = getbufvar(a:bnum, '&filetype') + + if bufloaded(a:bnum) + " For loaded buffers, the 'filetype' is already determined + return buf_ft + endif + + " For unloaded buffers, if the 'filetype' option is set, return it + if buf_ft != '' + return buf_ft + endif + + " Skip non-existent buffers + if !bufexists(a:bnum) + return '' + endif + + " For buffers whose filetype is not yet determined, try to determine + " the filetype + let bname = bufname(a:bnum) + + return s:Tlist_Detect_Filetype(bname) +endfunction + +" Tlist_Discard_TagInfo +" Discard the stored tag information for a file +function! s:Tlist_Discard_TagInfo(fidx) + call s:Tlist_Log_Msg('Tlist_Discard_TagInfo (' . + \ s:tlist_{a:fidx}_filename . ')') + let ftype = s:tlist_{a:fidx}_filetype + + " Discard information about the tags defined in the file + let i = 1 + while i <= s:tlist_{a:fidx}_tag_count + let fidx_i = 's:tlist_' . a:fidx . '_' . i + unlet! {fidx_i}_tag + unlet! {fidx_i}_tag_name + unlet! {fidx_i}_tag_type + unlet! {fidx_i}_ttype_idx + unlet! {fidx_i}_tag_proto + unlet! {fidx_i}_tag_searchpat + unlet! {fidx_i}_tag_linenum + let i = i + 1 + endwhile + + let s:tlist_{a:fidx}_tag_count = 0 + + " Discard information about tag type groups + let i = 1 + while i <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{i}_name + if s:tlist_{a:fidx}_{ttype} != '' + let fidx_ttype = 's:tlist_' . a:fidx . '_' . ttype + let {fidx_ttype} = '' + let {fidx_ttype}_offset = 0 + let cnt = {fidx_ttype}_count + let {fidx_ttype}_count = 0 + let j = 1 + while j <= cnt + unlet! {fidx_ttype}_{j} + let j = j + 1 + endwhile + endif + let i = i + 1 + endwhile + + " Discard the stored menu command also + let s:tlist_{a:fidx}_menu_cmd = '' +endfunction + +" Tlist_Window_Update_Line_Offsets +" Update the line offsets for tags for files starting from start_idx +" and displayed in the taglist window by the specified offset +function! s:Tlist_Window_Update_Line_Offsets(start_idx, increment, offset) + let i = a:start_idx + + while i < s:tlist_file_count + if s:tlist_{i}_visible + " Update the start/end line number only if the file is visible + if a:increment + let s:tlist_{i}_start = s:tlist_{i}_start + a:offset + let s:tlist_{i}_end = s:tlist_{i}_end + a:offset + else + let s:tlist_{i}_start = s:tlist_{i}_start - a:offset + let s:tlist_{i}_end = s:tlist_{i}_end - a:offset + endif + endif + let i = i + 1 + endwhile +endfunction + +" Tlist_Discard_FileInfo +" Discard the stored information for a file +function! s:Tlist_Discard_FileInfo(fidx) + call s:Tlist_Log_Msg('Tlist_Discard_FileInfo (' . + \ s:tlist_{a:fidx}_filename . ')') + call s:Tlist_Discard_TagInfo(a:fidx) + + let ftype = s:tlist_{a:fidx}_filetype + + let i = 1 + while i <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{i}_name + unlet! s:tlist_{a:fidx}_{ttype} + unlet! s:tlist_{a:fidx}_{ttype}_offset + unlet! s:tlist_{a:fidx}_{ttype}_count + let i = i + 1 + endwhile + + unlet! s:tlist_{a:fidx}_filename + unlet! s:tlist_{a:fidx}_sort_type + unlet! s:tlist_{a:fidx}_filetype + unlet! s:tlist_{a:fidx}_mtime + unlet! s:tlist_{a:fidx}_start + unlet! s:tlist_{a:fidx}_end + unlet! s:tlist_{a:fidx}_valid + unlet! s:tlist_{a:fidx}_visible + unlet! s:tlist_{a:fidx}_tag_count + unlet! s:tlist_{a:fidx}_menu_cmd +endfunction + +" Tlist_Window_Remove_File_From_Display +" Remove the specified file from display +function! s:Tlist_Window_Remove_File_From_Display(fidx) + call s:Tlist_Log_Msg('Tlist_Window_Remove_File_From_Display (' . + \ s:tlist_{a:fidx}_filename . ')') + " If the file is not visible then no need to remove it + if !s:tlist_{a:fidx}_visible + return + endif + + " Remove the tags displayed for the specified file from the window + let start = s:tlist_{a:fidx}_start + " Include the empty line after the last line also + if g:Tlist_Compact_Format + let end = s:tlist_{a:fidx}_end + else + let end = s:tlist_{a:fidx}_end + 1 + endif + + setlocal modifiable + exe 'silent! ' . start . ',' . end . 'delete _' + setlocal nomodifiable + + " Correct the start and end line offsets for all the files following + " this file, as the tags for this file are removed + call s:Tlist_Window_Update_Line_Offsets(a:fidx + 1, 0, end - start + 1) +endfunction + +" Tlist_Remove_File +" Remove the file under the cursor or the specified file index +" user_request - User requested to remove the file from taglist +function! s:Tlist_Remove_File(file_idx, user_request) + let fidx = a:file_idx + + if fidx == -1 + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.')) + if fidx == -1 + return + endif + endif + call s:Tlist_Log_Msg('Tlist_Remove_File (' . + \ s:tlist_{fidx}_filename . ', ' . a:user_request . ')') + + let save_winnr = winnr() + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + " Taglist window is open, remove the file from display + + if save_winnr != winnum + let old_eventignore = &eventignore + set eventignore=all + exe winnum . 'wincmd w' + endif + + call s:Tlist_Window_Remove_File_From_Display(fidx) + + if save_winnr != winnum + exe save_winnr . 'wincmd w' + let &eventignore = old_eventignore + endif + endif + + let fname = s:tlist_{fidx}_filename + + if a:user_request + " As the user requested to remove the file from taglist, + " add it to the removed list + call s:Tlist_Update_Remove_List(fname, 1) + endif + + " Remove the file name from the taglist list of filenames + let idx = stridx(s:tlist_file_names, fname . "\n") + let text_before = strpart(s:tlist_file_names, 0, idx) + let rem_text = strpart(s:tlist_file_names, idx) + let next_idx = stridx(rem_text, "\n") + let text_after = strpart(rem_text, next_idx + 1) + let s:tlist_file_names = text_before . text_after + + call s:Tlist_Discard_FileInfo(fidx) + + " Shift all the file variables by one index + let i = fidx + 1 + + while i < s:tlist_file_count + let j = i - 1 + + let s:tlist_{j}_filename = s:tlist_{i}_filename + let s:tlist_{j}_sort_type = s:tlist_{i}_sort_type + let s:tlist_{j}_filetype = s:tlist_{i}_filetype + let s:tlist_{j}_mtime = s:tlist_{i}_mtime + let s:tlist_{j}_start = s:tlist_{i}_start + let s:tlist_{j}_end = s:tlist_{i}_end + let s:tlist_{j}_valid = s:tlist_{i}_valid + let s:tlist_{j}_visible = s:tlist_{i}_visible + let s:tlist_{j}_tag_count = s:tlist_{i}_tag_count + let s:tlist_{j}_menu_cmd = s:tlist_{i}_menu_cmd + + let k = 1 + while k <= s:tlist_{j}_tag_count + let s:tlist_{j}_{k}_tag = s:tlist_{i}_{k}_tag + let s:tlist_{j}_{k}_tag_name = s:tlist_{i}_{k}_tag_name + let s:tlist_{j}_{k}_tag_type = s:Tlist_Get_Tag_Type_By_Tag(i, k) + let s:tlist_{j}_{k}_ttype_idx = s:tlist_{i}_{k}_ttype_idx + let s:tlist_{j}_{k}_tag_proto = s:Tlist_Get_Tag_Prototype(i, k) + let s:tlist_{j}_{k}_tag_searchpat = s:Tlist_Get_Tag_SearchPat(i, k) + let s:tlist_{j}_{k}_tag_linenum = s:Tlist_Get_Tag_Linenum(i, k) + let k = k + 1 + endwhile + + let ftype = s:tlist_{i}_filetype + + let k = 1 + while k <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{k}_name + let s:tlist_{j}_{ttype} = s:tlist_{i}_{ttype} + let s:tlist_{j}_{ttype}_offset = s:tlist_{i}_{ttype}_offset + let s:tlist_{j}_{ttype}_count = s:tlist_{i}_{ttype}_count + if s:tlist_{j}_{ttype} != '' + let l = 1 + while l <= s:tlist_{j}_{ttype}_count + let s:tlist_{j}_{ttype}_{l} = s:tlist_{i}_{ttype}_{l} + let l = l + 1 + endwhile + endif + let k = k + 1 + endwhile + + " As the file and tag information is copied to the new index, + " discard the previous information + call s:Tlist_Discard_FileInfo(i) + + let i = i + 1 + endwhile + + " Reduce the number of files displayed + let s:tlist_file_count = s:tlist_file_count - 1 + + if g:Tlist_Show_One_File + " If the tags for only one file is displayed and if we just + " now removed that file, then invalidate the current file idx + if s:tlist_cur_file_idx == fidx + let s:tlist_cur_file_idx = -1 + endif + endif +endfunction + +" Tlist_Window_Goto_Window +" Goto the taglist window +function! s:Tlist_Window_Goto_Window() + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + if winnr() != winnum + call s:Tlist_Exe_Cmd_No_Acmds(winnum . 'wincmd w') + endif + endif +endfunction + +" Tlist_Window_Create +" Create a new taglist window. If it is already open, jump to it +function! s:Tlist_Window_Create() + call s:Tlist_Log_Msg('Tlist_Window_Create()') + " If the window is open, jump to it + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + " Jump to the existing window + if winnr() != winnum + exe winnum . 'wincmd w' + endif + return + endif + + " If used with winmanager don't open windows. Winmanager will handle + " the window/buffer management + if s:tlist_app_name == "winmanager" + return + endif + + " Create a new window. If user prefers a horizontal window, then open + " a horizontally split window. Otherwise open a vertically split + " window + if g:Tlist_Use_Horiz_Window + " Open a horizontally split window + let win_dir = 'botright' + " Horizontal window height + let win_size = g:Tlist_WinHeight + else + if s:tlist_winsize_chgd == -1 + " Open a vertically split window. Increase the window size, if + " needed, to accomodate the new window + if g:Tlist_Inc_Winwidth && + \ &columns < (80 + g:Tlist_WinWidth) + " Save the original window position + let s:tlist_pre_winx = getwinposx() + let s:tlist_pre_winy = getwinposy() + + " one extra column is needed to include the vertical split + let &columns= &columns + g:Tlist_WinWidth + 1 + + let s:tlist_winsize_chgd = 1 + else + let s:tlist_winsize_chgd = 0 + endif + endif + + if g:Tlist_Use_Right_Window + " Open the window at the rightmost place + let win_dir = 'botright vertical' + else + " Open the window at the leftmost place + let win_dir = 'topleft vertical' + endif + let win_size = g:Tlist_WinWidth + endif + + " If the tag listing temporary buffer already exists, then reuse it. + " Otherwise create a new buffer + let bufnum = bufnr(g:TagList_title) + if bufnum == -1 + " Create a new buffer + let wcmd = g:TagList_title + else + " Edit the existing buffer + let wcmd = '+buffer' . bufnum + endif + + " Create the taglist window + exe 'silent! ' . win_dir . ' ' . win_size . 'split ' . wcmd + + " Save the new window position + let s:tlist_winx = getwinposx() + let s:tlist_winy = getwinposy() + + " Initialize the taglist window + call s:Tlist_Window_Init() +endfunction + +" Tlist_Window_Zoom +" Zoom (maximize/minimize) the taglist window +function! s:Tlist_Window_Zoom() + if s:tlist_win_maximized + " Restore the window back to the previous size + if g:Tlist_Use_Horiz_Window + exe 'resize ' . g:Tlist_WinHeight + else + exe 'vert resize ' . g:Tlist_WinWidth + endif + let s:tlist_win_maximized = 0 + else + " Set the window size to the maximum possible without closing other + " windows + if g:Tlist_Use_Horiz_Window + resize + else + vert resize + endif + let s:tlist_win_maximized = 1 + endif +endfunction + +" Tlist_Ballon_Expr +" When the mouse cursor is over a tag in the taglist window, display the +" tag prototype (balloon) +function! Tlist_Ballon_Expr() + " Get the file index + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(v:beval_lnum) + if fidx == -1 + return '' + endif + + " Get the tag output line for the current tag + let tidx = s:Tlist_Window_Get_Tag_Index(fidx, v:beval_lnum) + if tidx == 0 + return '' + endif + + " Get the tag search pattern and display it + return s:Tlist_Get_Tag_Prototype(fidx, tidx) +endfunction + +" Tlist_Window_Check_Width +" Check the width of the taglist window. For horizontally split windows, the +" 'winfixheight' option is used to fix the height of the window. For +" vertically split windows, Vim doesn't support the 'winfixwidth' option. So +" need to handle window width changes from this function. +function! s:Tlist_Window_Check_Width() + let tlist_winnr = bufwinnr(g:TagList_title) + if tlist_winnr == -1 + return + endif + + let width = winwidth(tlist_winnr) + if width != g:Tlist_WinWidth + call s:Tlist_Log_Msg("Tlist_Window_Check_Width: Changing window " . + \ "width from " . width . " to " . g:Tlist_WinWidth) + let save_winnr = winnr() + if save_winnr != tlist_winnr + call s:Tlist_Exe_Cmd_No_Acmds(tlist_winnr . 'wincmd w') + endif + exe 'vert resize ' . g:Tlist_WinWidth + if save_winnr != tlist_winnr + call s:Tlist_Exe_Cmd_No_Acmds('wincmd p') + endif + endif +endfunction + +" Tlist_Window_Exit_Only_Window +" If the 'Tlist_Exit_OnlyWindow' option is set, then exit Vim if only the +" taglist window is present. +function! s:Tlist_Window_Exit_Only_Window() + " Before quitting Vim, delete the taglist buffer so that + " the '0 mark is correctly set to the previous buffer. + if v:version < 700 + if winbufnr(2) == -1 + bdelete + quit + endif + else + if winbufnr(2) == -1 + if tabpagenr('$') == 1 + " Only one tag page is present + bdelete + quit + else + " More than one tab page is present. Close only the current + " tab page + close + endif + endif + endif +endfunction + +" Tlist_Window_Init +" Set the default options for the taglist window +function! s:Tlist_Window_Init() + call s:Tlist_Log_Msg('Tlist_Window_Init()') + + " The 'readonly' option should not be set for the taglist buffer. + " If Vim is started as "view/gview" or if the ":view" command is + " used, then the 'readonly' option is set for all the buffers. + " Unset it for the taglist buffer + setlocal noreadonly + + " Set the taglist buffer filetype to taglist + setlocal filetype=taglist + + " Define taglist window element highlighting + syntax match TagListComment '^" .*' + syntax match TagListFileName '^[^" ].*$' + syntax match TagListTitle '^ \S.*$' + syntax match TagListTagScope '\s\[.\{-\}\]$' + + " Define the highlighting only if colors are supported + if has('gui_running') || &t_Co > 2 + " Colors to highlight various taglist window elements + " If user defined highlighting group exists, then use them. + " Otherwise, use default highlight groups. + if hlexists('MyTagListTagName') + highlight link TagListTagName MyTagListTagName + else + highlight default link TagListTagName Search + endif + " Colors to highlight comments and titles + if hlexists('MyTagListComment') + highlight link TagListComment MyTagListComment + else + highlight clear TagListComment + highlight default link TagListComment Comment + endif + if hlexists('MyTagListTitle') + highlight link TagListTitle MyTagListTitle + else + highlight clear TagListTitle + highlight default link TagListTitle Title + endif + if hlexists('MyTagListFileName') + highlight link TagListFileName MyTagListFileName + else + highlight clear TagListFileName + highlight default TagListFileName guibg=Grey ctermbg=darkgray + \ guifg=white ctermfg=white + endif + if hlexists('MyTagListTagScope') + highlight link TagListTagScope MyTagListTagScope + else + highlight clear TagListTagScope + highlight default link TagListTagScope Identifier + endif + else + highlight default TagListTagName term=reverse cterm=reverse + endif + + " Folding related settings + setlocal foldenable + setlocal foldminlines=0 + setlocal foldmethod=manual + setlocal foldlevel=9999 + if g:Tlist_Enable_Fold_Column + setlocal foldcolumn=3 + else + setlocal foldcolumn=0 + endif + setlocal foldtext=v:folddashes.getline(v:foldstart) + + if s:tlist_app_name != "winmanager" + " Mark buffer as scratch + silent! setlocal buftype=nofile + if s:tlist_app_name == "none" + silent! setlocal bufhidden=delete + endif + silent! setlocal noswapfile + " Due to a bug in Vim 6.0, the winbufnr() function fails for unlisted + " buffers. So if the taglist buffer is unlisted, multiple taglist + " windows will be opened. This bug is fixed in Vim 6.1 and above + if v:version >= 601 + silent! setlocal nobuflisted + endif + endif + + silent! setlocal nowrap + + " If the 'number' option is set in the source window, it will affect the + " taglist window. So forcefully disable 'number' option for the taglist + " window + silent! setlocal nonumber + + " Use fixed height when horizontally split window is used + if g:Tlist_Use_Horiz_Window + if v:version >= 602 + set winfixheight + endif + endif + if !g:Tlist_Use_Horiz_Window && v:version >= 700 + set winfixwidth + endif + + " Setup balloon evaluation to display tag prototype + if v:version >= 700 && has('balloon_eval') + setlocal balloonexpr=Tlist_Ballon_Expr() + set ballooneval + endif + + " Setup the cpoptions properly for the maps to work + let old_cpoptions = &cpoptions + set cpoptions&vim + + " Create buffer local mappings for jumping to the tags and sorting the list + nnoremap <buffer> <silent> <CR> + \ :call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR> + nnoremap <buffer> <silent> o + \ :call <SID>Tlist_Window_Jump_To_Tag('newwin')<CR> + nnoremap <buffer> <silent> p + \ :call <SID>Tlist_Window_Jump_To_Tag('preview')<CR> + nnoremap <buffer> <silent> P + \ :call <SID>Tlist_Window_Jump_To_Tag('prevwin')<CR> + if v:version >= 700 + nnoremap <buffer> <silent> t + \ :call <SID>Tlist_Window_Jump_To_Tag('checktab')<CR> + nnoremap <buffer> <silent> <C-t> + \ :call <SID>Tlist_Window_Jump_To_Tag('newtab')<CR> + endif + nnoremap <buffer> <silent> <2-LeftMouse> + \ :call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR> + nnoremap <buffer> <silent> s + \ :call <SID>Tlist_Change_Sort('cmd', 'toggle', '')<CR> + nnoremap <buffer> <silent> + :silent! foldopen<CR> + nnoremap <buffer> <silent> - :silent! foldclose<CR> + nnoremap <buffer> <silent> * :silent! %foldopen!<CR> + nnoremap <buffer> <silent> = :silent! %foldclose<CR> + nnoremap <buffer> <silent> <kPlus> :silent! foldopen<CR> + nnoremap <buffer> <silent> <kMinus> :silent! foldclose<CR> + nnoremap <buffer> <silent> <kMultiply> :silent! %foldopen!<CR> + nnoremap <buffer> <silent> <Space> :call <SID>Tlist_Window_Show_Info()<CR> + nnoremap <buffer> <silent> u :call <SID>Tlist_Window_Update_File()<CR> + nnoremap <buffer> <silent> d :call <SID>Tlist_Remove_File(-1, 1)<CR> + nnoremap <buffer> <silent> x :call <SID>Tlist_Window_Zoom()<CR> + nnoremap <buffer> <silent> [[ :call <SID>Tlist_Window_Move_To_File(-1)<CR> + nnoremap <buffer> <silent> <BS> :call <SID>Tlist_Window_Move_To_File(-1)<CR> + nnoremap <buffer> <silent> ]] :call <SID>Tlist_Window_Move_To_File(1)<CR> + nnoremap <buffer> <silent> <Tab> :call <SID>Tlist_Window_Move_To_File(1)<CR> + nnoremap <buffer> <silent> <F1> :call <SID>Tlist_Window_Toggle_Help_Text()<CR> + nnoremap <buffer> <silent> q :close<CR> + + " Insert mode mappings + inoremap <buffer> <silent> <CR> + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR> + " Windows needs return + inoremap <buffer> <silent> <Return> + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR> + inoremap <buffer> <silent> o + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('newwin')<CR> + inoremap <buffer> <silent> p + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('preview')<CR> + inoremap <buffer> <silent> P + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('prevwin')<CR> + if v:version >= 700 + inoremap <buffer> <silent> t + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('checktab')<CR> + inoremap <buffer> <silent> <C-t> + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('newtab')<CR> + endif + inoremap <buffer> <silent> <2-LeftMouse> + \ <C-o>:call <SID>Tlist_Window_Jump_To_Tag('useopen')<CR> + inoremap <buffer> <silent> s + \ <C-o>:call <SID>Tlist_Change_Sort('cmd', 'toggle', '')<CR> + inoremap <buffer> <silent> + <C-o>:silent! foldopen<CR> + inoremap <buffer> <silent> - <C-o>:silent! foldclose<CR> + inoremap <buffer> <silent> * <C-o>:silent! %foldopen!<CR> + inoremap <buffer> <silent> = <C-o>:silent! %foldclose<CR> + inoremap <buffer> <silent> <kPlus> <C-o>:silent! foldopen<CR> + inoremap <buffer> <silent> <kMinus> <C-o>:silent! foldclose<CR> + inoremap <buffer> <silent> <kMultiply> <C-o>:silent! %foldopen!<CR> + inoremap <buffer> <silent> <Space> <C-o>:call + \ <SID>Tlist_Window_Show_Info()<CR> + inoremap <buffer> <silent> u + \ <C-o>:call <SID>Tlist_Window_Update_File()<CR> + inoremap <buffer> <silent> d <C-o>:call <SID>Tlist_Remove_File(-1, 1)<CR> + inoremap <buffer> <silent> x <C-o>:call <SID>Tlist_Window_Zoom()<CR> + inoremap <buffer> <silent> [[ <C-o>:call <SID>Tlist_Window_Move_To_File(-1)<CR> + inoremap <buffer> <silent> <BS> <C-o>:call <SID>Tlist_Window_Move_To_File(-1)<CR> + inoremap <buffer> <silent> ]] <C-o>:call <SID>Tlist_Window_Move_To_File(1)<CR> + inoremap <buffer> <silent> <Tab> <C-o>:call <SID>Tlist_Window_Move_To_File(1)<CR> + inoremap <buffer> <silent> <F1> <C-o>:call <SID>Tlist_Window_Toggle_Help_Text()<CR> + inoremap <buffer> <silent> q <C-o>:close<CR> + + " Map single left mouse click if the user wants this functionality + if g:Tlist_Use_SingleClick == 1 + " Contributed by Bindu Wavell + " attempt to perform single click mapping, it would be much + " nicer if we could nnoremap <buffer> ... however vim does + " not fire the <buffer> <leftmouse> when you use the mouse + " to enter a buffer. + let clickmap = ':if bufname("%") =~ "__Tag_List__" <bar> ' . + \ 'call <SID>Tlist_Window_Jump_To_Tag("useopen") ' . + \ '<bar> endif <CR>' + if maparg('<leftmouse>', 'n') == '' + " no mapping for leftmouse + exe ':nnoremap <silent> <leftmouse> <leftmouse>' . clickmap + else + " we have a mapping + let mapcmd = ':nnoremap <silent> <leftmouse> <leftmouse>' + let mapcmd = mapcmd . substitute(substitute( + \ maparg('<leftmouse>', 'n'), '|', '<bar>', 'g'), + \ '\c^<leftmouse>', '', '') + let mapcmd = mapcmd . clickmap + exe mapcmd + endif + endif + + " Define the taglist autocommands + augroup TagListAutoCmds + autocmd! + " Display the tag prototype for the tag under the cursor. + autocmd CursorHold __Tag_List__ call s:Tlist_Window_Show_Info() + " Highlight the current tag periodically + autocmd CursorHold * silent call s:Tlist_Window_Highlight_Tag( + \ fnamemodify(bufname('%'), ':p'), line('.'), 1, 0) + + " Adjust the Vim window width when taglist window is closed + autocmd BufUnload __Tag_List__ call s:Tlist_Post_Close_Cleanup() + " Close the fold for this buffer when leaving the buffer + if g:Tlist_File_Fold_Auto_Close + autocmd BufEnter * silent + \ call s:Tlist_Window_Open_File_Fold(expand('<abuf>')) + endif + " Exit Vim itself if only the taglist window is present (optional) + if g:Tlist_Exit_OnlyWindow + autocmd BufEnter __Tag_List__ nested + \ call s:Tlist_Window_Exit_Only_Window() + endif + if s:tlist_app_name != "winmanager" && + \ !g:Tlist_Process_File_Always && + \ (!has('gui_running') || !g:Tlist_Show_Menu) + " Auto refresh the taglist window + autocmd BufEnter * call s:Tlist_Refresh() + endif + + if !g:Tlist_Use_Horiz_Window + if v:version < 700 + autocmd WinEnter * call s:Tlist_Window_Check_Width() + endif + endif + if v:version >= 700 + autocmd TabEnter * silent call s:Tlist_Refresh_Folds() + endif + augroup end + + " Restore the previous cpoptions settings + let &cpoptions = old_cpoptions +endfunction + +" Tlist_Window_Refresh +" Display the tags for all the files in the taglist window +function! s:Tlist_Window_Refresh() + call s:Tlist_Log_Msg('Tlist_Window_Refresh()') + " Set report option to a huge value to prevent informational messages + " while deleting the lines + let old_report = &report + set report=99999 + + " Mark the buffer as modifiable + setlocal modifiable + + " Delete the contents of the buffer to the black-hole register + silent! %delete _ + + " As we have cleared the taglist window, mark all the files + " as not visible + let i = 0 + while i < s:tlist_file_count + let s:tlist_{i}_visible = 0 + let i = i + 1 + endwhile + + if g:Tlist_Compact_Format == 0 + " Display help in non-compact mode + call s:Tlist_Window_Display_Help() + endif + + " Mark the buffer as not modifiable + setlocal nomodifiable + + " Restore the report option + let &report = old_report + + " If the tags for only one file should be displayed in the taglist + " window, then no need to add the tags here. The bufenter autocommand + " will add the tags for that file. + if g:Tlist_Show_One_File + return + endif + + " List all the tags for the previously processed files + " Do this only if taglist is configured to display tags for more than + " one file. Otherwise, when Tlist_Show_One_File is configured, + " tags for the wrong file will be displayed. + let i = 0 + while i < s:tlist_file_count + call s:Tlist_Window_Refresh_File(s:tlist_{i}_filename, + \ s:tlist_{i}_filetype) + let i = i + 1 + endwhile + + if g:Tlist_Auto_Update + " Add and list the tags for all buffers in the Vim buffer list + let i = 1 + let last_bufnum = bufnr('$') + while i <= last_bufnum + if buflisted(i) + let fname = fnamemodify(bufname(i), ':p') + let ftype = s:Tlist_Get_Buffer_Filetype(i) + " If the file doesn't support tag listing, skip it + if !s:Tlist_Skip_File(fname, ftype) + call s:Tlist_Window_Refresh_File(fname, ftype) + endif + endif + let i = i + 1 + endwhile + endif + + " If Tlist_File_Fold_Auto_Close option is set, then close all the folds + if g:Tlist_File_Fold_Auto_Close + " Close all the folds + silent! %foldclose + endif + + " Move the cursor to the top of the taglist window + normal! gg +endfunction + +" Tlist_Post_Close_Cleanup() +" Close the taglist window and adjust the Vim window width +function! s:Tlist_Post_Close_Cleanup() + call s:Tlist_Log_Msg('Tlist_Post_Close_Cleanup()') + " Mark all the files as not visible + let i = 0 + while i < s:tlist_file_count + let s:tlist_{i}_visible = 0 + let i = i + 1 + endwhile + + " Remove the taglist autocommands + silent! autocmd! TagListAutoCmds + + " Clear all the highlights + match none + + silent! syntax clear TagListTitle + silent! syntax clear TagListComment + silent! syntax clear TagListTagScope + + " Remove the left mouse click mapping if it was setup initially + if g:Tlist_Use_SingleClick + if hasmapto('<LeftMouse>') + nunmap <LeftMouse> + endif + endif + + if s:tlist_app_name != "winmanager" + if g:Tlist_Use_Horiz_Window || g:Tlist_Inc_Winwidth == 0 || + \ s:tlist_winsize_chgd != 1 || + \ &columns < (80 + g:Tlist_WinWidth) + " No need to adjust window width if using horizontally split taglist + " window or if columns is less than 101 or if the user chose not to + " adjust the window width + else + " If the user didn't manually move the window, then restore the window + " position to the pre-taglist position + if s:tlist_pre_winx != -1 && s:tlist_pre_winy != -1 && + \ getwinposx() == s:tlist_winx && + \ getwinposy() == s:tlist_winy + exe 'winpos ' . s:tlist_pre_winx . ' ' . s:tlist_pre_winy + endif + + " Adjust the Vim window width + let &columns= &columns - (g:Tlist_WinWidth + 1) + endif + endif + + let s:tlist_winsize_chgd = -1 + + " Reset taglist state variables + if s:tlist_app_name == "winmanager" + let s:tlist_app_name = "none" + endif + let s:tlist_window_initialized = 0 +endfunction + +" Tlist_Window_Refresh_File() +" List the tags defined in the specified file in a Vim window +function! s:Tlist_Window_Refresh_File(filename, ftype) + call s:Tlist_Log_Msg('Tlist_Window_Refresh_File (' . a:filename . ')') + " First check whether the file already exists + let fidx = s:Tlist_Get_File_Index(a:filename) + if fidx != -1 + let file_listed = 1 + else + let file_listed = 0 + endif + + if !file_listed + " Check whether this file is removed based on user request + " If it is, then don't display the tags for this file + if s:Tlist_User_Removed_File(a:filename) + return + endif + endif + + if file_listed && s:tlist_{fidx}_visible + " Check whether the file tags are currently valid + if s:tlist_{fidx}_valid + " Goto the first line in the file + exe s:tlist_{fidx}_start + + " If the line is inside a fold, open the fold + if foldclosed('.') != -1 + exe "silent! " . s:tlist_{fidx}_start . "," . + \ s:tlist_{fidx}_end . "foldopen!" + endif + return + endif + + " Discard and remove the tags for this file from display + call s:Tlist_Discard_TagInfo(fidx) + call s:Tlist_Window_Remove_File_From_Display(fidx) + endif + + " Process and generate a list of tags defined in the file + if !file_listed || !s:tlist_{fidx}_valid + let ret_fidx = s:Tlist_Process_File(a:filename, a:ftype) + if ret_fidx == -1 + return + endif + let fidx = ret_fidx + endif + + " Set report option to a huge value to prevent informational messages + " while adding lines to the taglist window + let old_report = &report + set report=99999 + + if g:Tlist_Show_One_File + " Remove the previous file + if s:tlist_cur_file_idx != -1 + call s:Tlist_Window_Remove_File_From_Display(s:tlist_cur_file_idx) + let s:tlist_{s:tlist_cur_file_idx}_visible = 0 + let s:tlist_{s:tlist_cur_file_idx}_start = 0 + let s:tlist_{s:tlist_cur_file_idx}_end = 0 + endif + let s:tlist_cur_file_idx = fidx + endif + + " Mark the buffer as modifiable + setlocal modifiable + + " Add new files to the end of the window. For existing files, add them at + " the same line where they were previously present. If the file is not + " visible, then add it at the end + if s:tlist_{fidx}_start == 0 || !s:tlist_{fidx}_visible + if g:Tlist_Compact_Format + let s:tlist_{fidx}_start = line('$') + else + let s:tlist_{fidx}_start = line('$') + 1 + endif + endif + + let s:tlist_{fidx}_visible = 1 + + " Goto the line where this file should be placed + if g:Tlist_Compact_Format + exe s:tlist_{fidx}_start + else + exe s:tlist_{fidx}_start - 1 + endif + + let txt = fnamemodify(s:tlist_{fidx}_filename, ':t') . ' (' . + \ fnamemodify(s:tlist_{fidx}_filename, ':p:h') . ')' + if g:Tlist_Compact_Format == 0 + silent! put =txt + else + silent! put! =txt + " Move to the next line + exe line('.') + 1 + endif + let file_start = s:tlist_{fidx}_start + + " Add the tag names grouped by tag type to the buffer with a title + let i = 1 + let ttype_cnt = s:tlist_{a:ftype}_count + while i <= ttype_cnt + let ttype = s:tlist_{a:ftype}_{i}_name + " Add the tag type only if there are tags for that type + let fidx_ttype = 's:tlist_' . fidx . '_' . ttype + let ttype_txt = {fidx_ttype} + if ttype_txt != '' + let txt = ' ' . s:tlist_{a:ftype}_{i}_fullname + if g:Tlist_Compact_Format == 0 + let ttype_start_lnum = line('.') + 1 + silent! put =txt + else + let ttype_start_lnum = line('.') + silent! put! =txt + endif + silent! put =ttype_txt + + let {fidx_ttype}_offset = ttype_start_lnum - file_start + + " create a fold for this tag type + let fold_start = ttype_start_lnum + let fold_end = fold_start + {fidx_ttype}_count + exe fold_start . ',' . fold_end . 'fold' + + " Adjust the cursor position + if g:Tlist_Compact_Format == 0 + exe ttype_start_lnum + {fidx_ttype}_count + else + exe ttype_start_lnum + {fidx_ttype}_count + 1 + endif + + if g:Tlist_Compact_Format == 0 + " Separate the tag types by a empty line + silent! put ='' + endif + endif + let i = i + 1 + endwhile + + if s:tlist_{fidx}_tag_count == 0 + if g:Tlist_Compact_Format == 0 + silent! put ='' + endif + endif + + let s:tlist_{fidx}_end = line('.') - 1 + + " Create a fold for the entire file + exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'fold' + exe 'silent! ' . s:tlist_{fidx}_start . ',' . + \ s:tlist_{fidx}_end . 'foldopen!' + + " Goto the starting line for this file, + exe s:tlist_{fidx}_start + + if s:tlist_app_name == "winmanager" + " To handle a bug in the winmanager plugin, add a space at the + " last line + call setline('$', ' ') + endif + + " Mark the buffer as not modifiable + setlocal nomodifiable + + " Restore the report option + let &report = old_report + + " Update the start and end line numbers for all the files following this + " file + let start = s:tlist_{fidx}_start + " include the empty line after the last line + if g:Tlist_Compact_Format + let end = s:tlist_{fidx}_end + else + let end = s:tlist_{fidx}_end + 1 + endif + call s:Tlist_Window_Update_Line_Offsets(fidx + 1, 1, end - start + 1) + + " Now that we have updated the taglist window, update the tags + " menu (if present) + if g:Tlist_Show_Menu + call s:Tlist_Menu_Update_File(1) + endif +endfunction + +" Tlist_Init_File +" Initialize the variables for a new file +function! s:Tlist_Init_File(filename, ftype) + call s:Tlist_Log_Msg('Tlist_Init_File (' . a:filename . ')') + " Add new files at the end of the list + let fidx = s:tlist_file_count + let s:tlist_file_count = s:tlist_file_count + 1 + " Add the new file name to the taglist list of file names + let s:tlist_file_names = s:tlist_file_names . a:filename . "\n" + + " Initialize the file variables + let s:tlist_{fidx}_filename = a:filename + let s:tlist_{fidx}_sort_type = g:Tlist_Sort_Type + let s:tlist_{fidx}_filetype = a:ftype + let s:tlist_{fidx}_mtime = -1 + let s:tlist_{fidx}_start = 0 + let s:tlist_{fidx}_end = 0 + let s:tlist_{fidx}_valid = 0 + let s:tlist_{fidx}_visible = 0 + let s:tlist_{fidx}_tag_count = 0 + let s:tlist_{fidx}_menu_cmd = '' + + " Initialize the tag type variables + let i = 1 + while i <= s:tlist_{a:ftype}_count + let ttype = s:tlist_{a:ftype}_{i}_name + let s:tlist_{fidx}_{ttype} = '' + let s:tlist_{fidx}_{ttype}_offset = 0 + let s:tlist_{fidx}_{ttype}_count = 0 + let i = i + 1 + endwhile + + return fidx +endfunction + +" Tlist_Get_Tag_Type_By_Tag +" Return the tag type for the specified tag index +function! s:Tlist_Get_Tag_Type_By_Tag(fidx, tidx) + let ttype_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_type' + + " Already parsed and have the tag name + if exists(ttype_var) + return {ttype_var} + endif + + let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag + let {ttype_var} = s:Tlist_Extract_Tagtype(tag_line) + + return {ttype_var} +endfunction + +" Tlist_Get_Tag_Prototype +function! s:Tlist_Get_Tag_Prototype(fidx, tidx) + let tproto_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_proto' + + " Already parsed and have the tag prototype + if exists(tproto_var) + return {tproto_var} + endif + + " Parse and extract the tag prototype + let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag + let start = stridx(tag_line, '/^') + 2 + let end = stridx(tag_line, '/;"' . "\t") + if tag_line[end - 1] == '$' + let end = end -1 + endif + let tag_proto = strpart(tag_line, start, end - start) + let {tproto_var} = substitute(tag_proto, '\s*', '', '') + + return {tproto_var} +endfunction + +" Tlist_Get_Tag_SearchPat +function! s:Tlist_Get_Tag_SearchPat(fidx, tidx) + let tpat_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_searchpat' + + " Already parsed and have the tag search pattern + if exists(tpat_var) + return {tpat_var} + endif + + " Parse and extract the tag search pattern + let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag + let start = stridx(tag_line, '/^') + 2 + let end = stridx(tag_line, '/;"' . "\t") + if tag_line[end - 1] == '$' + let end = end -1 + endif + let {tpat_var} = '\V\^' . strpart(tag_line, start, end - start) . + \ (tag_line[end] == '$' ? '\$' : '') + + return {tpat_var} +endfunction + +" Tlist_Get_Tag_Linenum +" Return the tag line number, given the tag index +function! s:Tlist_Get_Tag_Linenum(fidx, tidx) + let tline_var = 's:tlist_' . a:fidx . '_' . a:tidx . '_tag_linenum' + + " Already parsed and have the tag line number + if exists(tline_var) + return {tline_var} + endif + + " Parse and extract the tag line number + let tag_line = s:tlist_{a:fidx}_{a:tidx}_tag + let start = strridx(tag_line, 'line:') + 5 + let end = strridx(tag_line, "\t") + if end < start + let {tline_var} = strpart(tag_line, start) + 0 + else + let {tline_var} = strpart(tag_line, start, end - start) + 0 + endif + + return {tline_var} +endfunction + +" Tlist_Parse_Tagline +" Parse a tag line from the ctags output. Separate the tag output based on the +" tag type and store it in the tag type variable. +" The format of each line in the ctags output is: +" +" tag_name<TAB>file_name<TAB>ex_cmd;"<TAB>extension_fields +" +function! s:Tlist_Parse_Tagline(tag_line) + if a:tag_line == '' + " Skip empty lines + return + endif + + " Extract the tag type + let ttype = s:Tlist_Extract_Tagtype(a:tag_line) + + " Make sure the tag type is a valid and supported one + if ttype == '' || stridx(s:ctags_flags, ttype) == -1 + " Line is not in proper tags format or Tag type is not supported + return + endif + + " Update the total tag count + let s:tidx = s:tidx + 1 + + " The following variables are used to optimize this code. Vim is slow in + " using curly brace names. To reduce the amount of processing needed, the + " curly brace variables are pre-processed here + let fidx_tidx = 's:tlist_' . s:fidx . '_' . s:tidx + let fidx_ttype = 's:tlist_' . s:fidx . '_' . ttype + + " Update the count of this tag type + let ttype_idx = {fidx_ttype}_count + 1 + let {fidx_ttype}_count = ttype_idx + + " Store the ctags output for this tag + let {fidx_tidx}_tag = a:tag_line + + " Store the tag index and the tag type index (back pointers) + let {fidx_ttype}_{ttype_idx} = s:tidx + let {fidx_tidx}_ttype_idx = ttype_idx + + " Extract the tag name + let tag_name = strpart(a:tag_line, 0, stridx(a:tag_line, "\t")) + + " Extract the tag scope/prototype + if g:Tlist_Display_Prototype + let ttxt = ' ' . s:Tlist_Get_Tag_Prototype(s:fidx, s:tidx) + else + let ttxt = ' ' . tag_name + + " Add the tag scope, if it is available and is configured. Tag + " scope is the last field after the 'line:<num>\t' field + if g:Tlist_Display_Tag_Scope + let tag_scope = s:Tlist_Extract_Tag_Scope(a:tag_line) + if tag_scope != '' + let ttxt = ttxt . ' [' . tag_scope . ']' + endif + endif + endif + + " Add this tag to the tag type variable + let {fidx_ttype} = {fidx_ttype} . ttxt . "\n" + + " Save the tag name + let {fidx_tidx}_tag_name = tag_name +endfunction + +" Tlist_Process_File +" Get the list of tags defined in the specified file and store them +" in Vim variables. Returns the file index where the tags are stored. +function! s:Tlist_Process_File(filename, ftype) + call s:Tlist_Log_Msg('Tlist_Process_File (' . a:filename . ', ' . + \ a:ftype . ')') + " Check whether this file is supported + if s:Tlist_Skip_File(a:filename, a:ftype) + return -1 + endif + + " If the tag types for this filetype are not yet created, then create + " them now + let var = 's:tlist_' . a:ftype . '_count' + if !exists(var) + if s:Tlist_FileType_Init(a:ftype) == 0 + return -1 + endif + endif + + " If this file is already processed, then use the cached values + let fidx = s:Tlist_Get_File_Index(a:filename) + if fidx == -1 + " First time, this file is loaded + let fidx = s:Tlist_Init_File(a:filename, a:ftype) + else + " File was previously processed. Discard the tag information + call s:Tlist_Discard_TagInfo(fidx) + endif + + let s:tlist_{fidx}_valid = 1 + + " Exuberant ctags arguments to generate a tag list + let ctags_args = ' -f - --format=2 --excmd=pattern --fields=nks ' + + " Form the ctags argument depending on the sort type + if s:tlist_{fidx}_sort_type == 'name' + let ctags_args = ctags_args . '--sort=yes' + else + let ctags_args = ctags_args . '--sort=no' + endif + + " Add the filetype specific arguments + let ctags_args = ctags_args . ' ' . s:tlist_{a:ftype}_ctags_args + + " Ctags command to produce output with regexp for locating the tags + let ctags_cmd = g:Tlist_Ctags_Cmd . ctags_args + let ctags_cmd = ctags_cmd . ' "' . a:filename . '"' + + if &shellxquote == '"' + " Double-quotes within double-quotes will not work in the + " command-line.If the 'shellxquote' option is set to double-quotes, + " then escape the double-quotes in the ctags command-line. + let ctags_cmd = escape(ctags_cmd, '"') + endif + + " In Windows 95, if not using cygwin, disable the 'shellslash' + " option. Otherwise, this will cause problems when running the + " ctags command. + if has('win95') && !has('win32unix') + let old_shellslash = &shellslash + set noshellslash + endif + + if has('win32') && !has('win32unix') && !has('win95') + \ && (&shell =~ 'cmd.exe') + " Windows does not correctly deal with commands that have more than 1 + " set of double quotes. It will strip them all resulting in: + " 'C:\Program' is not recognized as an internal or external command + " operable program or batch file. To work around this, place the + " command inside a batch file and call the batch file. + " Do this only on Win2K, WinXP and above. + " Contributed by: David Fishburn. + let s:taglist_tempfile = fnamemodify(tempname(), ':h') . + \ '\taglist.cmd' + exe 'redir! > ' . s:taglist_tempfile + silent echo ctags_cmd + redir END + + call s:Tlist_Log_Msg('Cmd inside batch file: ' . ctags_cmd) + let ctags_cmd = '"' . s:taglist_tempfile . '"' + endif + + call s:Tlist_Log_Msg('Cmd: ' . ctags_cmd) + + " Run ctags and get the tag list + let cmd_output = system(ctags_cmd) + + " Restore the value of the 'shellslash' option. + if has('win95') && !has('win32unix') + let &shellslash = old_shellslash + endif + + if exists('s:taglist_tempfile') + " Delete the temporary cmd file created on MS-Windows + call delete(s:taglist_tempfile) + endif + + " Handle errors + if v:shell_error + let msg = "Taglist: Failed to generate tags for " . a:filename + call s:Tlist_Warning_Msg(msg) + if cmd_output != '' + call s:Tlist_Warning_Msg(cmd_output) + endif + return fidx + endif + + " Store the modification time for the file + let s:tlist_{fidx}_mtime = getftime(a:filename) + + " No tags for current file + if cmd_output == '' + call s:Tlist_Log_Msg('No tags defined in ' . a:filename) + return fidx + endif + + call s:Tlist_Log_Msg('Generated tags information for ' . a:filename) + + if v:version > 601 + " The following script local variables are used by the + " Tlist_Parse_Tagline() function. + let s:ctags_flags = s:tlist_{a:ftype}_ctags_flags + let s:fidx = fidx + let s:tidx = 0 + + " Process the ctags output one line at a time. The substitute() + " command is used to parse the tag lines instead of using the + " matchstr()/stridx()/strpart() functions for performance reason + call substitute(cmd_output, "\\([^\n]\\+\\)\n", + \ '\=s:Tlist_Parse_Tagline(submatch(1))', 'g') + + " Save the number of tags for this file + let s:tlist_{fidx}_tag_count = s:tidx + + " The following script local variables are no longer needed + unlet! s:ctags_flags + unlet! s:tidx + unlet! s:fidx + else + " Due to a bug in Vim earlier than version 6.1, + " we cannot use substitute() to parse the ctags output. + " Instead the slow str*() functions are used + let ctags_flags = s:tlist_{a:ftype}_ctags_flags + let tidx = 0 + + while cmd_output != '' + " Extract one line at a time + let idx = stridx(cmd_output, "\n") + let one_line = strpart(cmd_output, 0, idx) + " Remove the line from the tags output + let cmd_output = strpart(cmd_output, idx + 1) + + if one_line == '' + " Line is not in proper tags format + continue + endif + + " Extract the tag type + let ttype = s:Tlist_Extract_Tagtype(one_line) + + " Make sure the tag type is a valid and supported one + if ttype == '' || stridx(ctags_flags, ttype) == -1 + " Line is not in proper tags format or Tag type is not + " supported + continue + endif + + " Update the total tag count + let tidx = tidx + 1 + + " The following variables are used to optimize this code. Vim is + " slow in using curly brace names. To reduce the amount of + " processing needed, the curly brace variables are pre-processed + " here + let fidx_tidx = 's:tlist_' . fidx . '_' . tidx + let fidx_ttype = 's:tlist_' . fidx . '_' . ttype + + " Update the count of this tag type + let ttype_idx = {fidx_ttype}_count + 1 + let {fidx_ttype}_count = ttype_idx + + " Store the ctags output for this tag + let {fidx_tidx}_tag = one_line + + " Store the tag index and the tag type index (back pointers) + let {fidx_ttype}_{ttype_idx} = tidx + let {fidx_tidx}_ttype_idx = ttype_idx + + " Extract the tag name + let tag_name = strpart(one_line, 0, stridx(one_line, "\t")) + + " Extract the tag scope/prototype + if g:Tlist_Display_Prototype + let ttxt = ' ' . s:Tlist_Get_Tag_Prototype(fidx, tidx) + else + let ttxt = ' ' . tag_name + + " Add the tag scope, if it is available and is configured. Tag + " scope is the last field after the 'line:<num>\t' field + if g:Tlist_Display_Tag_Scope + let tag_scope = s:Tlist_Extract_Tag_Scope(one_line) + if tag_scope != '' + let ttxt = ttxt . ' [' . tag_scope . ']' + endif + endif + endif + + " Add this tag to the tag type variable + let {fidx_ttype} = {fidx_ttype} . ttxt . "\n" + + " Save the tag name + let {fidx_tidx}_tag_name = tag_name + endwhile + + " Save the number of tags for this file + let s:tlist_{fidx}_tag_count = tidx + endif + + call s:Tlist_Log_Msg('Processed ' . s:tlist_{fidx}_tag_count . + \ ' tags in ' . a:filename) + + return fidx +endfunction + +" Tlist_Update_File +" Update the tags for a file (if needed) +function! Tlist_Update_File(filename, ftype) + call s:Tlist_Log_Msg('Tlist_Update_File (' . a:filename . ')') + " If the file doesn't support tag listing, skip it + if s:Tlist_Skip_File(a:filename, a:ftype) + return + endif + + " Convert the file name to a full path + let fname = fnamemodify(a:filename, ':p') + + " First check whether the file already exists + let fidx = s:Tlist_Get_File_Index(fname) + + if fidx != -1 && s:tlist_{fidx}_valid + " File exists and the tags are valid + " Check whether the file was modified after the last tags update + " If it is modified, then update the tags + if s:tlist_{fidx}_mtime == getftime(fname) + return + endif + else + " If the tags were removed previously based on a user request, + " as we are going to update the tags (based on the user request), + " remove the filename from the deleted list + call s:Tlist_Update_Remove_List(fname, 0) + endif + + " If the taglist window is opened, update it + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + " Taglist window is not present. Just update the taglist + " and return + call s:Tlist_Process_File(fname, a:ftype) + else + if g:Tlist_Show_One_File && s:tlist_cur_file_idx != -1 + " If tags for only one file are displayed and we are not + " updating the tags for that file, then no need to + " refresh the taglist window. Otherwise, the taglist + " window should be updated. + if s:tlist_{s:tlist_cur_file_idx}_filename != fname + call s:Tlist_Process_File(fname, a:ftype) + return + endif + endif + + " Save the current window number + let save_winnr = winnr() + + " Goto the taglist window + call s:Tlist_Window_Goto_Window() + + " Save the cursor position + let save_line = line('.') + let save_col = col('.') + + " Update the taglist window + call s:Tlist_Window_Refresh_File(fname, a:ftype) + + " Restore the cursor position + if v:version >= 601 + call cursor(save_line, save_col) + else + exe save_line + exe 'normal! ' . save_col . '|' + endif + + if winnr() != save_winnr + " Go back to the original window + call s:Tlist_Exe_Cmd_No_Acmds(save_winnr . 'wincmd w') + endif + endif + + " Update the taglist menu + if g:Tlist_Show_Menu + call s:Tlist_Menu_Update_File(1) + endif +endfunction + +" Tlist_Window_Close +" Close the taglist window +function! s:Tlist_Window_Close() + call s:Tlist_Log_Msg('Tlist_Window_Close()') + " Make sure the taglist window exists + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + call s:Tlist_Warning_Msg('Error: Taglist window is not open') + return + endif + + if winnr() == winnum + " Already in the taglist window. Close it and return + if winbufnr(2) != -1 + " If a window other than the taglist window is open, + " then only close the taglist window. + close + endif + else + " Goto the taglist window, close it and then come back to the + " original window + let curbufnr = bufnr('%') + exe winnum . 'wincmd w' + close + " Need to jump back to the original window only if we are not + " already in that window + let winnum = bufwinnr(curbufnr) + if winnr() != winnum + exe winnum . 'wincmd w' + endif + endif +endfunction + +" Tlist_Window_Mark_File_Window +" Mark the current window as the file window to use when jumping to a tag. +" Only if the current window is a non-plugin, non-preview and non-taglist +" window +function! s:Tlist_Window_Mark_File_Window() + if getbufvar('%', '&buftype') == '' && !&previewwindow + let w:tlist_file_window = "yes" + endif +endfunction + +" Tlist_Window_Open +" Open and refresh the taglist window +function! s:Tlist_Window_Open() + call s:Tlist_Log_Msg('Tlist_Window_Open()') + " If the window is open, jump to it + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + " Jump to the existing window + if winnr() != winnum + exe winnum . 'wincmd w' + endif + return + endif + + if s:tlist_app_name == "winmanager" + " Taglist plugin is no longer part of the winmanager app + let s:tlist_app_name = "none" + endif + + " Get the filename and filetype for the specified buffer + let curbuf_name = fnamemodify(bufname('%'), ':p') + let curbuf_ftype = s:Tlist_Get_Buffer_Filetype('%') + let cur_lnum = line('.') + + " Mark the current window as the desired window to open a file when a tag + " is selected. + call s:Tlist_Window_Mark_File_Window() + + " Open the taglist window + call s:Tlist_Window_Create() + + call s:Tlist_Window_Refresh() + + if g:Tlist_Show_One_File + " Add only the current buffer and file + " + " If the file doesn't support tag listing, skip it + if !s:Tlist_Skip_File(curbuf_name, curbuf_ftype) + call s:Tlist_Window_Refresh_File(curbuf_name, curbuf_ftype) + endif + endif + + if g:Tlist_File_Fold_Auto_Close + " Open the fold for the current file, as all the folds in + " the taglist window are closed + let fidx = s:Tlist_Get_File_Index(curbuf_name) + if fidx != -1 + exe "silent! " . s:tlist_{fidx}_start . "," . + \ s:tlist_{fidx}_end . "foldopen!" + endif + endif + + " Highlight the current tag + call s:Tlist_Window_Highlight_Tag(curbuf_name, cur_lnum, 1, 1) +endfunction + +" Tlist_Window_Toggle() +" Open or close a taglist window +function! s:Tlist_Window_Toggle() + call s:Tlist_Log_Msg('Tlist_Window_Toggle()') + " If taglist window is open then close it. + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + call s:Tlist_Window_Close() + return + endif + + call s:Tlist_Window_Open() + + " Go back to the original window, if Tlist_GainFocus_On_ToggleOpen is not + " set + if !g:Tlist_GainFocus_On_ToggleOpen + call s:Tlist_Exe_Cmd_No_Acmds('wincmd p') + endif + + " Update the taglist menu + if g:Tlist_Show_Menu + call s:Tlist_Menu_Update_File(0) + endif +endfunction + +" Tlist_Process_Filelist +" Process multiple files. Each filename is separated by "\n" +" Returns the number of processed files +function! s:Tlist_Process_Filelist(file_names) + let flist = a:file_names + + " Enable lazy screen updates + let old_lazyredraw = &lazyredraw + set lazyredraw + + " Keep track of the number of processed files + let fcnt = 0 + + " Process one file at a time + while flist != '' + let nl_idx = stridx(flist, "\n") + let one_file = strpart(flist, 0, nl_idx) + + " Remove the filename from the list + let flist = strpart(flist, nl_idx + 1) + + if one_file == '' + continue + endif + + " Skip directories + if isdirectory(one_file) + continue + endif + + let ftype = s:Tlist_Detect_Filetype(one_file) + + echon "\r " + echon "\rProcessing tags for " . fnamemodify(one_file, ':p:t') + + let fcnt = fcnt + 1 + + call Tlist_Update_File(one_file, ftype) + endwhile + + " Clear the displayed informational messages + echon "\r " + + " Restore the previous state + let &lazyredraw = old_lazyredraw + + return fcnt +endfunction + +" Tlist_Process_Dir +" Process the files in a directory matching the specified pattern +function! s:Tlist_Process_Dir(dir_name, pat) + let flist = glob(a:dir_name . '/' . a:pat) . "\n" + + let fcnt = s:Tlist_Process_Filelist(flist) + + let len = strlen(a:dir_name) + if a:dir_name[len - 1] == '\' || a:dir_name[len - 1] == '/' + let glob_expr = a:dir_name . '*' + else + let glob_expr = a:dir_name . '/*' + endif + let all_files = glob(glob_expr) . "\n" + + while all_files != '' + let nl_idx = stridx(all_files, "\n") + let one_file = strpart(all_files, 0, nl_idx) + + let all_files = strpart(all_files, nl_idx + 1) + if one_file == '' + continue + endif + + " Skip non-directory names + if !isdirectory(one_file) + continue + endif + + echon "\r " + echon "\rProcessing files in directory " . fnamemodify(one_file, ':t') + let fcnt = fcnt + s:Tlist_Process_Dir(one_file, a:pat) + endwhile + + return fcnt +endfunction + +" Tlist_Add_Files_Recursive +" Add files recursively from a directory +function! s:Tlist_Add_Files_Recursive(dir, ...) + let dir_name = fnamemodify(a:dir, ':p') + if !isdirectory(dir_name) + call s:Tlist_Warning_Msg('Error: ' . dir_name . ' is not a directory') + return + endif + + if a:0 == 1 + " User specified file pattern + let pat = a:1 + else + " Default file pattern + let pat = '*' + endif + + echon "\r " + echon "\rProcessing files in directory " . fnamemodify(dir_name, ':t') + let fcnt = s:Tlist_Process_Dir(dir_name, pat) + + echon "\rAdded " . fcnt . " files to the taglist" +endfunction + +" Tlist_Add_Files +" Add the specified list of files to the taglist +function! s:Tlist_Add_Files(...) + let flist = '' + let i = 1 + + " Get all the files matching the file patterns supplied as argument + while i <= a:0 + let flist = flist . glob(a:{i}) . "\n" + let i = i + 1 + endwhile + + if flist == '' + call s:Tlist_Warning_Msg('Error: No matching files are found') + return + endif + + let fcnt = s:Tlist_Process_Filelist(flist) + echon "\rAdded " . fcnt . " files to the taglist" +endfunction + +" Tlist_Extract_Tagtype +" Extract the tag type from the tag text +function! s:Tlist_Extract_Tagtype(tag_line) + " The tag type is after the tag prototype field. The prototype field + " ends with the /;"\t string. We add 4 at the end to skip the characters + " in this special string.. + let start = strridx(a:tag_line, '/;"' . "\t") + 4 + let end = strridx(a:tag_line, 'line:') - 1 + let ttype = strpart(a:tag_line, start, end - start) + + return ttype +endfunction + +" Tlist_Extract_Tag_Scope +" Extract the tag scope from the tag text +function! s:Tlist_Extract_Tag_Scope(tag_line) + let start = strridx(a:tag_line, 'line:') + let end = strridx(a:tag_line, "\t") + if end <= start + return '' + endif + + let tag_scope = strpart(a:tag_line, end + 1) + let tag_scope = strpart(tag_scope, stridx(tag_scope, ':') + 1) + + return tag_scope +endfunction + +" Tlist_Refresh() +" Refresh the taglist +function! s:Tlist_Refresh() + call s:Tlist_Log_Msg('Tlist_Refresh (Skip_Refresh = ' . + \ s:Tlist_Skip_Refresh . ', ' . bufname('%') . ')') + " If we are entering the buffer from one of the taglist functions, then + " no need to refresh the taglist window again. + if s:Tlist_Skip_Refresh + " We still need to update the taglist menu + if g:Tlist_Show_Menu + call s:Tlist_Menu_Update_File(0) + endif + return + endif + + " If part of the winmanager plugin and not configured to process + " tags always and not configured to display the tags menu, then return + if (s:tlist_app_name == 'winmanager') && !g:Tlist_Process_File_Always + \ && !g:Tlist_Show_Menu + return + endif + + " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help + if &buftype != '' + return + endif + + let filename = fnamemodify(bufname('%'), ':p') + let ftype = s:Tlist_Get_Buffer_Filetype('%') + + " If the file doesn't support tag listing, skip it + if s:Tlist_Skip_File(filename, ftype) + return + endif + + let tlist_win = bufwinnr(g:TagList_title) + + " If the taglist window is not opened and not configured to process + " tags always and not displaying the tags menu, then return + if tlist_win == -1 && !g:Tlist_Process_File_Always && !g:Tlist_Show_Menu + return + endif + + let fidx = s:Tlist_Get_File_Index(filename) + if fidx == -1 + " Check whether this file is removed based on user request + " If it is, then don't display the tags for this file + if s:Tlist_User_Removed_File(filename) + return + endif + + " If the taglist should not be auto updated, then return + if !g:Tlist_Auto_Update + return + endif + endif + + let cur_lnum = line('.') + + if fidx == -1 + " Update the tags for the file + let fidx = s:Tlist_Process_File(filename, ftype) + else + let mtime = getftime(filename) + if s:tlist_{fidx}_mtime != mtime + " Invalidate the tags listed for this file + let s:tlist_{fidx}_valid = 0 + + " Update the taglist and the window + call Tlist_Update_File(filename, ftype) + + " Store the new file modification time + let s:tlist_{fidx}_mtime = mtime + endif + endif + + " Update the taglist window + if tlist_win != -1 + " Disable screen updates + let old_lazyredraw = &lazyredraw + set nolazyredraw + + " Save the current window number + let save_winnr = winnr() + + " Goto the taglist window + call s:Tlist_Window_Goto_Window() + + if !g:Tlist_Auto_Highlight_Tag || !g:Tlist_Highlight_Tag_On_BufEnter + " Save the cursor position + let save_line = line('.') + let save_col = col('.') + endif + + " Update the taglist window + call s:Tlist_Window_Refresh_File(filename, ftype) + + " Open the fold for the file + exe "silent! " . s:tlist_{fidx}_start . "," . + \ s:tlist_{fidx}_end . "foldopen!" + + if g:Tlist_Highlight_Tag_On_BufEnter && g:Tlist_Auto_Highlight_Tag + if g:Tlist_Show_One_File && s:tlist_cur_file_idx != fidx + " If displaying tags for only one file in the taglist + " window and about to display the tags for a new file, + " then center the current tag line for the new file + let center_tag_line = 1 + else + let center_tag_line = 0 + endif + + " Highlight the current tag + call s:Tlist_Window_Highlight_Tag(filename, cur_lnum, 1, center_tag_line) + else + " Restore the cursor position + if v:version >= 601 + call cursor(save_line, save_col) + else + exe save_line + exe 'normal! ' . save_col . '|' + endif + endif + + " Jump back to the original window + if save_winnr != winnr() + call s:Tlist_Exe_Cmd_No_Acmds(save_winnr . 'wincmd w') + endif + + " Restore screen updates + let &lazyredraw = old_lazyredraw + endif + + " Update the taglist menu + if g:Tlist_Show_Menu + call s:Tlist_Menu_Update_File(0) + endif +endfunction + +" Tlist_Change_Sort() +" Change the sort order of the tag listing +" caller == 'cmd', command used in the taglist window +" caller == 'menu', taglist menu +" action == 'toggle', toggle sort from name to order and vice versa +" action == 'set', set the sort order to sort_type +function! s:Tlist_Change_Sort(caller, action, sort_type) + call s:Tlist_Log_Msg('Tlist_Change_Sort (caller = ' . a:caller . + \ ', action = ' . a:action . ', sort_type = ' . a:sort_type . ')') + if a:caller == 'cmd' + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.')) + if fidx == -1 + return + endif + + " Remove the previous highlighting + match none + elseif a:caller == 'menu' + let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p')) + if fidx == -1 + return + endif + endif + + if a:action == 'toggle' + let sort_type = s:tlist_{fidx}_sort_type + + " Toggle the sort order from 'name' to 'order' and vice versa + if sort_type == 'name' + let s:tlist_{fidx}_sort_type = 'order' + else + let s:tlist_{fidx}_sort_type = 'name' + endif + else + let s:tlist_{fidx}_sort_type = a:sort_type + endif + + " Invalidate the tags listed for this file + let s:tlist_{fidx}_valid = 0 + + if a:caller == 'cmd' + " Save the current line for later restoration + let curline = '\V\^' . getline('.') . '\$' + + call s:Tlist_Window_Refresh_File(s:tlist_{fidx}_filename, + \ s:tlist_{fidx}_filetype) + + exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'foldopen!' + + " Go back to the cursor line before the tag list is sorted + call search(curline, 'w') + + call s:Tlist_Menu_Update_File(1) + else + call s:Tlist_Menu_Remove_File() + + call s:Tlist_Refresh() + endif +endfunction + +" Tlist_Update_Current_File() +" Update taglist for the current buffer by regenerating the tag list +" Contributed by WEN Guopeng. +function! s:Tlist_Update_Current_File() + call s:Tlist_Log_Msg('Tlist_Update_Current_File()') + if winnr() == bufwinnr(g:TagList_title) + " In the taglist window. Update the current file + call s:Tlist_Window_Update_File() + else + " Not in the taglist window. Update the current buffer + let filename = fnamemodify(bufname('%'), ':p') + let fidx = s:Tlist_Get_File_Index(filename) + if fidx != -1 + let s:tlist_{fidx}_valid = 0 + endif + let ft = s:Tlist_Get_Buffer_Filetype('%') + call Tlist_Update_File(filename, ft) + endif +endfunction + +" Tlist_Window_Update_File() +" Update the tags displayed in the taglist window +function! s:Tlist_Window_Update_File() + call s:Tlist_Log_Msg('Tlist_Window_Update_File()') + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.')) + if fidx == -1 + return + endif + + " Remove the previous highlighting + match none + + " Save the current line for later restoration + let curline = '\V\^' . getline('.') . '\$' + + let s:tlist_{fidx}_valid = 0 + + " Update the taglist window + call s:Tlist_Window_Refresh_File(s:tlist_{fidx}_filename, + \ s:tlist_{fidx}_filetype) + + exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'foldopen!' + + " Go back to the tag line before the list is updated + call search(curline, 'w') +endfunction + +" Tlist_Window_Get_Tag_Type_By_Linenum() +" Return the tag type index for the specified line in the taglist window +function! s:Tlist_Window_Get_Tag_Type_By_Linenum(fidx, lnum) + let ftype = s:tlist_{a:fidx}_filetype + + " Determine to which tag type the current line number belongs to using the + " tag type start line number and the number of tags in a tag type + let i = 1 + while i <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{i}_name + let start_lnum = + \ s:tlist_{a:fidx}_start + s:tlist_{a:fidx}_{ttype}_offset + let end = start_lnum + s:tlist_{a:fidx}_{ttype}_count + if a:lnum >= start_lnum && a:lnum <= end + break + endif + let i = i + 1 + endwhile + + " Current line doesn't belong to any of the displayed tag types + if i > s:tlist_{ftype}_count + return '' + endif + + return ttype +endfunction + +" Tlist_Window_Get_Tag_Index() +" Return the tag index for the specified line in the taglist window +function! s:Tlist_Window_Get_Tag_Index(fidx, lnum) + let ttype = s:Tlist_Window_Get_Tag_Type_By_Linenum(a:fidx, a:lnum) + + " Current line doesn't belong to any of the displayed tag types + if ttype == '' + return 0 + endif + + " Compute the index into the displayed tags for the tag type + let ttype_lnum = s:tlist_{a:fidx}_start + s:tlist_{a:fidx}_{ttype}_offset + let tidx = a:lnum - ttype_lnum + if tidx == 0 + return 0 + endif + + " Get the corresponding tag line and return it + return s:tlist_{a:fidx}_{ttype}_{tidx} +endfunction + +" Tlist_Window_Highlight_Line +" Highlight the current line +function! s:Tlist_Window_Highlight_Line() + " Clear previously selected name + match none + + " Highlight the current line + if g:Tlist_Display_Prototype == 0 + let pat = '/\%' . line('.') . 'l\s\+\zs.*/' + else + let pat = '/\%' . line('.') . 'l.*/' + endif + + exe 'match TagListTagName ' . pat +endfunction + +" Tlist_Window_Open_File +" Open the specified file in either a new window or an existing window +" and place the cursor at the specified tag pattern +function! s:Tlist_Window_Open_File(win_ctrl, filename, tagpat) + call s:Tlist_Log_Msg('Tlist_Window_Open_File (' . a:filename . ',' . + \ a:win_ctrl . ')') + let prev_Tlist_Skip_Refresh = s:Tlist_Skip_Refresh + let s:Tlist_Skip_Refresh = 1 + + if s:tlist_app_name == "winmanager" + " Let the winmanager edit the file + call WinManagerFileEdit(a:filename, a:win_ctrl == 'newwin') + else + + if a:win_ctrl == 'newtab' + " Create a new tab + exe 'tabnew ' . escape(a:filename, ' ') + " Open the taglist window in the new tab + call s:Tlist_Window_Open() + endif + + if a:win_ctrl == 'checktab' + " Check whether the file is present in any of the tabs. + " If the file is present in the current tab, then use the + " current tab. + if bufwinnr(a:filename) != -1 + let file_present_in_tab = 1 + let i = tabpagenr() + else + let i = 1 + let bnum = bufnr(a:filename) + let file_present_in_tab = 0 + while i <= tabpagenr('$') + if index(tabpagebuflist(i), bnum) != -1 + let file_present_in_tab = 1 + break + endif + let i += 1 + endwhile + endif + + if file_present_in_tab + " Goto the tab containing the file + exe 'tabnext ' . i + else + " Open a new tab + exe 'tabnew ' . escape(a:filename, ' ') + + " Open the taglist window + call s:Tlist_Window_Open() + endif + endif + + let winnum = -1 + if a:win_ctrl == 'prevwin' + " Open the file in the previous window, if it is usable + let cur_win = winnr() + wincmd p + if &buftype == '' && !&previewwindow + exe "edit " . escape(a:filename, ' ') + let winnum = winnr() + else + " Previous window is not usable + exe cur_win . 'wincmd w' + endif + endif + + " Goto the window containing the file. If the window is not there, open a + " new window + if winnum == -1 + let winnum = bufwinnr(a:filename) + endif + + if winnum == -1 + " Locate the previously used window for opening a file + let fwin_num = 0 + let first_usable_win = 0 + + let i = 1 + let bnum = winbufnr(i) + while bnum != -1 + if getwinvar(i, 'tlist_file_window') == 'yes' + let fwin_num = i + break + endif + if first_usable_win == 0 && + \ getbufvar(bnum, '&buftype') == '' && + \ !getwinvar(i, '&previewwindow') + " First non-taglist, non-plugin and non-preview window + let first_usable_win = i + endif + let i = i + 1 + let bnum = winbufnr(i) + endwhile + + " If a previously used window is not found, then use the first + " non-taglist window + if fwin_num == 0 + let fwin_num = first_usable_win + endif + + if fwin_num != 0 + " Jump to the file window + exe fwin_num . "wincmd w" + + " If the user asked to jump to the tag in a new window, then split + " the existing window into two. + if a:win_ctrl == 'newwin' + split + endif + exe "edit " . escape(a:filename, ' ') + else + " Open a new window + if g:Tlist_Use_Horiz_Window + exe 'leftabove split ' . escape(a:filename, ' ') + else + if winbufnr(2) == -1 + " Only the taglist window is present + if g:Tlist_Use_Right_Window + exe 'leftabove vertical split ' . + \ escape(a:filename, ' ') + else + exe 'rightbelow vertical split ' . + \ escape(a:filename, ' ') + endif + + " Go to the taglist window to change the window size to + " the user configured value + call s:Tlist_Exe_Cmd_No_Acmds('wincmd p') + if g:Tlist_Use_Horiz_Window + exe 'resize ' . g:Tlist_WinHeight + else + exe 'vertical resize ' . g:Tlist_WinWidth + endif + " Go back to the file window + call s:Tlist_Exe_Cmd_No_Acmds('wincmd p') + else + " A plugin or help window is also present + wincmd w + exe 'leftabove split ' . escape(a:filename, ' ') + endif + endif + endif + " Mark the window, so that it can be reused. + call s:Tlist_Window_Mark_File_Window() + else + if v:version >= 700 + " If the file is opened in more than one window, then check + " whether the last accessed window has the selected file. + " If it does, then use that window. + let lastwin_bufnum = winbufnr(winnr('#')) + if bufnr(a:filename) == lastwin_bufnum + let winnum = winnr('#') + endif + endif + exe winnum . 'wincmd w' + + " If the user asked to jump to the tag in a new window, then split the + " existing window into two. + if a:win_ctrl == 'newwin' + split + endif + endif + endif + + " Jump to the tag + if a:tagpat != '' + " Add the current cursor position to the jump list, so that user can + " jump back using the ' and ` marks. + mark ' + silent call search(a:tagpat, 'w') + + " Bring the line to the middle of the window + normal! z. + + " If the line is inside a fold, open the fold + if foldclosed('.') != -1 + .foldopen + endif + endif + + " If the user selects to preview the tag then jump back to the + " taglist window + if a:win_ctrl == 'preview' + " Go back to the taglist window + let winnum = bufwinnr(g:TagList_title) + exe winnum . 'wincmd w' + else + " If the user has selected to close the taglist window, when a + " tag is selected, close the taglist window + if g:Tlist_Close_On_Select + call s:Tlist_Window_Goto_Window() + close + + " Go back to the window displaying the selected file + let wnum = bufwinnr(a:filename) + if wnum != -1 && wnum != winnr() + call s:Tlist_Exe_Cmd_No_Acmds(wnum . 'wincmd w') + endif + endif + endif + + let s:Tlist_Skip_Refresh = prev_Tlist_Skip_Refresh +endfunction + +" Tlist_Window_Jump_To_Tag() +" Jump to the location of the current tag +" win_ctrl == useopen - Reuse the existing file window +" win_ctrl == newwin - Open a new window +" win_ctrl == preview - Preview the tag +" win_ctrl == prevwin - Open in previous window +" win_ctrl == newtab - Open in new tab +function! s:Tlist_Window_Jump_To_Tag(win_ctrl) + call s:Tlist_Log_Msg('Tlist_Window_Jump_To_Tag(' . a:win_ctrl . ')') + " Do not process comment lines and empty lines + let curline = getline('.') + if curline =~ '^\s*$' || curline[0] == '"' + return + endif + + " If inside a closed fold, then use the first line of the fold + " and jump to the file. + let lnum = foldclosed('.') + if lnum == -1 + " Jump to the selected tag or file + let lnum = line('.') + else + " Open the closed fold + .foldopen! + endif + + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(lnum) + if fidx == -1 + return + endif + + " Get the tag output for the current tag + let tidx = s:Tlist_Window_Get_Tag_Index(fidx, lnum) + if tidx != 0 + let tagpat = s:Tlist_Get_Tag_SearchPat(fidx, tidx) + + " Highlight the tagline + call s:Tlist_Window_Highlight_Line() + else + " Selected a line which is not a tag name. Just edit the file + let tagpat = '' + endif + + call s:Tlist_Window_Open_File(a:win_ctrl, s:tlist_{fidx}_filename, tagpat) +endfunction + +" Tlist_Window_Show_Info() +" Display information about the entry under the cursor +function! s:Tlist_Window_Show_Info() + call s:Tlist_Log_Msg('Tlist_Window_Show_Info()') + + " Clear the previously displayed line + echo + + " Do not process comment lines and empty lines + let curline = getline('.') + if curline =~ '^\s*$' || curline[0] == '"' + return + endif + + " If inside a fold, then don't display the prototype + if foldclosed('.') != -1 + return + endif + + let lnum = line('.') + + " Get the file index + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(lnum) + if fidx == -1 + return + endif + + if lnum == s:tlist_{fidx}_start + " Cursor is on a file name + let fname = s:tlist_{fidx}_filename + if strlen(fname) > 50 + let fname = fnamemodify(fname, ':t') + endif + echo fname . ', Filetype=' . s:tlist_{fidx}_filetype . + \ ', Tag count=' . s:tlist_{fidx}_tag_count + return + endif + + " Get the tag output line for the current tag + let tidx = s:Tlist_Window_Get_Tag_Index(fidx, lnum) + if tidx == 0 + " Cursor is on a tag type + let ttype = s:Tlist_Window_Get_Tag_Type_By_Linenum(fidx, lnum) + if ttype == '' + return + endif + + let ttype_name = '' + + let ftype = s:tlist_{fidx}_filetype + let i = 1 + while i <= s:tlist_{ftype}_count + if ttype == s:tlist_{ftype}_{i}_name + let ttype_name = s:tlist_{ftype}_{i}_fullname + break + endif + let i = i + 1 + endwhile + + echo 'Tag type=' . ttype_name . + \ ', Tag count=' . s:tlist_{fidx}_{ttype}_count + return + endif + + " Get the tag search pattern and display it + echo s:Tlist_Get_Tag_Prototype(fidx, tidx) +endfunction + +" Tlist_Find_Nearest_Tag_Idx +" Find the tag idx nearest to the supplied line number +" Returns -1, if a tag couldn't be found for the specified line number +function! s:Tlist_Find_Nearest_Tag_Idx(fidx, linenum) + let sort_type = s:tlist_{a:fidx}_sort_type + + let left = 1 + let right = s:tlist_{a:fidx}_tag_count + + if sort_type == 'order' + " Tags sorted by order, use a binary search. + " The idea behind this function is taken from the ctags.vim script (by + " Alexey Marinichev) available at the Vim online website. + + " If the current line is the less than the first tag, then no need to + " search + let first_lnum = s:Tlist_Get_Tag_Linenum(a:fidx, 1) + + if a:linenum < first_lnum + return -1 + endif + + while left < right + let middle = (right + left + 1) / 2 + let middle_lnum = s:Tlist_Get_Tag_Linenum(a:fidx, middle) + + if middle_lnum == a:linenum + let left = middle + break + endif + + if middle_lnum > a:linenum + let right = middle - 1 + else + let left = middle + endif + endwhile + else + " Tags sorted by name, use a linear search. (contributed by Dave + " Eggum). + " Look for a tag with a line number less than or equal to the supplied + " line number. If multiple tags are found, then use the tag with the + " line number closest to the supplied line number. IOW, use the tag + " with the highest line number. + let closest_lnum = 0 + let final_left = 0 + while left <= right + let lnum = s:Tlist_Get_Tag_Linenum(a:fidx, left) + + if lnum < a:linenum && lnum > closest_lnum + let closest_lnum = lnum + let final_left = left + elseif lnum == a:linenum + let closest_lnum = lnum + let final_left = left + break + else + let left = left + 1 + endif + endwhile + if closest_lnum == 0 + return -1 + endif + if left >= right + let left = final_left + endif + endif + + return left +endfunction + +" Tlist_Window_Highlight_Tag() +" Highlight the current tag +" cntx == 1, Called by the taglist plugin itself +" cntx == 2, Forced by the user through the TlistHighlightTag command +" center = 1, move the tag line to the center of the taglist window +function! s:Tlist_Window_Highlight_Tag(filename, cur_lnum, cntx, center) + " Highlight the current tag only if the user configured the + " taglist plugin to do so or if the user explictly invoked the + " command to highlight the current tag. + if !g:Tlist_Auto_Highlight_Tag && a:cntx == 1 + return + endif + + if a:filename == '' + return + endif + + " Make sure the taglist window is present + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + call s:Tlist_Warning_Msg('Error: Taglist window is not open') + return + endif + + let fidx = s:Tlist_Get_File_Index(a:filename) + if fidx == -1 + return + endif + + " If the file is currently not displayed in the taglist window, then retrn + if !s:tlist_{fidx}_visible + return + endif + + " If there are no tags for this file, then no need to proceed further + if s:tlist_{fidx}_tag_count == 0 + return + endif + + " Ignore all autocommands + let old_ei = &eventignore + set eventignore=all + + " Save the original window number + let org_winnr = winnr() + + if org_winnr == winnum + let in_taglist_window = 1 + else + let in_taglist_window = 0 + endif + + " Go to the taglist window + if !in_taglist_window + exe winnum . 'wincmd w' + endif + + " Clear previously selected name + match none + + let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, a:cur_lnum) + if tidx == -1 + " Make sure the current tag line is visible in the taglist window. + " Calling the winline() function makes the line visible. Don't know + " of a better way to achieve this. + let lnum = line('.') + + if lnum < s:tlist_{fidx}_start || lnum > s:tlist_{fidx}_end + " Move the cursor to the beginning of the file + exe s:tlist_{fidx}_start + endif + + if foldclosed('.') != -1 + .foldopen + endif + + call winline() + + if !in_taglist_window + exe org_winnr . 'wincmd w' + endif + + " Restore the autocommands + let &eventignore = old_ei + return + endif + + " Extract the tag type + let ttype = s:Tlist_Get_Tag_Type_By_Tag(fidx, tidx) + + " Compute the line number + " Start of file + Start of tag type + offset + let lnum = s:tlist_{fidx}_start + s:tlist_{fidx}_{ttype}_offset + + \ s:tlist_{fidx}_{tidx}_ttype_idx + + " Goto the line containing the tag + exe lnum + + " Open the fold + if foldclosed('.') != -1 + .foldopen + endif + + if a:center + " Move the tag line to the center of the taglist window + normal! z. + else + " Make sure the current tag line is visible in the taglist window. + " Calling the winline() function makes the line visible. Don't know + " of a better way to achieve this. + call winline() + endif + + " Highlight the tag name + call s:Tlist_Window_Highlight_Line() + + " Go back to the original window + if !in_taglist_window + exe org_winnr . 'wincmd w' + endif + + " Restore the autocommands + let &eventignore = old_ei + return +endfunction + +" Tlist_Get_Tag_Prototype_By_Line +" Get the prototype for the tag on or before the specified line number in the +" current buffer +function! Tlist_Get_Tag_Prototype_By_Line(...) + if a:0 == 0 + " Arguments are not supplied. Use the current buffer name + " and line number + let filename = bufname('%') + let linenr = line('.') + elseif a:0 == 2 + " Filename and line number are specified + let filename = a:1 + let linenr = a:2 + if linenr !~ '\d\+' + " Invalid line number + return "" + endif + else + " Sufficient arguments are not supplied + let msg = 'Usage: Tlist_Get_Tag_Prototype_By_Line <filename> ' . + \ '<line_number>' + call s:Tlist_Warning_Msg(msg) + return "" + endif + + " Expand the file to a fully qualified name + let filename = fnamemodify(filename, ':p') + if filename == '' + return "" + endif + + let fidx = s:Tlist_Get_File_Index(filename) + if fidx == -1 + return "" + endif + + " If there are no tags for this file, then no need to proceed further + if s:tlist_{fidx}_tag_count == 0 + return "" + endif + + " Get the tag text using the line number + let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, linenr) + if tidx == -1 + return "" + endif + + return s:Tlist_Get_Tag_Prototype(fidx, tidx) +endfunction + +" Tlist_Get_Tagname_By_Line +" Get the tag name on or before the specified line number in the +" current buffer +function! Tlist_Get_Tagname_By_Line(...) + if a:0 == 0 + " Arguments are not supplied. Use the current buffer name + " and line number + let filename = bufname('%') + let linenr = line('.') + elseif a:0 == 2 + " Filename and line number are specified + let filename = a:1 + let linenr = a:2 + if linenr !~ '\d\+' + " Invalid line number + return "" + endif + else + " Sufficient arguments are not supplied + let msg = 'Usage: Tlist_Get_Tagname_By_Line <filename> <line_number>' + call s:Tlist_Warning_Msg(msg) + return "" + endif + + " Make sure the current file has a name + let filename = fnamemodify(filename, ':p') + if filename == '' + return "" + endif + + let fidx = s:Tlist_Get_File_Index(filename) + if fidx == -1 + return "" + endif + + " If there are no tags for this file, then no need to proceed further + if s:tlist_{fidx}_tag_count == 0 + return "" + endif + + " Get the tag name using the line number + let tidx = s:Tlist_Find_Nearest_Tag_Idx(fidx, linenr) + if tidx == -1 + return "" + endif + + return s:tlist_{fidx}_{tidx}_tag_name +endfunction + +" Tlist_Window_Move_To_File +" Move the cursor to the beginning of the current file or the next file +" or the previous file in the taglist window +" dir == -1, move to start of current or previous function +" dir == 1, move to start of next function +function! s:Tlist_Window_Move_To_File(dir) + if foldlevel('.') == 0 + " Cursor is on a non-folded line (it is not in any of the files) + " Move it to a folded line + if a:dir == -1 + normal! zk + else + " While moving down to the start of the next fold, + " no need to do go to the start of the next file. + normal! zj + return + endif + endif + + let fidx = s:Tlist_Window_Get_File_Index_By_Linenum(line('.')) + if fidx == -1 + return + endif + + let cur_lnum = line('.') + + if a:dir == -1 + if cur_lnum > s:tlist_{fidx}_start + " Move to the beginning of the current file + exe s:tlist_{fidx}_start + return + endif + + if fidx != 0 + " Move to the beginning of the previous file + let fidx = fidx - 1 + else + " Cursor is at the first file, wrap around to the last file + let fidx = s:tlist_file_count - 1 + endif + + exe s:tlist_{fidx}_start + return + else + " Move to the beginning of the next file + let fidx = fidx + 1 + + if fidx >= s:tlist_file_count + " Cursor is at the last file, wrap around to the first file + let fidx = 0 + endif + + if s:tlist_{fidx}_start != 0 + exe s:tlist_{fidx}_start + endif + return + endif +endfunction + +" Tlist_Session_Load +" Load a taglist session (information about all the displayed files +" and the tags) from the specified file +function! s:Tlist_Session_Load(...) + if a:0 == 0 || a:1 == '' + call s:Tlist_Warning_Msg('Usage: TlistSessionLoad <filename>') + return + endif + + let sessionfile = a:1 + + if !filereadable(sessionfile) + let msg = 'Taglist: Error - Unable to open file ' . sessionfile + call s:Tlist_Warning_Msg(msg) + return + endif + + " Mark the current window as the file window + call s:Tlist_Window_Mark_File_Window() + + " Source the session file + exe 'source ' . sessionfile + + let new_file_count = g:tlist_file_count + unlet! g:tlist_file_count + + let i = 0 + while i < new_file_count + let ftype = g:tlist_{i}_filetype + unlet! g:tlist_{i}_filetype + + if !exists('s:tlist_' . ftype . '_count') + if s:Tlist_FileType_Init(ftype) == 0 + let i = i + 1 + continue + endif + endif + + let fname = g:tlist_{i}_filename + unlet! g:tlist_{i}_filename + + let fidx = s:Tlist_Get_File_Index(fname) + if fidx != -1 + let s:tlist_{fidx}_visible = 0 + let i = i + 1 + continue + else + " As we are loading the tags from the session file, if this + " file was previously deleted by the user, now we need to + " add it back. So remove the file from the deleted list. + call s:Tlist_Update_Remove_List(fname, 0) + endif + + let fidx = s:Tlist_Init_File(fname, ftype) + + let s:tlist_{fidx}_filename = fname + + let s:tlist_{fidx}_sort_type = g:tlist_{i}_sort_type + unlet! g:tlist_{i}_sort_type + + let s:tlist_{fidx}_filetype = ftype + let s:tlist_{fidx}_mtime = getftime(fname) + + let s:tlist_{fidx}_start = 0 + let s:tlist_{fidx}_end = 0 + + let s:tlist_{fidx}_valid = 1 + + let s:tlist_{fidx}_tag_count = g:tlist_{i}_tag_count + unlet! g:tlist_{i}_tag_count + + let j = 1 + while j <= s:tlist_{fidx}_tag_count + let s:tlist_{fidx}_{j}_tag = g:tlist_{i}_{j}_tag + let s:tlist_{fidx}_{j}_tag_name = g:tlist_{i}_{j}_tag_name + let s:tlist_{fidx}_{j}_ttype_idx = g:tlist_{i}_{j}_ttype_idx + unlet! g:tlist_{i}_{j}_tag + unlet! g:tlist_{i}_{j}_tag_name + unlet! g:tlist_{i}_{j}_ttype_idx + let j = j + 1 + endwhile + + let j = 1 + while j <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{j}_name + + if exists('g:tlist_' . i . '_' . ttype) + let s:tlist_{fidx}_{ttype} = g:tlist_{i}_{ttype} + unlet! g:tlist_{i}_{ttype} + let s:tlist_{fidx}_{ttype}_offset = 0 + let s:tlist_{fidx}_{ttype}_count = g:tlist_{i}_{ttype}_count + unlet! g:tlist_{i}_{ttype}_count + + let k = 1 + while k <= s:tlist_{fidx}_{ttype}_count + let s:tlist_{fidx}_{ttype}_{k} = g:tlist_{i}_{ttype}_{k} + unlet! g:tlist_{i}_{ttype}_{k} + let k = k + 1 + endwhile + else + let s:tlist_{fidx}_{ttype} = '' + let s:tlist_{fidx}_{ttype}_offset = 0 + let s:tlist_{fidx}_{ttype}_count = 0 + endif + + let j = j + 1 + endwhile + + let i = i + 1 + endwhile + + " If the taglist window is open, then update it + let winnum = bufwinnr(g:TagList_title) + if winnum != -1 + let save_winnr = winnr() + + " Goto the taglist window + call s:Tlist_Window_Goto_Window() + + " Refresh the taglist window + call s:Tlist_Window_Refresh() + + " Go back to the original window + if save_winnr != winnr() + call s:Tlist_Exe_Cmd_No_Acmds('wincmd p') + endif + endif +endfunction + +" Tlist_Session_Save +" Save a taglist session (information about all the displayed files +" and the tags) into the specified file +function! s:Tlist_Session_Save(...) + if a:0 == 0 || a:1 == '' + call s:Tlist_Warning_Msg('Usage: TlistSessionSave <filename>') + return + endif + + let sessionfile = a:1 + + if s:tlist_file_count == 0 + " There is nothing to save + call s:Tlist_Warning_Msg('Warning: Taglist is empty. Nothing to save.') + return + endif + + if filereadable(sessionfile) + let ans = input('Do you want to overwrite ' . sessionfile . ' (Y/N)?') + if ans !=? 'y' + return + endif + + echo "\n" + endif + + let old_verbose = &verbose + set verbose&vim + + exe 'redir! > ' . sessionfile + + silent! echo '" Taglist session file. This file is auto-generated.' + silent! echo '" File information' + silent! echo 'let tlist_file_count = ' . s:tlist_file_count + + let i = 0 + + while i < s:tlist_file_count + " Store information about the file + silent! echo 'let tlist_' . i . "_filename = '" . + \ s:tlist_{i}_filename . "'" + silent! echo 'let tlist_' . i . '_sort_type = "' . + \ s:tlist_{i}_sort_type . '"' + silent! echo 'let tlist_' . i . '_filetype = "' . + \ s:tlist_{i}_filetype . '"' + silent! echo 'let tlist_' . i . '_tag_count = ' . + \ s:tlist_{i}_tag_count + " Store information about all the tags + let j = 1 + while j <= s:tlist_{i}_tag_count + let txt = escape(s:tlist_{i}_{j}_tag, '"\\') + silent! echo 'let tlist_' . i . '_' . j . '_tag = "' . txt . '"' + silent! echo 'let tlist_' . i . '_' . j . '_tag_name = "' . + \ s:tlist_{i}_{j}_tag_name . '"' + silent! echo 'let tlist_' . i . '_' . j . '_ttype_idx' . ' = ' . + \ s:tlist_{i}_{j}_ttype_idx + let j = j + 1 + endwhile + + " Store information about all the tags grouped by their type + let ftype = s:tlist_{i}_filetype + let j = 1 + while j <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{j}_name + if s:tlist_{i}_{ttype}_count != 0 + let txt = escape(s:tlist_{i}_{ttype}, '"\') + let txt = substitute(txt, "\n", "\\\\n", 'g') + silent! echo 'let tlist_' . i . '_' . ttype . ' = "' . + \ txt . '"' + silent! echo 'let tlist_' . i . '_' . ttype . '_count = ' . + \ s:tlist_{i}_{ttype}_count + let k = 1 + while k <= s:tlist_{i}_{ttype}_count + silent! echo 'let tlist_' . i . '_' . ttype . '_' . k . + \ ' = ' . s:tlist_{i}_{ttype}_{k} + let k = k + 1 + endwhile + endif + let j = j + 1 + endwhile + + silent! echo + + let i = i + 1 + endwhile + + redir END + + let &verbose = old_verbose +endfunction + +" Tlist_Buffer_Removed +" A buffer is removed from the Vim buffer list. Remove the tags defined +" for that file +function! s:Tlist_Buffer_Removed(filename) + call s:Tlist_Log_Msg('Tlist_Buffer_Removed (' . a:filename . ')') + + " Make sure a valid filename is supplied + if a:filename == '' + return + endif + + " Get tag list index of the specified file + let fidx = s:Tlist_Get_File_Index(a:filename) + if fidx == -1 + " File not present in the taglist + return + endif + + " Remove the file from the list + call s:Tlist_Remove_File(fidx, 0) +endfunction + +" When a buffer is deleted, remove the file from the taglist +autocmd BufDelete * silent call s:Tlist_Buffer_Removed(expand('<afile>:p')) + +" Tlist_Window_Open_File_Fold +" Open the fold for the specified file and close the fold for all the +" other files +function! s:Tlist_Window_Open_File_Fold(acmd_bufnr) + call s:Tlist_Log_Msg('Tlist_Window_Open_File_Fold (' . a:acmd_bufnr . ')') + + " Make sure the taglist window is present + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + call s:Tlist_Warning_Msg('Taglist: Error - Taglist window is not open') + return + endif + + " Save the original window number + let org_winnr = winnr() + if org_winnr == winnum + let in_taglist_window = 1 + else + let in_taglist_window = 0 + endif + + if in_taglist_window + " When entering the taglist window, no need to update the folds + return + endif + + " Go to the taglist window + if !in_taglist_window + call s:Tlist_Exe_Cmd_No_Acmds(winnum . 'wincmd w') + endif + + " Close all the folds + silent! %foldclose + + " Get tag list index of the specified file + let fname = fnamemodify(bufname(a:acmd_bufnr + 0), ':p') + if filereadable(fname) + let fidx = s:Tlist_Get_File_Index(fname) + if fidx != -1 + " Open the fold for the file + exe "silent! " . s:tlist_{fidx}_start . "," . + \ s:tlist_{fidx}_end . "foldopen" + endif + endif + + " Go back to the original window + if !in_taglist_window + call s:Tlist_Exe_Cmd_No_Acmds(org_winnr . 'wincmd w') + endif +endfunction + +" Tlist_Window_Check_Auto_Open +" Open the taglist window automatically on Vim startup. +" Open the window only when files present in any of the Vim windows support +" tags. +function! s:Tlist_Window_Check_Auto_Open() + let open_window = 0 + + let i = 1 + let buf_num = winbufnr(i) + while buf_num != -1 + let filename = fnamemodify(bufname(buf_num), ':p') + let ft = s:Tlist_Get_Buffer_Filetype(buf_num) + if !s:Tlist_Skip_File(filename, ft) + let open_window = 1 + break + endif + let i = i + 1 + let buf_num = winbufnr(i) + endwhile + + if open_window + call s:Tlist_Window_Toggle() + endif +endfunction + +" Tlist_Refresh_Folds +" Remove and create the folds for all the files displayed in the taglist +" window. Used after entering a tab. If this is not done, then the folds +" are not properly created for taglist windows displayed in multiple tabs. +function! s:Tlist_Refresh_Folds() + let winnum = bufwinnr(g:TagList_title) + if winnum == -1 + return + endif + + let save_wnum = winnr() + exe winnum . 'wincmd w' + + " First remove all the existing folds + normal! zE + + " Create the folds for each in the tag list + let fidx = 0 + while fidx < s:tlist_file_count + let ftype = s:tlist_{fidx}_filetype + + " Create the folds for each tag type in a file + let j = 1 + while j <= s:tlist_{ftype}_count + let ttype = s:tlist_{ftype}_{j}_name + if s:tlist_{fidx}_{ttype}_count + let s = s:tlist_{fidx}_start + s:tlist_{fidx}_{ttype}_offset + let e = s + s:tlist_{fidx}_{ttype}_count + exe s . ',' . e . 'fold' + endif + let j = j + 1 + endwhile + + exe s:tlist_{fidx}_start . ',' . s:tlist_{fidx}_end . 'fold' + exe 'silent! ' . s:tlist_{fidx}_start . ',' . + \ s:tlist_{fidx}_end . 'foldopen!' + let fidx = fidx + 1 + endwhile + + exe save_wnum . 'wincmd w' +endfunction + +function! s:Tlist_Menu_Add_Base_Menu() + call s:Tlist_Log_Msg('Adding the base menu') + + " Add the menu + anoremenu <silent> T&ags.Refresh\ menu :call <SID>Tlist_Menu_Refresh()<CR> + anoremenu <silent> T&ags.Sort\ menu\ by.Name + \ :call <SID>Tlist_Change_Sort('menu', 'set', 'name')<CR> + anoremenu <silent> T&ags.Sort\ menu\ by.Order + \ :call <SID>Tlist_Change_Sort('menu', 'set', 'order')<CR> + anoremenu T&ags.-SEP1- : + + if &mousemodel =~ 'popup' + anoremenu <silent> PopUp.T&ags.Refresh\ menu + \ :call <SID>Tlist_Menu_Refresh()<CR> + anoremenu <silent> PopUp.T&ags.Sort\ menu\ by.Name + \ :call <SID>Tlist_Change_Sort('menu', 'set', 'name')<CR> + anoremenu <silent> PopUp.T&ags.Sort\ menu\ by.Order + \ :call <SID>Tlist_Change_Sort('menu', 'set', 'order')<CR> + anoremenu PopUp.T&ags.-SEP1- : + endif +endfunction + +let s:menu_char_prefix = + \ '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + +" Tlist_Menu_Get_Tag_Type_Cmd +" Get the menu command for the specified tag type +" fidx - File type index +" ftype - File Type +" add_ttype_name - To add or not to add the tag type name to the menu entries +" ttype_idx - Tag type index +function! s:Tlist_Menu_Get_Tag_Type_Cmd(fidx, ftype, add_ttype_name, ttype_idx) + " Curly brace variable name optimization + let ftype_ttype_idx = a:ftype . '_' . a:ttype_idx + + let ttype = s:tlist_{ftype_ttype_idx}_name + if a:add_ttype_name + " If the tag type name contains space characters, escape it. This + " will be used to create the menu entries. + let ttype_fullname = escape(s:tlist_{ftype_ttype_idx}_fullname, ' ') + endif + + " Curly brace variable name optimization + let fidx_ttype = a:fidx . '_' . ttype + + " Number of tag entries for this tag type + let tcnt = s:tlist_{fidx_ttype}_count + if tcnt == 0 " No entries for this tag type + return '' + endif + + let mcmd = '' + + " Create the menu items for the tags. + " Depending on the number of tags of this type, split the menu into + " multiple sub-menus, if needed. + if tcnt > g:Tlist_Max_Submenu_Items + let j = 1 + while j <= tcnt + let final_index = j + g:Tlist_Max_Submenu_Items - 1 + if final_index > tcnt + let final_index = tcnt + endif + + " Extract the first and last tag name and form the + " sub-menu name + let tidx = s:tlist_{fidx_ttype}_{j} + let first_tag = s:tlist_{a:fidx}_{tidx}_tag_name + + let tidx = s:tlist_{fidx_ttype}_{final_index} + let last_tag = s:tlist_{a:fidx}_{tidx}_tag_name + + " Truncate the names, if they are greater than the + " max length + let first_tag = strpart(first_tag, 0, g:Tlist_Max_Tag_Length) + let last_tag = strpart(last_tag, 0, g:Tlist_Max_Tag_Length) + + " Form the menu command prefix + let m_prefix = 'anoremenu <silent> T\&ags.' + if a:add_ttype_name + let m_prefix = m_prefix . ttype_fullname . '.' + endif + let m_prefix = m_prefix . first_tag . '\.\.\.' . last_tag . '.' + + " Character prefix used to number the menu items (hotkey) + let m_prefix_idx = 0 + + while j <= final_index + let tidx = s:tlist_{fidx_ttype}_{j} + + let tname = s:tlist_{a:fidx}_{tidx}_tag_name + + let mcmd = mcmd . m_prefix . '\&' . + \ s:menu_char_prefix[m_prefix_idx] . '\.' . + \ tname . ' :call <SID>Tlist_Menu_Jump_To_Tag(' . + \ tidx . ')<CR>|' + + let m_prefix_idx = m_prefix_idx + 1 + let j = j + 1 + endwhile + endwhile + else + " Character prefix used to number the menu items (hotkey) + let m_prefix_idx = 0 + + let m_prefix = 'anoremenu <silent> T\&ags.' + if a:add_ttype_name + let m_prefix = m_prefix . ttype_fullname . '.' + endif + let j = 1 + while j <= tcnt + let tidx = s:tlist_{fidx_ttype}_{j} + + let tname = s:tlist_{a:fidx}_{tidx}_tag_name + + let mcmd = mcmd . m_prefix . '\&' . + \ s:menu_char_prefix[m_prefix_idx] . '\.' . + \ tname . ' :call <SID>Tlist_Menu_Jump_To_Tag(' . tidx + \ . ')<CR>|' + + let m_prefix_idx = m_prefix_idx + 1 + let j = j + 1 + endwhile + endif + + return mcmd +endfunction + +" Update the taglist menu with the tags for the specified file +function! s:Tlist_Menu_File_Refresh(fidx) + call s:Tlist_Log_Msg('Refreshing the tag menu for ' . s:tlist_{a:fidx}_filename) + " The 'B' flag is needed in the 'cpoptions' option + let old_cpoptions = &cpoptions + set cpoptions&vim + + exe s:tlist_{a:fidx}_menu_cmd + + " Update the popup menu (if enabled) + if &mousemodel =~ 'popup' + let cmd = substitute(s:tlist_{a:fidx}_menu_cmd, ' T\\&ags\.', + \ ' PopUp.T\\\&ags.', "g") + exe cmd + endif + + " The taglist menu is not empty now + let s:tlist_menu_empty = 0 + + " Restore the 'cpoptions' settings + let &cpoptions = old_cpoptions +endfunction + +" Tlist_Menu_Update_File +" Add the taglist menu +function! s:Tlist_Menu_Update_File(clear_menu) + if !has('gui_running') + " Not running in GUI mode + return + endif + + call s:Tlist_Log_Msg('Updating the tag menu, clear_menu = ' . a:clear_menu) + + " Remove the tags menu + if a:clear_menu + call s:Tlist_Menu_Remove_File() + + endif + + " Skip buffers with 'buftype' set to nofile, nowrite, quickfix or help + if &buftype != '' + return + endif + + let filename = fnamemodify(bufname('%'), ':p') + let ftype = s:Tlist_Get_Buffer_Filetype('%') + + " If the file doesn't support tag listing, skip it + if s:Tlist_Skip_File(filename, ftype) + return + endif + + let fidx = s:Tlist_Get_File_Index(filename) + if fidx == -1 || !s:tlist_{fidx}_valid + " Check whether this file is removed based on user request + " If it is, then don't display the tags for this file + if s:Tlist_User_Removed_File(filename) + return + endif + + " Process the tags for the file + let fidx = s:Tlist_Process_File(filename, ftype) + if fidx == -1 + return + endif + endif + + let fname = escape(fnamemodify(bufname('%'), ':t'), '.') + if fname != '' + exe 'anoremenu T&ags.' . fname . ' <Nop>' + anoremenu T&ags.-SEP2- : + endif + + if !s:tlist_{fidx}_tag_count + return + endif + + if s:tlist_{fidx}_menu_cmd != '' + " Update the menu with the cached command + call s:Tlist_Menu_File_Refresh(fidx) + + return + endif + + " We are going to add entries to the tags menu, so the menu won't be + " empty + let s:tlist_menu_empty = 0 + + let cmd = '' + + " Determine whether the tag type name needs to be added to the menu + " If more than one tag type is present in the taglisting for a file, + " then the tag type name needs to be present + let add_ttype_name = -1 + let i = 1 + while i <= s:tlist_{ftype}_count && add_ttype_name < 1 + let ttype = s:tlist_{ftype}_{i}_name + if s:tlist_{fidx}_{ttype}_count + let add_ttype_name = add_ttype_name + 1 + endif + let i = i + 1 + endwhile + + " Process the tags by the tag type and get the menu command + let i = 1 + while i <= s:tlist_{ftype}_count + let mcmd = s:Tlist_Menu_Get_Tag_Type_Cmd(fidx, ftype, add_ttype_name, i) + if mcmd != '' + let cmd = cmd . mcmd + endif + + let i = i + 1 + endwhile + + " Cache the menu command for reuse + let s:tlist_{fidx}_menu_cmd = cmd + + " Update the menu + call s:Tlist_Menu_File_Refresh(fidx) +endfunction + +" Tlist_Menu_Remove_File +" Remove the tags displayed in the tags menu +function! s:Tlist_Menu_Remove_File() + if !has('gui_running') || s:tlist_menu_empty + return + endif + + call s:Tlist_Log_Msg('Removing the tags menu for a file') + + " Cleanup the Tags menu + silent! unmenu T&ags + if &mousemodel =~ 'popup' + silent! unmenu PopUp.T&ags + endif + + " Add a dummy menu item to retain teared off menu + noremenu T&ags.Dummy l + + silent! unmenu! T&ags + if &mousemodel =~ 'popup' + silent! unmenu! PopUp.T&ags + endif + + call s:Tlist_Menu_Add_Base_Menu() + + " Remove the dummy menu item + unmenu T&ags.Dummy + + let s:tlist_menu_empty = 1 +endfunction + +" Tlist_Menu_Refresh +" Refresh the taglist menu +function! s:Tlist_Menu_Refresh() + call s:Tlist_Log_Msg('Refreshing the tags menu') + let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p')) + if fidx != -1 + " Invalidate the cached menu command + let s:tlist_{fidx}_menu_cmd = '' + endif + + " Update the taglist, menu and window + call s:Tlist_Update_Current_File() +endfunction + +" Tlist_Menu_Jump_To_Tag +" Jump to the selected tag +function! s:Tlist_Menu_Jump_To_Tag(tidx) + let fidx = s:Tlist_Get_File_Index(fnamemodify(bufname('%'), ':p')) + if fidx == -1 + return + endif + + let tagpat = s:Tlist_Get_Tag_SearchPat(fidx, a:tidx) + if tagpat == '' + return + endif + + " Add the current cursor position to the jump list, so that user can + " jump back using the ' and ` marks. + mark ' + + silent call search(tagpat, 'w') + + " Bring the line to the middle of the window + normal! z. + + " If the line is inside a fold, open the fold + if foldclosed('.') != -1 + .foldopen + endif +endfunction + +" Tlist_Menu_Init +" Initialize the taglist menu +function! s:Tlist_Menu_Init() + call s:Tlist_Menu_Add_Base_Menu() + + " Automatically add the tags defined in the current file to the menu + augroup TagListMenuCmds + autocmd! + + if !g:Tlist_Process_File_Always + autocmd BufEnter * call s:Tlist_Refresh() + endif + autocmd BufLeave * call s:Tlist_Menu_Remove_File() + augroup end + + call s:Tlist_Menu_Update_File(0) +endfunction + +" Tlist_Vim_Session_Load +" Initialize the taglist window/buffer, which is created when loading +" a Vim session file. +function! s:Tlist_Vim_Session_Load() + call s:Tlist_Log_Msg('Tlist_Vim_Session_Load') + + " Initialize the taglist window + call s:Tlist_Window_Init() + + " Refresh the taglist window + call s:Tlist_Window_Refresh() +endfunction + +" Tlist_Set_App +" Set the name of the external plugin/application to which taglist +" belongs. +" Taglist plugin is part of another plugin like cream or winmanager. +function! Tlist_Set_App(name) + if a:name == "" + return + endif + + let s:tlist_app_name = a:name +endfunction + +" Winmanager integration + +" Initialization required for integration with winmanager +function! TagList_Start() + " If current buffer is not taglist buffer, then don't proceed + if bufname('%') != '__Tag_List__' + return + endif + + call Tlist_Set_App('winmanager') + + " Get the current filename from the winmanager plugin + let bufnum = WinManagerGetLastEditedFile() + if bufnum != -1 + let filename = fnamemodify(bufname(bufnum), ':p') + let ftype = s:Tlist_Get_Buffer_Filetype(bufnum) + endif + + " Initialize the taglist window, if it is not already initialized + if !exists('s:tlist_window_initialized') || !s:tlist_window_initialized + call s:Tlist_Window_Init() + call s:Tlist_Window_Refresh() + let s:tlist_window_initialized = 1 + endif + + " Update the taglist window + if bufnum != -1 + if !s:Tlist_Skip_File(filename, ftype) && g:Tlist_Auto_Update + call s:Tlist_Window_Refresh_File(filename, ftype) + endif + endif +endfunction + +function! TagList_IsValid() + return 0 +endfunction + +function! TagList_WrapUp() + return 0 +endfunction + +" restore 'cpo' +let &cpo = s:cpo_save +unlet s:cpo_save + diff --git a/.vim/plugin/toggle_words.vim b/.vim/plugin/toggle_words.vim new file mode 100644 index 0000000..2d79df5 --- /dev/null +++ b/.vim/plugin/toggle_words.vim @@ -0,0 +1,67 @@ +" toggle_words.vim +" Author: Vincent Wang (linsong dot qizi at gmail dot com) +" Created: Fri Oct 13 07:51:16 CST 2006 +" Requires: Vim Ver7.0+ +" Version: 1.0 +" TODO: +" +" Documentation: +" The purpose of this plugin is very simple, it can toggle words among +" ['true', 'false'], ['on', 'off'], ['yes', 'no'], ['if', 'elseif', 'else', +" 'endif'] etc . It will search the candicates words to toggle based on +" current filetype, for example, you can put the following configuration +" into your .vimrc to define some words for python: +" let g:toggle_words_dict = {'python': [['if', 'elif', 'else'], ['True', +" 'False']]} +" +" There are some default words for toggling predefined in the +" script(g:_toogle_words_dict) that will work for all filetypes. +" Any comment, suggestion, bug report are welcomed. + +if v:version < 700 + "TODO: maybe I should make this script works under vim7.0 + echo "This script required vim7.0 or above version." + finish +endif + +if exists("g:load_toggle_words") + finish +endif + +let s:keepcpo= &cpo +set cpo&vim + +let g:load_toggle_words = "1.0" + +let g:_toggle_words_dict = {'*': [['true', 'false'], ['on', 'off'], ['yes', 'no'], ['+', '-'], ['define', 'undef'], ['if', 'elseif', 'else', 'endif'], ['>', '<'], ['{', '}'], ['(', ')'], ['[', ']'] ], } + +if exists('g:toggle_words_dict') + :call extend(g:_toggle_words_dict, g:toggle_words_dict) +endif + +function! s:ToggleWord() + let cur_filetype = &filetype + if ! has_key(g:_toggle_words_dict, cur_filetype) + let words_candicates_array = g:_toggle_words_dict['*'] + else + let words_candicates_array = g:_toggle_words_dict[cur_filetype] + g:_toggle_words_dict['*'] + endif + let cur_word = expand("<cword>") + for words_candicates in words_candicates_array + let index = index(words_candicates, cur_word) + if index != -1 + let new_word_index = (index+1)%len(words_candicates) + let new_word = words_candicates[new_word_index] + " use the new word to replace the old word + exec "norm ciw" . new_word . "" + break + endif + endfor +endfunction + +command! ToggleWord :call <SID>ToggleWord() <CR> +nmap ,t :call <SID>ToggleWord()<CR> +vmap ,t <ESC>:call <SID>ToggleWord()<CR> + +let &cpo= s:keepcpo +unlet s:keepcpo |