Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for ruff formatter #4645

Merged
merged 4 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions autoload/ale/fix/registry.vim
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,11 @@ let s:default_registry = {
\ 'suggested_filetypes': ['python'],
\ 'description': 'Fix python files with ruff.',
\ },
\ 'ruff_format': {
\ 'function': 'ale#fixers#ruff_format#Fix',
\ 'suggested_filetypes': ['python'],
\ 'description': 'Fix python files with the ruff formatter.',
\ },
\ 'pycln': {
\ 'function': 'ale#fixers#pycln#Fix',
\ 'suggested_filetypes': ['python'],
Expand Down
72 changes: 72 additions & 0 deletions autoload/ale/fixers/ruff_format.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
" Author: Yining <[email protected]>, Joseph Henrich <[email protected]>
" Description: ruff formatter as ALE fixer for python files

call ale#Set('python_ruff_format_executable', 'ruff')
call ale#Set('python_ruff_format_options', '')
call ale#Set('python_ruff_format_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('python_ruff_format_change_directory', 1)
call ale#Set('python_ruff_format_auto_pipenv', 0)
call ale#Set('python_ruff_format_auto_poetry', 0)

function! ale#fixers#ruff_format#GetCwd(buffer) abort
if ale#Var(a:buffer, 'python_ruff_format_change_directory')
" Run from project root if found, else from buffer dir.
let l:project_root = ale#python#FindProjectRoot(a:buffer)

return !empty(l:project_root) ? l:project_root : '%s:h'
endif

return '%s:h'
endfunction

function! ale#fixers#ruff_format#GetExecutable(buffer) abort
if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_ruff_format_auto_pipenv'))
\ && ale#python#PipenvPresent(a:buffer)
return 'pipenv'
endif

if (ale#Var(a:buffer, 'python_auto_poetry') || ale#Var(a:buffer, 'python_ruff_format_auto_poetry'))
\ && ale#python#PoetryPresent(a:buffer)
return 'poetry'
endif

return ale#python#FindExecutable(a:buffer, 'python_ruff_format', ['ruff'])
endfunction

function! ale#fixers#ruff_format#GetCommand(buffer) abort
let l:executable = ale#fixers#ruff_format#GetExecutable(a:buffer)
let l:exec_args = l:executable =~? 'pipenv\|poetry$'
\ ? ' run ruff'
\ : ''

return ale#Escape(l:executable) . l:exec_args
endfunction

function! ale#fixers#ruff_format#Fix(buffer) abort
let l:executable = ale#fixers#ruff_format#GetExecutable(a:buffer)
let l:cmd = [ale#Escape(l:executable)]

if l:executable =~? 'pipenv\|poetry$'
call extend(l:cmd, ['run', 'ruff'])
endif

let l:options = ale#Var(a:buffer, 'python_ruff_format_options')

" when --stdin-filename present, ruff will use it for proj root resolution
" https://github.com/charliermarsh/ruff/pull/1281
let l:fname = expand('#' . a:buffer . '...')
call add(l:cmd, 'format')

if !empty(l:options)
call add(l:cmd, l:options)
endif

call add(l:cmd, '--stdin-filename '.ale#Escape(ale#path#Simplify(l:fname)))

call add(l:cmd, '-')

return {
\ 'cwd': ale#fixers#ruff_format#GetCwd(a:buffer),
\ 'command': join(l:cmd, ' '),
\}
endfunction
64 changes: 64 additions & 0 deletions doc/ale-python.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,70 @@ g:ale_python_ruff_auto_poetry *g:ale_python_ruff_auto_poetry*
if true. This is overridden by a manually-set executable.


===============================================================================
ruff-format *ale-python-ruff-format*

g:ale_python_ruff_format_change_directory
*g:ale_python_ruff_format_change_directory*
*b:ale_python_ruff_format_change_directory*
Type: |Number|
Default: `1`

If set to `1`, `ruff` will be run from a detected project root, per
|ale-python-root|. if set to `0` or no project root detected,
`ruff` will be run from the buffer's directory.


g:ale_python_ruff_format_executable *g:ale_python_ruff_format_executable*
*b:ale_python_ruff_format_executable*
Type: |String|
Default: `'ruff'`

See |ale-integrations-local-executables|

Set this to `'pipenv'` to invoke `'pipenv` `run` `ruff'`.
Set this to `'poetry'` to invoke `'poetry` `run` `ruff'`.


g:ale_python_ruff_format_options *g:ale_python_ruff_format_options*
*b:ale_python_ruff_format_options*
Type: |String|
Default: `''`

This variable can be changed to add command-line arguments to the ruff
invocation.

For example, to select/enable and/or disable some error codes,
you may want to set >
let g:ale_python_ruff_format_options = '--ignore F401'


g:ale_python_ruff_format_use_global *g:ale_python_ruff_format_use_global*
*b:ale_python_ruff_format_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`

See |ale-integrations-local-executables|


g:ale_python_ruff_format_auto_pipenv *g:ale_python_ruff_format_auto_pipenv*
*b:ale_python_ruff_format_auto_pipenv*
Type: |Number|
Default: `0`

Detect whether the file is inside a pipenv, and set the executable to `pipenv`
if true. This is overridden by a manually-set executable.


g:ale_python_ruff_format_auto_poetry *g:ale_python_ruff_format_auto_poetry*
*b:ale_python_ruff_format_auto_poetry*
Type: |Number|
Default: `0`

Detect whether the file is inside a poetry, and set the executable to `poetry`
if true. This is overridden by a manually-set executable.


===============================================================================
unimport *ale-python-unimport*

Expand Down
1 change: 1 addition & 0 deletions doc/ale-supported-languages-and-tools.txt
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ Notes:
* `refurb`
* `reorder-python-imports`
* ruff
* ruff-format
* `unimport`
* `vulture`!!
* `yapf`
Expand Down
1 change: 1 addition & 0 deletions doc/ale.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3303,6 +3303,7 @@ documented in additional help files.
refurb................................|ale-python-refurb|
reorder-python-imports................|ale-python-reorder_python_imports|
ruff..................................|ale-python-ruff|
ruff-format...........................|ale-python-ruff-format|
unimport..............................|ale-python-unimport|
vulture...............................|ale-python-vulture|
yapf..................................|ale-python-yapf|
Expand Down
1 change: 1 addition & 0 deletions supported-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ formatting.
* [refurb](https://github.com/dosisod/refurb) :floppy_disk:
* [reorder-python-imports](https://github.com/asottile/reorder_python_imports)
* [ruff](https://github.com/charliermarsh/ruff)
* [ruff-format](https://docs.astral.sh/ruff/formatter/)
* [unimport](https://github.com/hakancelik96/unimport)
* [vulture](https://github.com/jendrikseipp/vulture) :warning: :floppy_disk:
* [yapf](https://github.com/google/yapf)
Expand Down
86 changes: 86 additions & 0 deletions test/fixers/test_ruff_format_fixer_callback.vader
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
Before:
call ale#assert#SetUpFixerTest('python', 'ruff_format')

let b:bin_dir = has('win32') ? 'Scripts' : 'bin'

After:
call ale#assert#TearDownFixerTest()

unlet! g:dir
unlet! b:bin_dir

Execute(The ruff callback should not change directory if the option is set to 0):
let g:ale_python_ruff_format_change_directory = 0

let file_path = g:dir . '/../test-files/python/with_virtualenv/subdir/foo/bar.py'

silent execute 'file ' . fnameescape(file_path)

let fname = ale#Escape(ale#path#Simplify(file_path))

AssertFixer
\ {
\ 'cwd': '%s:h',
\ 'command': ale#Escape(ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir . '/ruff')) . ' format --stdin-filename ' . fname . ' -',
\ }

Execute(The ruff callback should respect custom options):
let g:ale_python_ruff_format_options = '--ignore F401 -q'

let file_path = g:dir . '/../test-files/python/with_virtualenv/subdir/foo/bar.py'

silent execute 'file ' . fnameescape(file_path)

let fname = ale#Escape(ale#path#Simplify(file_path))

AssertFixer
\ {
\ 'cwd': ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/subdir'),
\ 'command': ale#Escape(ale#path#Simplify(g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir . '/ruff'))
\ . ' format --ignore F401 -q --stdin-filename '. fname . ' -',
\ }

Execute(Pipenv is detected when python_ruff_format_auto_pipenv is set):
let g:ale_python_ruff_format_auto_pipenv = 1
let g:ale_python_ruff_format_change_directory = 0

let file_path = '../test-files/python/pipenv/whatever.py'

call ale#test#SetFilename(file_path)

let fname = ale#Escape(ale#path#Simplify(g:dir . '/'. file_path))

AssertFixer
\ {
\ 'cwd': '%s:h',
\ 'command': ale#Escape('pipenv') . ' run ruff format --stdin-filename ' . fname . ' -'
\ }

Execute(Poetry is detected when python_ruff_auto_poetry is set):
let g:ale_python_ruff_format_auto_poetry = 1
let g:ale_python_ruff_format_change_directory = 0

call ale#test#SetFilename('../test-files/python/poetry/whatever.py')

let fname = ale#Escape(ale#path#Simplify(g:dir .'/../test-files/python/poetry/whatever.py'))

AssertFixer
\ {
\ 'cwd': '%s:h',
\ 'command': ale#Escape('poetry') . ' run ruff format --stdin-filename ' . fname . ' -'
\ }

Execute(Poetry is detected when python_ruff_format_auto_poetry is set, and cwd respects change_directory option):
let g:ale_python_ruff_format_auto_poetry = 1
let g:ale_python_ruff_format_change_directory = 1

call ale#test#SetFilename('../test-files/python/poetry/whatever.py')

let fname = ale#Escape(ale#path#Simplify(g:dir .'/../test-files/python/poetry/whatever.py'))

AssertFixer
\ {
\ 'cwd': ale#path#Simplify(g:dir . '/../test-files/python/poetry'),
\ 'command': ale#Escape('poetry') . ' run ruff format --stdin-filename ' . fname . ' -'
\ }