From f9de268816e23dbcd963be3f13c8975e6c3d5866 Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Mon, 5 May 2025 02:01:45 -0400 Subject: [PATCH] Adding Roc language linters and fixers. (#4966) * `roc_language_server` * `roc format` * `roc format annotate` --- ale_linters/roc/roc_language_server.vim | 25 ++++++ autoload/ale/fix/registry.vim | 10 +++ autoload/ale/fixers/roc_annotate.vim | 21 +++++ autoload/ale/fixers/roc_format.vim | 20 +++++ doc/ale-roc.txt | 79 +++++++++++++++++++ doc/ale-supported-languages-and-tools.txt | 4 + doc/ale.txt | 4 + supported-tools.md | 4 + .../test_roc_annotate_fixer_callback.vader | 20 +++++ .../test_roc_format_fixer_callback.vader | 20 +++++ .../linter/test_roc_roc_language_server.vader | 23 ++++++ test/test-files/roc/main.roc | 0 12 files changed, 230 insertions(+) create mode 100644 ale_linters/roc/roc_language_server.vim create mode 100644 autoload/ale/fixers/roc_annotate.vim create mode 100644 autoload/ale/fixers/roc_format.vim create mode 100644 doc/ale-roc.txt create mode 100644 test/fixers/test_roc_annotate_fixer_callback.vader create mode 100644 test/fixers/test_roc_format_fixer_callback.vader create mode 100644 test/linter/test_roc_roc_language_server.vader create mode 100644 test/test-files/roc/main.roc diff --git a/ale_linters/roc/roc_language_server.vim b/ale_linters/roc/roc_language_server.vim new file mode 100644 index 00000000..1bb4c39e --- /dev/null +++ b/ale_linters/roc/roc_language_server.vim @@ -0,0 +1,25 @@ +" Author: Benjamin Block +" Description: A language server for Roc. + +function! ale_linters#roc#roc_language_server#GetProjectRoot(buffer) abort + let l:roc_main_file = ale#path#FindNearestFile(a:buffer, 'main.roc') + + if !empty(l:roc_main_file) + return fnamemodify(l:roc_main_file, ':p:h') + else + return fnamemodify('', ':h') + endif +endfunction + +call ale#Set('roc_roc_language_server_executable', 'roc_language_server') +call ale#Set('roc_roc_language_server_config', {}) + +call ale#linter#Define('roc', { +\ 'name': 'roc_language_server', +\ 'lsp': 'stdio', +\ 'language': 'roc', +\ 'lsp_config': {b -> ale#Var(b, 'roc_roc_language_server_config')}, +\ 'executable': {b -> ale#Var(b, 'roc_roc_language_server_executable')}, +\ 'command': '%e', +\ 'project_root': function('ale_linters#roc#roc_language_server#GetProjectRoot'), +\}) diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index efa1fe75..ee26079f 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -722,6 +722,16 @@ let s:default_registry = { \ 'suggested_filetypes': ['typst'], \ 'description': 'A formatter for Typst files', \ }, +\ 'roc_format': { +\ 'function': 'ale#fixers#roc_format#Fix', +\ 'suggested_filetypes': ['roc'], +\ 'description': 'Formats Roc files.', +\ }, +\ 'roc_annotate': { +\ 'function': 'ale#fixers#roc_annotate#Fix', +\ 'suggested_filetypes': ['roc'], +\ 'description': 'Annotates all top-level definitions in Roc files.', +\ }, \} " Reset the function registry to the default entries. diff --git a/autoload/ale/fixers/roc_annotate.vim b/autoload/ale/fixers/roc_annotate.vim new file mode 100644 index 00000000..0c9d8228 --- /dev/null +++ b/autoload/ale/fixers/roc_annotate.vim @@ -0,0 +1,21 @@ +" Author: Benjamin Block +" Description: Official type annotation tool for Roc. + +call ale#Set('roc_roc_annotate_executable', 'roc') +call ale#Set('roc_roc_annotate_options', '') + +function! ale#fixers#roc_annotate#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'roc_roc_annotate_executable') + let l:command = l:executable . ' format annotate' + let l:options = ale#Var(a:buffer, 'roc_roc_annotate_options') + + if l:options isnot# '' + let l:command .= ' ' . l:options + endif + + return { + \ 'command': l:command . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction + diff --git a/autoload/ale/fixers/roc_format.vim b/autoload/ale/fixers/roc_format.vim new file mode 100644 index 00000000..dc1b309b --- /dev/null +++ b/autoload/ale/fixers/roc_format.vim @@ -0,0 +1,20 @@ +" Author: Benjamin Block +" Description: Official formatter for Roc. + +call ale#Set('roc_roc_format_executable', 'roc') +call ale#Set('roc_roc_format_options', '') + +function! ale#fixers#roc_format#Fix(buffer) abort + let l:executable = ale#Var(a:buffer, 'roc_roc_format_executable') + let l:command = l:executable . ' format' + let l:options = ale#Var(a:buffer, 'roc_roc_format_options') + + if l:options isnot# '' + let l:command .= ' ' . l:options + endif + + return { + \ 'command': l:command . ' %t', + \ 'read_temporary_file': 1, + \} +endfunction diff --git a/doc/ale-roc.txt b/doc/ale-roc.txt new file mode 100644 index 00000000..392a1135 --- /dev/null +++ b/doc/ale-roc.txt @@ -0,0 +1,79 @@ +=============================================================================== +ALE Roc Integration *ale-roc-options* + *ale-integration-roc* + +=============================================================================== +roc_language_server *ale-roc-roc-language-server* + + *ale-options.roc_roc_language_server_executable* + *g:ale_roc_roc_language_server_executable* + *b:ale_roc_roc_language_server_executable* +roc_roc_language_server_executable +g:ale_roc_roc_language_server_executable + Type: |String| + Default: `'roc_language_server'` + + This variable can be modified to change the executable path for + `roc_language_server`. + + *ale-options.roc_roc_language_server_config* + *g:ale_roc_roc_language_server_config* + *b:ale_roc_roc_language_server_config* +roc_roc_language_server_config +g:ale_roc_roc_language_server_config + Type: |Dictionary| + Default: `{}` + + Dictionary with configuration settings for roc_language_server. + + +=============================================================================== +roc_format *ale-roc-roc-format* + + *ale-options.roc_roc_format_executable* + *g:ale_roc_roc_format_executable* + *b:ale_roc_roc_format_executable* +roc_roc_format_executable +g:ale_roc_roc_format_executable + Type: |String| + Default: `'roc'` + + This variable can be modified to change the executable path for `roc`. + + *ale-options.roc_roc_format_options* + *g:ale_roc_roc_format_options* + *b:ale_roc_roc_format_options* +roc_roc_format_options +g:ale_roc_roc_format_options + Type: String + Default: `''` + + Additional flags for `roc format`. + + +=============================================================================== +roc_annotate *ale-roc-roc-annotate* + + *ale-options.roc_roc_annotate_executable* + *g:ale_roc_roc_annotate_executable* + *b:ale_roc_roc_annotate_executable* +roc_roc_annotate_executable +g:ale_roc_roc_annotate_executable + Type: |String| + Default: `'roc'` + + This variable can be modified to change the executable path for `roc`. + + *ale-options.roc_roc_annotate_options* + *g:ale_roc_roc_annotate_options* + *b:ale_roc_roc_annotate_options* +roc_roc_annotate_options +g:ale_roc_roc_annotate_options + Type: String + Default: `''` + + Additional flags for `roc format annotate`. + + +=============================================================================== + 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 ba785a62..38a9c170 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -586,6 +586,10 @@ Notes: * `write-good` * Robot * `rflint` +* Roc + * roc_annotate + * roc_format + * roc_language_server * RPM spec * `rpmlint` * Ruby diff --git a/doc/ale.txt b/doc/ale.txt index 8f391c3c..c8765bca 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -3820,6 +3820,10 @@ documented in additional help files. write-good............................|ale-restructuredtext-write-good| robot...................................|ale-robot-options| rflint................................|ale-robot-rflint| + roc.....................................|ale-roc-options| + roc_language_server...................|ale-roc-roc-language-server| + roc_format............................|ale-roc-roc-format| + roc_annotate..........................|ale-roc-roc-annotate| ruby....................................|ale-ruby-options| brakeman..............................|ale-ruby-brakeman| cspell................................|ale-ruby-cspell| diff --git a/supported-tools.md b/supported-tools.md index 7224d433..7cea0cf9 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -595,6 +595,10 @@ formatting. * [write-good](https://github.com/btford/write-good) * Robot * [rflint](https://github.com/boakley/robotframework-lint) +* Roc + * [roc_annotate](https://github.com/roc-lang/roc) + * [roc_format](https://github.com/roc-lang/roc) + * [roc_language_server](https://github.com/roc-lang/roc) * RPM spec * [rpmlint](https://github.com/rpm-software-management/rpmlint) :warning: (see `:help ale-integration-spec`) * Ruby diff --git a/test/fixers/test_roc_annotate_fixer_callback.vader b/test/fixers/test_roc_annotate_fixer_callback.vader new file mode 100644 index 00000000..5c766355 --- /dev/null +++ b/test/fixers/test_roc_annotate_fixer_callback.vader @@ -0,0 +1,20 @@ +Before: + call ale#assert#SetUpFixerTest('roc', 'roc_annotate') + +After: + call ale#assert#TearDownFixerTest() + +Execute(The roc annotate callback should return the correct default values): + AssertFixer { + \ 'command': 'roc format annotate %t', + \ 'read_temporary_file': 1, + \} + +Execute(The roc annotate callback should allow a custom executable): + let g:ale_roc_roc_annotate_executable = 'foo/bar' + + AssertFixer { + \ 'command': 'foo/bar format annotate %t', + \ 'read_temporary_file': 1, + \} + diff --git a/test/fixers/test_roc_format_fixer_callback.vader b/test/fixers/test_roc_format_fixer_callback.vader new file mode 100644 index 00000000..4269c515 --- /dev/null +++ b/test/fixers/test_roc_format_fixer_callback.vader @@ -0,0 +1,20 @@ +Before: + call ale#assert#SetUpFixerTest('roc', 'roc_format') + +After: + call ale#assert#TearDownFixerTest() + +Execute(The roc format callback should return the correct default values): + AssertFixer { + \ 'command': 'roc format %t', + \ 'read_temporary_file': 1, + \} + +Execute(The roc format callback should allow a custom executable): + let g:ale_roc_roc_format_executable = 'foo/bar' + + AssertFixer { + \ 'command': 'foo/bar format %t', + \ 'read_temporary_file': 1, + \} + diff --git a/test/linter/test_roc_roc_language_server.vader b/test/linter/test_roc_roc_language_server.vader new file mode 100644 index 00000000..ececb70a --- /dev/null +++ b/test/linter/test_roc_roc_language_server.vader @@ -0,0 +1,23 @@ +Before: + call ale#assert#SetUpLinterTest('roc', 'roc_language_server') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default executable path should be correct): + AssertLinter 'roc_language_server', ale#Escape('roc_language_server') + +Execute(The project root should be detected correctly in empty directory): + AssertLSPProject '.' + +Execute(The project root should be detected correctly with main.roc): + call ale#test#SetFilename('../test-files/roc/main.roc') + AssertLSPProject ale#path#Simplify(g:dir . '/../test-files/roc') + +Execute(The LSP values should be set correctly): + call ale#test#SetFilename('../test-files/roc/main.roc') + + AssertLSPLanguage 'roc' + AssertLSPOptions {} + AssertLSPConfig {} + AssertLSPProject ale#path#Simplify(g:dir . '/../test-files/roc') diff --git a/test/test-files/roc/main.roc b/test/test-files/roc/main.roc new file mode 100644 index 00000000..e69de29b