Skip to content

Commit

Permalink
Allowing passing of ranges directly to formatting tools
Browse files Browse the repository at this point in the history
Neoformat supports formatting only part of a file, but it just passes
that chunk to formatting tools. A lot of tools need the context of the
surrounding lines, but support passing the requested range as an
argument.

This commit adds support for ranges by allowing certain variables to be
substituted in the arguments. In that case the 'range_mode' option should be
to indicate that the tool itself will handle the range.

Most tools use range_mode 1, which means that the tool takes the
entire buffer and a range as input, and outputs the entire buffer with
only that range formatted.

Some tools use range_mode 2, which means that the tool takes same input
as range_mode 1, but outputs only the range that was formatted.
  • Loading branch information
bzvl committed Feb 2, 2019
1 parent e57ec78 commit a651938
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 10 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,20 @@ Options:
| `no_append` | do not append the `path` of the file to the formatter command, used when the `path` is in the middle of a command | 0 | optional |
| `env` | list of environment variable definitions to be prepended to the formatter command | \[] | optional |
| `valid_exit_codes` | list of valid exit codes for formatters who do not respect common unix practices | \[0] | optional |
| `range_mode` | determines behavior when a range is used with Neoformat | 0 | optional |

Example:

```viml
let g:neoformat_python_autopep8 = {
\ 'exe': 'autopep8',
\ 'args': ['-s 4', '-E'],
\ 'args': ['-s 4', '-E', '--range <start_line> <end_line>'],
\ 'replace': 1 " replace the file, instead of updating buffer (default: 0),
\ 'stdin': 1, " send data to stdin of formatter (default: 0)
\ 'env': ["DEBUG=1"], " prepend environment variables to formatter command
\ 'valid_exit_codes': [0, 23],
\ 'no_append': 1,
\ 'range_mode': 1,
\ }
let g:neoformat_enabled_python = ['autopep8']
Expand Down
42 changes: 34 additions & 8 deletions autoload/neoformat.vim
Original file line number Diff line number Diff line change
Expand Up @@ -75,20 +75,40 @@ function! s:neoformat(bang, user_input, start_line, end_line) abort
continue
endif

let stdin = getbufline(bufnr('%'), a:start_line, a:end_line)
if cmd.range_mode > 0
" Pass the entire buffer, the formatter itself takes a range.
let stdin = getbufline(bufnr('%'), 1, '$')
else
let stdin = getbufline(bufnr('%'), a:start_line, a:end_line)
end

let original_buffer = getbufline(bufnr('%'), 1, '$')

let exe = cmd.exe
let replacements = {
\ 'start_byte': line2byte(a:start_line),
\ 'end_byte': line2byte(a:end_line),
\ 'bytes': line2byte(a:end_line) - line2byte(a:start_line),
\ 'start_line': a:start_line,
\ 'end_line': a:end_line,
\ 'lines': a:end_line - a:start_line,
\ }

for [key, value] in items(replacements)
let exe = substitute(exe, '<'.key.'>', value, 'g')
endfor

call neoformat#utils#log(stdin)

call neoformat#utils#log(cmd.exe)
call neoformat#utils#log(exe)
if cmd.stdin
call neoformat#utils#log('using stdin')
let stdin_str = join(stdin, "\n")
let stdout = split(system(cmd.exe, stdin_str), '\n')
let stdout = split(system(exe, stdin_str), '\n')
else
call neoformat#utils#log('using tmp file')
call writefile(stdin, cmd.tmp_file_path)
let stdout = split(system(cmd.exe), '\n')
let stdout = split(system(exe), '\n')
endif

" read from /tmp file if formatter replaces file on format
Expand All @@ -108,11 +128,16 @@ function! s:neoformat(bang, user_input, start_line, end_line) abort
call neoformat#utils#log_file_content(cmd.stderr_log)
endif
if process_ran_succesfully
" 1. append the lines that are before and after the formatterd content
let lines_after = getbufline(bufnr('%'), a:end_line + 1, '$')
let lines_before = getbufline(bufnr('%'), 1, a:start_line - 1)
if cmd.range_mode == 1
" In range_mode 1, the formatter outputs the entire file contents.
let new_buffer = stdout
else
" 1. append the lines that are before and after the formatterd content
let lines_after = getbufline(bufnr('%'), a:end_line + 1, '$')
let lines_before = getbufline(bufnr('%'), 1, a:start_line - 1)
let new_buffer = lines_before + stdout + lines_after
endif

let new_buffer = lines_before + stdout + lines_after
if new_buffer !=# original_buffer

call s:deletelines(len(new_buffer), line('$'))
Expand Down Expand Up @@ -279,6 +304,7 @@ function! s:generate_cmd(definition, filetype) abort
\ 'name': a:definition.exe,
\ 'replace': get(a:definition, 'replace', 0),
\ 'tmp_file_path': path,
\ 'range_mode': get(a:definition, 'range_mode', 0),
\ 'valid_exit_codes': get(a:definition, 'valid_exit_codes', [0]),
\ }
endfunction
Expand Down
5 changes: 4 additions & 1 deletion autoload/neoformat/formatters/javascript.vim
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ endfunction
function! neoformat#formatters#javascript#prettier() abort
return {
\ 'exe': 'prettier',
\ 'args': ['--stdin', '--stdin-filepath', '"%:p"'],
\ 'args': ['--stdin', '--stdin-filepath', '%:p',
\ '--range-start', '<start_byte>',
\ '--range-end', '<end_byte>'],
\ 'stdin': 1,
\ 'range_mode': 1,
\ }
endfunction

Expand Down
2 changes: 2 additions & 0 deletions autoload/neoformat/formatters/python.vim
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ endfunction
function! neoformat#formatters#python#yapf() abort
return {
\ 'exe': 'yapf',
\ 'args': ['--lines', '<start_line>-<end_line>'],
\ 'stdin': 1,
\ 'range_mode': 1,
\ }
endfunction

Expand Down
42 changes: 42 additions & 0 deletions doc/neoformat.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Introduction |neoformat-introduction|
Install |neoformat-install|
Usage |neoformat-usage|
Managing Undo History |neoformat-managing-undo-history|
Argument Templates |neoformat-argument-templates|
Supported Filetypes |neoformat-supported-filetypes|

==============================================================================
Expand Down Expand Up @@ -92,6 +93,16 @@ Options:
| `stderr` | used to specify whether stderr output should be read along with
the stdin, otherwise redirects stderr to `stderr.log` file in neoformat's
temporary directory | default 0 | optional
| `range_mode` | Sets behavior when neoformat is run with a range (e.g. visual mode).
when using this mode, make sure the range arguments are sent via `args`
(See |neoformat-argument-templates|)
mode 0: only the selected lines are sent to the formatter
mode 1: The range is passed as an argument. the formatter
returns the entire buffer, with only those lines formatted.
mode 2: The range is passed as an argument. the entire file is
sent to the formatter with a range argument. The formatter
returns only the lines that it formatted.
default: 0 | optional
| `no_append` | do not append the `path` of the file to the formatter command,
used when the `path` is in the middle of a command | default: 0 |
optional
Expand Down Expand Up @@ -202,6 +213,37 @@ When |undojoin| is used this way pressing |u| will "skip over" the Neoformat
changes - it will revert both the changes made by Neoformat and the change
that caused Neoformat to be invoked.

==============================================================================
ARGUMENT TEMPLATES *neoformat-argument-templates*

The following strings will be expanded in the 'args' field:

- <start_line> - starting line of range
- <end_line> - ending line of range
- <lines> - number of lines in range
- <start_byte> - starting byte of range (from line2byte)
- <end_byte> - ending byte of range (from line2byte)
- <bytes> - number of bytes in range
- any expressions supported by |expand()|

Examples:

let g:neoformat_cpp_clangformat = {
\ 'exe': 'clang-format',
\ 'args': ['-lines=<start_line>:<end_line>'],
\ 'range_mode': 1,
\ 'stdin', 1,
\ }

or

let g:neoformat_cpp_clangformat = {
\ 'exe': 'clang-format',
\ 'args': ['-offset=<start_byte>', '-length=<bytes>'],
\ 'range_mode': 1,
\ 'stdin', 1,
\ }

==============================================================================
SUPPORTED FILETYPES *neoformat-supported-filetypes*

Expand Down
5 changes: 5 additions & 0 deletions test/visual_after/python_yapf_2_4
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def one(): pass


def two():
pass
4 changes: 4 additions & 0 deletions test/visual_before/python_yapf_2_4
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def one(): pass

def two(): pass

0 comments on commit a651938

Please sign in to comment.