" 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 . " " }}} " Global Variables {{{ let g:eclim_locate_default_updatetime = &updatetime " disable autocomplpop in the locate prompt if exists('g:acp_behavior') let g:acp_behavior['locate_prompt'] = [] endif " }}} " Script Variables {{{ let s:command_locate = '-command locate_file -s ""' let s:scopes = [ \ 'project', \ 'workspace', \ 'buffers', \ 'quickfix', \ ] let s:help = [ \ ' - close the locate prompt + results', \ ', - select the next file', \ ', - select the previous file', \ ' - open selected file w/ default action', \ ' - open with :edit', \ ' - open in a split window', \ ' - open in a new tab', \ ' - choose search scope', \ ' - toggle help buffer', \ ] " }}} function! eclim#common#locate#LocateFile(action, file, ...) " {{{ " Locates a file using the specified action for opening the file when found. " action - '' (use user default), 'split', 'edit', etc. " file - 'somefile.txt', " '', (kick off completion mode), " '' (locate the file under the cursor) " scope - optional scope to search in (project, workspace, buffers, etc.) let project = eclim#project#util#GetCurrentProjectName() let scope = a:0 > 0 ? a:1 : g:EclimLocateFileScope if !eclim#util#ListContains(s:scopes, scope) && \ !eclim#util#ListContains(g:EclimLocateUserScopes, scope) call eclim#util#EchoWarning('Unrecognized scope: ' . scope) return endif if scope == 'project' && (project == '' || !eclim#EclimAvailable(0)) let scope = g:EclimLocateFileNonProjectScope endif let workspace = '' if scope == 'project' || scope == 'workspace' let instance = eclim#client#nailgun#ChooseEclimdInstance() if type(instance) != g:DICT_TYPE return endif let workspace = instance.workspace if !eclim#PingEclim(0, workspace) call eclim#util#EchoError('Unable to connect to eclimd.') return endif endif let results = [] let action = a:action if action == '' let action = g:EclimLocateFileDefaultAction endif let file = a:file if file == '' call s:LocateFileCompletionInit(action, scope, project, workspace) return elseif file == '' let file = eclim#util#GrabUri() " if grabbing a relative url, remove any anchor info or query parameters let file = substitute(file, '[#?].*', '', '') endif let name = fnamemodify(file, ':t') if name == '' call eclim#util#Echo('Please supply more than just a directory name.') return endif let pattern = file let pattern = s:LocateFileConvertPattern(pattern, 0) let pattern = '[^/]*' . pattern try let b:workspace = workspace let b:project = project let results = s:LocateFileFunction(scope)(pattern) finally unlet! b:workspace unlet! b:project endtry call map(results, "v:val.path") let result = '' " One result. if len(results) == 1 let result = results[0] " More than one result. elseif len(results) > 1 let message = "Multiple results, choose the file to open" let response = eclim#util#PromptList(message, results, g:EclimHighlightInfo) if response == -1 return endif let result = results[response] " No results else call eclim#util#Echo('Unable to locate file pattern "' . file . '".') return endif if has('win32unix') let result = eclim#cygwin#CygwinPath(result) endif call eclim#util#GoToBufferWindowOrOpen(eclim#util#Simplify(result), action) call eclim#util#Echo(' ') endfunction " }}} function! eclim#common#locate#LocateFileCompletion() " {{{ let line = getline('.') if line !~ '^> ' call setline(1, substitute(line, '^>\?\s*', '> \1', '')) call cursor(1, 3) let line = getline('.') endif let completions = [] let display = [] let name = substitute(line, '^>\s*', '', '') if name !~ '^\s*$' let pattern = name let pattern = s:LocateFileConvertPattern(pattern, g:EclimLocateFileFuzzy) let results = s:LocateFileFunction(b:scope)(pattern) if !empty(results) for result in results let rel = eclim#util#Simplify(get(result, 'projectPath', result.path)) let dict = {'word': result.name, 'menu': rel, 'info': result.path} call add(completions, dict) call add(display, result.name . ' ' . rel) endfor endif endif let b:completions = completions let winnr = winnr() noautocmd exec bufwinnr(b:results_bufnum) . 'winc w' setlocal modifiable 1,$delete _ call append(1, display) 1,1delete _ setlocal nomodifiable exec winnr . 'winc w' " part of bad hack for gvim on windows let b:start_selection = 1 call s:LocateFileSelection(1) endfunction " }}} function! eclim#common#locate#LocateFileClose() " {{{ if bufname(bufnr('%')) !~ '^\[Locate.*\]$' let bufnr = bufnr('\[Locate in *\]') let winnr = bufwinnr(bufnr) if winnr != -1 let curbuf = bufnr('%') exec winnr . 'winc w' try exec 'bw ' . b:results_bufnum bw autocmd! locate_file_init stopinsert finally exec bufwinnr(curbuf) . 'winc w' endtry endif endif endfunction " }}} function! s:LocateFileCompletionInit(action, scope, project, workspace) " {{{ let file = expand('%') let bufnum = bufnr('%') let winnr = winnr() let winrestcmd = winrestcmd() topleft 12split [Locate\ Results] set filetype=locate_results setlocal nonumber nowrap setlocal noswapfile nobuflisted setlocal nospell norelativenumber setlocal buftype=nofile bufhidden=delete let results_bufnum = bufnr('%') let locate_in = (a:scope == 'project' ? a:project : a:scope) exec 'topleft 1split ' . escape('[Locate in ' . locate_in . ']', ' -') setlocal modifiable call setline(1, '> ') call cursor(1, col('$')) set filetype=locate_prompt syntax match Keyword /^>/ setlocal winfixheight setlocal nonumber setlocal nolist setlocal noswapfile nobuflisted setlocal nospell norelativenumber setlocal buftype=nofile bufhidden=delete let b:bufnum = bufnum let b:winnr = winnr let b:project = a:project let b:workspace = a:workspace let b:scope = a:scope let b:results_bufnum = results_bufnum let b:help_bufnum = 0 let b:selection = 1 let b:winrestcmd = winrestcmd set updatetime=300 augroup locate_file_init autocmd! autocmd BufEnter nested startinsert! | let &updatetime = 300 autocmd BufLeave \[Locate*\] \ call eclim#util#DelayedCommand('call eclim#common#locate#LocateFileClose()') exec 'autocmd InsertLeave ' . \ 'let &updatetime = g:eclim_locate_default_updatetime | ' . \ 'doautocmd BufWinLeave | bw | ' . \ 'doautocmd BufWinLeave | bw ' . b:results_bufnum . ' | ' . \ 'call eclim#util#GoToBufferWindow(' . b:bufnum . ') | ' . \ 'doautocmd BufEnter | ' . \ 'doautocmd WinEnter | ' . \ winrestcmd exec 'autocmd WinEnter ' \ 'exec bufwinnr(' . bufnr('%') . ') "winc w"' augroup END " enable completion after user starts typing call s:LocateFileCompletionAutocmdDeferred() inoremap =LocateFileSelection("n") inoremap =LocateFileSelection("n") inoremap =LocateFileSelection("n") inoremap =LocateFileSelection("p") inoremap =LocateFileSelection("p") inoremap =LocateFileSelection("p") exec 'inoremap ' . \ '=LocateFileSelect("' . a:action . '")' inoremap =LocateFileSelect('edit') inoremap =LocateFileSelect('split') inoremap =LocateFileSelect("tablast \| tabnew") inoremap =LocateFileChangeScope() inoremap =LocateFileHelp() startinsert! endfunction " }}} function! s:LocateFileCompletionAutocmd() " {{{ augroup locate_file autocmd! autocmd CursorHoldI call eclim#common#locate#LocateFileCompletion() augroup END endfunction " }}} function! s:LocateFileCompletionAutocmdDeferred() " {{{ augroup locate_file autocmd! autocmd CursorMovedI call LocateFileCompletionAutocmd() augroup END endfunction " }}} function! s:LocateFileSelection(sel) " {{{ " pause completion while tabbing though results augroup locate_file autocmd! augroup END let sel = a:sel let prev_sel = b:selection " bad hack for gvim on windows let start_sel = b:start_selection let double_defer = 0 if sel =~ '^[np]$' && (has('win32') || has('win64')) let double_defer = b:start_selection == 1 let b:start_selection = 0 endif let winnr = winnr() noautocmd exec bufwinnr(b:results_bufnum) . 'winc w' if sel == 'n' let sel = prev_sel < line('$') ? prev_sel + 1 : 1 elseif sel == 'p' let sel = prev_sel > 1 ? prev_sel - 1 : line('$') endif syntax clear exec 'syntax match PmenuSel /\%' . sel . 'l.*/' exec 'call cursor(' . sel . ', 1)' let save_scrolloff = &scrolloff let &scrolloff = 5 normal! zt let &scrolloff = save_scrolloff exec winnr . 'winc w' exec 'let b:selection = ' . sel if double_defer augroup locate_file autocmd! autocmd CursorMovedI call LocateFileCompletionAutocmdDeferred() augroup END else call s:LocateFileCompletionAutocmdDeferred() endif return '' endfunction " }}} function! s:LocateFileSelect(action) " {{{ if exists('b:completions') && !empty(b:completions) let &updatetime = g:eclim_locate_default_updatetime let file = eclim#util#Simplify(b:completions[b:selection - 1].info) if has('win32unix') let file = eclim#cygwin#CygwinPath(file) endif let bufnum = b:bufnum let winnr = b:winnr let winrestcmd = b:winrestcmd " close locate windows exec 'bdelete ' . b:results_bufnum exec 'bdelete ' . bufnr('%') " reset windows to pre-locate sizes exec winrestcmd " open the selected result exec winnr . "wincmd w" " call eclim#util#GoToBufferWindow(bufnum) call eclim#util#GoToBufferWindowOrOpen(file, a:action) call feedkeys("\", 'n') doautocmd WinEnter endif return '' endfunction " }}} function! s:LocateFileChangeScope() " {{{ if b:help_bufnum && bufexists(b:help_bufnum) exec 'bdelete ' . b:help_bufnum endif let bufnr = bufnr('%') let winnr = winnr() " trigger [Locate] buffer's BufLeave autocmd before we leave the buffer doautocmd BufLeave noautocmd exec bufwinnr(b:results_bufnum) . 'winc w' silent noautocmd exec '50vnew [Locate\ Scope]' let b:locate_bufnr = bufnr let b:locate_winnr = winnr stopinsert setlocal modifiable call append(1, s:scopes + g:EclimLocateUserScopes) 1,1delete _ call append(line('$'), \ ['', '" - select a scope', '" , , or q - cancel']) syntax match Comment /^".*/ setlocal nomodifiable setlocal winfixheight setlocal nonumber setlocal nolist setlocal noswapfile nobuflisted setlocal nospell norelativenumber setlocal buftype=nofile bufhidden=delete nnoremap :call ChooseScope() nnoremap q :call CloseScopeChooser() nnoremap :call CloseScopeChooser() nnoremap :call CloseScopeChooser() autocmd BufLeave call CloseScopeChooser() return '' endfunction " }}} function! s:ChooseScope() " {{{ let scope = getline('.') if scope =~ '^"\|^\s*$' return endif let project = '' let locate_in = scope if scope == 'project' let project = '' let names = eclim#project#util#GetProjectNames() let prompt = 'Choose a project (ctrl-c to cancel): ' while project == '' let project = input( \ prompt, '', 'customlist,eclim#project#util#CommandCompleteProject') if project == '' echo '' return endif if !eclim#util#ListContains(names, project) let prompt = "Project '" . project . "' not found (ctrl-c to cancel): " let project = '' endif endwhile let locate_in = project let workspace = eclim#project#util#GetProjectWorkspace(project) if type(workspace) == g:LIST_TYPE let workspaces = workspace unlet workspace let response = eclim#util#PromptList( \ 'Muliple workspaces found, please choose the target workspace', \ workspaces, g:EclimHighlightInfo) " user cancelled, error, etc. if response < 0 return endif let workspace = workspaces[response] endif elseif scope == 'workspace' let project = '' let instance = eclim#client#nailgun#ChooseEclimdInstance() if type(instance) != g:DICT_TYPE return endif let workspace = instance.workspace else let workspace = '' endif call s:CloseScopeChooser() let b:scope = scope let b:project = project let b:workspace = workspace != '' ? workspace : b:workspace exec 'file ' . escape('[Locate in ' . locate_in . ']', ' ') call eclim#common#locate#LocateFileCompletion() endfunction " }}} function! s:CloseScopeChooser() " {{{ let winnum = b:locate_winnr bwipeout exec winnum . 'winc w' " hack to make :q work like the other close mappings doautocmd BufEnter " if we end up in a non-Locate window, make sure everything is as it should " be (a hack for the above hack). augroup locate_file_chooser_hack autocmd! autocmd BufEnter * \ if bufname('%') !~ '^\[Locate in .*\]$' | \ call eclim#common#locate#LocateFileClose() | \ endif | \ autocmd! locate_file_chooser_hack augroup END endfunction " }}} function! s:LocateFileHelp() " {{{ let winnr = winnr() noautocmd exec bufwinnr(b:results_bufnum) . 'winc w' let help_bufnum = eclim#help#BufferHelp(s:help, 'vertical', 50) exec winnr . 'winc w' let b:help_bufnum = help_bufnum return '' endfunction " }}} function! s:LocateFileConvertPattern(pattern, fuzzy) " {{{ let pattern = a:pattern if a:fuzzy let pattern = '.*' . substitute(pattern, '\(.\)', '\1.*?', 'g') let pattern = substitute(pattern, '\.\([^*]\)', '\\.\1', 'g') else " if the user supplied a path, prepend a '.*/' to it so that they don't need " to type full paths to match. if pattern =~ '.\+/' let pattern = '.*/' . pattern endif let pattern = substitute(pattern, '\*\*', '.*', 'g') let pattern = substitute(pattern, '\(^\|\([^.]\)\)\*', '\1[^/]*?', 'g') let pattern = substitute(pattern, '\.\([^*]\)', '\\.\1', 'g') "let pattern = substitute(pattern, '\([^*]\)?', '\1.', 'g') let pattern .= '.*' endif return pattern endfunction " }}} function! s:LocateFileFunction(scope) " {{{ if eclim#util#ListContains(s:scopes, a:scope) return function('s:LocateFile_' . a:scope) endif return function('LocateFile_' . a:scope) endfunction " }}} function! s:LocateFileCommand(pattern) " {{{ let command = s:command_locate if g:EclimLocateFileCaseInsensitive == 'always' || \ (a:pattern !~# '[A-Z]' && g:EclimLocateFileCaseInsensitive != 'never') let command .= ' -i' endif let command .= ' -p "' . a:pattern . '"' return command endfunction " }}} function! s:LocateFile_workspace(pattern) " {{{ let command = substitute(s:LocateFileCommand(a:pattern), '', 'workspace', '') let results = eclim#Execute(command, {'workspace': b:workspace}) if type(results) != g:LIST_TYPE return [] endif return results endfunction " }}} function! s:LocateFile_project(pattern) " {{{ let command = substitute(s:LocateFileCommand(a:pattern), '', 'project', '') let command .= ' -n "' . b:project . '"' let results = eclim#Execute(command, {'workspace': b:workspace}) if type(results) != g:LIST_TYPE return [] endif return results endfunction " }}} function! s:LocateFile_buffers(pattern) " {{{ redir => list silent exec 'buffers' redir END let buffers = map(split(list, '\n'), \ "substitute(v:val, '.\\{-}\"\\(.\\{-}\\)\".*', '\\1', '')") if a:pattern =~ '/' let buffers = map(buffers, "fnamemodify(v:val, ':p')") endif if len(buffers) > 0 let tempfile = substitute(tempname(), '\', '/', 'g') call writefile(buffers, tempfile) try return eclim#common#locate#LocateFileFromFileList(a:pattern, tempfile) finally call delete(tempfile) endtry endif return [] endfunction " }}} function! s:LocateFile_quickfix(pattern) " {{{ let buffers = [] let prev = '' for entry in getqflist() let name = bufname(entry.bufnr) if a:pattern =~ '/' let name = fnamemodify(name, ':p') endif if name != prev call add(buffers, name) let prev = name endif endfor if len(buffers) > 0 let tempfile = substitute(tempname(), '\', '/', 'g') call writefile(buffers, tempfile) try return eclim#common#locate#LocateFileFromFileList(a:pattern, tempfile) finally call delete(tempfile) endtry endif return [] endfunction " }}} function! eclim#common#locate#LocateFileFromFileList(pattern, file) " {{{ let file = a:file if has('win32unix') let file = eclim#cygwin#WindowsPath(file) endif if eclim#EclimAvailable(0) let command = substitute(s:LocateFileCommand(a:pattern), '', 'list', '') let command .= ' -f "' . file . '"' let results = eclim#Execute(command, {'workspace': b:workspace}) if type(results) != g:LIST_TYPE return [] endif else let results = [] for result in readfile(file) call add(results, {'name': fnamemodify(result, ':t'), 'path': result}) endfor endif return results endfunction " }}} " vim:ft=vim:fdm=marker