Close #2102 - Add support for the Angular language server

This commit is contained in:
w0rp 2021-03-14 21:10:05 +00:00
parent 80a48d01be
commit b45ee8ec6c
No known key found for this signature in database
GPG Key ID: 0FC1ECAA8C81CD83
13 changed files with 230 additions and 48 deletions

View File

@ -0,0 +1,52 @@
" Author: w0rp <devw0rp@gmail.com>
" Description: tsserver integration for ALE
call ale#Set('html_angular_executable', 'ngserver')
call ale#Set('html_angular_use_global', get(g:, 'ale_use_global_executables', 0))
function! ale_linters#html#angular#GetProjectRoot(buffer) abort
return ale#path#Dirname(
\ ale#path#FindNearestDirectory(a:buffer, 'node_modules')
\)
endfunction
function! ale_linters#html#angular#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'html_angular', [
\ 'node_modules/@angular/language-server/bin/ngserver',
\ 'node_modules/@angular/language-server/index.js',
\])
endfunction
function! ale_linters#html#angular#GetCommand(buffer) abort
let l:language_service_dir = ale#path#Simplify(
\ ale#path#FindNearestDirectory(
\ a:buffer,
\ 'node_modules/@angular/language-service'
\ )
\)
if empty(l:language_service_dir)
return ''
endif
let l:language_service_dir = fnamemodify(l:language_service_dir, ':h')
let l:typescript_dir = ale#path#Simplify(
\ fnamemodify(l:language_service_dir, ':h:h')
\ . '/typescript'
\)
let l:executable = ale_linters#html#angular#GetExecutable(a:buffer)
return ale#node#Executable(a:buffer, l:executable)
\ . ' --ngProbeLocations ' . ale#Escape(l:language_service_dir)
\ . ' --tsProbeLocations ' . ale#Escape(l:typescript_dir)
\ . ' --stdio'
endfunction
call ale#linter#Define('html', {
\ 'name': 'angular',
\ 'aliases': ['angular-language-server'],
\ 'lsp': 'stdio',
\ 'executable': function('ale_linters#html#angular#GetExecutable'),
\ 'command': function('ale_linters#html#angular#GetCommand'),
\ 'project_root': function('ale_linters#html#angular#GetProjectRoot'),
\})

View File

@ -169,8 +169,21 @@ function! ale#assert#LinterNotExecuted() abort
let l:buffer = bufnr('')
let l:linter = s:GetLinter()
let l:executable = ale#linter#GetExecutable(l:buffer, l:linter)
let l:executed = 1
Assert empty(l:executable), "The linter will be executed when it shouldn't be"
if !empty(l:executable)
let l:command = ale#linter#GetCommand(l:buffer, l:linter)
if type(l:command) is v:t_list
let l:command = l:command[-1]
endif
let l:executed = !empty(l:command)
else
let l:executed = 0
endif
Assert !l:executed, "The linter will be executed when it shouldn't be"
endfunction
function! ale#assert#LSPOptions(expected_options) abort

View File

@ -66,9 +66,17 @@ function! ale#definition#HandleLSPResponse(conn_id, response) abort
endif
for l:item in l:result
let l:filename = ale#path#FromURI(l:item.uri)
let l:line = l:item.range.start.line + 1
let l:column = l:item.range.start.character + 1
if has_key(l:item, 'targetUri')
" LocationLink items use targetUri
let l:filename = ale#path#FromURI(l:item.targetUri)
let l:line = l:item.targetRange.start.line + 1
let l:column = l:item.targetRange.start.character + 1
else
" LocationLink items use uri
let l:filename = ale#path#FromURI(l:item.uri)
let l:line = l:item.range.start.line + 1
let l:column = l:item.range.start.character + 1
endif
call ale#definition#UpdateTagStack()
call ale#util#Open(l:filename, l:line, l:column, l:options)

View File

@ -294,14 +294,15 @@ function! s:StartLSP(options, address, executable, command) abort
call ale#lsp#MarkConnectionAsTsserver(l:conn_id)
endif
let l:cwd = ale#linter#GetCwd(l:buffer, l:linter)
let l:command = ale#command#FormatCommand(
\ l:buffer,
\ a:executable,
\ a:command,
\ 0,
\ v:false,
\ v:null,
\ [],
\ l:cwd,
\ ale#GetFilenameMappings(l:buffer, l:linter.name),
\)[1]
let l:command = ale#job#PrepareCommand(l:buffer, l:command)
let l:ready = ale#lsp#StartProgram(l:conn_id, a:executable, l:command)

