You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

565 lines
17 KiB
VimL

" Copyright (c) 2011-2012 EditorConfig Team
" All rights reserved.
"
" Redistribution and use in source and binary forms, with or without
" modification, are permitted provided that the following conditions are met:
"
" 1. Redistributions of source code must retain the above copyright notice,
" this list of conditions and the following disclaimer.
" 2. Redistributions in binary form must reproduce the above copyright notice,
" this list of conditions and the following disclaimer in the documentation
" and/or other materials provided with the distribution.
"
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
" POSSIBILITY OF SUCH DAMAGE.
"
if v:version < 700
finish
endif
" check whether this script is already loaded
if exists("g:loaded_EditorConfig")
finish
endif
let g:loaded_EditorConfig = 1
let s:saved_cpo = &cpo
set cpo&vim
" variables {{{1
if !exists('g:EditorConfig_exec_path')
let g:EditorConfig_exec_path = ''
endif
if !exists('g:EditorConfig_python_files_dir')
let g:EditorConfig_python_files_dir = 'plugin/editorconfig-core-py'
endif
if !exists('g:EditorConfig_verbose')
let g:EditorConfig_verbose = 0
endif
if exists('g:EditorConfig_core_mode') && !empty(g:EditorConfig_core_mode)
let s:editorconfig_core_mode = g:EditorConfig_core_mode
else
let s:editorconfig_core_mode = ''
endif
function! s:FindPythonInterp() " {{{1
" Find python interp. If found, return python command; if not found, return ''
if has('unix')
let l:searching_list = [
\ 'python',
\ 'python27',
\ 'python26',
\ 'python25',
\ 'python24',
\ '/usr/local/bin/python',
\ '/usr/local/bin/python27',
\ '/usr/local/bin/python26',
\ '/usr/local/bin/python25',
\ '/usr/local/bin/python24',
\ '/usr/bin/python',
\ '/usr/bin/python27',
\ '/usr/bin/python26',
\ '/usr/bin/python25',
\ '/usr/bin/python24']
elseif has('win32')
let l:searching_list = [
\ 'python',
\ 'python27',
\ 'python26',
\ 'python25',
\ 'python24',
\ 'C:\Python27\python.exe',
\ 'C:\Python26\python.exe',
\ 'C:\Python25\python.exe',
\ 'C:\Python24\python.exe']
endif
for possible_python_interp in l:searching_list
if executable(possible_python_interp)
return possible_python_interp
endif
endfor
return ''
endfunction
function! s:FindPythonFiles() " {{{1
" Find EditorConfig Core python files
" On Windows, we still use slash rather than backslash
let l:old_shellslash = &shellslash
set shellslash
let l:python_core_files_dir = substitute(
\ findfile(g:EditorConfig_python_files_dir . '/main.py',
\ ','.&runtimepath), '/main.py$', '', '')
if empty(l:python_core_files_dir)
return ''
endif
" expand python core file path to full path, and remove the appending '/'
let l:python_core_files_dir = substitute(
\ fnamemodify(l:python_core_files_dir, ':p'), '/$', '', '')
let &shellslash = l:old_shellslash
return l:python_core_files_dir
endfunction
" Mode initialization functions {{{1
function! s:InitializeExternalCommand() " {{{2
" Initialize external_command mode
let s:EditorConfig_exec_path = ''
" User has specified an EditorConfig command. Use that one.
if exists('g:EditorConfig_exec_path') &&
\ !empty(g:EditorConfig_exec_path)
if executable(g:EditorConfig_exec_path)
let s:EditorConfig_exec_path = g:EditorConfig_exec_path
return 0
else
return 1
endif
endif
" User does not specify an EditorConfig command. Let's search for it.
if has('unix')
let l:searching_list = [
\ 'editorconfig',
\ '/usr/local/bin/editorconfig',
\ '/usr/bin/editorconfig',
\ '/opt/bin/editorconfig',
\ '/opt/editorconfig/bin/editorconfig',
\ 'editorconfig.py',
\ '/usr/local/bin/editorconfig.py',
\ '/usr/bin/editorconfig.py',
\ '/opt/bin/editorconfig.py',
\ '/opt/editorconfig/bin/editorconfig.py']
elseif has('win32')
let l:searching_list = [
\ 'editorconfig',
\ 'C:\editorconfig\bin\editorconfig',
\ 'D:\editorconfig\bin\editorconfig',
\ 'E:\editorconfig\bin\editorconfig',
\ 'F:\editorconfig\bin\editorconfig',
\ 'C:\Program Files\editorconfig\bin\editorconfig',
\ 'D:\Program Files\editorconfig\bin\editorconfig',
\ 'E:\Program Files\editorconfig\bin\editorconfig',
\ 'F:\Program Files\editorconfig\bin\editorconfig',
\ 'editorconfig.py']
endif
" search for editorconfig core executable
for possible_cmd in l:searching_list
if executable(possible_cmd)
let s:EditorConfig_exec_path = possible_cmd
break
endif
endfor
if empty(s:EditorConfig_exec_path)
return 2
endif
return 0
endfunction
function! s:InitializePythonExternal() " {{{2
" Initialize external python. Before calling this function, please make sure
" s:FindPythonFiles is called and the return value is set to
" s:editorconfig_core_py_dir
if !exists('s:editorconfig_core_py_dir') ||
\ empty(s:editorconfig_core_py_dir)
return 2
endif
" Find python interp
if !exists('g:editorconfig_python_interp') ||
\ empty('g:editorconfig_python_interp')
let s:editorconfig_python_interp = s:FindPythonInterp()
endif
if empty(s:editorconfig_python_interp) ||
\ !executable(s:editorconfig_python_interp)
return 1
endif
return 0
endfunction
function! s:InitializePythonBuiltin(editorconfig_core_py_dir) " {{{2
" Initialize builtin python. The parameter is the Python Core directory
if exists('s:builtin_python_initialized') && s:builtin_python_initialized
return 0
endif
let s:builtin_python_initialized = 1
let l:ret = 0
if !has('python')
return 1
endif
python << EEOOFF
try:
import vim
import sys
except:
vim.command('let l:ret = 2')
EEOOFF
if l:ret != 0
return l:ret
endif
python << EEOOFF
try:
sys.path.insert(0, vim.eval('a:editorconfig_core_py_dir'))
import editorconfig
import editorconfig.exceptions as editorconfig_except
except:
vim.command('let l:ret = 3')
del sys.path[0]
ec_data = {} # used in order to keep clean Python namespace
EEOOFF
if l:ret != 0
return l:ret
endif
return 0
endfunction
" Do some initalization for the case that the user has specified core mode {{{1
if !empty(s:editorconfig_core_mode)
if s:editorconfig_core_mode == 'external_command'
if s:InitializeExternalCommand()
echo 'EditorConfig: Failed to initialize external_command mode'
finish
endif
else
let s:editorconfig_core_py_dir = s:FindPythonFiles()
if empty(s:editorconfig_core_py_dir)
echo 'EditorConfig: '.
\ 'EditorConfig Python Core files could not be found.'
finish
endif
if s:editorconfig_core_mode == 'python_builtin' &&
\ s:InitializePythonBuiltin(s:editorconfig_core_py_dir)
echo 'EditorConfig: Failed to initialize vim built-in python.'
finish
elseif s:editorconfig_core_mode == 'python_external' &&
\ s:InitializePythonExternal()
echo 'EditorConfig: Failed to find external Python interpreter.'
finish
endif
endif
endif
" Determine the editorconfig_core_mode we should use {{{1
while 1
" If user has specified a mode, just break
if exists('s:editorconfig_core_mode') && !empty(s:editorconfig_core_mode)
break
endif
" Find Python core files. If not found, we try external_command mode
let s:editorconfig_core_py_dir = s:FindPythonFiles()
if empty(s:editorconfig_core_py_dir) " python files are not found
if !s:InitializeExternalCommand()
let s:editorconfig_core_mode = 'external_command'
endif
break
endif
" Builtin python mode first
if !s:InitializePythonBuiltin(s:editorconfig_core_py_dir)
let s:editorconfig_core_mode = 'python_builtin'
break
endif
" Then external_command mode
if !s:InitializeExternalCommand()
let s:editorconfig_core_mode = 'external_command'
break
endif
" Finally external python mode
if !s:InitializePythonExternal()
let s:editorconfig_core_mode = 'python_external'
break
endif
break
endwhile
" No EditorConfig Core is available
if empty(s:editorconfig_core_mode)
echo "EditorConfig: ".
\ "No EditorConfig Core is available. The plugin won't work."
finish
endif
function! s:UseConfigFiles()
if s:editorconfig_core_mode == 'external_command'
call s:UseConfigFiles_ExternalCommand()
elseif s:editorconfig_core_mode == 'python_builtin'
call s:UseConfigFiles_Python_Builtin()
elseif s:editorconfig_core_mode == 'python_external'
call s:UseConfigFiles_Python_External()
else
echohl Error |
\ echo "Unknown EditorConfig Core: " .
\ s:editorconfig_core_mode |
\ echohl None
endif
endfunction
" command, autoload {{{1
command! EditorConfigReload call s:UseConfigFiles() " Reload EditorConfig files
augroup editorconfig
autocmd! editorconfig
autocmd editorconfig BufNewFile,BufReadPost * call s:UseConfigFiles()
autocmd editorconfig BufNewFile,BufRead .editorconfig set filetype=dosini
augroup END
" UseConfigFiles function for different mode {{{1
function! s:UseConfigFiles_Python_Builtin() " {{{2
" Use built-in python to run the python EditorConfig core
let l:config = {}
let l:ret = 0
" ignore buffers that do not have a file path associated
if empty(expand('%:p'))
return 0
endif
python << EEOOFF
ec_data['filename'] = vim.eval("expand('%:p')")
ec_data['conf_file'] = ".editorconfig"
try:
ec_data['options'] = editorconfig.get_properties(ec_data['filename'])
except editorconfig_except.EditorConfigError, e:
if int(vim.eval('g:EditorConfig_verbose')) != 0:
print >> sys.stderr, str(e)
vim.command('let l:ret = 1')
EEOOFF
if l:ret != 0
return l:ret
endif
python << EEOOFF
for key, value in ec_data['options'].items():
vim.command("let l:config['" + key.replace("'", "''") + "'] = " +
"'" + value.replace("'", "''") + "'")
EEOOFF
call s:ApplyConfig(l:config)
return 0
endfunction
function! s:UseConfigFiles_Python_External() " {{{2
" Use external python interp to run the python EditorConfig Core
let l:cmd = s:editorconfig_python_interp . ' ' .
\ s:editorconfig_core_py_dir . '/main.py'
call s:SpawnExternalParser(l:cmd)
return 0
endfunction
function! s:UseConfigFiles_ExternalCommand() " {{{2
" Use external EditorConfig core (The C core, or editorconfig.py)
call s:SpawnExternalParser(s:EditorConfig_exec_path)
endfunction
function! s:SpawnExternalParser(cmd) " {{{2
" Spawn external EditorConfig. Used by s:UseConfigFiles_Python_External() and
" s:UseConfigFiles_ExternalCommand()
let l:cmd = a:cmd
" ignore buffers that do not have a file path associated
if empty(expand("%:p"))
return
endif
" if editorconfig is present, we use this as our parser
if !empty(l:cmd)
let l:config = {}
" In Windows, 'shellslash' also changes the behavior of 'shellescape'.
" It makes 'shellescape' behave like in UNIX environment. So ':setl
" noshellslash' before evaluating 'shellescape' and restore the
" settings afterwards.
let l:old_shellslash = &l:shellslash
setlocal noshellslash
let l:cmd = l:cmd . ' ' . shellescape(expand('%:p'))
let &l:shellslash = l:old_shellslash
let l:parsing_result = split(system(l:cmd), '\n')
" if editorconfig core's exit code is not zero, give out an error
" message
if v:shell_error != 0
echohl ErrorMsg
echo 'Failed to execute "' . l:cmd . '". Exit code: ' .
\ v:shell_error
echo ''
echo 'Message:'
echo l:parsing_result
echohl None
return
endif
for one_line in l:parsing_result
let l:eq_pos = stridx(one_line, '=')
if l:eq_pos == -1 " = is not found. Skip this line
continue
endif
let l:eq_left = strpart(one_line, 0, l:eq_pos)
if l:eq_pos + 1 < strlen(one_line)
let l:eq_right = strpart(one_line, l:eq_pos + 1)
else
let l:eq_right = ''
endif
let l:config[l:eq_left] = l:eq_right
endfor
call s:ApplyConfig(l:config)
endif
endfunction
function! s:ApplyConfig(config) " {{{1
" Set the indentation style according to the config values
if has_key(a:config, "indent_style")
if a:config["indent_style"] == "tab"
setl noexpandtab
elseif a:config["indent_style"] == "space"
setl expandtab
endif
endif
if has_key(a:config, "tab_width")
let &l:tabstop = str2nr(a:config["tab_width"])
endif
if has_key(a:config, "indent_size")
" if indent_size is 'tab', set shiftwidth to tabstop;
" if indent_size is a positive integer, set shiftwidth to the integer
" value
if a:config["indent_size"] == "tab"
let &l:shiftwidth = &l:tabstop
let &l:softtabstop = &l:shiftwidth
else
let l:indent_size = str2nr(a:config["indent_size"])
if l:indent_size > 0
let &l:shiftwidth = l:indent_size
let &l:softtabstop = &l:shiftwidth
endif
endif
endif
if has_key(a:config, "end_of_line") && &l:modifiable
if a:config["end_of_line"] == "lf"
setl fileformat=unix
elseif a:config["end_of_line"] == "crlf"
setl fileformat=dos
elseif a:config["end_of_line"] == "cr"
setl fileformat=mac
endif
endif
if has_key(a:config, "charset") && &l:modifiable
if a:config["charset"] == "utf-8"
setl fileencoding=utf-8
setl nobomb
elseif a:config["charset"] == "utf-8-bom"
setl fileencoding=utf-8
setl bomb
elseif a:config["charset"] == "latin1"
setl fileencoding=latin1
setl nobomb
elseif a:config["charset"] == "utf-16be"
setl fileencoding=utf-16be
setl bomb
elseif a:config["charset"] == "utf-16le"
setl fileencoding=utf-16le
setl bomb
endif
endif
if has_key(a:config, "trim_trailing_whitespace")
augroup editorconfig_trim_trailing_whitespace
autocmd! editorconfig_trim_trailing_whitespace
if a:config["trim_trailing_whitespace"] == "true"
autocmd editorconfig_trim_trailing_whitespace BufWritePre <buffer> :%s/\s\+$//e
endif
augroup END " editorconfig_trim_trailing_whitespace group
endif
if has_key(a:config, 'max_line_length')
let l:max_line_length = str2nr(a:config['max_line_length'])
if l:max_line_length > 0
let &l:textwidth = l:max_line_length
" highlight the column
if exists('+colorcolumn')
let &l:colorcolumn = l:max_line_length
endif
endif
endif
call editorconfig#ApplyHooks(a:config)
endfunction
" }}}
let &cpo = s:saved_cpo
unlet! s:saved_cpo
" vim: fdm=marker fdc=3