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-12-15 07:15:05 -05:00
let verbose = &verbose | | g :autoformat_verbosemode = = 1
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" )
2016-02-10 22:49:16 -05:00
if ftype = ~ ? "[.]"
2015-06-06 05:54:30 -04:00
" 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
2016-09-01 19:54:49 -04:00
let formatters_var = "b:formatters_" .supertype
if ! exists ( formatters_var )
let formatters_var = "g:formatters_" .supertype
endif
2015-05-31 15:56:00 -04:00
if ! exists ( formatters_var )
" No formatters defined
if verbose
2016-02-10 22:49:16 -05:00
echoerr "No formatters defined for supertype " .supertype
2015-05-31 15:56:00 -04:00
endif
else
let formatters = eval ( formatters_var )
2016-02-10 22:49:16 -05:00
if type ( formatters ) ! = type ( [])
2016-09-01 19:54:49 -04:00
echoerr formatters_var ." is not a list"
2015-05-31 15:56:00 -04:00
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-12-04 03:59:23 -05:00
" Detect verbosity
2016-02-18 04:13:07 -05:00
let verbose = &verbose | | g :autoformat_verbosemode = = 1
2015-12-04 03:59:23 -05:00
2015-05-18 13:27:00 -04:00
" Make sure formatters are defined and detected
if ! call ( '<SID>find_formatters' , a :000 )
2015-12-15 07:15:05 -05:00
" No formatters defined
2016-03-11 12:09:05 -05:00
if verbose
echomsg "No format definitions are defined for this filetype."
2015-12-14 03:41:01 -05:00
endif
2016-03-11 12:09:05 -05:00
call s :Fallback ( )
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
" Formatter definition must be existent
2016-09-01 19:54:49 -04:00
let formatdef_var = 'b:formatdef_' .b :formatters [s :index ]
if ! exists ( formatdef_var )
let formatdef_var = 'g:formatdef_' .b :formatters [s :index ]
endif
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
2016-03-10 11:18:15 -05:00
let b :formatprg = eval ( eval ( formatdef_var ) )
2015-06-19 17:17:31 -04:00
2017-06-26 03:29:22 -04:00
if verbose
echomsg "Trying definition from " .formatdef_var
echomsg "Evaluated formatprg: " .b :formatprg
endif
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
2016-03-21 10:46:29 -04:00
if has ( "python3" )
2017-03-06 12:33:02 -05:00
if verbose
echomsg "Using python 3 code..."
endif
2015-06-19 17:17:31 -04:00
let success = s :TryFormatterPython3 ( )
2016-03-21 10:46:29 -04:00
else
2017-03-06 12:33:02 -05:00
if verbose
echomsg "Using python 2 code..."
endif
2016-03-21 10:46:29 -04:00
let success = s :TryFormatterPython ( )
2015-06-19 17:17:31 -04:00
endif
2017-03-06 12:33:02 -05:00
if success = = 0
2015-12-04 03:59:23 -05:00
if verbose
2016-02-18 04:13:07 -05:00
echomsg "Definition in '" .formatdef_var ."' was successful."
2015-12-04 03:59:23 -05:00
endif
2015-05-23 04:02:05 -04:00
return 1
else
2015-12-04 03:59:23 -05:00
if verbose
2016-02-18 04:13:07 -05:00
echomsg "Definition in '" .formatdef_var ."' was unsuccessful."
2015-12-04 03:59:23 -05:00
endif
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-12-04 03:59:23 -05:00
if verbose
2016-02-18 04:13:07 -05:00
echomsg "No format definitions were successful."
2015-12-23 11:01:37 -05:00
endif
2015-12-15 07:15:05 -05:00
" Tried all formatters, none worked
2016-03-11 12:09:05 -05:00
call s :Fallback ( )
2016-03-17 06:23:30 -04:00
return 0
2015-05-23 04:02:05 -04:00
endif
endwhile
2016-03-11 12:09:05 -05:00
endfunction
2015-05-23 04:02:05 -04:00
2016-03-11 12:09:05 -05:00
function ! s :Fallback ( )
" Detect verbosity
let verbose = &verbose | | g :autoformat_verbosemode = = 1
2016-03-30 04:01:56 -04:00
if exists ( 'b:autoformat_remove_trailing_spaces' ) ? b :autoformat_remove_trailing_spaces = = 1 : g :autoformat_remove_trailing_spaces = = 1
2016-03-11 12:09:05 -05:00
if verbose
echomsg "Removing trailing whitespace..."
endif
call s :RemoveTrailingSpaces ( )
endif
2016-03-30 04:01:56 -04:00
if exists ( 'b:autoformat_retab' ) ? b :autoformat_retab = = 1 : g :autoformat_retab = = 1
2016-03-11 12:09:05 -05:00
if verbose
echomsg "Retabbing..."
endif
retab
endif
2016-03-30 04:01:56 -04:00
if exists ( 'b:autoformat_autoindent' ) ? b :autoformat_autoindent = = 1 : g :autoformat_autoindent = = 1
2016-03-11 12:09:05 -05:00
if verbose
echomsg "Autoindenting..."
endif
" Autoindent code
2018-05-13 14:31:43 -04:00
exe "normal! gg=G"
2016-03-11 12:09:05 -05:00
endif
2016-03-30 04:01:56 -04:00
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
2017-03-06 12:33:02 -05:00
" If stderr is empty, apply result, return 0
" Otherwise, return 1
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
2015-12-15 07:15:05 -05:00
let verbose = &verbose | | g :autoformat_verbosemode = = 1
2013-03-14 13:50:31 -04:00
2015-05-18 13:27:00 -04:00
python << EOF
2018-10-22 03:07:54 -04:00
import vim , subprocess , os
2015-05-18 13:27:00 -04:00
from subprocess import Popen , PIPE
2015-11-12 05:30:59 -05:00
text = os .linesep .join ( vim .current .buffer [:]) + os .linesep
2016-03-10 11:18:15 -05:00
formatprg = vim .eval ( 'b:formatprg' )
2015-05-30 10:18:30 -04:00
verbose = bool ( int ( vim .eval ( 'verbose' ) ) )
2016-03-21 10:46:29 -04:00
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' )
2018-10-22 03:07:54 -04:00
env ['PATH' ] = os .pathsep .join ( extra_path ) + os .pathsep + env ['PATH' ]
2015-05-31 15:56:00 -04:00
2016-03-21 10:46:29 -04:00
# When an entry is unicode , Popen can 't deal with it in Python 2 .
# As a pragmatic fix , we 'll omit that entry .
2016-09-07 04:49:52 -04:00
newenv = {}
for key , val in env .iteritems ( ) :
if type ( key ) = = type ( val ) = = str :
newenv [key ] = val
env = newenv
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 )
2016-03-21 10:46:29 -04:00
2018-10-22 03:23:41 -04:00
formattername = vim .eval ( 'b:formatters[s:index]' )
2015-05-18 13:27:00 -04:00
if stderrdata :
2015-05-23 04:02:05 -04:00
if verbose :
2017-06-26 03:29:22 -04:00
print ( 'Formatter {} has errors: {}' .format ( formattername , stderrdata ) )
2017-03-06 12:33:02 -05:00
vim .command ( 'return 1' )
2018-10-22 03:23:41 -04:00
elif p .returncode > 0 :
if verbose :
print ( 'Formatter {} gives nonzero returncode: {}' .format ( formattername , p .returncode ) )
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 :
2016-02-15 03:50:41 -05:00
if len ( stdoutdata ) > 0 and stdoutdata [-1 ] = = eol :
2015-10-29 12:25:39 -04:00
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 ) ]
2017-10-20 08:45:24 -04:00
if vim .current .buffer [:] ! = lines :
vim .current .buffer [:] = lines
2015-05-18 13:27:00 -04:00
EOF
2017-03-06 12:33:02 -05:00
return 0
2013-03-14 13:50:31 -04:00
endfunction
2015-06-19 17:17:31 -04:00
" +python3 version
function ! s :TryFormatterPython3 ( )
" Detect verbosity
2015-12-15 07:15:05 -05:00
let verbose = &verbose | | g :autoformat_verbosemode = = 1
2015-06-19 17:17:31 -04:00
python3 < < EOF
2018-10-22 03:07:54 -04:00
import vim , subprocess , os
2015-06-19 17:17:31 -04:00
from subprocess import Popen , PIPE
2017-03-06 12:33:02 -05:00
# The return code is `failure `, unless otherwise specified
vim .command ( 'return 1' )
2015-11-12 05:30:59 -05:00
text = bytes ( os .linesep .join ( vim .current .buffer [:]) + os .linesep , 'utf-8' )
2016-03-10 11:18:15 -05:00
formatprg = vim .eval ( 'b:formatprg' )
2015-06-19 17:17:31 -04:00
verbose = bool ( int ( vim .eval ( 'verbose' ) ) )
env = os .environ .copy ( )
if int ( vim .eval ( 'exists("g:formatterpath")' ) ) :
extra_path = vim .eval ( 'g:formatterpath' )
2018-10-22 03:07:54 -04:00
env ['PATH' ] = os .pathsep .join ( extra_path ) + os .pathsep + env ['PATH' ]
2015-06-19 17:17:31 -04:00
2017-03-06 12:33:02 -05:00
try :
p = subprocess .Popen ( formatprg , env = env , shell = True , stdin = PIPE , stdout = PIPE , stderr = PIPE )
stdoutdata , stderrdata = p .communicate ( text )
except ( BrokenPipeError , IOError ) :
2015-06-19 17:17:31 -04:00
if verbose :
2017-03-06 12:33:02 -05:00
raise
2015-06-19 17:17:31 -04:00
else :
2018-10-22 03:23:41 -04:00
formattername = vim .eval ( 'b:formatters[s:index]' )
2017-03-06 12:33:02 -05:00
if stderrdata :
if verbose :
2017-06-26 03:29:22 -04:00
print ( 'Formatter {} has errors: {}' .format ( formattername , stderrdata ) )
2018-10-22 03:23:41 -04:00
elif p .returncode > 0 :
if verbose :
print ( 'Formatter {} gives nonzero returncode: {}' .format ( formattername , p .returncode ) )
2017-03-06 12:33:02 -05:00
elif not stdoutdata :
if verbose :
2017-06-26 03:29:22 -04:00
print ( 'Formatter {} gives empty result: {}' .format ( formattername , stderrdata ) )
2017-03-06 12:33:02 -05:00
else :
# 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' ]
stdoutdata = stdoutdata .decode ( 'utf-8' )
# 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 .
for eol in possible_eols :
if len ( stdoutdata ) > 0 and stdoutdata [-1 ] = = eol :
stdoutdata = stdoutdata [:-1 ]
lines = [stdoutdata ]
for eol in possible_eols :
lines = [splitline for line in lines for splitline in line .split ( eol ) ]
2017-10-20 08:45:24 -04:00
if vim .current .buffer [:] ! = lines :
vim .current .buffer [:] = lines
2017-03-06 12:33:02 -05:00
vim .command ( 'return 0' )
2015-06-19 17:17:31 -04:00
EOF
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
2018-10-22 03:23:41 -04:00
" Write and read viminfo to restore marks
2018-10-10 12:57:12 -04:00
command ! - nargs = ? - range = % - complete = filetype - bar Autoformat let winview = winsaveview ( ) | wviminfo | < line1 > , < line2 > call s :TryAllFormatters ( < f - args > ) | call winrestview ( winview ) | rviminfo
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 )
2016-02-18 04:13:07 -05:00
echomsg '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
2016-02-18 04:13:07 -05:00
echomsg 'Selected formatter: ' .b :formatters [b :current_formatter_index ]
2015-05-18 13:27:00 -04:00
endfunction
2015-12-04 03:59:23 -05:00
function ! s :CurrentFormatter ( )
call s :find_formatters ( )
if ! exists ( 'b:current_formatter_index' )
let b :current_formatter_index = 0
endif
2016-02-18 04:13:07 -05:00
echomsg 'Selected formatter: ' .b :formatters [b :current_formatter_index ]
2015-12-04 03:59:23 -05:00
endfunction
2015-05-18 13:27:00 -04:00
" Create commands for iterating through formatter list
command ! NextFormatter call s :NextFormatter ( )
command ! PreviousFormatter call s :PreviousFormatter ( )
2015-12-04 03:59:23 -05:00
command ! CurrentFormatter call s :CurrentFormatter ( )
2016-03-11 12:09:05 -05:00
" Other commands
function ! s :RemoveTrailingSpaces ( )
let user_gdefault = &gdefault
try
set nogdefault
silent ! %s /\s \+ $
finally
let &gdefault = user_gdefault
endtry
endfunction
command ! RemoveTrailingSpaces call s :RemoveTrailingSpaces ( )