I wrote a function to execute different external commands according to different file types and get the corresponding formatted results:
:function! FileFormat()
let cursorLine = line(".")
let filetype = &filetype
let tab_width = 4
let text = join(getbufline(".", 1, "$"), "\n")
let output
if filetype == "json"
output = system("jq .")
elseif filetype == 'cpp' || filetype == "c"
output = system("astyle --style=java --indent=spaces=" . tab_width . " --pad-oper -N -C --indent-labels -xw -xW -w --mode=c")
elseif filetype == "java"
output = system("astyle --style=java --indent=spaces=" . tab_width . " --mode=java")
elseif filetype == 'sh' || filetype == "zsh"
output = system("shfmt -i " . tab_width)
elseif filetype == 'javascript' || filetype == "js" || filetype == "typescript" || filetype == "ts"
output = system("prettier --parser typescript --print-width 160 --tab-width " . tab_width)
elseif filetype == 'css' || filetype == "scss" || filetype == "less" || filetype == "graphql" || filetype == "markdown" || filetype == "vue" || filetype == "html"
output = system("prettier --parser " . filetype . " --print-width 160 --tab-width " . tab_width)
elseif filetype == 'python'
output = system("autopep8 --max-line-length 10000 -")
elseif filetype == 'lua'
output = system("stylua - --indent-type Spaces --indent-width " . tab_width . " --call-parentheses None --quote-style AutoPreferDouble")
elseif filetype == 'tex' || filetype == "plaintex"
output = system("latexindent")
elseif filetype == "xml"
output = system("xmllint --encode \"UTF-8\" --format -")
else
echo "Formatting of " . filetype . " files is not currently supported."
return
endif
if v:shell_error
echo output
return
endif
deletebufline(".", 1, "$")
setbufline('.', 1, split(output, '\n'))
execute cursorLine
:endfunction
This function requires the following steps:
Get the text and file type of the current buffer.
According to the file type, call different external commands, transfer the text to the external tool, and receive the formatted result of the external tool
If an error occurs when the external tool formats the text, output an error message
Update the formatted result to the current buffer and place the cursor at the end of the original line.
vimscript doesn't have C's popen function, how do I write text out to the stdin stream of an external tool?
system() can only get the text output by the external tool to stdout, if the external tool outputs the error message to stderr, how can I get it?
To get the text of the current buffer, I need to pass getbufline(), and then join the text with "\n". Is there a way to get all the text directly? This "\n" is used as a string, not an ASCII newline, will this affect the formatting result?
ggVG
and hit the=
key, vim re-formats the selection according to whatever rules are set up. Whatever "formatprg" is set to (usually vim's own internal tool) does the job. Here's a bit on vimwiki about using par, which is a nice little tool for stuff other than code: vim.fandom.com/wiki/Par_text_reformatter:autocmd BufNewFile,BufRead *.json set formatprg=jq\ .
, my text is{"demo": Hello Wolrd}
, use When jq formats json, a syntax error will occur, and jq will feedback error information. In this case, it is a bad thing to directly update the error information returned by jq to the buffer. It is better to output the error information returned by jq through echo. How does this need to be achieved?let output
should fail, andoutput = …
is not a valid vimscript statement (trylet output = …
). In vim9script things are different, though