From 0360a73644e068f5e538bc702dee96284cd21e62 Mon Sep 17 00:00:00 2001 From: John Jackson Date: Sun, 21 Dec 2025 04:25:42 -0500 Subject: [PATCH] Add ReScript support (#5072) This adds support for both rescript format and rescript-language-server. Closes https://github.com/dense-analysis/ale/issues/3335 --- .../rescript/rescript_language_server.vim | 24 +++++++++ autoload/ale/fix/registry.vim | 5 ++ autoload/ale/fixers/rescript_format.vim | 33 ++++++++++++ doc/ale-rescript.txt | 54 +++++++++++++++++++ doc/ale-supported-languages-and-tools.txt | 3 ++ doc/ale.txt | 3 ++ supported-tools.md | 3 ++ .../fixers/test_rescript_fixer_callback.vader | 46 ++++++++++++++++ .../test_rescript_language_server.vader | 25 +++++++++ test/test-files/rescript/rescript.json | 0 test/test-files/rescript/testfile-noextension | 0 test/test-files/rescript/testfile.res | 0 test/test-files/rescript/testfile.resi | 0 13 files changed, 196 insertions(+) create mode 100644 ale_linters/rescript/rescript_language_server.vim create mode 100644 autoload/ale/fixers/rescript_format.vim create mode 100644 doc/ale-rescript.txt create mode 100644 test/fixers/test_rescript_fixer_callback.vader create mode 100644 test/linter/test_rescript_language_server.vader create mode 100644 test/test-files/rescript/rescript.json create mode 100644 test/test-files/rescript/testfile-noextension create mode 100644 test/test-files/rescript/testfile.res create mode 100644 test/test-files/rescript/testfile.resi diff --git a/ale_linters/rescript/rescript_language_server.vim b/ale_linters/rescript/rescript_language_server.vim new file mode 100644 index 00000000..b404d7ba --- /dev/null +++ b/ale_linters/rescript/rescript_language_server.vim @@ -0,0 +1,24 @@ +" Author: John Jackson +" Description: The official language server for ReScript. + +call ale#Set('rescript_language_server_executable', 'rescript-language-server') +call ale#Set( +\ 'rescript_language_server_use_global', +\ get(g:, 'ale_use_global_executables', v:true), +\ ) + +function! s:GetProjectRoot(buffer) abort + let l:config_file = ale#path#FindNearestFile(a:buffer, 'rescript.json') + + return !empty(l:config_file) ? fnamemodify(l:config_file, ':h') : '' +endfunction + +call ale#linter#Define('rescript', { +\ 'name': 'rescript_language_server', +\ 'lsp': 'stdio', +\ 'executable': {b -> ale#path#FindExecutable(b, 'rescript_language_server', [ +\ 'node_modules/.bin/rescript-language-server' +\ ])}, +\ 'command': '%e --stdio', +\ 'project_root': function('s:GetProjectRoot'), +\}) diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index eac4efb9..5e4660c6 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -672,6 +672,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['racket'], \ 'description': 'Fix Racket files with raco fmt.', \ }, +\ 'rescript_format': { +\ 'function': 'ale#fixers#rescript_format#Fix', +\ 'suggested_filetypes': ['rescript'], +\ 'description': 'Official formatter for ReScript.', +\ }, \ 'ruff': { \ 'function': 'ale#fixers#ruff#Fix', \ 'suggested_filetypes': ['python'], diff --git a/autoload/ale/fixers/rescript_format.vim b/autoload/ale/fixers/rescript_format.vim new file mode 100644 index 00000000..46e31036 --- /dev/null +++ b/autoload/ale/fixers/rescript_format.vim @@ -0,0 +1,33 @@ +" Author: John Jackson +" Description: Fix ReScript files with the ReScript formatter. + +call ale#Set('rescript_format_executable', 'rescript') +call ale#Set( +\ 'rescript_format_use_global', +\ get(g:, 'ale_use_global_executables', v:false) +\ ) + +function! s:GetExecutable(buffer) abort + return ale#path#FindExecutable(a:buffer, 'rescript_format', [ + \ 'node_modules/.bin/rescript', + \]) +endfunction + +function! s:FixWithVersion(buffer, version) abort + let l:exe = ale#Escape(s:GetExecutable(a:buffer)) + let l:stdin = ale#semver#GTE(a:version, [12, 0, 0]) ? ' --stdin' : ' -stdin' + let l:ext = fnamemodify(bufname(a:buffer), ':e') is? 'resi' + \ ? ' .resi' + \ : ' .res' + + return {'command': l:exe . ' format' . l:stdin . l:ext} +endfunction + +function! ale#fixers#rescript_format#Fix(buffer) abort + return ale#semver#RunWithVersionCheck( + \ a:buffer, + \ s:GetExecutable(a:buffer), + \ '%e --version', + \ function('s:FixWithVersion'), + \) +endfunction diff --git a/doc/ale-rescript.txt b/doc/ale-rescript.txt new file mode 100644 index 00000000..7d2fc46e --- /dev/null +++ b/doc/ale-rescript.txt @@ -0,0 +1,54 @@ +=============================================================================== +ALE ReScript Integration *ale-rescript-options* + + +=============================================================================== +rescript-language-server *ale-rescript-language-server* + + *ale-options.rescript_language_server_executable* + *g:ale_rescript_language_server_executable* + *b:ale_rescript_language_server_executable* +ale_rescript_language_server_executable +g:ale_rescript_language_server_executable + Type: |String| + Default: `'rescript-language-server'` + + See |ale-integrations-local-executables| + + *ale-options.rescript_language_server_use_global* + *g:ale_rescript_language_server_use_global* + *b:ale_rescript_language_server_use_global* +rescript_language_server_use_global +g:ale_rescript_language_server_use_global + Type: |Number| + Default: `get(g:, 'ale_use_global_executables', v:true)` + + See |ale-integrations-local-executables| + + +=============================================================================== +rescript_format *ale-rescript-format* + + *ale-options.rescript_format_executable* + *g:ale_rescript_format_executable* + *b:ale_rescript_format_executable* +rescript_format_executable +g:ale_rescript_format_executable + Type: |String| + Default: `'rescript'` + + See |ale-integrations-local-executables| + + *ale-options.rescript_format_use_global* + *g:ale_rescript_format_use_global* + *b:ale_rescript_format_use_global* +rescript_format_use_global +g:ale_rescript_format_use_global + Type: |Number| + Default: `get(g:, 'ale_use_global_executables', v:false)` + + See |ale-integrations-local-executables| + + +=============================================================================== + vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index 5c3e3e54..8cb95b56 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -582,6 +582,9 @@ Notes: * `cspell` * `opacheck` * `opafmt` +* ReScript + * `rescript-language-server` + * `rescript_format` * REST * kulala_fmt * reStructuredText diff --git a/doc/ale.txt b/doc/ale.txt index a2cd542b..3cc59f8e 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -3862,6 +3862,9 @@ documented in additional help files. cspell................................|ale-rego-cspell| opacheck..............................|ale-rego-opa-check| opafmt................................|ale-rego-opa-fmt-fixer| + rescript................................|ale-rescript-options| + rescript-language-server..............|ale-rescript-language-server| + rescript_format.......................|ale-rescript-format| rest....................................|ale-rest-options| kulala_fmt............................|ale-rest-kulala_fmt| restructuredtext........................|ale-restructuredtext-options| diff --git a/supported-tools.md b/supported-tools.md index 74cf59f0..706c3e15 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -592,6 +592,9 @@ formatting. * [cspell](https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell) * [opacheck](https://www.openpolicyagent.org/docs/latest/cli/#opa-check) * [opafmt](https://www.openpolicyagent.org/docs/latest/cli/#opa-fmt) +* ReScript + * [rescript-language-server](https://www.npmjs.com/package/@rescript/language-server) :speech_balloon: + * [rescript_format](https://rescript-lang.org/) * REST * [kulala_fmt](https://github.com/mistweaverco/kulala-fmt) * reStructuredText diff --git a/test/fixers/test_rescript_fixer_callback.vader b/test/fixers/test_rescript_fixer_callback.vader new file mode 100644 index 00000000..a0a45c07 --- /dev/null +++ b/test/fixers/test_rescript_fixer_callback.vader @@ -0,0 +1,46 @@ +Before: + call ale#assert#SetUpFixerTest('rescript', 'rescript_format') + +After: + call ale#assert#TearDownFixerTest() + +Execute(The rescript callback should return the correct default values): + call ale#test#SetFilename('../test-files/rescript/testfile-noextension') + + set filetype=rescript + GivenCommandOutput ['12.0.0'] + AssertFixer + \ { + \ 'command': + \ ale#Escape(g:ale_rescript_format_executable) . ' format --stdin .res' + \ } + +Execute(The rescript callback should correctly detect res files): + call ale#test#SetFilename('../test-files/rescript/testfile.res') + + GivenCommandOutput ['12.0.0'] + AssertFixer + \ { + \ 'command': + \ ale#Escape(g:ale_rescript_format_executable) . ' format --stdin .res' + \ } + +Execute(The rescript callback should correctly detect resi files): + call ale#test#SetFilename('../test-files/rescript/testfile.resi') + + GivenCommandOutput ['12.0.0'] + AssertFixer + \ { + \ 'command': + \ ale#Escape(g:ale_rescript_format_executable) . ' format --stdin .resi' + \ } + +Execute(The version check should be correct): + call ale#test#SetFilename('../test-files/rescript/testfile.res') + + GivenCommandOutput ['11.0.0'] + AssertFixer + \ { + \ 'command': + \ ale#Escape(g:ale_rescript_format_executable) . ' format -stdin .res' + \ } diff --git a/test/linter/test_rescript_language_server.vader b/test/linter/test_rescript_language_server.vader new file mode 100644 index 00000000..e2a8f6ab --- /dev/null +++ b/test/linter/test_rescript_language_server.vader @@ -0,0 +1,25 @@ +Before: + call ale#assert#SetUpLinterTest('rescript', 'rescript_language_server') + + Save &filetype + let &filetype = 'rescript' + +After: + call ale#assert#TearDownLinterTest() + +Execute(The language string should be correct): + AssertLSPLanguage 'rescript' + +Execute(The project root should be detected correctly): + AssertLSPProject '' + + call ale#test#SetFilename('../test-files/rescript/testfile.res') + + AssertLSPProject ale#path#Simplify(g:dir . '/../test-files/rescript') + +Execute(The command should be correct): + call ale#test#SetFilename('../test-files/rescript/testfile.res') + + AssertLinter + \ 'rescript-language-server', + \ ale#Escape('rescript-language-server') . ' --stdio' diff --git a/test/test-files/rescript/rescript.json b/test/test-files/rescript/rescript.json new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/rescript/testfile-noextension b/test/test-files/rescript/testfile-noextension new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/rescript/testfile.res b/test/test-files/rescript/testfile.res new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/rescript/testfile.resi b/test/test-files/rescript/testfile.resi new file mode 100644 index 00000000..e69de29b