View File

@ -34,12 +34,11 @@ function! ale#test#RestoreDirectory() abort
unlet! g:dir
endfunction
" Change the filename for the current buffer using a relative path to
" the script without running autocmd commands.
" Get a filename for the current buffer using a relative path to the script.
"
" If a g:dir variable is set, it will be used as the path to the directory
" containing the test file.
function! ale#test#SetFilename(path) abort
function! ale#test#GetFilename(path) abort
let l:dir = get(g:, 'dir', '')
if empty(l:dir)
@ -50,7 +49,17 @@ function! ale#test#SetFilename(path) abort
\ ? a:path
\ : l:dir . '/' . a:path
silent! noautocmd execute 'file ' . fnameescape(ale#path#Simplify(l:full_path))
return ale#path#Simplify(l:full_path)
endfunction
" Change the filename for the current buffer using a relative path to
" the script without running autocmd commands.
"
" If a g:dir variable is set, it will be used as the path to the directory
" containing the test file.
function! ale#test#SetFilename(path) abort
let l:full_path = ale#test#GetFilename(a:path)
silent! noautocmd execute 'file ' . fnameescape(l:full_path)
endfunction
function! s:RemoveModule(results) abort

View File

@ -2,13 +2,41 @@
ALE HTML Integration *ale-html-options*
===============================================================================
angular *ale-html-angular*
ALE supports language server features for Angular. You can install it via `npm`: >
$ npm install --save-dev @angular/language-server
<
Angular 11 and up are supported.
g:ale_html_angular_executable *g:ale_html_angular_executable*
*b:ale_html_angular_executable*
Type: |String|
Default: `'ngserver'`
See |ale-integrations-local-executables|
g:ale_html_angular_use_global *g:ale_html_angular_use_global*
*b:ale_html_angular_use_global*
Type: |String|
Default: `get(g:, 'ale_use_global_executables', 0)`
See |ale-integrations-local-executables|
===============================================================================
fecs *ale-html-fecs*
`fecs` options for HTMl is the same as the options for JavaScript,
and both of them reads `./.fecsrc` as the default configuration file.
`fecs` options for HTML are the same as the options for JavaScript, and both
of them read `./.fecsrc` as the default configuration file.
See: |ale-javascript-fecs|.
===============================================================================
html-beautify *ale-html-beautify*
@ -47,6 +75,40 @@ g:ale_html_htmlhint_use_global *g:ale_html_htmlhint_use_global*
See |ale-integrations-local-executables|
===============================================================================
prettier *ale-html-prettier*
See |ale-javascript-prettier| for information about the available options.
===============================================================================
stylelint *ale-html-stylelint*
g:ale_html_stylelint_executable *g:ale_html_stylelint_executable*
*b:ale_html_stylelint_executable*
Type: |String|
Default: `'stylelint'`
See |ale-integrations-local-executables|
g:ale_html_stylelint_options *g:ale_html_stylelint_options*
*b:ale_html_stylelint_options*
Type: |String|
Default: `''`
This variable can be set to pass additional options to stylelint.
g:ale_html_stylelint_use_global *g:ale_html_stylelint_use_global*
*b:ale_html_stylelint_use_global*
Type: |String|
Default: `0`
See |ale-integrations-local-executables|
===============================================================================
tidy *ale-html-tidy*
@ -97,39 +159,6 @@ g:ale_html_tidy_use_global *g:html_tidy_use_global*
See |ale-integrations-local-executables|
===============================================================================
prettier *ale-html-prettier*
See |ale-javascript-prettier| for information about the available options.
===============================================================================
stylelint *ale-html-stylelint*
g:ale_html_stylelint_executable *g:ale_html_stylelint_executable*
*b:ale_html_stylelint_executable*
Type: |String|
Default: `'stylelint'`
See |ale-integrations-local-executables|
g:ale_html_stylelint_options *g:ale_html_stylelint_options*
*b:ale_html_stylelint_options*
Type: |String|
Default: `''`
This variable can be set to pass additional options to stylelint.
g:ale_html_stylelint_use_global *g:ale_html_stylelint_use_global*
*b:ale_html_stylelint_use_global*
Type: |String|
Default: `0`
See |ale-integrations-local-executables|
===============================================================================
write-good *ale-html-write-good*

View File

@ -216,10 +216,11 @@ Notes:
* HCL
* `terraform-fmt`
* HTML
* `HTMLHint`
* `alex`!!
* `angular`
* `fecs`
* `html-beautify`
* `htmlhint`
* `prettier`
* `proselint`
* `tidy`

View File

@ -2777,12 +2777,13 @@ documented in additional help files.
hcl.....................................|ale-hcl-options|
terraform-fmt.........................|ale-hcl-terraform-fmt|
html....................................|ale-html-options|
angular...............................|ale-html-angular|
fecs..................................|ale-html-fecs|
html-beautify.........................|ale-html-beautify|
htmlhint..............................|ale-html-htmlhint|
tidy..................................|ale-html-tidy|
prettier..............................|ale-html-prettier|
stylelint.............................|ale-html-stylelint|
tidy..................................|ale-html-tidy|
write-good............................|ale-html-write-good|
idris...................................|ale-idris-options|
idris.................................|ale-idris-idris|

View File

@ -225,10 +225,11 @@ formatting.
* HCL
* [terraform-fmt](https://github.com/hashicorp/terraform)
* HTML
* [HTMLHint](http://htmlhint.com/)
* [alex](https://github.com/wooorm/alex) :floppy_disk:
* [angular](https://www.npmjs.com/package/@angular/language-server)
* [fecs](http://fecs.baidu.com/)
* [html-beautify](https://beautifier.io/)
* [htmlhint](http://htmlhint.com/)
* [prettier](https://github.com/prettier/prettier)
* [proselint](http://proselint.com/)
* [tidy](http://www.html-tidy.org/)

View File

@ -0,0 +1,44 @@
Before:
call ale#assert#SetUpLinterTest('html', 'angular')
let g:paths = {}
After:
call ale#assert#TearDownLinterTest()
unlet g:paths
Execute(The Angular LSP connection shouldn't be created outside of Angular projects):
AssertLSPLanguage 'html'
AssertLSPConfig {}
AssertLSPProject ''
AssertLinterNotExecuted
Execute(The default command for Angular should be correct):
call ale#test#SetFilename('../test-projects/angular/test.html')
let g:paths = {
\ 'ngserver': ale#test#GetFilename('../test-projects/angular/node_modules/@angular/language-server/bin/ngserver'),
\ 'service': ale#test#GetFilename('../test-projects/angular/node_modules/@angular/language-service'),
\ 'typescript': ale#test#GetFilename('../test-projects/angular/node_modules/typescript'),
\}
AssertLSPLanguage 'html'
AssertLSPProject ale#test#GetFilename('../test-projects/angular')
AssertLinter g:paths.ngserver, ale#Escape(g:paths.ngserver)
\ . ' --ngProbeLocations ' . ale#Escape(g:paths.service)
\ . ' --tsProbeLocations ' . ale#Escape(g:paths.typescript)
\ . ' --stdio'
Execute(It should be possible to use the global ngserver):
let b:ale_html_angular_use_global = 1
call ale#test#SetFilename('../test-projects/angular/test.html')
let g:paths = {
\ 'service': ale#test#GetFilename('../test-projects/angular/node_modules/@angular/language-service'),
\ 'typescript': ale#test#GetFilename('../test-projects/angular/node_modules/typescript'),
\}
AssertLSPLanguage 'html'
AssertLSPProject ale#test#GetFilename('../test-projects/angular')
AssertLinter 'ngserver', ale#Escape('ngserver')
\ . ' --ngProbeLocations ' . ale#Escape(g:paths.service)
\ . ' --tsProbeLocations ' . ale#Escape(g:paths.typescript)
\ . ' --stdio'

View File

@ -343,6 +343,29 @@ Execute(Other files should be jumped to for LSP definition responses):
AssertEqual [3, 8], getpos('.')[1:2]
AssertEqual {}, ale#definition#GetMap()
Execute(Newer LocationLink items should be supported):
call ale#definition#SetMap({3: {'open_in': 'current-buffer'}})
call ale#definition#HandleLSPResponse(
\ 1,
\ {
\ 'id': 3,
\ 'result': {
\ 'targetUri': ale#path#ToURI(ale#path#Simplify(g:dir . '/completion_dummy_file')),
\ 'targetRange': {
\ 'start': {'line': 2, 'character': 7},
\ },
\ },
\ }
\)
AssertEqual
\ [
\ 'edit +3 ' . fnameescape(ale#path#Simplify(g:dir . '/completion_dummy_file')),
\ ],
\ g:expr_list
AssertEqual [3, 8], getpos('.')[1:2]
AssertEqual {}, ale#definition#GetMap()
Execute(Locations inside the same file should be jumped to without using :edit):
call ale#definition#SetMap({3: {'open_in': 'current-buffer'}})
call ale#definition#HandleLSPResponse(