589 lines
18 KiB
VimL
589 lines
18 KiB
VimL
" Author: Eric Van Dewoestine
|
|
"
|
|
" Description: {{{
|
|
" Plugin that integrates vim with the eclipse plugin eclim (ECLipse
|
|
" IMproved).
|
|
"
|
|
" This plugin contains shared functions that can be used regardless of the
|
|
" current file type being edited.
|
|
"
|
|
" 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 Variables {{{
|
|
let s:echo_connection_errors = 1
|
|
" }}}
|
|
|
|
" Script Variables {{{
|
|
let s:command_ping = '-command ping'
|
|
let s:command_settings = '-command settings'
|
|
let s:command_settings_update = '-command settings_update -s "<settings>"'
|
|
let s:command_shutdown = "-command shutdown"
|
|
let s:command_jobs = '-command jobs'
|
|
let s:connect= '^connect: .*$'
|
|
|
|
let s:vim_settings = {}
|
|
let s:vim_settings_defaults = {}
|
|
let s:vim_settings_types = {}
|
|
let s:vim_settings_validators = {}
|
|
" }}}
|
|
|
|
function! eclim#Execute(command, ...) " {{{
|
|
" Optional args:
|
|
" options {
|
|
" One of the following to determine the eclimd instance to use, honored in
|
|
" the order shown here:
|
|
" instance: dictionary representing an eclimd instance.
|
|
" project: project name
|
|
" workspace: workspace path
|
|
" dir: directory path to use as the current dir
|
|
" exec: 1 to execute the command using execute instead of system.
|
|
" raw: 1 to get the result without evaluating as json
|
|
" }
|
|
|
|
if exists('g:EclimDisabled')
|
|
" if we are not in an autocmd or the autocmd is for an acwrite buffer,
|
|
" alert the user that eclimd is disabled.
|
|
if expand('<abuf>') == '' || &buftype == 'acwrite'
|
|
call eclim#util#EchoWarning(
|
|
\ "eclim is currently disabled. use :EclimEnable to enable it.")
|
|
endif
|
|
return
|
|
endif
|
|
|
|
if !eclim#EclimAvailable()
|
|
return
|
|
endif
|
|
|
|
let command = '-editor vim ' . a:command
|
|
|
|
" encode special characters
|
|
" http://www.w3schools.com/TAGS/ref_urlencode.asp
|
|
let command = substitute(command, '\*', '%2A', 'g')
|
|
let command = substitute(command, '\$', '%24', 'g')
|
|
let command = substitute(command, '<', '%3C', 'g')
|
|
let command = substitute(command, '>', '%3E', 'g')
|
|
|
|
" determine the eclimd instance to use
|
|
let options = a:0 ? a:1 : {}
|
|
let instance = get(options, 'instance', {})
|
|
if len(instance) == 0
|
|
let project = get(options, 'project', '')
|
|
if 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
|
|
else
|
|
let workspace = ''
|
|
endif
|
|
|
|
if workspace == ''
|
|
let workspace = get(options, 'workspace', '')
|
|
endif
|
|
|
|
let dir = workspace != '' ? workspace : get(options, 'dir', '')
|
|
let chosen = eclim#client#nailgun#ChooseEclimdInstance(dir)
|
|
if type(chosen) != g:DICT_TYPE
|
|
return
|
|
endif
|
|
let instance = chosen
|
|
endif
|
|
|
|
let exec = get(options, 'exec', 0)
|
|
let [retcode, result] = eclim#client#nailgun#Execute(instance, command, exec)
|
|
let result = substitute(result, '\n$', '', '')
|
|
|
|
" not sure this is the best place to handle this, but when using the python
|
|
" client, the result has a trailing ctrl-m on windows. also account for
|
|
" running under cygwin vim.
|
|
if has('win32') || has('win64') || has('win32unix')
|
|
let result = substitute(result, "\<c-m>$", '', '')
|
|
endif
|
|
|
|
" an echo during startup causes an annoying issue with vim.
|
|
"call eclim#util#Echo(' ')
|
|
|
|
" check for errors
|
|
let error = ''
|
|
if result =~ '^[^\n]*Exception:\?[^\n]*\n\s\+\<at\> ' ||
|
|
\ result =~ '^[^\n]*ResourceException(.\{-})\[[0-9]\+\]:[^\n]*\n\s\+\<at\> '
|
|
if g:EclimLogLevel != 'trace'
|
|
let error = substitute(result, '\(.\{-}\)\n.*', '\1', '')
|
|
else
|
|
let error = result
|
|
endif
|
|
elseif retcode
|
|
let error = result
|
|
endif
|
|
|
|
if retcode || error != ''
|
|
if s:echo_connection_errors
|
|
if error =~ s:connect
|
|
" if we are not in an autocmd or the autocmd is for an acwrite buffer,
|
|
" alert the user that eclimd is not running.
|
|
if expand('<abuf>') == '' || &buftype == 'acwrite'
|
|
call eclim#util#EchoWarning(
|
|
\ "unable to connect to eclimd (port: " . instance.port . ") - " . error)
|
|
endif
|
|
else
|
|
let error = error . "\n" .
|
|
\ 'while executing command (port: ' . instance.port . '): ' . command
|
|
" if we are not in an autocmd or in a autocmd for an acwrite buffer,
|
|
" echo the error, otherwise just log it.
|
|
if expand('<abuf>') == '' || &buftype == 'acwrite'
|
|
call eclim#util#EchoError(error)
|
|
else
|
|
call eclim#util#EchoDebug(error)
|
|
endif
|
|
endif
|
|
endif
|
|
return
|
|
endif
|
|
|
|
let raw = get(options, 'raw', 0)
|
|
return result != '' && !raw ? eval(result) : result
|
|
endfunction " }}}
|
|
|
|
function! eclim#Disable() " {{{
|
|
if !exists('g:EclimDisabled')
|
|
let g:EclimDisabled = 1
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! eclim#Enable() " {{{
|
|
if exists('g:EclimDisabled')
|
|
unlet g:EclimDisabled
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! eclim#EclimAvailable(...) " {{{
|
|
" Optional args:
|
|
" echo: Whether or not to echo an error if eclim is not available
|
|
" (default: 1)
|
|
let instances = eclim#UserHome() . '/.eclim/.eclimd_instances'
|
|
let available = filereadable(instances)
|
|
let echo = a:0 ? a:1 : 1
|
|
if echo && !available && expand('<abuf>') == ''
|
|
call eclim#util#EchoError(printf(
|
|
\ 'No eclimd instances found running (eclimd created file not found %s)',
|
|
\ eclim#UserHome() . '/.eclim/.eclimd_instances'))
|
|
endif
|
|
return available
|
|
endfunction " }}}
|
|
|
|
function! eclim#PingEclim(echo, ...) " {{{
|
|
" If echo is non 0, then the result is echoed to the user.
|
|
" Optional args:
|
|
" workspace
|
|
|
|
let workspace = a:0 ? a:1 : ''
|
|
if a:echo
|
|
let result = eclim#Execute(s:command_ping, {'workspace': workspace})
|
|
if type(result) == g:DICT_TYPE
|
|
call eclim#util#Echo(
|
|
\ 'eclim ' . result.eclim . "\n" .
|
|
\ 'eclipse ' . result.eclipse)
|
|
endif
|
|
else
|
|
let savedErr = s:echo_connection_errors
|
|
let savedLog = g:EclimLogLevel
|
|
let s:echo_connection_errors = 0
|
|
let g:EclimLogLevel = 'off'
|
|
|
|
let result = eclim#Execute(s:command_ping, {'workspace': workspace})
|
|
|
|
let s:echo_connection_errors = savedErr
|
|
let g:EclimLogLevel = savedLog
|
|
|
|
return type(result) == g:DICT_TYPE
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! eclim#ParseSettingErrors(errors) " {{{
|
|
let errors = []
|
|
for error in a:errors
|
|
let message = error.message
|
|
let setting = substitute(message, '^\(.\{-}\): .*', '\1', '')
|
|
let message = substitute(message, '^.\{-}: \(.*\)', '\1', '')
|
|
if error.line == 1 && setting != error.message
|
|
let line = search('^\s*' . setting . '\s*=', 'cnw')
|
|
let error.line = line > 0 ? line : 1
|
|
endif
|
|
call add(errors, {
|
|
\ 'bufnr': bufnr('%'),
|
|
\ 'lnum': error.line,
|
|
\ 'text': message,
|
|
\ 'type': error.warning == 1 ? 'w' : 'e',
|
|
\ })
|
|
endfor
|
|
return errors
|
|
endfunction " }}}
|
|
|
|
function! eclim#SaveSettings(command, project) " {{{
|
|
" don't check modified since undo seems to not set the modified flag
|
|
"if &modified
|
|
let tempfile = substitute(tempname(), '\', '/', 'g')
|
|
|
|
" get all lines, filtering out comments and blank lines
|
|
let lines = filter(getline(1, line('$')), 'v:val !~ "^\\s*\\(#\\|$\\)"')
|
|
|
|
" convert lines into a settings dict
|
|
let index = 0
|
|
let settings = {}
|
|
let pattern = '^\s*\([[:alnum:]_.-]\+\)\s*=\s*\(.*\)'
|
|
while index < len(lines)
|
|
if lines[index] =~ pattern
|
|
let name = substitute(lines[index], pattern, '\1', '')
|
|
let value = substitute(lines[index], pattern, '\2', '')
|
|
while value =~ '\\$'
|
|
let index += 1
|
|
let value = substitute(value, '\\$', '', '')
|
|
let value .= substitute(lines[index], '^\s*', '', '')
|
|
endwhile
|
|
let settings[name] = value
|
|
endif
|
|
let index += 1
|
|
endwhile
|
|
call writefile([string(settings)], tempfile)
|
|
|
|
if has('win32unix')
|
|
let tempfile = eclim#cygwin#WindowsPath(tempfile)
|
|
endif
|
|
|
|
let command = a:command
|
|
let command = substitute(command, '<project>', a:project, '')
|
|
let command = substitute(command, '<settings>', tempfile, '')
|
|
|
|
if exists('b:eclimd_instance')
|
|
let result = eclim#Execute(command, {'instance': b:eclimd_instance})
|
|
else
|
|
let result = eclim#Execute(command)
|
|
endif
|
|
|
|
if type(result) == g:LIST_TYPE
|
|
call eclim#util#EchoError
|
|
\ ("Operation contained errors. See location list for details.")
|
|
let errors = eclim#ParseSettingErrors(result)
|
|
call eclim#util#SetLocationList(errors)
|
|
else
|
|
call eclim#util#ClearLocationList()
|
|
call eclim#util#Echo(result)
|
|
endif
|
|
|
|
setlocal nomodified
|
|
"endif
|
|
endfunction " }}}
|
|
|
|
function! eclim#Settings(workspace) " {{{
|
|
let instance = eclim#client#nailgun#ChooseEclimdInstance(a:workspace)
|
|
if type(instance) != g:DICT_TYPE
|
|
return
|
|
endif
|
|
|
|
let settings = eclim#Execute(s:command_settings, {'instance': instance})
|
|
if type(settings) != g:LIST_TYPE
|
|
return
|
|
endif
|
|
|
|
let content = ['# Global settings for workspace: ' . instance.workspace, '']
|
|
let path = ''
|
|
for setting in settings
|
|
if setting.path != path
|
|
if path != ''
|
|
let content += ['# }', '']
|
|
endif
|
|
let path = setting.path
|
|
call add(content, '# ' . path . ' {')
|
|
endif
|
|
let description = split(setting.description, '\n')
|
|
let content += map(description, "'\t# ' . v:val")
|
|
call add(content, "\t" . setting.name . '=' . setting.value)
|
|
endfor
|
|
if path != ''
|
|
call add(content, '# }')
|
|
endif
|
|
|
|
call eclim#util#TempWindow("Workspace_Settings", content)
|
|
setlocal buftype=acwrite
|
|
setlocal filetype=jproperties
|
|
setlocal noreadonly
|
|
setlocal modifiable
|
|
setlocal foldmethod=marker
|
|
setlocal foldmarker={,}
|
|
let b:eclimd_instance = instance
|
|
|
|
augroup eclim_settings
|
|
autocmd! BufWriteCmd <buffer>
|
|
exec 'autocmd BufWriteCmd <buffer> ' .
|
|
\ 'call eclim#SaveSettings(s:command_settings_update, "")'
|
|
augroup END
|
|
endfunction " }}}
|
|
|
|
function! eclim#ShutdownEclim() " {{{
|
|
call eclim#Execute(s:command_shutdown)
|
|
endfunction " }}}
|
|
|
|
function! eclim#LoadVimSettings() " {{{
|
|
let settings_file = eclim#UserHome() . '/.eclim/.eclim_settings'
|
|
if filereadable(settings_file)
|
|
let lines = readfile(settings_file)
|
|
if len(lines) == 1 && lines[0] =~ '^{.*}$'
|
|
let settings = eval(lines[0])
|
|
for [key, value] in items(settings)
|
|
let name = 'g:Eclim' . key
|
|
if !exists(name)
|
|
exec 'let ' . name . ' = ' . string(value)
|
|
endif
|
|
unlet value
|
|
endfor
|
|
endif
|
|
endif
|
|
|
|
" Handle legacy sign/log level values
|
|
let legacy = {0: 'off', 1: 'error', 2: 'error', 3: 'warning', 4: 'info', 5: 'debug', 6: 'trace'}
|
|
if exists('g:EclimLogLevel') && type(g:EclimLogLevel) == g:NUMBER_TYPE
|
|
let g:EclimLogLevel = get(legacy, g:EclimLogLevel, 'info')
|
|
endif
|
|
if exists('g:EclimSignLevel') && type(g:EclimSignLevel) == g:NUMBER_TYPE
|
|
let g:EclimSignLevel = get(legacy, g:EclimSignLevel, 'info')
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! eclim#AddVimSetting(namespace, name, default, desc, ...) " {{{
|
|
" Optional args:
|
|
" regex: regular expression used to validate the setting's value.
|
|
if !has_key(s:vim_settings, a:namespace)
|
|
let s:vim_settings[a:namespace] = {}
|
|
endif
|
|
let name = substitute(a:name, 'g:Eclim', '', '')
|
|
let s:vim_settings[a:namespace][name] = {'desc': a:desc}
|
|
let s:vim_settings_defaults[name] = a:default
|
|
let s:vim_settings_types[name] = type(a:default)
|
|
let regex = a:0 ? a:1 : ''
|
|
if regex != ''
|
|
let s:vim_settings_validators[name] = regex
|
|
if exists(a:name)
|
|
exec 'let value = ' . a:name
|
|
if value !~ '^' . regex . '$'
|
|
echo a:name . ' must match ' . regex
|
|
exec 'unlet ' . a:name
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
if !exists(a:name)
|
|
exec 'let ' . a:name . ' = ' . string(a:default)
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! eclim#VimSettings() " {{{
|
|
let content = [
|
|
\ "# Eclim's global vim settings",
|
|
\ "# The settings here allow you to configure the vim side behavior of eclim.",
|
|
\ "# You can use <cr> on a setting name to open the eclim docs for that setting.",
|
|
\ "#",
|
|
\ "# Note: If you have g:EclimXXX variables set in your .vimrc, those will take",
|
|
\ "# precedence over any changes you make here.",
|
|
\ ]
|
|
for namespace in sort(keys(s:vim_settings))
|
|
let content += ['', '# ' . namespace . ' {{{']
|
|
for name in sort(keys(s:vim_settings[namespace]))
|
|
let setting = s:vim_settings[namespace][name]
|
|
let desc = split(setting.desc, '\n')
|
|
let content += map(desc, "'\t# ' . v:val")
|
|
exec 'let value = string(g:Eclim' . name . ')'
|
|
call add(content, "\t" . name . '=' . value)
|
|
endfor
|
|
let content += ['# }}}']
|
|
endfor
|
|
|
|
call eclim#util#TempWindow("Vim_Settings", content)
|
|
setlocal buftype=acwrite
|
|
setlocal filetype=jproperties
|
|
setlocal noreadonly
|
|
setlocal modifiable
|
|
setlocal foldmethod=marker
|
|
setlocal foldmarker={{{,}}}
|
|
nnoremap <cr> :call <SID>VimSettingHelp()<cr>
|
|
|
|
augroup eclim_settings
|
|
autocmd! BufWriteCmd <buffer>
|
|
autocmd BufWriteCmd <buffer> call eclim#SaveVimSettings()
|
|
augroup END
|
|
endfunction " }}}
|
|
|
|
function! s:VimSettingHelp() " {{{
|
|
let pos = getpos('.')
|
|
try
|
|
call cursor(0, 1)
|
|
normal! w
|
|
let syntax = synIDattr(synID(line('.'), col('.'), 1), 'name')
|
|
finally
|
|
call setpos('.', pos)
|
|
endtry
|
|
|
|
if syntax == 'jpropertiesIdentifier'
|
|
let line = getline('.')
|
|
let name = substitute(line, '^\s*\(\w\+\)=.*', '\1', '')
|
|
if name != line
|
|
exec 'EclimHelp g:Eclim' . name
|
|
endif
|
|
endif
|
|
endfunction " }}}
|
|
|
|
function! eclim#SaveVimSettings() " {{{
|
|
" get all lines, filtering out comments and blank lines
|
|
let lines = filter(getline(1, line('$')), 'v:val !~ "^\\s*\\(#\\|$\\)"')
|
|
|
|
" convert lines into a settings dict
|
|
let index = 0
|
|
let settings = {}
|
|
let pattern = '^\s*\([[:alnum:]_.-]\+\)\s*=\s*\(.*\)'
|
|
let errors = []
|
|
while index < len(lines)
|
|
try
|
|
if lines[index] =~ pattern
|
|
let name = substitute(lines[index], pattern, '\1', '')
|
|
if !has_key(s:vim_settings_types, name)
|
|
continue
|
|
endif
|
|
|
|
let value = substitute(lines[index], pattern, '\2', '')
|
|
while value =~ '\\$'
|
|
let index += 1
|
|
let value = substitute(value, '\\$', '', '')
|
|
let value .= substitute(lines[index], '^\s*', '', '')
|
|
endwhile
|
|
let value = substitute(value, "\\(^['\"]\\|['\"]$\\)", '', 'g')
|
|
|
|
if has_key(s:vim_settings_validators, name)
|
|
let regex = s:vim_settings_validators[name]
|
|
if value !~ '^' . regex . '$'
|
|
let [line, col] = searchpos('^\s*' . name . '=', 'nw')
|
|
call add(errors, {
|
|
\ 'filename': expand('%'),
|
|
\ 'message': name . ': must match ' . regex,
|
|
\ 'line': line,
|
|
\ 'column': col,
|
|
\ 'type': 'error',
|
|
\ })
|
|
continue
|
|
endif
|
|
endif
|
|
|
|
if s:vim_settings_types[name] != g:STRING_TYPE
|
|
try
|
|
let typed_value = eval(value)
|
|
unlet value
|
|
let value = typed_value
|
|
catch /E121\|E115/
|
|
let [line, col] = searchpos('^\s*' . name . '=', 'nw')
|
|
call add(errors, {
|
|
\ 'filename': expand('%'),
|
|
\ 'message': name . ': ' . v:exception,
|
|
\ 'line': line,
|
|
\ 'column': col,
|
|
\ 'type': 'error',
|
|
\ })
|
|
continue
|
|
endtry
|
|
endif
|
|
|
|
let default = s:vim_settings_defaults[name]
|
|
if value != default
|
|
let settings[name] = value
|
|
endif
|
|
endif
|
|
finally
|
|
let index += 1
|
|
unlet! value typed_value default
|
|
endtry
|
|
endwhile
|
|
|
|
if len(errors)
|
|
call eclim#util#SetLocationList(eclim#util#ParseLocationEntries(errors))
|
|
call eclim#util#EchoError(
|
|
\ len(errors) . ' error' . (len(errors) > 1 ? 's' : '') . ' found.')
|
|
return
|
|
endif
|
|
|
|
call eclim#util#ClearLocationList()
|
|
|
|
if !isdirectory(eclim#UserHome() . '/.eclim')
|
|
call mkdir(eclim#UserHome() . '/.eclim')
|
|
endif
|
|
let settings_file = eclim#UserHome() . '/.eclim/.eclim_settings'
|
|
if writefile([string(settings)], settings_file) == 0
|
|
call eclim#util#Echo('Settings saved. You may need to restart vim for all changes to take affect.')
|
|
else
|
|
call eclim#util#Echo('Unable to write settings.')
|
|
endif
|
|
|
|
setlocal nomodified
|
|
endfunction " }}}
|
|
|
|
function! eclim#UserHome() " {{{
|
|
let home = expand('$HOME')
|
|
if has('win32unix')
|
|
let home = eclim#cygwin#WindowsHome()
|
|
elseif has('win32') || has('win64')
|
|
let home = expand('$USERPROFILE')
|
|
endif
|
|
return substitute(home, '\', '/', 'g')
|
|
endfunction " }}}
|
|
|
|
function! eclim#WaitOnRunningJobs(timeout) " {{{
|
|
" Args:
|
|
" timeout: max time to wait in milliseconds
|
|
let running = 1
|
|
let waited = 0
|
|
while running && waited < a:timeout
|
|
let jobs = eclim#Execute(s:command_jobs)
|
|
if type(jobs) == g:LIST_TYPE
|
|
let running = 0
|
|
for job in jobs
|
|
if job.status == 'running'
|
|
call eclim#util#EchoDebug('Wait on job: ' . job.job)
|
|
let running = 1
|
|
let waited += 300
|
|
sleep 300m
|
|
break
|
|
endif
|
|
endfor
|
|
endif
|
|
endwhile
|
|
if running
|
|
call eclim#util#EchoDebug('Timeout exceeded waiting on jobs')
|
|
endif
|
|
endfunction " }}}
|
|
|
|
" vim:ft=vim:fdm=marker
|