517 lines
15 KiB
VimL
517 lines
15 KiB
VimL
" Author: Eric Van Dewoestine
|
|
"
|
|
" License: {{{
|
|
"
|
|
" Copyright (C) 2005 - 2014 Eric Van Dewoestine
|
|
"
|
|
" This program is free software: you can redistribute it and/or modify
|
|
" it under the terms of the GNU General Public License as published by
|
|
" the Free Software Foundation, either version 3 of the License, or
|
|
" (at your option) any later version.
|
|
"
|
|
" This program is distributed in the hope that it will be useful,
|
|
" but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
" GNU General Public License for more details.
|
|
"
|
|
" You should have received a copy of the GNU General Public License
|
|
" along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
"
|
|
" }}}
|
|
|
|
" Global Variables {{{
|
|
if !exists('g:EclimProjectTreeActions')
|
|
let g:EclimProjectTreeActions = [
|
|
\ {'pattern': '.*', 'name': 'Split', 'action': 'split'},
|
|
\ {'pattern': '.*', 'name': 'VSplit', 'action': 'vsplit'},
|
|
\ {'pattern': '.*', 'name': 'Tab', 'action': 'tablast | tabnew'},
|
|
\ {'pattern': '.*', 'name': 'Edit', 'action': 'edit'},
|
|
\ ]
|
|
endif
|
|
" }}}
|
|
|
|
" Script Variables {{{
|
|
let s:project_tree_ids = 0
|
|
let s:shared_instances_by_buffer = {}
|
|
let s:shared_instances_by_names = {}
|
|
" }}}
|
|
|
|
function! eclim#project#tree#ProjectTree(...) " {{{
|
|
" Open a tree view of the current or specified projects.
|
|
|
|
" no project dirs supplied, use current project
|
|
if len(a:000) == 0
|
|
let name = eclim#project#util#GetCurrentProjectName()
|
|
let names = [name]
|
|
if name == ''
|
|
if exists('t:cwd')
|
|
let names = [t:cwd]
|
|
else
|
|
call eclim#project#util#UnableToDetermineProject()
|
|
return
|
|
endif
|
|
endif
|
|
|
|
" list of project names supplied
|
|
elseif type(a:000[0]) == g:LIST_TYPE
|
|
let names = a:000[0]
|
|
if len(names) == 1 && (names[0] == '0' || names[0] == '')
|
|
return
|
|
endif
|
|
|
|
" list or project names
|
|
else
|
|
let names = a:000
|
|
endif
|
|
|
|
let dirs = []
|
|
let index = 0
|
|
let names_copy = copy(names)
|
|
for name in names
|
|
if name == 'CURRENT'
|
|
let name = eclim#project#util#GetCurrentProjectName()
|
|
let names_copy[index] = name
|
|
endif
|
|
|
|
let dir = eclim#project#util#GetProjectRoot(name)
|
|
if dir == ''
|
|
let dir = expand(name, ':p')
|
|
if !isdirectory(dir)
|
|
if eclim#EclimAvailable(0)
|
|
call eclim#util#EchoWarning('Project not found: ' . name)
|
|
endif
|
|
call remove(names_copy, index)
|
|
continue
|
|
endif
|
|
let names_copy[index] = fnamemodify(substitute(dir, '/$', '', ''), ':t')
|
|
endif
|
|
call add(dirs, dir)
|
|
let index += 1
|
|
endfor
|
|
let names = names_copy
|
|
|
|
if len(dirs) == 0
|
|
return
|
|
endif
|
|
|
|
" for session reload
|
|
let g:Eclim_project_tree_names = join(names, '|')
|
|
|
|
let display = len(names) == 1 ?
|
|
\ 'Project: ' . names[0] :
|
|
\ 'Projects: ' . join(names, ', ')
|
|
|
|
call eclim#project#tree#ProjectTreeClose()
|
|
call eclim#project#tree#ProjectTreeOpen(display, names, dirs)
|
|
endfunction " }}}
|
|
|
|
function! eclim#project#tree#ProjectTreeToggle() " {{{
|
|
let title = s:GetTreeTitle()
|
|
let bufnum = bufnr(title)
|
|
let winnum = bufwinnr(title)
|
|
if bufnum == -1 || winnum == -1
|
|
call eclim#project#tree#ProjectTree()
|
|
else
|
|
exec winnum . 'winc w'
|
|
close
|
|
winc p
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! eclim#project#tree#ProjectTreeOpen(display, names, dirs) " {{{
|
|
let expand = len(a:dirs) == 1
|
|
let expandDir = ''
|
|
if expand && g:EclimProjectTreeExpandPathOnOpen
|
|
let expandDir = substitute(expand('%:p:h'), '\', '/', 'g')
|
|
endif
|
|
|
|
" see if we should just use a shared tree
|
|
let shared = s:GetSharedTreeBuffer(a:names)
|
|
if shared != -1 && bufloaded(shared)
|
|
call eclim#display#window#VerticalToolWindowOpen(bufname(shared), 9)
|
|
"exec 'buffer ' . shared
|
|
if line('$') > 1 || getline(1) !~ '^\s*$'
|
|
setlocal nowrap nonumber
|
|
setlocal foldmethod=manual foldtext=getline(v:foldstart)
|
|
exec 'setlocal statusline=' . escape(a:display, ' ')
|
|
if !exists('t:project_tree_name')
|
|
exec 'let t:project_tree_id = ' .
|
|
\ substitute(bufname(shared), g:EclimProjectTreeTitle . '\(\d\+\)', '\1', '')
|
|
endif
|
|
|
|
if expand && expandDir != ''
|
|
call eclim#tree#ExpandPath(s:GetTreeTitle(), expandDir)
|
|
endif
|
|
|
|
return
|
|
endif
|
|
endif
|
|
|
|
" clear the project tree id if we are replacing a shared tree instance
|
|
if g:EclimProjectTreeSharedInstance && exists('t:project_tree_id')
|
|
unlet t:project_tree_id
|
|
endif
|
|
|
|
call eclim#display#window#VerticalToolWindowOpen(s:GetTreeTitle(), 9)
|
|
|
|
" command used to navigate to a content window before executing a command.
|
|
if !exists('g:EclimProjectTreeContentWincmd')
|
|
if g:VerticalToolWindowSide == 'right'
|
|
let g:EclimProjectTreeContentWincmd = 'winc h'
|
|
else
|
|
let g:EclimProjectTreeContentWincmd = 'winc l'
|
|
endif
|
|
endif
|
|
|
|
if exists('g:TreeSettingsFunction')
|
|
let s:TreeSettingsFunction = g:TreeSettingsFunction
|
|
endif
|
|
let g:TreeSettingsFunction = 'eclim#project#tree#ProjectTreeSettings'
|
|
|
|
try
|
|
call eclim#tree#Tree(s:GetTreeTitle(), a:dirs, a:names, expand, [])
|
|
finally
|
|
if exists('s:TreeSettingsFunction')
|
|
let g:TreeSettingsFunction = s:TreeSettingsFunction
|
|
else
|
|
unlet g:TreeSettingsFunction
|
|
endif
|
|
endtry
|
|
|
|
setlocal bufhidden=hide
|
|
exec 'setlocal statusline=' . escape(a:display, ' ')
|
|
|
|
if expand && expandDir != ''
|
|
call eclim#util#DelayedCommand(
|
|
\ 'call eclim#tree#ExpandPath("' . s:GetTreeTitle() . '", "' . expandDir . '")')
|
|
endif
|
|
|
|
normal! zs
|
|
|
|
let instance_names = join(a:names, '_')
|
|
let instance_names = substitute(instance_names, '\W', '_', 'g')
|
|
|
|
" remove the old associated tree value if one exists
|
|
silent! unlet s:shared_instances_by_names[s:shared_instances_by_buffer[bufnr('%')]]
|
|
|
|
let s:shared_instances_by_buffer[bufnr('%')] = instance_names
|
|
let s:shared_instances_by_names[instance_names] = bufnr('%')
|
|
|
|
call s:Mappings()
|
|
setlocal modifiable
|
|
call append(line('$'), ['', '" use ? to view help'])
|
|
call s:InfoLine()
|
|
setlocal nomodifiable
|
|
endfunction " }}}
|
|
|
|
function! eclim#project#tree#ProjectTreeClose() " {{{
|
|
if exists('t:project_tree_name') || exists('t:project_tree_id')
|
|
let winnr = bufwinnr(s:GetTreeTitle())
|
|
if winnr != -1
|
|
exec winnr . 'winc w'
|
|
close
|
|
endif
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! eclim#project#tree#Restore() " {{{
|
|
if exists('t:project_tree_restoring')
|
|
return
|
|
endif
|
|
let t:project_tree_restoring = 1
|
|
|
|
" prevent auto open from firing after session is loaded.
|
|
augroup project_tree_autoopen
|
|
autocmd!
|
|
augroup END
|
|
|
|
let title = s:GetTreeTitle()
|
|
let winnum = bufwinnr(title)
|
|
if winnum != -1
|
|
if exists('g:Eclim_project_tree_names')
|
|
let projects = split(g:Eclim_project_tree_names, '|')
|
|
call map(projects, 'escape(v:val, " ")')
|
|
let names = join(projects, ' ')
|
|
call eclim#util#DelayedCommand(
|
|
\ 'let bufnum = bufnr("%") | ' .
|
|
\ 'exec "ProjectTree ' . names . '" | ' .
|
|
\ 'exec bufwinnr(bufnum) . "winc w" | ' .
|
|
\ 'unlet t:project_tree_restoring')
|
|
else
|
|
exec 'bd ' . bufnr(title)
|
|
endif
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! s:GetTreeTitle() " {{{
|
|
" support a custom name from an external plugin
|
|
if exists('t:project_tree_name')
|
|
return t:project_tree_name
|
|
endif
|
|
|
|
if !exists('t:project_tree_id')
|
|
let t:project_tree_id = s:project_tree_ids + 1
|
|
let s:project_tree_ids += 1
|
|
endif
|
|
return g:EclimProjectTreeTitle . t:project_tree_id
|
|
endfunction " }}}
|
|
|
|
function! s:GetSharedTreeBuffer(names) " {{{
|
|
let instance_names = join(a:names, '_')
|
|
let instance_names = substitute(instance_names, '\W', '_', 'g')
|
|
if g:EclimProjectTreeSharedInstance &&
|
|
\ has_key(s:shared_instances_by_names, instance_names)
|
|
return s:shared_instances_by_names[instance_names]
|
|
endif
|
|
return -1
|
|
endfunction " }}}
|
|
|
|
function! s:Mappings() " {{{
|
|
nnoremap <buffer> <silent> E :call <SID>OpenFile('edit')<cr>
|
|
nnoremap <buffer> <silent> S :call <SID>OpenFile('split')<cr>
|
|
nnoremap <buffer> <silent> \| :call <SID>OpenFile('vsplit')<cr>
|
|
nnoremap <buffer> <silent> T :call <SID>OpenFile('tablast \| tabnew')<cr>
|
|
nnoremap <buffer> <silent> F :call <SID>OpenFileName()<cr>
|
|
nnoremap <buffer> <silent> Y :call <SID>YankFileName()<cr>
|
|
|
|
" assign to buffer var to get around weird vim issue passing list containing
|
|
" a string w/ a '<' in it on execution of mapping.
|
|
let b:project_tree_help = [
|
|
\ '<cr> - open/close dir, open file',
|
|
\ 'o - toggle dir fold, choose file open action',
|
|
\ 'E - open with :edit',
|
|
\ 'S - open in a new split window',
|
|
\ '| (pipe) - open in a new vertical split window',
|
|
\ 'T - open in a new tab',
|
|
\ 'R - refresh directory',
|
|
\ 'i - view file info',
|
|
\ 's - open shell at directory',
|
|
\ 'p - move cursor to parent dir',
|
|
\ 'P - move cursor to last child of dir',
|
|
\ 'C - set root to dir under the cursor',
|
|
\ 'B - set root up one dir',
|
|
\ '~ - set root to home dir',
|
|
\ 'K - set root to top most dir',
|
|
\ 'F - open/create a file by name',
|
|
\ 'D - create a new directory',
|
|
\ 'Y - yank current file/dir path to the clipboard',
|
|
\ 'A - toggle hide/view hidden files',
|
|
\ ':CD <dir> - set the root to <dir>',
|
|
\ ]
|
|
nnoremap <buffer> <silent> ?
|
|
\ :call eclim#help#BufferHelp(b:project_tree_help, 'horizontal', 10)<cr>
|
|
endfunction " }}}
|
|
|
|
function! s:InfoLine() " {{{
|
|
setlocal modifiable
|
|
let pos = getpos('.')
|
|
if len(b:roots) == 1
|
|
let lnum = line('$') - 1
|
|
if getline(lnum) =~ '^"'
|
|
exec lnum . ',' . lnum . 'delete _'
|
|
endif
|
|
|
|
let info = ''
|
|
try
|
|
let info = function('vcs#util#GetInfo')(b:roots[0])
|
|
catch /E\(117\|700\)/
|
|
" fall back to fugitive
|
|
try
|
|
" fugitive calls a User autocmd, so stop if that one is triggering
|
|
" this one to prevent a recursive loop
|
|
if exists('b:eclim_fugative_autocmd')
|
|
return
|
|
endif
|
|
|
|
" make sure fugitive has the git dir for the current project
|
|
if !exists('b:git_dir') || (b:git_dir !~ '^\M' . b:roots[0])
|
|
let cwd = ''
|
|
if getcwd() . '/' != b:roots[0]
|
|
let cwd = getcwd()
|
|
exec 'lcd ' . escape(b:roots[0], ' ')
|
|
endif
|
|
|
|
if exists('b:git_dir')
|
|
unlet b:git_dir
|
|
endif
|
|
|
|
" slight hack to prevent recursive autocmd loop with fugitive
|
|
let b:eclim_fugative_autocmd = 1
|
|
|
|
silent! doautocmd fugitive BufReadPost %
|
|
|
|
if cwd != ''
|
|
exec 'lcd ' . escape(cwd, ' ')
|
|
endif
|
|
endif
|
|
|
|
let info = function('fugitive#statusline')()
|
|
if info != ''
|
|
let branch = substitute(info, '^\[\Git(\(.*\))\]$', '\1', 'g')
|
|
if branch != info
|
|
let info = 'git:' . branch
|
|
endif
|
|
endif
|
|
catch /E\(117\|700\)/
|
|
" noop if the neither function was found
|
|
finally
|
|
silent! unlet b:eclim_fugative_autocmd
|
|
endtry
|
|
endtry
|
|
|
|
" &modifiable check for silly side effect of fugitive autocmd
|
|
if info != '' && &modifiable
|
|
call append(line('$') - 1, '" ' . info)
|
|
endif
|
|
endif
|
|
call setpos('.', pos)
|
|
setlocal nomodifiable
|
|
endfunction " }}}
|
|
|
|
function! s:PathEcho() " {{{
|
|
if mode() != 'n'
|
|
return
|
|
endif
|
|
|
|
let path = eclim#tree#GetPath()
|
|
let path = substitute(path, eclim#tree#GetRoot(), '', '')
|
|
if path !~ '^"'
|
|
call eclim#util#WideMessage('echo', path)
|
|
else
|
|
call eclim#util#WideMessage('echo', '')
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! s:OpenFile(action) " {{{
|
|
let path = eclim#tree#GetPath()
|
|
if path !~ '/$'
|
|
if !filereadable(path)
|
|
echo "File is not readable or has been deleted."
|
|
return
|
|
endif
|
|
|
|
call eclim#tree#ExecuteAction(path,
|
|
\ "call eclim#project#tree#OpenProjectFile('" . a:action . "', '<file>')")
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! s:OpenFileName() " {{{
|
|
let path = eclim#tree#GetPath()
|
|
if !isdirectory(path)
|
|
let path = fnamemodify(path, ':h') . '/'
|
|
endif
|
|
|
|
let response = input('file: ', path, 'file')
|
|
if response != ''
|
|
let actions = eclim#tree#GetFileActions(response)
|
|
call eclim#tree#ExecuteAction(response, actions[0].action)
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! s:YankFileName() " {{{
|
|
let path = eclim#tree#GetPath()
|
|
let [@*, @+, @"] = [path, path, path]
|
|
call eclim#util#Echo('Copied path to clipboard: ' . path)
|
|
endfunction " }}}
|
|
|
|
function! eclim#project#tree#ProjectTreeSettings() " {{{
|
|
for action in g:EclimProjectTreeActions
|
|
call eclim#tree#RegisterFileAction(action.pattern, action.name,
|
|
\ "call eclim#project#tree#OpenProjectFile('" . action.action . "', '<file>')")
|
|
endfor
|
|
|
|
call eclim#tree#RegisterDirAction(function('eclim#project#tree#InjectLinkedResources'))
|
|
|
|
if exists('s:TreeSettingsFunction')
|
|
let l:Settings = function(s:TreeSettingsFunction)
|
|
call l:Settings()
|
|
endif
|
|
|
|
augroup eclim_tree
|
|
autocmd User <buffer> call <SID>InfoLine()
|
|
if g:EclimProjectTreePathEcho
|
|
autocmd CursorMoved <buffer> call <SID>PathEcho()
|
|
endif
|
|
augroup END
|
|
endfunction " }}}
|
|
|
|
function! eclim#project#tree#OpenProjectFile(cmd, file) " {{{
|
|
" Execute the supplied command in one of the main content windows.
|
|
if eclim#util#GoToBufferWindow(a:file)
|
|
return
|
|
endif
|
|
|
|
let file = a:file
|
|
let cmd = a:cmd
|
|
let cwd = getcwd()
|
|
|
|
exec g:EclimProjectTreeContentWincmd
|
|
|
|
" if the buffer is a no name and action is split, use edit instead.
|
|
if cmd =~ 'split' && expand('%') == '' &&
|
|
\ !&modified && line('$') == 1 && getline(1) == ''
|
|
let cmd = 'edit'
|
|
endif
|
|
|
|
" current file doesn't share same cwd as the project tree
|
|
let lcwd = getcwd()
|
|
if lcwd != cwd && !filereadable(file)
|
|
let file = escape(substitute(cwd, '\', '/', 'g'), ' &') . '/' . file
|
|
endif
|
|
|
|
try
|
|
exec cmd . ' ' file
|
|
catch /E325/
|
|
" ignore attention error since the user should be prompted to handle it.
|
|
finally
|
|
if lcwd != cwd
|
|
exec 'lcd ' . escape(cwd, ' ')
|
|
endif
|
|
endtry
|
|
endfunction " }}}
|
|
|
|
function! eclim#project#tree#InjectLinkedResources(dir, contents) " {{{
|
|
let project = eclim#project#util#GetProject(a:dir)
|
|
if len(project) == 0
|
|
return
|
|
endif
|
|
|
|
" listing the project root, so inject our project links
|
|
if len(get(project, 'links', {})) &&
|
|
\ substitute(a:dir, '/$', '', '') == project.path
|
|
if !exists('b:links')
|
|
let b:links = {}
|
|
endif
|
|
call extend(b:links, project.links)
|
|
|
|
let links = keys(project.links)
|
|
call sort(links)
|
|
|
|
let index = 0
|
|
for entry in copy(a:contents)
|
|
if !len(links)
|
|
break
|
|
endif
|
|
|
|
while len(links) && links[0] < fnamemodify(entry, ':h:t')
|
|
call insert(a:contents, a:dir . remove(links, 0) . '/', index)
|
|
endwhile
|
|
let index += 1
|
|
endfor
|
|
|
|
for link in links
|
|
call add(a:contents, a:dir . link . '/')
|
|
endfor
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! eclim#project#tree#HorizontalContentWindow() " {{{
|
|
" Command for g:EclimProjectTreeContentWincmd used when relative to a
|
|
" horizontal taglist window.
|
|
winc k
|
|
if exists('g:TagList_title') && bufname(bufnr('%')) == g:TagList_title
|
|
winc k
|
|
endif
|
|
endfunction " }}}
|
|
|
|
" vim:ft=vim:fdm=marker
|