From 06efbdd25a3a8cccf17692f7bd4eac71ae7d6e05 Mon Sep 17 00:00:00 2001 From: Dmitri Vereshchagin Date: Sat, 29 Oct 2022 14:58:30 +0300 Subject: Add erlang_ls linter for Erlang files (#4346) --- ale_linters/erlang/erlang_ls.vim | 49 ++++++++++++++++++++++ doc/ale-erlang.txt | 25 +++++++++++ doc/ale-supported-languages-and-tools.txt | 1 + doc/ale.txt | 1 + supported-tools.md | 1 + test/linter/test_erlang_erlang_ls.vader | 48 +++++++++++++++++++++ .../erlang/app/_build/default/lib/dep/rebar.lock | 0 .../erlang/app/_build/default/lib/dep/src/dep.erl | 0 test/test-files/erlang/app/rebar.lock | 0 test/test-files/erlang/app/src/app.erl | 0 .../_build/default/lib/dep/erlang_ls.config | 0 .../_build/default/lib/dep/src/dep.erl | 0 .../app_with_erlang_ls_config/erlang_ls.config | 0 .../erlang/app_with_erlang_ls_config/src/app.erl | 0 test/test-files/erlang/kerl_otp_root/.kerl_config | 0 .../kerl_otp_root/lib/stdlib-4.1.1/src/array.erl | 0 16 files changed, 125 insertions(+) create mode 100644 ale_linters/erlang/erlang_ls.vim create mode 100644 test/linter/test_erlang_erlang_ls.vader create mode 100644 test/test-files/erlang/app/_build/default/lib/dep/rebar.lock create mode 100644 test/test-files/erlang/app/_build/default/lib/dep/src/dep.erl create mode 100644 test/test-files/erlang/app/rebar.lock create mode 100644 test/test-files/erlang/app/src/app.erl create mode 100644 test/test-files/erlang/app_with_erlang_ls_config/_build/default/lib/dep/erlang_ls.config create mode 100644 test/test-files/erlang/app_with_erlang_ls_config/_build/default/lib/dep/src/dep.erl create mode 100644 test/test-files/erlang/app_with_erlang_ls_config/erlang_ls.config create mode 100644 test/test-files/erlang/app_with_erlang_ls_config/src/app.erl create mode 100644 test/test-files/erlang/kerl_otp_root/.kerl_config create mode 100644 test/test-files/erlang/kerl_otp_root/lib/stdlib-4.1.1/src/array.erl diff --git a/ale_linters/erlang/erlang_ls.vim b/ale_linters/erlang/erlang_ls.vim new file mode 100644 index 00000000..b747e454 --- /dev/null +++ b/ale_linters/erlang/erlang_ls.vim @@ -0,0 +1,49 @@ +" Author: Dmitri Vereshchagin +" Description: LSP linter for Erlang files + +call ale#Set('erlang_erlang_ls_executable', 'erlang_ls') +call ale#Set('erlang_erlang_ls_log_dir', '') +call ale#Set('erlang_erlang_ls_log_level', 'info') + +function! s:GetCommand(buffer) abort + let l:log_dir = ale#Var(a:buffer, 'erlang_erlang_ls_log_dir') + let l:log_level = ale#Var(a:buffer, 'erlang_erlang_ls_log_level') + + let l:command = '%e' + + if !empty(l:log_dir) + let l:command .= ' --log-dir=' . ale#Escape(l:log_dir) + endif + + let l:command .= ' --log-level=' . ale#Escape(l:log_level) + + return l:command +endfunction + +function! s:FindProjectRoot(buffer) abort + let l:markers = ['_build/', 'erlang_ls.config', 'rebar.lock'] + + " This is a way to find Erlang/OTP root (the one that is managed + " by kerl or asdf). Useful if :ALEGoToDefinition takes us there. + let l:markers += ['.kerl_config'] + + for l:marker in l:markers + let l:path = l:marker[-1:] is# '/' + \ ? ale#path#FindNearestDirectory(a:buffer, l:marker) + \ : ale#path#FindNearestFile(a:buffer, l:marker) + + if !empty(l:path) + return ale#path#Dirname(l:path) + endif + endfor + + return '' +endfunction + +call ale#linter#Define('erlang', { +\ 'name': 'erlang_ls', +\ 'executable': {b -> ale#Var(b, 'erlang_erlang_ls_executable')}, +\ 'command': function('s:GetCommand'), +\ 'lsp': 'stdio', +\ 'project_root': function('s:FindProjectRoot'), +\}) diff --git a/doc/ale-erlang.txt b/doc/ale-erlang.txt index ede179d1..2c6ff22a 100644 --- a/doc/ale-erlang.txt +++ b/doc/ale-erlang.txt @@ -51,6 +51,31 @@ g:ale_erlang_elvis_executable *g:ale_erlang_elvis_executable* This variable can be changed to specify the elvis executable. +------------------------------------------------------------------------------- +erlang_ls *ale-erlang-erlang_ls* + +g:ale_erlang_erlang_ls_executable *g:ale_erlang_erlang_ls_executable* + *b:ale_erlang_erlang_ls_executable* + Type: |String| + Default: `'erlang_ls'` + + This variable can be changed to specify the erlang_ls executable. + +g:ale_erlang_erlang_ls_log_dir *g:ale_erlang_erlang_ls_log_dir* + *b:ale_erlang_erlang_ls_log_dir* + Type: |String| + Default: `''` + + If set this variable overrides default directory where logs will be written. + +g:ale_erlang_erlang_ls_log_level *g:ale_erlang_erlang_ls_log_level* + *b:ale_erlang_erlang_ls_log_level* + Type: |String| + Default: `'info'` + + This variable can be changed to specify log level. + + ------------------------------------------------------------------------------- erlc *ale-erlang-erlc* diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index 476b57f6..ef430995 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -178,6 +178,7 @@ Notes: * `SyntaxErl` * `dialyzer`!! * `elvis`!! + * `erlang_ls` * `erlc` * `erlfmt` * Fish diff --git a/doc/ale.txt b/doc/ale.txt index f038e95f..f8924f1d 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2885,6 +2885,7 @@ documented in additional help files. erlang..................................|ale-erlang-options| dialyzer..............................|ale-erlang-dialyzer| elvis.................................|ale-erlang-elvis| + erlang_ls.............................|ale-erlang-erlang_ls| erlc..................................|ale-erlang-erlc| erlfmt................................|ale-erlang-erlfmt| syntaxerl.............................|ale-erlang-syntaxerl| diff --git a/supported-tools.md b/supported-tools.md index face9e39..280ef760 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -187,6 +187,7 @@ formatting. * [SyntaxErl](https://github.com/ten0s/syntaxerl) * [dialyzer](http://erlang.org/doc/man/dialyzer.html) :floppy_disk: * [elvis](https://github.com/inaka/elvis) :floppy_disk: + * [erlang_ls](https://github.com/erlang-ls/erlang_ls) * [erlc](http://erlang.org/doc/man/erlc.html) * [erlfmt](https://github.com/WhatsApp/erlfmt) * Fish diff --git a/test/linter/test_erlang_erlang_ls.vader b/test/linter/test_erlang_erlang_ls.vader new file mode 100644 index 00000000..3870b5c4 --- /dev/null +++ b/test/linter/test_erlang_erlang_ls.vader @@ -0,0 +1,48 @@ +Before: + let b:files = '../test-files/erlang' + call ale#assert#SetUpLinterTest('erlang', 'erlang_ls') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct): + AssertLinter 'erlang_ls', + \ ale#Escape('erlang_ls') . ' --log-level=' . ale#Escape('info') + +Execute(Executable should be configurable): + let b:ale_erlang_erlang_ls_executable = '/path/to/erlang_ls' + + AssertLinter '/path/to/erlang_ls', + \ ale#Escape('/path/to/erlang_ls') . ' --log-level=' . ale#Escape('info') + +Execute(Log level should be configurable): + let b:ale_erlang_erlang_ls_log_level = 'debug' + + AssertLinter 'erlang_ls', + \ ale#Escape('erlang_ls') . ' --log-level=' . ale#Escape('debug') + +Execute(Log directory should be configurable): + let b:ale_erlang_erlang_ls_log_dir = '/path/to/logs' + + AssertLinter 'erlang_ls', + \ ale#Escape('erlang_ls') + \ . ' --log-dir=' . ale#Escape('/path/to/logs') + \ . ' --log-level=' . ale#Escape('info') + +Execute(Project root should be detected using erlang_ls.config): + call ale#test#SetFilename(b:files . '/app_with_erlang_ls_config/src/app.erl') + AssertLSPProject ale#test#GetFilename(b:files . '/app_with_erlang_ls_config') + + call ale#test#SetFilename(b:files . '/app_with_erlang_ls_config/_build/default/lib/dep/src/dep.erl') + AssertLSPProject ale#test#GetFilename(b:files . '/app_with_erlang_ls_config') + +Execute(Root of Rebar3 project should be detected): + call ale#test#SetFilename(b:files . '/app/src/app.erl') + AssertLSPProject ale#test#GetFilename(b:files . '/app') + + call ale#test#SetFilename(b:files . '/app/_build/default/lib/dep/src/dep.erl') + AssertLSPProject ale#test#GetFilename(b:files . '/app') + +Execute(Root of kerl managed Erlang/OTP installation should be detected): + call ale#test#SetFilename(b:files . '/kerl_otp_root/lib/stdlib-4.1.1/array.erl') + AssertLSPProject ale#test#GetFilename(b:files . '/kerl_otp_root') diff --git a/test/test-files/erlang/app/_build/default/lib/dep/rebar.lock b/test/test-files/erlang/app/_build/default/lib/dep/rebar.lock new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/erlang/app/_build/default/lib/dep/src/dep.erl b/test/test-files/erlang/app/_build/default/lib/dep/src/dep.erl new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/erlang/app/rebar.lock b/test/test-files/erlang/app/rebar.lock new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/erlang/app/src/app.erl b/test/test-files/erlang/app/src/app.erl new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/erlang/app_with_erlang_ls_config/_build/default/lib/dep/erlang_ls.config b/test/test-files/erlang/app_with_erlang_ls_config/_build/default/lib/dep/erlang_ls.config new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/erlang/app_with_erlang_ls_config/_build/default/lib/dep/src/dep.erl b/test/test-files/erlang/app_with_erlang_ls_config/_build/default/lib/dep/src/dep.erl new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/erlang/app_with_erlang_ls_config/erlang_ls.config b/test/test-files/erlang/app_with_erlang_ls_config/erlang_ls.config new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/erlang/app_with_erlang_ls_config/src/app.erl b/test/test-files/erlang/app_with_erlang_ls_config/src/app.erl new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/erlang/kerl_otp_root/.kerl_config b/test/test-files/erlang/kerl_otp_root/.kerl_config new file mode 100644 index 00000000..e69de29b diff --git a/test/test-files/erlang/kerl_otp_root/lib/stdlib-4.1.1/src/array.erl b/test/test-files/erlang/kerl_otp_root/lib/stdlib-4.1.1/src/array.erl new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3