dotfiles/vim/bundle/eclim/autoload/eclim/java/search.vim

410 lines
14 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/>.
"
" }}}
" Script Varables {{{
let s:search_src = "java_search"
let s:search_doc = "java_docsearch"
let s:search_element =
\ '-command <search> -n "<project>" -f "<file>" ' .
\ '-o <offset> -e <encoding> -l <length> <args>'
let s:search_pattern = '-command <search>'
let s:options_map = {
\ '-p': [],
\ '-i': [],
\ '-a': ['split', 'vsplit', 'edit', 'tabnew', 'lopen'],
\ '-s': ['all', 'project'],
\ '-x': ['all', 'declarations', 'implementors', 'references'],
\ '-t': [
\ 'annotation',
\ 'class',
\ 'classOrEnum',
\ 'classOrInterface',
\ 'constructor',
\ 'enum',
\ 'field',
\ 'interface',
\ 'method',
\ 'package',
\ 'type',
\ ],
\ }
let s:search_alt_all = '\<<element>\>'
let s:search_alt_references = s:search_alt_all
let s:search_alt_implementors =
\ '\(implements\|extends\)\_[0-9A-Za-z,[:space:]]*\<<element>\>\_[0-9A-Za-z,[:space:]]*{'
" }}}
function! s:Search(command, ...) " {{{
" Executes a search.
" Usage closely resebles eclim command line client usage.
" When doing a non-pattern search the element under the cursor is searched for.
" Search for declarations of element under the cursor
" call s:Search("-x", "declarations")
" Search for references of HashMap
" call s:Search("-p", "HashM*", "-t", "class", "-x", "references")
" Or all the arguments can be passed in at once:
" call s:Search("-p 'HashM*' -t class -x references")
let argline = ""
let index = 1
while index <= a:0
if index != 1
let argline = argline . " "
endif
let argline = argline . a:{index}
let index = index + 1
endwhile
" check if pattern supplied without -p.
if argline !~ '^\s*-[a-z]' && argline !~ '^\s*$'
let argline = '-p ' . argline
endif
let in_project = eclim#project#util#IsCurrentFileInProject(0)
" element search
if argline !~ '-p\>'
if &ft != 'java'
call eclim#util#EchoWarning
\ ("Element searches only supported in java source files.")
return 0
endif
if !eclim#java#util#IsValidIdentifier(expand('<cword>'))
call eclim#util#EchoError
\ ("Element under the cursor is not a valid java identifier.")
return 0
endif
if !in_project
" build a pattern search and execute it
let results = s:SearchAlternate('-p ' . s:BuildPattern() . ' ' . argline, 1)
" kind of gross. if there was no alternate result and eclimd is not
" running, then make sure a message is echoed to the user so they know
" that eclimd not running *may* be the cause of no results.
if len(results) == 0 && !eclim#EclimAvailable()
return 0
endif
return results
endif
let project = eclim#project#util#GetCurrentProjectName()
let file = eclim#project#util#GetProjectRelativeFilePath()
let position = eclim#util#GetCurrentElementPosition()
let offset = substitute(position, '\(.*\);\(.*\)', '\1', '')
let length = substitute(position, '\(.*\);\(.*\)', '\2', '')
let search_cmd = s:search_element
let search_cmd = substitute(search_cmd, '<project>', project, '')
let search_cmd = substitute(search_cmd, '<search>', a:command, '')
let search_cmd = substitute(search_cmd, '<file>', file, '')
let search_cmd = substitute(search_cmd, '<offset>', offset, '')
let search_cmd = substitute(search_cmd, '<encoding>', eclim#util#GetEncoding(), '')
let search_cmd = substitute(search_cmd, '<length>', length, '')
let search_cmd = substitute(search_cmd, '<args>', argline, '')
let result = eclim#Execute(search_cmd)
" pattern search
else
let project = eclim#project#util#GetCurrentProjectName()
" pattern search
let search_cmd = s:search_pattern
let search_cmd = substitute(search_cmd, '<search>', a:command, '')
if project != ''
let search_cmd .= ' -n "' . project . '"'
endif
let file = eclim#project#util#GetProjectRelativeFilePath()
if file != ''
let search_cmd .= ' -f "' . file . '"'
endif
let search_cmd .= ' ' . argline
" quote the search pattern
let search_cmd =
\ substitute(search_cmd, '\(.*-p\s\+\)\(.\{-}\)\(\s\|$\)\(.*\)', '\1"\2"\3\4', '')
let result = eclim#Execute(search_cmd)
if !in_project && filereadable(expand('%'))
return result + s:SearchAlternate(argline, 0)
endif
endif
return result
endfunction " }}}
function! s:SearchAlternate(argline, element) " {{{
" Alternate search for non-project src files using vimgrep and &path.
call eclim#util#EchoInfo("Executing alternate search...")
if a:argline =~ '-t'
call eclim#util#EchoError
\ ("Alternate search doesn't support the type (-t) option yet.")
return []
endif
let search_pattern = ""
if a:argline =~ '-x all'
let search_pattern = s:search_alt_all
elseif a:argline =~ '-x implementors'
let search_pattern = s:search_alt_implementors
elseif a:argline =~ '-x references'
let search_pattern = s:search_alt_references
endif
let pattern = substitute(a:argline, '.*-p\s\+\(.\{-}\)\(\s.*\|$\)', '\1', '')
let file_pattern = substitute(pattern, '\.', '/', 'g') . ".java"
" search relative to the current dir first.
let package_path = substitute(eclim#java#util#GetPackage(), '\.', '/', 'g')
let path = substitute(expand('%:p:h'), '\', '/', 'g')
let path = substitute(path, package_path, '', '')
let files = split(eclim#util#Globpath(path, "**/" . file_pattern), '\n')
" if none found, then search the path.
if len(files) == 0
let files = eclim#util#FindFileInPath(file_pattern, 1)
let path = ""
endif
let results = []
if len(files) > 0 && search_pattern != ''
" narrow down to, hopefully, a distribution path for a narrower search.
let response = eclim#util#PromptList(
\ "Multiple type matches. Please choose the relevant file.",
\ files, g:EclimHighlightInfo)
if response == -1
return
endif
let file = substitute(get(files, response), '\', '/', 'g')
if path == ""
let path = eclim#util#GetPathEntry(file)
endif
let path = escape(path, '/\')
let path = substitute(file, '\(' . path . '[/\\]\?.\{-}[/\\]\).*', '\1', '')
let pattern = substitute(pattern, '\*', '.\\\\{-}', 'g')
let search_pattern = substitute(search_pattern, '<element>', pattern, '')
let command = "vimgrep /" . search_pattern . "/gj " . path . "**/*.java"
silent! exec command
let loclist = getloclist(0)
for entry in loclist
let bufname = bufname(entry.bufnr)
let result = {
\ 'filename': bufname,
\ 'message': entry.text,
\ 'line': entry.lnum,
\ 'column': entry.col,
\ }
" when searching for implementors, prevent dupes from the somewhat
" greedy pattern search (may need some more updating post conversion to
" dict results).
if a:argline !~ '-x implementors' || !eclim#util#ListContains(results, result)
call add(results, result)
endif
endfor
elseif len(files) > 0
for file in files
let fully_qualified = eclim#java#util#GetPackage(file) . '.' .
\ eclim#java#util#GetClassname(file)
" if an element search, filter out results that are not imported.
if !a:element || eclim#java#util#IsImported(fully_qualified)
call add(results, {
\ 'filename': file,
\ 'message': fully_qualified,
\ 'line': 1,
\ 'column': 1,
\ })
endif
endfor
endif
call eclim#util#Echo(' ')
return results
endfunction " }}}
function! s:BuildPattern() " {{{
" Builds a pattern based on the cursors current position in the file.
let class = expand('<cword>')
" see if the classname element selected is fully qualified.
let line = getline('.')
let package =
\ substitute(line, '.*\s\([0-9A-Za-z._]*\)\.' . class . '\>.*', '\1', '')
" not fully qualified, so attempt to determine package from import.
if package == line
let package = eclim#java#util#GetPackageFromImport(class)
" maybe the element is the current class?
if package == ""
if eclim#java#util#GetClassname() == class
let package = eclim#java#util#GetPackage()
endif
endif
endif
if package != ""
return package . "." . class
endif
return class
endfunction " }}}
function! eclim#java#search#SearchAndDisplay(type, args) " {{{
" Execute a search and displays the results via quickfix.
" if running from a non java source file, no SilentUpdate needed.
if &ft == 'java'
call eclim#lang#SilentUpdate()
endif
let argline = a:args
" check if just a pattern was supplied.
if argline =~ '^\s*\w'
let argline = '-p ' . argline
endif
" check for user supplied open action
let [action_args, argline] = eclim#util#ExtractCmdArgs(argline, '-a:')
let action = len(action_args) == 2 ? action_args[1] : g:EclimJavaSearchSingleResult
let results = s:Search(a:type, argline)
if type(results) != g:LIST_TYPE
return
endif
if !empty(results)
if a:type == 'java_search'
call eclim#util#SetLocationList(eclim#util#ParseLocationEntries(results))
let locs = getloclist(0)
" if only one result and it's for the current file, just jump to it.
" note: on windows the expand result must be escaped
if len(results) == 1 && locs[0].bufnr == bufnr('%')
if results[0].line != 1 && results[0].column != 1
lfirst
endif
" single result in another file
elseif len(results) == 1 && action != 'lopen'
let entry = getloclist(0)[0]
let name = substitute(bufname(entry.bufnr), '\', '/', 'g')
call eclim#util#GoToBufferWindowOrOpen(name, action)
call eclim#util#SetLocationList(eclim#util#ParseLocationEntries(results))
call eclim#display#signs#Update()
call cursor(entry.lnum, entry.col)
" multiple results and user specified an action other than lopen
elseif len(results) && len(action_args) && action != 'lopen'
let locs = getloclist(0)
let files = map(copy(locs), 'printf(' .
\ '"%s|%s col %s| %s", ' .
\ 'bufname(v:val.bufnr), v:val.lnum, v:val.col, v:val.text)')
let response = eclim#util#PromptList(
\ 'Please choose the file to ' . action,
\ files, g:EclimHighlightInfo)
if response == -1
return
endif
let entry = locs[response]
let name = substitute(bufname(entry.bufnr), '\', '/', 'g')
call eclim#util#GoToBufferWindowOrOpen(name, action)
call eclim#display#signs#Update()
call cursor(entry.lnum, entry.col)
else
exec 'lopen ' . g:EclimLocationListHeight
endif
elseif a:type == 'java_docsearch'
let window_name = "javadoc_search_results"
let filename = expand('%:p')
call eclim#util#TempWindowClear(window_name)
if len(results) == 1 && g:EclimJavaDocSearchSingleResult == 'open'
let entry = results[0]
call s:ViewDoc(entry)
else
call eclim#util#TempWindow(
\ window_name, results, {'height': g:EclimLocationListHeight})
nnoremap <silent> <buffer> <cr> :call <SID>ViewDoc()<cr>
augroup temp_window
autocmd! BufWinLeave <buffer>
call eclim#util#GoToBufferWindowRegister(filename)
augroup END
endif
endif
return 1
else
if argline =~ '-p '
let searchedFor = substitute(argline, '.*-p \(.\{-}\)\( .*\|$\)', '\1', '')
call eclim#util#EchoInfo("Pattern '" . searchedFor . "' not found.")
elseif &ft == 'java'
if !eclim#java#util#IsValidIdentifier(expand('<cword>'))
return
endif
let searchedFor = expand('<cword>')
call eclim#util#EchoInfo("No results for '" . searchedFor . "'.")
endif
endif
endfunction " }}}
function! s:ViewDoc(...) " {{{
" View the supplied file in a browser, or if none proved, the file under the
" cursor.
let url = a:0 > 0 ? a:1 : substitute(getline('.'), '\(.\{-}\)|.*', '\1', '')
call eclim#web#OpenUrl(url)
endfunction " }}}
function! eclim#java#search#CommandCompleteSearch(argLead, cmdLine, cursorPos) " {{{
let options_map = s:options_map
" omit the -a args on a javadoc search since those results are opened in a
" browser
if a:cmdLine =~ '^JavaDocS'
let options_map = copy(options_map)
unlet options_map['-a']
endif
return eclim#util#CommandCompleteOptions(
\ a:argLead, a:cmdLine, a:cursorPos, options_map)
endfunction " }}}
function! eclim#java#search#CommandCompleteSearchContext(argLead, cmdLine, cursorPos) " {{{
let options_map = {'-a': s:options_map['-a']}
return eclim#util#CommandCompleteOptions(
\ a:argLead, a:cmdLine, a:cursorPos, options_map)
endfunction " }}}
function! eclim#java#search#FindClassDeclaration() " {{{
" Used by non java source files to find the declaration of a classname under
" the cursor.
let line = getline('.')
let class = substitute(line,
\ '.\{-}\([0-9a-zA-Z_.]*\%' . col('.') . 'c[0-9a-zA-Z_.]*\).*', '\1', '')
if class != line && class != '' && class =~ '^[a-zA-Z]'
call eclim#java#search#SearchAndDisplay(
\ 'java_search', '-t classOrInterface -p ' . class)
endif
endfunction " }}}
" vim:ft=vim:fdm=marker