2015-05-18 13:27:00 -04:00
" Function for finding the formatters for this filetype
2015-05-23 04:02:05 -04:00
" Result is stored in b:formatters
2015-05-18 13:27:00 -04:00
function ! s :find_formatters ( ...)
" Detect verbosity
2015-05-23 04:02:05 -04:00
let verbose = &verbose | | exists ( "g:autoformat_verbosemode" )
2015-05-18 13:27:00 -04:00
" Extract filetype to be used
2015-05-31 15:56:00 -04:00
let ftype = a :0 ? a :1 : &filetype
2015-04-09 04:59:54 -04:00
" Support composite filetypes by replacing dots with underscores
2015-05-31 15:56:00 -04:00
let compoundtype = substitute ( ftype , "[.]" , "_" , "g" )
2015-06-06 05:54:30 -04:00
if ftype = ~ "[.]"
" Try all super filetypes in search for formatters in a sane order
let ftypes = [compoundtype ] + split ( ftype , "[.]" )
else
let ftypes = [compoundtype ]
endif
2014-06-29 15:30:05 -04:00
2015-05-30 15:00:16 -04:00
" Warn for backward incompatible configuration
2015-05-31 15:56:00 -04:00
let old_formatprg_var = "g:formatprg_" .compoundtype
let old_formatprg_args_var = "g:formatprg_args_" .compoundtype
let old_formatprg_args_expr_var = "g:formatprg_args_expr_" .compoundtype
2015-05-30 15:00:16 -04:00
if exists ( old_formatprg_var ) | | exists ( old_formatprg_args_var ) | | exists ( old_formatprg_args_expr_var )
2015-06-06 05:54:30 -04:00
echohl WarningMsg |
\ echomsg "WARNING: the options g:formatprg_<filetype>, g:formatprg_args_<filetype> and g:formatprg_args_expr_<filetype> are no longer supported as of June 2015, due to major backward-incompatible improvements. Please check the README for help on how to configure your formatters." |
\ echohl None
2015-05-30 15:00:16 -04:00
endif
2015-06-06 05:54:30 -04:00
" Detect configuration for all possible ftypes
2015-05-18 13:27:00 -04:00
let b :formatters = []
2015-06-06 05:54:30 -04:00
for supertype in ftypes
2015-05-31 15:56:00 -04:00
let formatters_var = "g:formatters_" .supertype
if ! exists ( formatters_var )
" No formatters defined
if verbose
echoerr "No formatters defined for supertype '" .supertype
endif
else
let formatters = eval ( formatters_var )
if type ( formatters ) ! = 3
echoerr formatter_var ." is not a list"
else
let b :formatters = b :formatters + formatters
endif
2013-03-16 05:36:16 -04:00
endif
2015-05-31 15:56:00 -04:00
endfor
2013-03-14 13:50:31 -04:00
2015-05-31 15:56:00 -04:00
if len ( b :formatters ) = = 0
2015-05-18 13:27:00 -04:00
" No formatters defined
2015-05-23 04:02:05 -04:00
if verbose
2015-05-31 15:56:00 -04:00
echoerr "No formatters defined for filetype '" .ftype ."'."
2013-03-14 13:50:31 -04:00
endif
2013-12-20 14:34:38 -05:00
return 0
2013-03-14 13:50:31 -04:00
endif
2015-05-18 13:27:00 -04:00
return 1
endfunction
2015-05-23 04:02:05 -04:00
" Try all formatters, starting with the currently selected one, until one
" works. If none works, autoindent the buffer.
2015-05-30 05:02:07 -04:00
function ! s :TryAllFormatters ( ...) range
2015-05-18 13:27:00 -04:00
" Make sure formatters are defined and detected
if ! call ( '<SID>find_formatters' , a :000 )
2015-08-08 08:37:21 -04:00
" No formatters defined, so autoindent code
2015-08-08 08:01:26 -04:00
exe "normal gg=G"
2015-05-18 13:27:00 -04:00
return 0
endif
" Make sure index exist and is valid
if ! exists ( 'b:current_formatter_index' )
let b :current_formatter_index = 0
endif
if b :current_formatter_index > = len ( b :formatters )
let b :current_formatter_index = 0
endif
2015-05-23 04:02:05 -04:00
" Try all formatters, starting with selected one
2015-05-23 08:25:29 -04:00
let s :index = b :current_formatter_index
2015-05-23 04:02:05 -04:00
while 1
2015-05-23 08:25:29 -04:00
let formatdef_var = 'g:formatdef_' .b :formatters [s :index ]
2015-05-23 04:02:05 -04:00
" Formatter definition must be existent
2015-05-23 08:25:29 -04:00
if ! exists ( formatdef_var )
echoerr "No format definition found in '" .formatdef_var ."'."
2015-05-23 04:02:05 -04:00
return 0
endif
" Eval twice, once for getting definition content,
" once for getting the final expression
2015-05-23 08:25:29 -04:00
let &formatprg = eval ( eval ( formatdef_var ) )
2015-06-19 17:17:31 -04:00
2015-06-22 03:18:49 -04:00
" Detect if +python or +python3 is available, and call the corresponding function
2015-06-19 17:17:31 -04:00
if ! has ( "python" ) && ! has ( "python3" )
echohl WarningMsg |
\ echomsg "WARNING: vim has no support for python, but it is required to run the formatter!" |
\ echohl None
return 1
endif
if has ( "python" )
let success = s :TryFormatterPython ( )
else
let success = s :TryFormatterPython3 ( )
endif
if success
2015-05-23 04:02:05 -04:00
return 1
else
2015-05-23 08:25:29 -04:00
let s :index = ( s :index + 1 ) % len ( b :formatters )
2015-05-23 04:02:05 -04:00
endif
2015-05-23 08:25:29 -04:00
if s :index = = b :current_formatter_index
2015-08-08 08:37:21 -04:00
" Tried all formatters, none worked so autoindent code
2015-08-08 08:01:26 -04:00
exe "normal gg=G"
2015-05-23 04:02:05 -04:00
return 0
endif
endwhile
2013-03-15 18:12:09 -04:00
endfunction
2013-03-01 09:06:13 -05:00
2015-05-18 13:27:00 -04:00
2015-05-23 04:02:05 -04:00
" Call formatter
" If stderr is empty, apply result, return 1
" Otherwise, return 0
2015-06-22 03:18:49 -04:00
2015-06-19 17:17:31 -04:00
" +python version
function ! s :TryFormatterPython ( )
2015-05-23 04:02:05 -04:00
" Detect verbosity
let verbose = &verbose | | exists ( "g:autoformat_verbosemode" )
2013-03-14 13:50:31 -04:00
2015-05-18 13:27:00 -04:00
python << EOF
2015-05-31 14:58:14 -04:00
import vim , subprocess , os
2015-05-18 13:27:00 -04:00
from subprocess import Popen , PIPE
2015-05-31 15:56:00 -04:00
2015-11-12 05:30:59 -05:00
text = os .linesep .join ( vim .current .buffer [:]) + os .linesep
2015-05-18 13:27:00 -04:00
formatprg = vim .eval ( '&formatprg' )
2015-05-30 10:18:30 -04:00
verbose = bool ( int ( vim .eval ( 'verbose' ) ) )
2015-05-31 14:58:14 -04:00
env = os .environ .copy ( )
if int ( vim .eval ( 'exists("g:formatterpath")' ) ) :
extra_path = vim .eval ( 'g:formatterpath' )
env ['PATH' ] = ':' .join ( extra_path ) + ':' + env ['PATH' ]
2015-05-31 15:56:00 -04:00
2015-05-31 14:58:14 -04:00
p = subprocess .Popen ( formatprg , env = env , shell = True , stdin = PIPE , stdout = PIPE , stderr = PIPE )
2015-05-18 13:27:00 -04:00
stdoutdata , stderrdata = p .communicate ( text )
if stderrdata :
2015-05-23 04:02:05 -04:00
if verbose :
2015-05-23 08:25:29 -04:00
formattername = vim .eval ( 'b:formatters[s:index]' )
2015-05-30 05:02:07 -04:00
print ( 'Formatter {} has errors: {}. Skipping.' .format ( formattername , stderrdata ) )
print ( 'Failing config: {} ' .format ( repr ( formatprg ) , stderrdata ) )
2015-05-23 04:02:05 -04:00
vim .command ( 'return 0' )
2015-05-18 13:27:00 -04:00
else :
2015-10-29 12:25:39 -04:00
# It is not certain what kind of line endings are being used by the format program .
# Therefore we simply split on all possible eol characters .
possible_eols = ['\r\n' , os .linesep , '\r' , '\n' ]
2015-07-31 23:57:54 -04:00
# Often shell commands will append a newline at the end of their output .
# It is not entirely clear when and why that happens .
# However , extra newlines are almost never required , while there are linters that complain
# about superfluous newlines , so we remove one empty newline at the end of the file .
2015-10-29 12:25:39 -04:00
for eol in possible_eols :
if stdoutdata [-1 ] = = eol :
stdoutdata = stdoutdata [:-1 ]
2015-10-28 08:57:22 -04:00
lines = [stdoutdata ]
2015-10-29 12:25:39 -04:00
for eol in possible_eols :
2015-10-28 08:57:22 -04:00
lines = [splitline for line in lines for splitline in line .split ( eol ) ]
vim .current .buffer [:] = lines
2015-05-18 13:27:00 -04:00
EOF
2015-05-23 04:02:05 -04:00
return 1
2013-03-14 13:50:31 -04:00
endfunction
2015-06-19 17:17:31 -04:00
" +python3 version
function ! s :TryFormatterPython3 ( )
" Detect verbosity
let verbose = &verbose | | exists ( "g:autoformat_verbosemode" )
python3 < < EOF
import vim , subprocess , os
from subprocess import Popen , PIPE
2015-11-12 05:30:59 -05:00
text = bytes ( os .linesep .join ( vim .current .buffer [:]) + os .linesep , 'utf-8' )
2015-06-19 17:17:31 -04:00
formatprg = vim .eval ( '&formatprg' )
verbose = bool ( int ( vim .eval ( 'verbose' ) ) )
env = os .environ .copy ( )
if int ( vim .eval ( 'exists("g:formatterpath")' ) ) :
extra_path = vim .eval ( 'g:formatterpath' )
env ['PATH' ] = ':' .join ( extra_path ) + ':' + env ['PATH' ]
p = subprocess .Popen ( formatprg , env = env , shell = True , stdin = PIPE , stdout = PIPE , stderr = PIPE )
stdoutdata , stderrdata = p .communicate ( text )
if stderrdata :
if verbose :
formattername = vim .eval ( 'b:formatters[s:index]' )
print ( 'Formatter {} has errors: {}. Skipping.' .format ( formattername , stderrdata ) )
print ( 'Failing config: {} ' .format ( repr ( formatprg ) , stderrdata ) )
vim .command ( 'return 0' )
else :
2015-10-29 12:25:39 -04:00
# It is not certain what kind of line endings are being used by the format program .
# Therefore we simply split on all possible eol characters .
possible_eols = ['\r\n' , os .linesep , '\r' , '\n' ]
2015-10-29 07:22:48 -04:00
stdoutdata = stdoutdata .decode ( 'utf-8' )
2015-07-31 23:57:54 -04:00
# Often shell commands will append a newline at the end of their output .
# It is not entirely clear when and why that happens .
# However , extra newlines are almost never required , while there are linters that complain
# about superfluous newlines , so we remove one empty newline at the end of the file .
2015-10-29 12:25:39 -04:00
for eol in possible_eols :
if stdoutdata [-1 ] = = eol :
stdoutdata = stdoutdata [:-1 ]
2015-10-28 08:57:22 -04:00
lines = [stdoutdata ]
2015-10-29 12:25:39 -04:00
for eol in possible_eols :
2015-10-28 08:57:22 -04:00
lines = [splitline for line in lines for splitline in line .split ( eol ) ]
vim .current .buffer [:] = lines
2015-06-19 17:17:31 -04:00
EOF
return 1
endfunction
2015-05-23 04:02:05 -04:00
2015-04-09 04:59:54 -04:00
" Create a command for formatting the entire buffer
2015-06-12 08:29:33 -04:00
" Save and recall window state to prevent vim from jumping to line 1
command ! - nargs = ? - range = % - complete = filetype Autoformat let winview = winsaveview ( ) | < line1 > , < line2 > call s :TryAllFormatters ( < f - args > ) | call winrestview ( winview )
2015-05-18 13:27:00 -04:00
" Functions for iterating through list of available formatters
function ! s :NextFormatter ( )
call s :find_formatters ( )
if ! exists ( 'b:current_formatter_index' )
let b :current_formatter_index = 0
endif
let b :current_formatter_index = ( b :current_formatter_index + 1 ) % len ( b :formatters )
2015-05-23 08:25:29 -04:00
echom 'Selected formatter: ' .b :formatters [b :current_formatter_index ]
2015-05-18 13:27:00 -04:00
endfunction
function ! s :PreviousFormatter ( )
call s :find_formatters ( )
if ! exists ( 'b:current_formatter_index' )
let b :current_formatter_index = 0
endif
2015-05-23 04:02:05 -04:00
let l = len ( b :formatters )
let b :current_formatter_index = ( b :current_formatter_index - 1 + l ) % l
2015-05-23 08:25:29 -04:00
echom 'Selected formatter: ' .b :formatters [b :current_formatter_index ]
2015-05-18 13:27:00 -04:00
endfunction
" Create commands for iterating through formatter list
command ! NextFormatter call s :NextFormatter ( )
command ! PreviousFormatter call s :PreviousFormatter ( )