diff --git a/ale_linters/python/pyrefly.vim b/ale_linters/python/pyrefly.vim new file mode 100644 index 00000000..135e0ddf --- /dev/null +++ b/ale_linters/python/pyrefly.vim @@ -0,0 +1,57 @@ +" Author: oliverralbertini +" Description: A performant type-checker supporting LSP for Python 3 created by Facebook + +call ale#Set('python_pyrefly_executable', 'pyrefly') +call ale#Set('python_pyrefly_use_global', get(g:, 'ale_use_global_executables', 0)) +call ale#Set('python_pyrefly_auto_pipenv', 0) +call ale#Set('python_pyrefly_auto_poetry', 0) +call ale#Set('python_pyrefly_auto_uv', 0) + +function! ale_linters#python#pyrefly#GetExecutable(buffer) abort + if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_pyrefly_auto_pipenv')) + \ && ale#python#PipenvPresent(a:buffer) + return 'pipenv' + endif + + if (ale#Var(a:buffer, 'python_auto_poetry') || ale#Var(a:buffer, 'python_pyrefly_auto_poetry')) + \ && ale#python#PoetryPresent(a:buffer) + return 'poetry' + endif + + if (ale#Var(a:buffer, 'python_auto_uv') || ale#Var(a:buffer, 'python_pyrefly_auto_uv')) + \ && ale#python#UvPresent(a:buffer) + return 'uv' + endif + + return ale#python#FindExecutable(a:buffer, 'python_pyrefly', ['pyrefly']) +endfunction + +function! ale_linters#python#pyrefly#GetCommand(buffer) abort + let l:executable = ale_linters#python#pyrefly#GetExecutable(a:buffer) + let l:exec_args = [ + \ ale#Escape(l:executable) + \ ] + \ + (l:executable =~? '\(pipenv\|poetry\|uv\)$' ? ['run', 'pyrefly'] : []) + \ + [ + \ 'lsp', + \ ] + + return join(l:exec_args, ' ') +endfunction + +function! ale_linters#python#pyrefly#GetCwd(buffer) abort + " 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' +endfunction + +call ale#linter#Define('python', { +\ 'name': 'pyrefly', +\ 'lsp': 'stdio', +\ 'executable': function('ale_linters#python#pyrefly#GetExecutable'), +\ 'command': function('ale_linters#python#pyrefly#GetCommand'), +\ 'project_root': function('ale#python#FindProjectRoot'), +\ 'completion_filter': 'ale#completion#python#CompletionItemFilter', +\ 'cwd': function('ale_linters#python#pyrefly#GetCwd'), +\}) diff --git a/doc/ale-python.txt b/doc/ale-python.txt index d9494d08..ce672acd 100644 --- a/doc/ale-python.txt +++ b/doc/ale-python.txt @@ -1616,6 +1616,69 @@ g:ale_python_pyre_auto_uv executable. +=============================================================================== +pyrefly *ale-python-pyrefly* + +`pyrefly` will be run from a detected project root, per |ale-python-root|. + + *ale-options.python_pyrefly_executable* + *g:ale_python_pyrefly_executable* + *b:ale_python_pyrefly_executable* +python_pyrefly_executable +g:ale_python_pyrefly_executable + Type: |String| + Default: `'pyrefly'` + + See |ale-integrations-local-executables| + + Set this to `'pipenv'` to invoke `'pipenv` `run` `pyrefly'`. + Set this to `'poetry'` to invoke `'poetry` `run` `pyrefly'`. + Set this to `'uv'` to invoke `'uv` `run` `pyrefly'`. + + *ale-options.python_pyrefly_use_global* + *g:ale_python_pyrefly_use_global* + *b:ale_python_pyrefly_use_global* +python_pyrefly_use_global +g:ale_python_pyrefly_use_global + Type: |Number| + Default: `get(g:, 'ale_use_global_executables', 0)` + + See |ale-integrations-local-executables| + + *ale-options.python_pyrefly_auto_pipenv* + *g:ale_python_pyrefly_auto_pipenv* + *b:ale_python_pyrefly_auto_pipenv* +python_pyrefly_auto_pipenv +g:ale_python_pyrefly_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. + + *ale-options.python_pyrefly_auto_poetry* + *g:ale_python_pyrefly_auto_poetry* + *b:ale_python_pyrefly_auto_poetry* +python_pyrefly_auto_poetry +g:ale_python_pyrefly_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. + + *ale-options.python_pyrefly_auto_uv* + *g:ale_python_pyrefly_auto_uv* + *b:ale_python_pyrefly_auto_uv* +python_pyrefly_auto_uv +g:ale_python_pyrefly_auto_uv + Type: |Number| + Default: `0` + + Set the executable to `uv` if true. This is overridden by a manually-set + executable. + + =============================================================================== pyright *ale-python-pyright* diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index 4ac12f6c..7f03e10a 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -545,6 +545,7 @@ Notes: * `pylint`!! * `pylsp` * `pyre` + * `pyrefly` * `pyright` * `refurb` * `reorder-python-imports` diff --git a/doc/ale.txt b/doc/ale.txt index b84f43b8..e508f769 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -3788,6 +3788,7 @@ documented in additional help files. pylint................................|ale-python-pylint| pylsp.................................|ale-python-pylsp| pyre..................................|ale-python-pyre| + pyrefly...............................|ale-python-pyrefly| pyright...............................|ale-python-pyright| refurb................................|ale-python-refurb| reorder-python-imports................|ale-python-reorder_python_imports| diff --git a/supported-tools.md b/supported-tools.md index 8c671980..137883be 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -554,6 +554,7 @@ formatting. * [pylint](https://www.pylint.org/) :floppy_disk: * [pylsp](https://github.com/python-lsp/python-lsp-server) :warning: * [pyre](https://github.com/facebook/pyre-check) :warning: + * [pyrefly](https://github.com/facebook/pyrefly) :warning: * [pyright](https://github.com/microsoft/pyright) * [refurb](https://github.com/dosisod/refurb) :floppy_disk: * [reorder-python-imports](https://github.com/asottile/reorder_python_imports) diff --git a/test/completion/test_ale_import_command.vader b/test/completion/test_ale_import_command.vader index 6f186aae..045b20f1 100644 --- a/test/completion/test_ale_import_command.vader +++ b/test/completion/test_ale_import_command.vader @@ -14,7 +14,7 @@ Before: let g:ale_completion_enabled = 0 let g:ale_completion_autoimport = 0 let g:ale_completion_max_suggestions = 50 - let g:ale_linters = {'typescript': ['tsserver'], 'python': ['pyre']} + let g:ale_linters = {'typescript': ['tsserver'], 'python': ['pyrefly']} unlet! b:ale_linters let g:server_started_value = 1 diff --git a/test/linter/test_pyrefly.vader b/test/linter/test_pyrefly.vader new file mode 100644 index 00000000..05e1ddf1 --- /dev/null +++ b/test/linter/test_pyrefly.vader @@ -0,0 +1,69 @@ +Before: + call ale#assert#SetUpLinterTest('python', 'pyrefly') + let b:bin_dir = has('win32') ? 'Scripts' : 'bin' + +After: + unlet! b:bin_dir + unlet! b:executable + call ale#assert#TearDownLinterTest() + +Execute(The pyrefly command callback should return default string): + call ale#test#SetFilename('./foo.py') + + AssertLinter 'pyrefly', ale#Escape('pyrefly') . ' lsp' + +Execute(The pyrefly executable should be configurable): + let g:ale_python_pyrefly_executable = '~/.local/bin/pyrefly' + + AssertLinter '~/.local/bin/pyrefly', + \ ale#Escape('~/.local/bin/pyrefly') . ' lsp' + +Execute(The pyrefly executable should be run from the virtualenv path): + call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py') + + let b:executable = ale#path#Simplify( + \ g:dir . '/../test-files/python/with_virtualenv/env/' . b:bin_dir . '/pyrefly' + \) + + AssertLinter b:executable, ale#Escape(b:executable) . ' lsp' + +Execute(You should be able to override the pyrefly virtualenv lookup): + call ale#test#SetFilename('../test-files/python/with_virtualenv/subdir/foo/bar.py') + + let g:ale_python_pyrefly_use_global = 1 + + AssertLinter 'pyrefly', ale#Escape('pyrefly') . ' lsp' + +Execute(Setting executable to 'pipenv' appends 'run pyrefly'): + let g:ale_python_pyrefly_executable = 'path/to/pipenv' + call ale#test#SetFilename('../test-files/dummy') + + AssertLinter 'path/to/pipenv', + \ ale#Escape('path/to/pipenv') . ' run pyrefly lsp' + +Execute(Pipenv is detected when python_pyrefly_auto_pipenv is set): + let g:ale_python_pyrefly_auto_pipenv = 1 + call ale#test#SetFilename('../test-files/python/pipenv/whatever.py') + + AssertLinter 'pipenv', + \ ale#Escape('pipenv') . ' run pyrefly lsp' + +Execute(Setting executable to 'poetry' appends 'run pyrefly lsp'): + let g:ale_python_pyrefly_executable = 'path/to/poetry' + + AssertLinter 'path/to/poetry', + \ ale#Escape('path/to/poetry') . ' run pyrefly lsp' + +Execute(Poetry is detected when python_pyrefly_auto_poetry is set): + let g:ale_python_pyrefly_auto_poetry = 1 + call ale#test#SetFilename('../test-files/python/poetry/whatever.py') + + AssertLinter 'poetry', + \ ale#Escape('poetry') . ' run pyrefly lsp' + +Execute(uv is detected when python_pyrefly_auto_uv is set): + let g:ale_python_pyrefly_auto_uv = 1 + call ale#test#SetFilename('../test-files/python/uv/whatever.py') + + AssertLinter 'uv', + \ ale#Escape('uv') . ' run pyrefly lsp' diff --git a/test/test-files/python/with_virtualenv/env/Scripts/pyrefly.exe b/test/test-files/python/with_virtualenv/env/Scripts/pyrefly.exe new file mode 100755 index 00000000..e69de29b diff --git a/test/test-files/python/with_virtualenv/env/bin/pyrefly b/test/test-files/python/with_virtualenv/env/bin/pyrefly new file mode 100755 index 00000000..e69de29b