From a651938e0c7be3daa830dc93e5bd2cf74540f19c Mon Sep 17 00:00:00 2001 From: bzvl Date: Mon, 3 Sep 2018 22:00:32 -0700 Subject: [PATCH] Allowing passing of ranges directly to formatting tools 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. --- README.md | 4 +- autoload/neoformat.vim | 42 ++++++++++++++++---- autoload/neoformat/formatters/javascript.vim | 5 ++- autoload/neoformat/formatters/python.vim | 2 + doc/neoformat.txt | 42 ++++++++++++++++++++ test/visual_after/python_yapf_2_4 | 5 +++ test/visual_before/python_yapf_2_4 | 4 ++ 7 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 test/visual_after/python_yapf_2_4 create mode 100644 test/visual_before/python_yapf_2_4 diff --git a/README.md b/README.md index ed1a6a66..431043bc 100644 --- a/README.md +++ b/README.md @@ -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 '], \ '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'] diff --git a/autoload/neoformat.vim b/autoload/neoformat.vim index 7891c83d..22ace645 100644 --- a/autoload/neoformat.vim +++ b/autoload/neoformat.vim @@ -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 @@ -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('$')) @@ -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 diff --git a/autoload/neoformat/formatters/javascript.vim b/autoload/neoformat/formatters/javascript.vim index 0ab4ed11..75f63f82 100644 --- a/autoload/neoformat/formatters/javascript.vim +++ b/autoload/neoformat/formatters/javascript.vim @@ -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', '', + \ '--range-end', ''], \ 'stdin': 1, + \ 'range_mode': 1, \ } endfunction diff --git a/autoload/neoformat/formatters/python.vim b/autoload/neoformat/formatters/python.vim index ec56cf6c..03ea851d 100644 --- a/autoload/neoformat/formatters/python.vim +++ b/autoload/neoformat/formatters/python.vim @@ -5,7 +5,9 @@ endfunction function! neoformat#formatters#python#yapf() abort return { \ 'exe': 'yapf', + \ 'args': ['--lines', '-'], \ 'stdin': 1, + \ 'range_mode': 1, \ } endfunction diff --git a/doc/neoformat.txt b/doc/neoformat.txt index c111d8a3..69f31c45 100644 --- a/doc/neoformat.txt +++ b/doc/neoformat.txt @@ -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| ============================================================================== @@ -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 @@ -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: + + - - starting line of range + - - ending line of range + - - number of lines in range + - - starting byte of range (from line2byte) + - - ending byte of range (from line2byte) + - - number of bytes in range + - any expressions supported by |expand()| + +Examples: + + let g:neoformat_cpp_clangformat = { + \ 'exe': 'clang-format', + \ 'args': ['-lines=:'], + \ 'range_mode': 1, + \ 'stdin', 1, + \ } + +or + + let g:neoformat_cpp_clangformat = { + \ 'exe': 'clang-format', + \ 'args': ['-offset=', '-length='], + \ 'range_mode': 1, + \ 'stdin', 1, + \ } + ============================================================================== SUPPORTED FILETYPES *neoformat-supported-filetypes* diff --git a/test/visual_after/python_yapf_2_4 b/test/visual_after/python_yapf_2_4 new file mode 100644 index 00000000..ad125286 --- /dev/null +++ b/test/visual_after/python_yapf_2_4 @@ -0,0 +1,5 @@ +def one(): pass + + +def two(): + pass diff --git a/test/visual_before/python_yapf_2_4 b/test/visual_before/python_yapf_2_4 new file mode 100644 index 00000000..74025966 --- /dev/null +++ b/test/visual_before/python_yapf_2_4 @@ -0,0 +1,4 @@ +def one(): pass + +def two(): pass +