mirror of
https://github.com/preservim/nerdtree.git
synced 2025-11-08 11:23:48 -05:00
It makes the most sense to sort the global bookmarks list just before
rendering them in the NERDTree window. Since Vim's sort function is fast
and stable, and since users are very unlikely to have a number of
bookmarks that is too large, we can sort before rendering without
concern for the negligible performance penalty.
This has two benefits:
1. Users can change their sort settings and have them take effect
on the next render or refresh.
2. As mentioned, code duplication is avoided.
324 lines
11 KiB
VimL
324 lines
11 KiB
VimL
"CLASS: Bookmark
|
|
"============================================================
|
|
let s:Bookmark = {}
|
|
let g:NERDTreeBookmark = s:Bookmark
|
|
|
|
" FUNCTION: Bookmark.activate(nerdtree) {{{1
|
|
function! s:Bookmark.activate(nerdtree, ...)
|
|
call self.open(a:nerdtree, a:0 ? a:1 : {})
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.AddBookmark(name, path) {{{1
|
|
" Class method to add a new bookmark to the list, if a previous bookmark exists
|
|
" with the same name, just update the path for that bookmark
|
|
function! s:Bookmark.AddBookmark(name, path)
|
|
for i in s:Bookmark.Bookmarks()
|
|
if i.name ==# a:name
|
|
let i.path = a:path
|
|
return
|
|
endif
|
|
endfor
|
|
call add(s:Bookmark.Bookmarks(), s:Bookmark.New(a:name, a:path))
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.Bookmarks() {{{1
|
|
" Class method to get all bookmarks. Lazily initializes the bookmarks global
|
|
" variable
|
|
function! s:Bookmark.Bookmarks()
|
|
if !exists("g:NERDTreeBookmarks")
|
|
let g:NERDTreeBookmarks = []
|
|
endif
|
|
return g:NERDTreeBookmarks
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.BookmarkExistsFor(name) {{{1
|
|
" class method that returns 1 if a bookmark with the given name is found, 0
|
|
" otherwise
|
|
function! s:Bookmark.BookmarkExistsFor(name)
|
|
try
|
|
call s:Bookmark.BookmarkFor(a:name)
|
|
return 1
|
|
catch /^NERDTree.BookmarkNotFoundError/
|
|
return 0
|
|
endtry
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.BookmarkFor(name) {{{1
|
|
" Class method to get the bookmark that has the given name. {} is return if no
|
|
" bookmark is found
|
|
function! s:Bookmark.BookmarkFor(name)
|
|
for i in s:Bookmark.Bookmarks()
|
|
if i.name ==# a:name
|
|
return i
|
|
endif
|
|
endfor
|
|
throw "NERDTree.BookmarkNotFoundError: no bookmark found for name: \"". a:name .'"'
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.BookmarkNames() {{{1
|
|
" Class method to return an array of all bookmark names
|
|
function! s:Bookmark.BookmarkNames()
|
|
let names = []
|
|
for i in s:Bookmark.Bookmarks()
|
|
call add(names, i.name)
|
|
endfor
|
|
return names
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.CacheBookmarks(silent) {{{1
|
|
" Class method to read all bookmarks from the bookmarks file initialize
|
|
" bookmark objects for each one.
|
|
"
|
|
" Args:
|
|
" silent - dont echo an error msg if invalid bookmarks are found
|
|
function! s:Bookmark.CacheBookmarks(silent)
|
|
if filereadable(g:NERDTreeBookmarksFile)
|
|
let g:NERDTreeBookmarks = []
|
|
let g:NERDTreeInvalidBookmarks = []
|
|
let bookmarkStrings = readfile(g:NERDTreeBookmarksFile)
|
|
let invalidBookmarksFound = 0
|
|
for i in bookmarkStrings
|
|
|
|
"ignore blank lines
|
|
if i != ''
|
|
|
|
let name = substitute(i, '^\(.\{-}\) .*$', '\1', '')
|
|
let path = substitute(i, '^.\{-} \(.*\)$', '\1', '')
|
|
let path = fnamemodify(path, ':p')
|
|
|
|
try
|
|
let bookmark = s:Bookmark.New(name, g:NERDTreePath.New(path))
|
|
call add(g:NERDTreeBookmarks, bookmark)
|
|
catch /^NERDTree.InvalidArgumentsError/
|
|
call add(g:NERDTreeInvalidBookmarks, i)
|
|
let invalidBookmarksFound += 1
|
|
endtry
|
|
endif
|
|
endfor
|
|
if invalidBookmarksFound
|
|
call s:Bookmark.Write()
|
|
if !a:silent
|
|
call nerdtree#echo(invalidBookmarksFound . " invalid bookmarks were read. See :help NERDTreeInvalidBookmarks for info.")
|
|
endif
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.CompareBookmarksByName(firstBookmark, secondBookmark) {{{1
|
|
" Class method that indicates the relative position of two bookmarks when
|
|
" placed in alphabetical order by name. Case-sensitivity is determined by an
|
|
" option. Supports the "s:Bookmark.SortBookmarksList()" method.
|
|
function! s:Bookmark.CompareBookmarksByName(firstBookmark, secondBookmark)
|
|
let l:result = 0
|
|
if g:NERDTreeBookmarksSort == 1
|
|
if a:firstBookmark.name <? a:secondBookmark.name
|
|
let l:result = -1
|
|
elseif a:firstBookmark.name >? a:secondBookmark.name
|
|
let l:result = 1
|
|
endif
|
|
elseif g:NERDTreeBookmarksSort == 2
|
|
if a:firstBookmark.name <# a:secondBookmark.name
|
|
let l:result = -1
|
|
elseif a:firstBookmark.name ># a:secondBookmark.name
|
|
let l:result = 1
|
|
endif
|
|
endif
|
|
return l:result
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.ClearAll() {{{1
|
|
" Class method to delete all bookmarks.
|
|
function! s:Bookmark.ClearAll()
|
|
for i in s:Bookmark.Bookmarks()
|
|
call i.delete()
|
|
endfor
|
|
call s:Bookmark.Write()
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.delete() {{{1
|
|
" Delete this bookmark. If the node for this bookmark is under the current
|
|
" root, then recache bookmarks for its Path object
|
|
function! s:Bookmark.delete()
|
|
call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self))
|
|
call s:Bookmark.Write()
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.getNode(nerdtree, searchFromAbsoluteRoot) {{{1
|
|
" Gets the treenode for this bookmark
|
|
"
|
|
" Args:
|
|
" searchFromAbsoluteRoot: specifies whether we should search from the current
|
|
" tree root, or the highest cached node
|
|
function! s:Bookmark.getNode(nerdtree, searchFromAbsoluteRoot)
|
|
let searchRoot = a:searchFromAbsoluteRoot ? a:nerdtree.root.AbsoluteTreeRoot() : a:nerdtree.root
|
|
let targetNode = searchRoot.findNode(self.path)
|
|
if empty(targetNode)
|
|
throw "NERDTree.BookmarkedNodeNotFoundError: no node was found for bookmark: " . self.name
|
|
endif
|
|
return targetNode
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.GetNodeForName(name, searchFromAbsoluteRoot, nerdtree) {{{1
|
|
" Class method that finds the bookmark with the given name and returns the
|
|
" treenode for it.
|
|
function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot, nerdtree)
|
|
let bookmark = s:Bookmark.BookmarkFor(a:name)
|
|
return bookmark.getNode(nerdtree, a:searchFromAbsoluteRoot)
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.GetSelected() {{{1
|
|
" returns the Bookmark the cursor is over, or {}
|
|
function! s:Bookmark.GetSelected()
|
|
let line = getline(".")
|
|
let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '')
|
|
if name != line
|
|
try
|
|
return s:Bookmark.BookmarkFor(name)
|
|
catch /^NERDTree.BookmarkNotFoundError/
|
|
return {}
|
|
endtry
|
|
endif
|
|
return {}
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.InvalidBookmarks() {{{1
|
|
" Class method to get all invalid bookmark strings read from the bookmarks
|
|
" file
|
|
function! s:Bookmark.InvalidBookmarks()
|
|
if !exists("g:NERDTreeInvalidBookmarks")
|
|
let g:NERDTreeInvalidBookmarks = []
|
|
endif
|
|
return g:NERDTreeInvalidBookmarks
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.mustExist() {{{1
|
|
function! s:Bookmark.mustExist()
|
|
if !self.path.exists()
|
|
call s:Bookmark.CacheBookmarks(1)
|
|
throw "NERDTree.BookmarkPointsToInvalidLocationError: the bookmark \"".
|
|
\ self.name ."\" points to a non existing location: \"". self.path.str()
|
|
endif
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.New(name, path) {{{1
|
|
" Create a new bookmark object with the given name and path object
|
|
function! s:Bookmark.New(name, path)
|
|
if a:name =~# ' '
|
|
throw "NERDTree.IllegalBookmarkNameError: illegal name:" . a:name
|
|
endif
|
|
|
|
let newBookmark = copy(self)
|
|
let newBookmark.name = a:name
|
|
let newBookmark.path = a:path
|
|
return newBookmark
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.open(nerdtree, [options]) {{{1
|
|
"Args:
|
|
"
|
|
"nerdtree: the tree to load open the bookmark in
|
|
"
|
|
"A dictionary containing the following keys (all optional):
|
|
" 'where': Specifies whether the node should be opened in new split/tab or in
|
|
" the previous window. Can be either 'v' (vertical split), 'h'
|
|
" (horizontal split), 't' (new tab) or 'p' (previous window).
|
|
" 'reuse': if a window is displaying the file then jump the cursor there
|
|
" 'keepopen': dont close the tree window
|
|
" 'stay': open the file, but keep the cursor in the tree win
|
|
"
|
|
function! s:Bookmark.open(nerdtree, ...)
|
|
let opts = a:0 ? a:1 : {}
|
|
|
|
if self.path.isDirectory && !has_key(opts, 'where')
|
|
call self.toRoot(a:nerdtree)
|
|
else
|
|
let opener = g:NERDTreeOpener.New(self.path, opts)
|
|
call opener.open(self)
|
|
endif
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.openInNewTab(options) {{{1
|
|
" Create a new bookmark object with the given name and path object
|
|
function! s:Bookmark.openInNewTab(options)
|
|
call nerdtree#deprecated('Bookmark.openInNewTab', 'is deprecated, use open() instead')
|
|
call self.open(a:options)
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.setPath(path) {{{1
|
|
" makes this bookmark point to the given path
|
|
function! s:Bookmark.setPath(path)
|
|
let self.path = a:path
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.SortBookmarksList() {{{1
|
|
" Class method that sorts the global list of bookmarks alphabetically by name.
|
|
" Note that case-sensitivity is determined by a user option.
|
|
function! s:Bookmark.SortBookmarksList()
|
|
call sort(s:Bookmark.Bookmarks(), s:Bookmark.CompareBookmarksByName)
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.str() {{{1
|
|
" Get the string that should be rendered in the view for this bookmark
|
|
function! s:Bookmark.str()
|
|
let pathStrMaxLen = winwidth(g:NERDTree.GetWinNum()) - 4 - len(self.name)
|
|
if &nu
|
|
let pathStrMaxLen = pathStrMaxLen - &numberwidth
|
|
endif
|
|
|
|
let pathStr = self.path.str({'format': 'UI'})
|
|
if len(pathStr) > pathStrMaxLen
|
|
let pathStr = '<' . strpart(pathStr, len(pathStr) - pathStrMaxLen)
|
|
endif
|
|
return '>' . self.name . ' ' . pathStr
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.toRoot(nerdtree) {{{1
|
|
" Make the node for this bookmark the new tree root
|
|
function! s:Bookmark.toRoot(nerdtree)
|
|
if self.validate()
|
|
try
|
|
let targetNode = self.getNode(a:nerdtree, 1)
|
|
catch /^NERDTree.BookmarkedNodeNotFoundError/
|
|
let targetNode = g:NERDTreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path, a:nerdtree)
|
|
endtry
|
|
call a:nerdtree.changeRoot(targetNode)
|
|
endif
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.ToRoot(name, nerdtree) {{{1
|
|
" Make the node for this bookmark the new tree root
|
|
function! s:Bookmark.ToRoot(name, nerdtree)
|
|
let bookmark = s:Bookmark.BookmarkFor(a:name)
|
|
call bookmark.toRoot(a:nerdtree)
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.validate() {{{1
|
|
function! s:Bookmark.validate()
|
|
if self.path.exists()
|
|
return 1
|
|
else
|
|
call s:Bookmark.CacheBookmarks(1)
|
|
call nerdtree#echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.")
|
|
return 0
|
|
endif
|
|
endfunction
|
|
|
|
" FUNCTION: Bookmark.Write() {{{1
|
|
" Class method to write all bookmarks to the bookmarks file
|
|
function! s:Bookmark.Write()
|
|
let bookmarkStrings = []
|
|
for i in s:Bookmark.Bookmarks()
|
|
call add(bookmarkStrings, i.name . ' ' . fnamemodify(i.path.str(), ':~'))
|
|
endfor
|
|
|
|
"add a blank line before the invalid ones
|
|
call add(bookmarkStrings, "")
|
|
|
|
for j in s:Bookmark.InvalidBookmarks()
|
|
call add(bookmarkStrings, j)
|
|
endfor
|
|
call writefile(bookmarkStrings, g:NERDTreeBookmarksFile)
|
|
endfunction
|
|
|
|
" vim: set sw=4 sts=4 et fdm=marker:
|